From f84a02ce09a985c4245190d0f94599e6a3e8ddae Mon Sep 17 00:00:00 2001 From: chalith Date: Tue, 31 Oct 2023 13:35:37 +0530 Subject: [PATCH] Implementation for message handling --- CMakeLists.txt | 4 +- examples/js_client/text-client.js | 3 + examples/nodejs_contract/echo_contract.js | 37 ++++++++++ src/conf.cpp | 32 +++++++++ src/conf.hpp | 7 ++ src/hpsh/hpsh.cpp | 76 +++++++++++++------- src/hpsh/hpsh.hpp | 23 ++---- src/hpsh/util.cpp | 45 ------------ src/hpsh/util.hpp | 30 -------- src/msg/bson/usrmsg_bson.cpp | 51 ++++++++++---- src/msg/bson/usrmsg_bson.hpp | 4 +- src/msg/json/usrmsg_json.cpp | 82 +++++++++++++++++----- src/msg/json/usrmsg_json.hpp | 4 +- src/msg/usrmsg_common.hpp | 4 +- src/msg/usrmsg_parser.cpp | 14 +++- src/msg/usrmsg_parser.hpp | 4 +- src/usr/usr.cpp | 14 ++-- test/bin/hpsh | Bin 34032 -> 33952 bytes 18 files changed, 265 insertions(+), 169 deletions(-) delete mode 100644 src/hpsh/util.cpp delete mode 100644 src/hpsh/util.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d75b111..b2e8590c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,10 +59,10 @@ add_executable(hpcore src/ledger/ledger_sync.cpp src/ledger/ledger_serve.cpp src/ledger/ledger.cpp + src/hpsh/hpsh.cpp src/status.cpp src/consensus.cpp src/main.cpp - src/hpsh/hpsh.cpp ) target_link_libraries(hpcore killswitch @@ -78,7 +78,7 @@ target_link_libraries(hpcore add_custom_command(TARGET hpcore POST_BUILD # COMMAND strip ./build/hpcore - COMMAND rm -f ./build/hpws ./build/hpfs ./build/hpsh && cp ./test/bin/hpws ./test/bin/hpfs ./test/bin/hpsh ./build/ + COMMAND cp ./test/bin/hpws ./test/bin/hpfs ./test/bin/hpsh ./build/ ) target_precompile_headers(hpcore PUBLIC src/pchheader.hpp) diff --git a/examples/js_client/text-client.js b/examples/js_client/text-client.js index 1c3a61d4..d402ac47 100644 --- a/examples/js_client/text-client.js +++ b/examples/js_client/text-client.js @@ -123,6 +123,9 @@ async function main() { else if (inp === "stat") { hpc.getStatus().then(stat => console.log(stat)); } + else if (inp.startsWith("hpsh ")) { + hpc.submitHpshRequest(inp.substr(5)).then(reply => console.log(reply)); + } else { if (inp.startsWith("upload ")) { diff --git a/examples/nodejs_contract/echo_contract.js b/examples/nodejs_contract/echo_contract.js index 7b152b55..8c2be74f 100644 --- a/examples/nodejs_contract/echo_contract.js +++ b/examples/nodejs_contract/echo_contract.js @@ -84,5 +84,42 @@ const echoContract = async (ctx) => { // await ctx.updateConfig(config); } +const fallback = async (ctx) => { + console.log("This is fallback mode"); + + const hpconfig = await ctx.getConfig(); + + for (const u of ctx.unl.list()) { + const gap = Math.abs(u.activeOn - ctx.timestamp); + // If last active timestamp is before the twice of roundtime, This node must be active. + if (u.activeOn && gap > (hpconfig.consensus.roundtime * 4)) { + console.log("Updating patch config", u); + hpconfig.unl = hpconfig.unl.filter(e => e !== u.publicKey); + await ctx.updateConfig(hpconfig); + } + } + + // NPL messages example. + // Start listening to incoming NPL messages before we send ours. + const promise = new Promise((resolve, reject) => { + let timeout = setTimeout(() => { + reject('NPL timeout.'); + }, 4000); + + let list = []; + ctx.unl.onMessage((node, msg) => { + console.log(`${node.publicKey} said ${msg} to me.`); + list.push(msg); + if (list.length == ctx.unl.list().length) { + clearTimeout(timeout); + resolve(); + } + }); + }); + + await ctx.unl.send("Hello"); + await promise; +} + const hpc = new HotPocket.Contract(); hpc.init(echoContract); \ No newline at end of file diff --git a/src/conf.cpp b/src/conf.cpp index 68d1dd01..227b62d8 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -194,6 +194,8 @@ namespace conf cfg.log.loggers.emplace("console"); cfg.log.loggers.emplace("file"); + cfg.hpsh.enabled = false; + // Save the default settings into the config file. if (write_config(cfg) != 0) return -1; @@ -512,6 +514,28 @@ namespace conf } } + // hpsh + { + jpath = "hpsh"; + + try + { + const jsoncons::ojson &hpsh = d["hpsh"]; + cfg.hpsh.enabled = hpsh["enabled"].as(); + + if (cfg.hpsh.run_as.from_string(hpsh["run_as"].as()) == -1) + { + std::cerr << "Invalid format for hpsh run as config (\"uid>0:gid>0\" expected).\n"; + return -1; + } + } + catch (const std::exception &e) + { + print_missing_field_error(jpath, e); + return -1; + } + } + return 0; } @@ -624,6 +648,14 @@ namespace conf d.insert_or_assign("log", log_config); } + // hpsh configs + { + jsoncons::ojson hpsh_config; + hpsh_config.insert_or_assign("enabled", cfg.hpsh.enabled); + hpsh_config.insert_or_assign("run_as", cfg.hpsh.run_as.to_string()); + d.insert_or_assign("hpsh", hpsh_config); + } + return write_json_file(ctx.config_file, d); } diff --git a/src/conf.hpp b/src/conf.hpp index a29a6294..b91764f4 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -208,6 +208,12 @@ namespace conf std::vector runtime_env_args; // Contract environment variables. }; + struct hpsh_config + { + bool enabled = false; // Whether or not to enable hpsh. + ugid run_as; // The user/groups id to execute the hpsh as. + }; + struct user_config { uint16_t port = 0; // Listening port for public user connections @@ -317,6 +323,7 @@ namespace conf hpfs_config hpfs; log_config log; health_config health; // For debugging only. Not included in the config file. + hpsh_config hpsh; }; // Global contract context struct exposed to the application. diff --git a/src/hpsh/hpsh.cpp b/src/hpsh/hpsh.cpp index e6994e59..50ac92e3 100644 --- a/src/hpsh/hpsh.cpp +++ b/src/hpsh/hpsh.cpp @@ -2,27 +2,27 @@ namespace hpsh { - constexpr const char *HPSH_EXEC_PATH = "./executable/hpsh"; - constexpr int MAX_BUFFER_LEN = 1024; - constexpr int POLL_TIMEOUT = 1000; + constexpr uint32_t POLL_TIMEOUT = 1000; constexpr uint32_t READ_BUFFER_SIZE = 128 * 1024; hpsh_context ctx; int init() { - std::cout << "Initializing hpsh process.." << std::endl; + // Do not initialize if disabled in config. + if (!conf::cfg.hpsh.enabled) + return 0; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, ctx.control_fds) == -1) { - std::cerr << errno << "Error initializing socket pair." << std::endl; + LOG_ERROR << errno << ": Error initializing socket pair."; return -1; } ctx.hpsh_pid = fork(); if (ctx.hpsh_pid == -1) { - std::cerr << errno << "Error forking." << std::endl; + LOG_ERROR << errno << ": Error forking."; close(ctx.control_fds[0]); close(ctx.control_fds[1]); return -1; @@ -39,27 +39,41 @@ namespace hpsh close(ctx.control_fds[1]); - std::cout << "Starting hpsh process... " << std::endl; - std::string fd_str; fd_str.resize(10); snprintf(fd_str.data(), sizeof(fd_str), "%d", ctx.control_fds[0]); + char *argv[] = {(char *)conf::ctx.hpsh_exe_path.data(), fd_str.data(), NULL}; + + // Just before we execv the hpsh binary, we set user execution user/group if specified in hp config. + // (Must set gid before setting uid) + if (!conf::cfg.hpsh.run_as.empty() && (setgid(conf::cfg.hpsh.run_as.gid) == -1 || setuid(conf::cfg.hpsh.run_as.uid) == -1)) + { + std::cerr << errno << ": Hpsh process setgid/uid failed." + << "\n"; + exit(1); + } - char *argv[] = {(char *)HPSH_EXEC_PATH, (char *)("-s1"), fd_str.data(), NULL}; execv(argv[0], argv); - std::cerr << errno << "Error executing hpfs." << std::endl; + std::cerr << errno << ": Error executing hpsh." + << "\n"; close(ctx.control_fds[0]); - exit(EXIT_FAILURE); + exit(1); } + ctx.is_initialized = true; + + LOG_INFO << "Hpsh handler started."; + return 0; } void deinit() { - std::cout << "De-initializing hpsh process.." << std::endl; + // This is not initialized if disabled in config. + if (!conf::cfg.hpsh.enabled) + return; ctx.is_shutting_down = true; @@ -89,7 +103,7 @@ namespace hpsh } } - std::cout << "Stopped hpsh process.." << std::endl; + LOG_INFO << "Hpsh handler stopped."; } int check_hpsh_exited(const bool block) @@ -103,7 +117,7 @@ namespace hpsh } if (wait_res == -1) { - std::cerr << errno << ": hpsh process waitpid error. pid:" << ctx.hpsh_pid << std::endl; + LOG_ERROR << errno << ": Hpsh process waitpid error. pid:" << ctx.hpsh_pid; ctx.hpsh_pid = 0; return -1; } @@ -113,18 +127,18 @@ namespace hpsh if (WIFEXITED(scstatus)) { - std::cerr << "Contract process ended normally." << std::endl; + LOG_DEBUG << "Hpsh process ended normally."; return 1; } else { - std::cerr << "Contract process ended prematurely. Exit code " << WEXITSTATUS(scstatus) << std::endl; + LOG_WARNING << "Hpsh process ended prematurely. Exit code " << WEXITSTATUS(scstatus); return -1; } } } - int execute(std::string_view pubkey, std::string_view message) + int execute(std::string_view id, std::string_view pubkey, std::string_view message) { if (ctx.is_shutting_down) return -1; @@ -132,7 +146,7 @@ namespace hpsh int child_fds[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, child_fds) == -1) { - std::cerr << errno << "Error initializing socket pair." << std::endl; + LOG_ERROR << errno << ": Error initializing socket pair."; return -1; } @@ -161,7 +175,7 @@ namespace hpsh if (sendmsg(ctx.control_fds[1], &msg, 0) < 0) { - std::cerr << errno << "Error writing to control fd." << std::endl; + LOG_ERROR << errno << ": Error writing to control fd."; close(child_fds[0]); close(child_fds[1]); return -1; @@ -169,7 +183,7 @@ namespace hpsh if (write(child_fds[1], message.data(), sizeof(message)) < 0) { - std::cerr << errno << "Error writing to child fd." << std::endl; + LOG_ERROR << errno << ": Error writing to child fd."; close(child_fds[0]); close(child_fds[1]); return -1; @@ -177,7 +191,7 @@ namespace hpsh { std::scoped_lock lock(ctx.command_mutex); - ctx.commands.push_back(command_context{std::string(pubkey), {child_fds[0], child_fds[1]}, std::string(), false}); + ctx.commands.push_back(command_context{std::string(id), std::string(pubkey), {child_fds[0], child_fds[1]}, std::string(), false}); } return 0; @@ -203,7 +217,7 @@ namespace hpsh if (poll(&pfd, 1, POLL_TIMEOUT) == -1) { - std::cerr << errno << "Error in poll" << std::endl; + LOG_ERROR << errno << ": Error in poll"; continue; } else if (pfd.revents & POLLIN) @@ -219,7 +233,7 @@ namespace hpsh if (errno == EPIPE || errno == ECONNRESET) itr->read_completed = true; else - std::cerr << errno << "Error reading from fd" << std::endl; + LOG_ERROR << errno << ": Error reading from fd"; } } else @@ -230,10 +244,20 @@ namespace hpsh // Send command back to user; if (itr->read_completed) { - std::cout << "Received full output for user " << itr->pubkey << std::endl; - std::cout << itr->response.data() << std::endl; + { + std::scoped_lock lock(usr::ctx.users_mutex); - std::list collected_commands; + // Find the user session by user pubkey. + const auto user_itr = usr::ctx.users.find(itr->pubkey); + if (user_itr != usr::ctx.users.end()) // match found + { + const usr::connected_user &user = user_itr->second; + msg::usrmsg::usrmsg_parser parser(user.protocol); + std::vector msg; + parser.create_hpsh_response_container(msg, itr->id, itr->response); + user.session.send(msg); + } + } { std::scoped_lock lock(ctx.command_mutex); itr = ctx.commands.erase(itr); diff --git a/src/hpsh/hpsh.hpp b/src/hpsh/hpsh.hpp index db41dfd3..7f902cf2 100644 --- a/src/hpsh/hpsh.hpp +++ b/src/hpsh/hpsh.hpp @@ -1,27 +1,15 @@ #ifndef _HP_HPSH_ #define _HP_HPSH_ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "util.hpp" - +#include "../pchheader.hpp" +#include "../conf.hpp" +#include "../usr/usr.hpp" namespace hpsh { struct command_context { + std::string id; std::string pubkey; int child_fds[2]; std::string response; @@ -36,6 +24,7 @@ namespace hpsh int hpsh_pid; std::thread watcher_thread; bool is_shutting_down; + bool is_initialized = false; }; extern hpsh_context ctx; @@ -46,7 +35,7 @@ namespace hpsh int check_hpsh_exited(const bool block); - int execute(std::string_view pubkey, std::string_view message); + int execute(std::string_view id, std::string_view pubkey, std::string_view message); void response_watcher(); } diff --git a/src/hpsh/util.cpp b/src/hpsh/util.cpp deleted file mode 100644 index 2e9d97a0..00000000 --- a/src/hpsh/util.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "util.hpp" - -namespace util -{ - constexpr mode_t DIR_PERMS = 0755; - - /** - * Sleeps the current thread for specified no. of milliseconds. - */ - void sleep(const uint64_t milliseconds) - { - std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); - } - - // Applies signal mask to the calling thread. - void mask_signal() - { - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGPIPE); - pthread_sigmask(SIG_BLOCK, &mask, NULL); - } - - /** - * Clears signal mask and signal handlers from the caller. - * Called by other processes forked from hpcore threads so they get detatched from - * the hpcore signal setup. - */ - void fork_detach() - { - // Restore signal handlers to defaults. - signal(SIGINT, SIG_DFL); - signal(SIGSEGV, SIG_DFL); - signal(SIGABRT, SIG_DFL); - - // Remove any signal masks applied by hpcore. - sigset_t mask; - sigemptyset(&mask); - pthread_sigmask(SIG_SETMASK, &mask, NULL); - - // Set process group id (so the terminal doesn't send kill signals to forked children). - setpgrp(); - } -} // namespace util diff --git a/src/hpsh/util.hpp b/src/hpsh/util.hpp deleted file mode 100644 index beb9833f..00000000 --- a/src/hpsh/util.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _HP_UTIL_UTIL_ -#define _HP_UTIL_UTIL_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * Contains helper functions and data structures used by multiple other subsystems. - */ - -namespace util -{ - void sleep(const uint64_t milliseconds); - - void mask_signal(); - - void fork_detach(); - -} // namespace util - -#endif diff --git a/src/msg/bson/usrmsg_bson.cpp b/src/msg/bson/usrmsg_bson.cpp index 7f8eb79c..2505e734 100644 --- a/src/msg/bson/usrmsg_bson.cpp +++ b/src/msg/bson/usrmsg_bson.cpp @@ -161,6 +161,31 @@ namespace msg::usrmsg::bson encoder.flush(); } + /** + * Constructs a hpsh response message. + * @param msg Buffer to construct the generated bson message into. + * Message format: + * { + * "type": "hpsh_response", + * "reply_for": "", + * "content": + * } + * @param content The contract binary output content to be put in the message. + */ + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) + { + jsoncons::bson::bson_bytes_encoder encoder(msg); + encoder.begin_object(); + encoder.key(msg::usrmsg::FLD_TYPE); + encoder.string_value(msg::usrmsg::MSGTYPE_HPSH_RESPONSE); + encoder.key(msg::usrmsg::FLD_REPLY_FOR); + encoder.string_value(reply_for); + encoder.key(msg::usrmsg::FLD_CONTENT); + encoder.byte_string_value(content); + encoder.end_object(); + encoder.flush(); + } + /** * Constructs a contract read response message. * @param msg Buffer to construct the generated bson message into. @@ -196,7 +221,7 @@ namespace msg::usrmsg::bson * "ledger_hash": , * "outputs": [, , ...], // The output order is the hash generation order. * "output_hash": , [output hash = hash(pubkey+all outputs for the user)] - * "hash_tree": [], // Collapsed merkle tree with user's hash element marked as null. + * "hash_tree": [], // Collapsed merkle tree with user's hash element marked as null. * "unl_sig": [["", ""], ...] // Binary UNL pubkeys and signatures of root hash. * } * @param hash This user's combined output hash. [output hash = hash(pubkey+all outputs for the user)] @@ -466,7 +491,7 @@ namespace msg::usrmsg::bson /** * Extracts a contract read request message sent by user. - * + * * @param extracted_content The content to be passed to the contract, extracted from the message. * @param d The bson document holding the read request message. * Accepted signed input container format: @@ -497,30 +522,30 @@ namespace msg::usrmsg::bson return 0; } - /** - * Extracts a contract shell input message sent by user. - * - * @param extracted_content The content to be passed to the contract, extracted from the message. - * @param d The bson document holding the shell input message. + /** + * Extracts a hpsh input message sent by user. + * + * @param extracted_content The content to be passed to the hpsh, extracted from the message. + * @param d The bson document holding the hpsh input message. * Accepted signed input container format: * { - * "type": "contract_shell_input", + * "type": "hpsh_request", * "id": "", * "content": * } * @return 0 on successful extraction. -1 for failure. */ - int extract_shell_input(std::string &extracted_id, std::string &extracted_content, const jsoncons::ojson &d) + int extract_hpsh_request(std::string &extracted_id, std::string &extracted_content, const jsoncons::ojson &d) { if (!d.contains(msg::usrmsg::FLD_ID) || !d[msg::usrmsg::FLD_ID].is()) { - LOG_DEBUG << "Shell input 'id' field missing or invalid."; + LOG_DEBUG << "Hpsh input 'id' field missing or invalid."; return -1; } if (!d.contains(msg::usrmsg::FLD_CONTENT) || !d[msg::usrmsg::FLD_CONTENT].is_byte_string_view()) { - LOG_DEBUG << "Shell input 'content' field missing or invalid."; + LOG_DEBUG << "Hpsh input 'content' field missing or invalid."; return -1; } @@ -532,9 +557,9 @@ namespace msg::usrmsg::bson /** * Extracts a signed input container message sent by user. - * + * * @param extracted_input_container The input container extracted from the message. - * @param extracted_sig The binary signature extracted from the message. + * @param extracted_sig The binary signature extracted from the message. * @param d The bson document holding the input container. * Accepted signed input container format: * { diff --git a/src/msg/bson/usrmsg_bson.hpp b/src/msg/bson/usrmsg_bson.hpp index 9a511f36..cc06be4f 100644 --- a/src/msg/bson/usrmsg_bson.hpp +++ b/src/msg/bson/usrmsg_bson.hpp @@ -17,6 +17,8 @@ namespace msg::usrmsg::bson void create_contract_input_status(std::vector &msg, std::string_view status, std::string_view reason, std::string_view input_hash, const uint64_t ledger_seq_no, const util::h32 &ledger_hash); + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content); + void create_contract_read_response_container(std::vector &msg, std::string_view reply_for, std::string_view content); void create_contract_output_container(std::vector &msg, std::string_view hash, const ::std::vector &outputs, @@ -43,7 +45,7 @@ namespace msg::usrmsg::bson int extract_read_request(std::string &extracted_id, std::string &extracted_content, const jsoncons::ojson &d); - int extract_shell_input(std::string &extracted_id, std::string &extracted_content, const jsoncons::ojson &d); + int extract_hpsh_request(std::string &extracted_id, std::string &extracted_content, const jsoncons::ojson &d); int extract_signed_input_container(std::string &extracted_input_container, std::string &extracted_sig, const jsoncons::ojson &d); diff --git a/src/msg/json/usrmsg_json.cpp b/src/msg/json/usrmsg_json.cpp index a867e99d..58d271cb 100644 --- a/src/msg/json/usrmsg_json.cpp +++ b/src/msg/json/usrmsg_json.cpp @@ -34,7 +34,7 @@ namespace msg::usrmsg::json * Constructs user challenge message json and the challenge string required for * initial user challenge handshake. This gets called when a user establishes * a web socket connection to HP. - * + * * @param msg Buffer to construct the generated json message string into. * Message format: * { @@ -84,7 +84,7 @@ namespace msg::usrmsg::json /** * Constructs server challenge response message json. This gets sent when we receive * a challenge from the user. - * + * * @param msg Buffer to construct the generated json message string into. * Message format: * { @@ -325,6 +325,52 @@ namespace msg::usrmsg::json msg += "\"}"; } + /** + * Constructs a hpsh response message. + * @param msg Buffer to construct the generated json message string into. + * Message format: + * { + * "type": "hpsh_response", + * "reply_for": "", + * "content": "" + * } + * @param content The contract binary output content to be put in the message. + */ + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) + { + msg.reserve(content.size() + 256); + msg += "{\""; + msg += msg::usrmsg::FLD_TYPE; + msg += SEP_COLON; + msg += msg::usrmsg::MSGTYPE_HPSH_RESPONSE; + msg += SEP_COMMA; + msg += msg::usrmsg::FLD_REPLY_FOR; + msg += SEP_COLON; + msg += reply_for; + msg += SEP_COMMA; + msg += msg::usrmsg::FLD_CONTENT; + msg += SEP_COLON_NOQUOTE; + + if (is_json_string(content)) + { + // Process the final string using jsoncons. + jsoncons::json jstring = content; + jsoncons::json_options options; + options.escape_all_non_ascii(true); + + std::string escaped_content; + jstring.dump(escaped_content); + + msg += escaped_content; + } + else + { + msg += content; + } + + msg += "}"; + } + /** * Constructs a contract read response message. * @param msg Buffer to construct the generated json message string into. @@ -381,7 +427,7 @@ namespace msg::usrmsg::json * "ledger_hash": "", * "outputs": ["", "", ...], // The output order is the hash generation order. * "output_hash": "", [output hash = hash(pubkey+all outputs for the user)] - * "hash_tree": [], // Collapsed merkle tree with user's hash element marked as null. + * "hash_tree": [], // Collapsed merkle tree with user's hash element marked as null. * "unl_sig": [["", ""], ...] // UNL pubkeys and signatures of root hash. * } * @param hash This user's combined output hash. [output hash = hash(pubkey+all outputs for the user)] @@ -566,12 +612,12 @@ namespace msg::usrmsg::json * { * "type": "health_event", * "event": "proposal" | "connectivity", - * + * * // proposal * "comm_latency": {min:0, max:0, avg:0}, * "read_latency": {min:0, max:0, avg:0} * "batch_size": 0 - * + * * // connectivity * "peer_count": 0, * "weakly_connected": true | false @@ -697,7 +743,7 @@ namespace msg::usrmsg::json /** * Verifies the user handshake response with the original challenge issued to the user * and the user public key contained in the response. - * + * * @param extracted_pubkeyhex The hex public key extracted from the response. * @param extracted_protocol The protocol code extracted from the response. * @param extracted_server_challenge Any server challenge issued by user. @@ -842,7 +888,7 @@ namespace msg::usrmsg::json /** * Extracts a contract read request message sent by user. - * + * * @param extracted_content The content to be passed to the contract, extracted from the message. * @param d The json document holding the read request message. * Accepted signed input container format: @@ -872,30 +918,30 @@ namespace msg::usrmsg::json return 0; } - /** - * Extracts a contract shell input message sent by user. - * - * @param extracted_content The content to be passed to the contract, extracted from the message. - * @param d The json document holding the shell input message. + /** + * Extracts a hpsh input message sent by user. + * + * @param extracted_content The content to be passed to the, extracted from the message. + * @param d The json document holding the hpsh input message. * Accepted signed input container format: * { - * "type": "contract_shell_input", + * "type": "hpsh_request", * "id": "", * "content": "" * } * @return 0 on successful extraction. -1 for failure. */ - int extract_shell_input(std::string &extracted_id, std::string &extracted_content, const jsoncons::json &d) + int extract_hpsh_request(std::string &extracted_id, std::string &extracted_content, const jsoncons::json &d) { if (!d.contains(msg::usrmsg::FLD_ID) || !d[msg::usrmsg::FLD_ID].is()) { - LOG_DEBUG << "Shell input 'id' field missing or invalid."; + LOG_DEBUG << "Hpsh input 'id' field missing or invalid."; return -1; } if (!d.contains(msg::usrmsg::FLD_CONTENT) || !d[msg::usrmsg::FLD_CONTENT].is()) { - LOG_DEBUG << "Shell input 'content' field missing or invalid."; + LOG_DEBUG << "Hpsh input 'content' field missing or invalid."; return -1; } @@ -906,9 +952,9 @@ namespace msg::usrmsg::json /** * Extracts a signed input container message sent by user. - * + * * @param extracted_input_container The input container extracted from the message. - * @param extracted_sig The binary signature extracted from the message. + * @param extracted_sig The binary signature extracted from the message. * @param d The json document holding the input container. * Accepted signed input container format: * { diff --git a/src/msg/json/usrmsg_json.hpp b/src/msg/json/usrmsg_json.hpp index eef0e06c..ff5c827d 100644 --- a/src/msg/json/usrmsg_json.hpp +++ b/src/msg/json/usrmsg_json.hpp @@ -21,6 +21,8 @@ namespace msg::usrmsg::json void create_contract_input_status(std::vector &msg, std::string_view status, std::string_view reason, std::string_view input_hash, const uint64_t ledger_seq_no, const util::h32 &ledger_hash); + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content); + void create_contract_read_response_container(std::vector &msg, std::string_view reply_for, std::string_view content); void create_contract_output_container(std::vector &msg, std::string_view hash, const ::std::vector &outputs, @@ -47,7 +49,7 @@ namespace msg::usrmsg::json int extract_read_request(std::string &extracted_id, std::string &extracted_content, const jsoncons::json &d); - int extract_shell_input(std::string &extracted_id, std::string &extracted_content, const jsoncons::json &d); + int extract_hpsh_request(std::string &extracted_id, std::string &extracted_content, const jsoncons::json &d); int extract_signed_input_container(std::string &extracted_input_container, std::string &extracted_sig, const jsoncons::json &d); diff --git a/src/msg/usrmsg_common.hpp b/src/msg/usrmsg_common.hpp index d8682e49..6844e578 100644 --- a/src/msg/usrmsg_common.hpp +++ b/src/msg/usrmsg_common.hpp @@ -80,8 +80,8 @@ namespace msg::usrmsg constexpr const char *MSGTYPE_SERVER_CHALLENGE_RESPONSE = "server_challenge_response"; constexpr const char *MSGTYPE_CONTRACT_READ_REQUEST = "contract_read_request"; constexpr const char *MSGTYPE_CONTRACT_READ_RESPONSE = "contract_read_response"; - constexpr const char *MSGTYPE_CONTRACT_SHELL_INPUT = "contract_shell_input"; - constexpr const char *MSGTYPE_CONTRACT_SHELL_OUTPUT = "contract_shell_output"; + constexpr const char *MSGTYPE_HPSH_REQUEST = "hpsh_request"; + constexpr const char *MSGTYPE_HPSH_RESPONSE = "hpsh_response"; constexpr const char *MSGTYPE_CONTRACT_INPUT = "contract_input"; constexpr const char *MSGTYPE_CONTRACT_INPUT_STATUS = "contract_input_status"; constexpr const char *MSGTYPE_CONTRACT_OUTPUT = "contract_output"; diff --git a/src/msg/usrmsg_parser.cpp b/src/msg/usrmsg_parser.cpp index d87822f4..69a3e4bd 100644 --- a/src/msg/usrmsg_parser.cpp +++ b/src/msg/usrmsg_parser.cpp @@ -38,6 +38,14 @@ namespace msg::usrmsg busrmsg::create_contract_input_status(msg, status, reason, input_hash, ledger_seq_no, ledger_hash); } + void usrmsg_parser::create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) const + { + if (protocol == util::PROTOCOL::JSON) + jusrmsg::create_hpsh_response_container(msg, reply_for, content); + else + busrmsg::create_hpsh_response_container(msg, reply_for, content); + } + void usrmsg_parser::create_contract_read_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) const { if (protocol == util::PROTOCOL::JSON) @@ -121,12 +129,12 @@ namespace msg::usrmsg return busrmsg::extract_read_request(extracted_id, extracted_content, bdoc); } - int usrmsg_parser::extract_shell_input(std::string &extracted_id, std::string &extracted_content) const + int usrmsg_parser::extract_hpsh_request(std::string &extracted_id, std::string &extracted_content) const { if (protocol == util::PROTOCOL::JSON) - return jusrmsg::extract_shell_input(extracted_id, extracted_content, jdoc); + return jusrmsg::extract_hpsh_request(extracted_id, extracted_content, jdoc); else - return busrmsg::extract_shell_input(extracted_id, extracted_content, bdoc); + return busrmsg::extract_hpsh_request(extracted_id, extracted_content, bdoc); } int usrmsg_parser::extract_signed_input_container(std::string &extracted_input_container, std::string &extracted_sig) const diff --git a/src/msg/usrmsg_parser.hpp b/src/msg/usrmsg_parser.hpp index 8d721416..8ca9ce2d 100644 --- a/src/msg/usrmsg_parser.hpp +++ b/src/msg/usrmsg_parser.hpp @@ -26,6 +26,8 @@ namespace msg::usrmsg void create_contract_input_status(std::vector &msg, std::string_view status, std::string_view reason, std::string_view input_hash, const uint64_t ledger_seq_no, const util::h32 &ledger_hash) const; + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) const; + void create_contract_read_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) const; void create_contract_output_container(std::vector &msg, std::string_view hash, const ::std::vector &outputs, @@ -49,7 +51,7 @@ namespace msg::usrmsg int extract_read_request(std::string &extracted_id, std::string &extracted_content) const; - int extract_shell_input(std::string &extracted_id, std::string &extracted_content) const; + int extract_hpsh_request(std::string &extracted_id, std::string &extracted_content) const; int extract_signed_input_container(std::string &extracted_input_container, std::string &extracted_sig) const; diff --git a/src/usr/usr.cpp b/src/usr/usr.cpp index 6871841c..096646dd 100644 --- a/src/usr/usr.cpp +++ b/src/usr/usr.cpp @@ -273,19 +273,13 @@ namespace usr user.session.send(resp); return 0; } - else if (msg_type == msg::usrmsg::MSGTYPE_CONTRACT_SHELL_INPUT) + else if (msg_type == msg::usrmsg::MSGTYPE_HPSH_REQUEST) { std::string id, content; - if (parser.extract_shell_input(id, content) != -1) + if (hpsh::ctx.is_initialized && parser.extract_hpsh_request(id, content) != -1) { - - LOG_INFO << "shell input received:" << content; - // std::string response = hpsh::serve(content.c_str()); - if (hpsh::execute(std::string("user_").append(std::to_string(1)), content.c_str()) == -1) - { - std::cout << "\nError sending message:" << content.c_str() << std::endl; - } - // LOG_INFO << "response: " << response; + if (hpsh::execute(id, user.pubkey, content.c_str()) == -1) + return -1; return 0; } diff --git a/test/bin/hpsh b/test/bin/hpsh index cf4cd294fed5b325afc51a1f7a4991fb72754f66..eb008ec0da3b9d4a9102b07d8298226fcf14cb78 100755 GIT binary patch delta 7699 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 delta 7697 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