diff --git a/examples/nodejs_client/file-client.js b/examples/nodejs_client/file-client.js index 529c3917..1bb59098 100644 --- a/examples/nodejs_client/file-client.js +++ b/examples/nodejs_client/file-client.js @@ -34,14 +34,14 @@ async function main() { // Establish HotPocket connection. if (!await hpc.connect()) { console.log('Connection failed.'); - exit; + exit(); } console.log('HotPocket Connected.'); // This will get fired if HP server disconnects unexpectedly. hpc.on(HotPocketEvents.disconnect, () => { console.log('Server diconnected'); - exit; + exit(); }) // This will get fired when contract sends an output. diff --git a/examples/nodejs_client/text-client.js b/examples/nodejs_client/text-client.js index 40e5a5cb..1fc36ef2 100644 --- a/examples/nodejs_client/text-client.js +++ b/examples/nodejs_client/text-client.js @@ -32,14 +32,14 @@ async function main() { // Establish HotPocket connection. if (!await hpc.connect()) { console.log('Connection failed.'); - exit; + exit(); } console.log('HotPocket Connected.'); // This will get fired if HP server disconnects unexpectedly. hpc.on(HotPocketEvents.disconnect, () => { - console.log('Server diconnected'); - exit; + console.log('Server disconnected'); + exit(); }) // This will get fired when contract sends an output. diff --git a/src/bill/corebill.h b/src/bill/corebill.h index 07f37a2c..8ac0bec3 100644 --- a/src/bill/corebill.h +++ b/src/bill/corebill.h @@ -11,8 +11,8 @@ namespace corebill */ struct violation_stat { - uint32_t counter; - uint64_t timestamp; + uint32_t counter = 0; + uint64_t timestamp = 0; }; void report_violation(const std::string host); diff --git a/src/comm/comm_server.cpp b/src/comm/comm_server.cpp index 11defd48..7b8a1717 100644 --- a/src/comm/comm_server.cpp +++ b/src/comm/comm_server.cpp @@ -79,22 +79,15 @@ namespace comm // Counter to track when to initiate outbound client connections. int16_t loop_counter = -1; + // Indicates whether at least some bytes were read from any of the clients during the previous iteration. + // If no bytes were read, we would force thread sleep to wait for bytes to arrive. + bool bytes_read = false; + while (true) { if (should_stop_listening) break; - // Prepare poll fd list. - const size_t fd_count = sessions.size() + 1; //+1 for the inclusion of accept_fd - pollfd pollfds[fd_count]; - if (poll_fds(pollfds, accept_fd, sessions) == -1) - { - util::sleep(10); - continue; - } - - util::sleep(10); - // Accept any new incoming connection if available. check_for_new_connection(sessions, accept_fd, session_type, is_binary, metric_thresholds); @@ -109,9 +102,27 @@ namespace comm loop_counter++; } - const size_t sessions_count = sessions.size(); + // Prepare poll fd list. + const size_t fd_count = sessions.size() + 1; //+1 for the inclusion of accept_fd + + pollfd pollfds[fd_count]; + memset(pollfds, 0, sizeof(pollfd) * fd_count); + + if (poll_fds(pollfds, accept_fd, sessions) == -1) + { + util::sleep(10); + continue; + } + + if (!bytes_read) + util::sleep(10); + bytes_read = false; + + // Loop through all session fds and read any data. + const size_t sessions_count = sessions.size(); + if (sessions_count == 0) + continue; - // Loop through all fds and read any data. for (size_t i = 1; i <= sessions_count; i++) { const short result = pollfds[i].revents; @@ -126,7 +137,18 @@ namespace comm if (!should_disconnect) { if (result & POLLIN) - should_disconnect = (session.attempt_read(max_msg_size) == -1); + { + const int read_result = session.attempt_read(max_msg_size); + + // read_result -1 means error and we should disconnect the client. + // read_result 0 means no bytes were read. + // read_result 1 means some bytes were read. + // read_result 2 means full message were read and processed successfully. + if (read_result > 0) + bytes_read = true; + else if (read_result == -1) + should_disconnect = true; + } if (result & (POLLERR | POLLHUP | POLLRDHUP | POLLNVAL)) should_disconnect = true; diff --git a/src/comm/comm_session.cpp b/src/comm/comm_session.cpp index a6ceb2e1..33c79396 100644 --- a/src/comm/comm_session.cpp +++ b/src/comm/comm_session.cpp @@ -12,6 +12,7 @@ namespace comm constexpr uint32_t INTERVALMS = 60000; constexpr uint8_t SIZE_HEADER_LEN = 8; + constexpr uint32_t READ_BUFFER_IDLE_SIZE = 64 * 1024; // Global instances of user and peer session handlers. usr::user_session_handler user_sess_handler; @@ -48,8 +49,10 @@ namespace comm /** * Attempts to read message data from the given socket fd and passes the message on to the session. - * @param should_disconnect Whether the client fd must be disconnected. * @param max_msg_size The allowed max byte length of a message to be read. + * @return -1 on error and client must be disconnected. 0 if no message data bytes were read. 1 if some + * bytes were read but a full message is not yet formed. 2 if a fully formed message has been + * read into the read buffer. */ int comm_session::attempt_read(const uint64_t max_msg_size) { @@ -59,33 +62,39 @@ namespace comm available_bytes > (max_msg_size + (is_binary ? SIZE_HEADER_LEN : 0)))) return -1; + int res = 0; + // Try to read a complete message using available bytes. - // If complete message is not available silently return. if (available_bytes > 0) { - const uint32_t read_len = is_binary ? get_binary_msg_read_len(available_bytes) : available_bytes; - - if (read_len == -1) + if (is_binary) { - return -1; + res = get_binary_msg_read_len(available_bytes); } - else if (read_len > 0) + else { - if (!is_binary) + read_buffer.resize(available_bytes); + res = read(read_fd, read_buffer.data(), available_bytes) < available_bytes ? -1 : 2; + } + + if (res == 2) // Full message has been read into read buffer. + { + if (on_message(std::string_view(read_buffer.data(), read_buffer.size())) == -1) + res = -1; + + // Reset the read buffer. + if (read_buffer.size() > READ_BUFFER_IDLE_SIZE) { - read_buffer.resize(read_len); - if (read(read_fd, read_buffer.data(), read_len) < read_len) - return -1; + read_buffer.resize(READ_BUFFER_IDLE_SIZE); + read_buffer.shrink_to_fit(); // This is to avaoid large idle memory allocations. } - int res = on_message(std::string_view(read_buffer.data(), read_len)); - read_buffer.clear(); // Clear the buffer after read operation. + read_buffer.clear(); read_buffer_filled_size = 0; - return res; } } - return 0; + return res; } int comm_session::on_message(std::string_view message) @@ -168,9 +177,11 @@ namespace comm /** * Retrieves the length of the binary message pending to be read. Only relevant for Binary mode. * @param available_bytes Count of bytes that is available to read from the client socket. - * @return Length of the message if the complete message available to be read. 0 if reading must be skipped. -1 if client must be disconnected. + * @return -1 on error and client must be disconnected. 0 if no message data bytes were read. 1 if some + * bytes were read but a full message is not yet formed. 2 if a fully formed message has been + * read into the read buffer. */ - uint32_t comm_session::get_binary_msg_read_len(const size_t available_bytes) + int comm_session::get_binary_msg_read_len(const size_t available_bytes) { // If we have previously encountered a size header and we are waiting until all message // bytes are received, we must have the expected message size > 0. @@ -210,7 +221,8 @@ namespace comm const size_t read_len = expected_msg_size; expected_msg_size = 0; // reset the expected msg size. - return read_len; + + return 2; // Full message has been read. } else { @@ -218,11 +230,12 @@ namespace comm if (read(read_fd, read_buffer.data() + read_buffer_filled_size, data_bytes) == -1) return -1; // Indicates that we should disconnect the client. read_buffer_filled_size += data_bytes; + + return 1; // Some bytes were read, but full message is not yet formed. } } - // Skip reading - return 0; + return 0; // No message data bytes was read. } /** diff --git a/src/comm/comm_session.hpp b/src/comm/comm_session.hpp index e8e17621..16e0ff2c 100644 --- a/src/comm/comm_session.hpp +++ b/src/comm/comm_session.hpp @@ -39,7 +39,7 @@ class comm_session std::vector read_buffer; // Local buffer to keep collecting data until a complete message can be constructed. uint32_t read_buffer_filled_size = 0; // How many bytes have been buffered so far. - uint32_t get_binary_msg_read_len(const size_t available_bytes); + int get_binary_msg_read_len(const size_t available_bytes); int on_message(std::string_view message); public: diff --git a/src/comm/comm_session_threshold.hpp b/src/comm/comm_session_threshold.hpp index fb2af16c..73ba77bb 100644 --- a/src/comm/comm_session_threshold.hpp +++ b/src/comm/comm_session_threshold.hpp @@ -33,10 +33,10 @@ enum SESSION_THRESHOLDS */ struct session_threshold { - uint64_t threshold_limit; - uint32_t intervalms; - uint64_t counter_value; - uint64_t timestamp; + uint64_t threshold_limit = 0; + uint32_t intervalms = 0; + uint64_t counter_value = 0; + uint64_t timestamp = 0; session_threshold(const uint64_t threshold_limit, const uint32_t intervalms) : threshold_limit(threshold_limit), intervalms(intervalms) diff --git a/src/conf.hpp b/src/conf.hpp index d49ea0cd..43e26128 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -10,114 +10,114 @@ namespace conf { -constexpr const char *SELF_HOST = "127.0.0.1"; + constexpr const char *SELF_HOST = "127.0.0.1"; -// Typedef to represent ip address and port pair. -typedef std::pair ip_port_pair; + // Typedef to represent ip address and port pair. + typedef std::pair ip_port_pair; -// The operating mode of the contract node. -enum OPERATING_MODE -{ - OBSERVER = 0, // Observer mode. Only emits NUPs. Does not participate in voting. - PROPOSER = 1 // Consensus participant mode. -}; + // The operating mode of the contract node. + enum OPERATING_MODE + { + OBSERVER = 0, // Observer mode. Only emits NUPs. Does not participate in voting. + PROPOSER = 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 exe_dir; // Hot Pocket executable dir. - std::string websocketd_exe_path; // Websocketd executable file path. - std::string websocat_exe_path; // Websocketd executable file path. - std::string hpfs_exe_path; // hpfs executable file path. + // Holds contextual information about the currently loaded contract. + struct contract_ctx + { + std::string command; // The CLI command issued to launch HotPocket + std::string exe_dir; // Hot Pocket executable dir. + std::string websocketd_exe_path; // Websocketd executable file path. + std::string websocat_exe_path; // Websocketd executable file path. + std::string hpfs_exe_path; // hpfs executable file path. - std::string contract_dir; // Contract base directory full path - std::string hist_dir; // Contract ledger history dir full path - std::string state_dir; // Contract state maintenence path (hpfs path) - std::string state_rw_dir; // Contract executation read/write state path. - std::string state_read_req_dir; // Contract executation state path for read requests. - 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 tls_key_file; // Full path to the tls secret key file - std::string tls_cert_file; // Full path to the tls certificate -}; + std::string contract_dir; // Contract base directory full path + std::string hist_dir; // Contract ledger history dir full path + std::string state_dir; // Contract state maintenence path (hpfs path) + std::string state_rw_dir; // Contract executation read/write state path. + std::string state_read_req_dir; // Contract executation state path for read requests. + 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 tls_key_file; // Full path to the tls secret key file + std::string tls_cert_file; // Full path to the tls certificate + }; -// Holds all the contract config values. -struct contract_config -{ - // Config elements which are initialized in memory (these are not directly loaded from the config file) + // Holds all the contract config values. + 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; // 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. + OPERATING_MODE current_mode = OPERATING_MODE::OBSERVER; // Current operating mode of the contract (Observer/Proposer) - // Config elements which are loaded from the config file. + // Config elements which are loaded from the config file. - OPERATING_MODE startup_mode; // 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::set peers; // Set 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 - - 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 + 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::set peers; // Set of peers keyed by ":" concatenated format + std::unordered_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 - std::string loglevel; // Log severity level (debug, info, warn, error) - std::unordered_set loggers; // List of enabled loggers (console, file) -}; + uint64_t pubmaxsize = 0; // User message max size in bytes + uint64_t pubmaxcpm = 0; // User message rate (characters(bytes) per minute) + uint64_t pubmaxbadmpm = 0; // User bad messages per minute + uint16_t pubmaxcons = 0; // Max inbound user connections -// Global contract context struct exposed to the application. -// Other modeuls will access context values via this. -extern contract_ctx ctx; + uint64_t peermaxsize = 0; // Peer message max size in bytes + uint64_t peermaxcpm = 0; // Peer message rate (characters(bytes) per minute) + uint64_t peermaxdupmpm = 0; // Peer max duplicate messages per minute + uint64_t peermaxbadmpm = 0; // Peer bad messages per minute + uint64_t peermaxbadsigpm = 0; // Peer bad signatures per minute + uint16_t peermaxcons = 0; // Max inbound peer connections -// Global configuration struct exposed to the application. -// Other modeuls will access config values via this. -extern contract_config cfg; + std::string loglevel; // Log severity level (debug, info, warn, error) + std::unordered_set loggers; // List of enabled loggers (console, file) + }; -int init(); + // Global contract context struct exposed to the application. + // Other modeuls will access context values via this. + extern contract_ctx ctx; -int rekey(); + // Global configuration struct exposed to the application. + // Other modeuls will access config values via this. + extern contract_config cfg; -int create_contract(); + int init(); -void set_contract_dir_paths(std::string exepath, std::string basedir); + int rekey(); -//------Internal-use functions for this namespace. + int create_contract(); -int load_config(); + void set_contract_dir_paths(std::string exepath, std::string basedir); -int save_config(); + //------Internal-use functions for this namespace. -int validate_config(); + int load_config(); -int validate_contract_dir_paths(); + int save_config(); -int is_schema_valid(const rapidjson::Document &d); + int validate_config(); -int binpair_to_hex(); + int validate_contract_dir_paths(); -int hexpair_to_bin(); + int is_schema_valid(const rapidjson::Document &d); -void change_operating_mode(const OPERATING_MODE mode); + int binpair_to_hex(); + + int hexpair_to_bin(); + + void change_operating_mode(const OPERATING_MODE mode); } // namespace conf diff --git a/src/cons/cons.hpp b/src/cons/cons.hpp index 4294e68a..8aac2272 100644 --- a/src/cons/cons.hpp +++ b/src/cons/cons.hpp @@ -19,7 +19,7 @@ namespace cons struct candidate_user_input { const std::string userpubkey; - const uint64_t maxledgerseqno; + const uint64_t maxledgerseqno = 0; std::string input; candidate_user_input(const std::string userpubkey, const std::string input, const uint64_t maxledgerseqno) diff --git a/src/cons/ledger_handler.hpp b/src/cons/ledger_handler.hpp index 16d4f12e..86ec07d8 100644 --- a/src/cons/ledger_handler.hpp +++ b/src/cons/ledger_handler.hpp @@ -19,7 +19,7 @@ struct ledger_cache_entry struct ledger_history { std::string lcl; - uint64_t led_seq_no; + uint64_t led_seq_no = 0; std::map cache; }; diff --git a/src/hpfs/h32.hpp b/src/hpfs/h32.hpp index 8a75edaf..a9cbab54 100644 --- a/src/hpfs/h32.hpp +++ b/src/hpfs/h32.hpp @@ -18,6 +18,11 @@ namespace hpfs std::string_view to_string_view() const; h32 &operator=(std::string_view sv); bool operator<(const h32 rhs) const; + + h32() + { + memset(data, 0, sizeof(data)); + } }; extern h32 h32_empty; diff --git a/src/hpfs/hpfs.hpp b/src/hpfs/hpfs.hpp index e564134f..f27dca44 100644 --- a/src/hpfs/hpfs.hpp +++ b/src/hpfs/hpfs.hpp @@ -8,9 +8,14 @@ namespace hpfs { struct child_hash_node { - bool is_file; + bool is_file = false; char name[256]; h32 hash; + + child_hash_node() + { + memset(name, 0, sizeof(name)); + } }; int init(); diff --git a/src/p2p/p2p.hpp b/src/p2p/p2p.hpp index bebbd8c0..f43d0bf8 100644 --- a/src/p2p/p2p.hpp +++ b/src/p2p/p2p.hpp @@ -16,9 +16,9 @@ namespace p2p struct proposal { std::string pubkey; - uint64_t timestamp; - uint64_t time; - uint8_t stage; + uint64_t timestamp = 0; + uint64_t time = 0; + uint8_t stage = 0; std::string lcl; hpfs::h32 state; std::set users; @@ -61,7 +61,7 @@ namespace p2p struct history_response { std::map hist_ledgers; - LEDGER_RESPONSE_ERROR error; + LEDGER_RESPONSE_ERROR error = LEDGER_RESPONSE_ERROR::NONE; }; // Represents an NPL message sent by a peer. @@ -76,24 +76,24 @@ namespace p2p struct state_request { std::string parent_path; // The requested file or dir path. - bool is_file; // Whether the path is a file or dir. - int32_t block_id; // Block id of the file if we are requesting for file block. Otherwise -1. + bool is_file = false; // Whether the path is a file or dir. + int32_t block_id = 0; // Block id of the file if we are requesting for file block. Otherwise -1. hpfs::h32 expected_hash; // The expected hash of the requested result. }; // Represents state file system entry. struct state_fs_hash_entry { - std::string name; // Name of the file/dir. - bool is_file; // Whether this is a file or dir. - hpfs::h32 hash; // Hash of the file or dir. + std::string name; // Name of the file/dir. + bool is_file = false; // Whether this is a file or dir. + hpfs::h32 hash; // Hash of the file or dir. }; // Represents a file block data resposne. struct block_response { std::string path; // Path of the file. - uint32_t block_id; // Id of the block where the data belongs to. + uint32_t block_id = 0; // Id of the block where the data belongs to. std::string_view data; // The block data. hpfs::h32 hash; // Hash of the bloc data. }; diff --git a/src/sc.hpp b/src/sc.hpp index 9f538a1c..c3f8a2e4 100644 --- a/src/sc.hpp +++ b/src/sc.hpp @@ -101,10 +101,10 @@ namespace sc std::vector hpscfds; // Holds the contract process id (if currently executing). - pid_t contract_pid; + pid_t contract_pid = 0; // Holds the hpfs rw process id (if currently executing). - pid_t hpfs_pid; + pid_t hpfs_pid = 0; // Thread to collect contract outputs while contract is running. std::thread output_fetcher_thread; diff --git a/src/state/state_sync.hpp b/src/state/state_sync.hpp index d431906a..cc6b6e9a 100644 --- a/src/state/state_sync.hpp +++ b/src/state/state_sync.hpp @@ -19,7 +19,7 @@ namespace state_sync // Represents a queued up state sync operation which needs to be performed. struct backlog_item { - BACKLOG_ITEM_TYPE type; + BACKLOG_ITEM_TYPE type = BACKLOG_ITEM_TYPE::DIR; std::string path; int32_t block_id = -1; // Only relevant if type=BLOCK hpfs::h32 expected_hash; diff --git a/src/usr/usr.hpp b/src/usr/usr.hpp index c1286204..63b96a97 100644 --- a/src/usr/usr.hpp +++ b/src/usr/usr.hpp @@ -34,7 +34,7 @@ namespace usr const comm::comm_session &session; // The messaging protocol used by this user. - const util::PROTOCOL protocol; + const util::PROTOCOL protocol = util::PROTOCOL::JSON; /** * @param session The web socket session the user is connected to. diff --git a/test/bin/hpfs b/test/bin/hpfs index 5aa37e76..90885c21 100755 Binary files a/test/bin/hpfs and b/test/bin/hpfs differ