diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e31633c..0fbccbe0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ add_executable(hpcore src/crypto.cpp src/conf.cpp src/hplog.cpp - src/proc.cpp + src/sc.cpp src/bill/corebill.cpp src/hpfs/h32.cpp src/hpfs/hpfs.cpp diff --git a/README.md b/README.md index 833058be..0bf740cd 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Code is divided into subsystems via namespaces. **crypto::** Handles cryptographic activities. Wraps libsodium and offers convenience functions. -**proc::** Handles contract process execution and managing user/SC I/O and npl I/O. Makes use of **usr** and **p2p**. +**sc::** Handles smart contract process execution and managing user/SC I/O and npl I/O. Makes use of **usr**, **p2p** and **hpfs**. **usr::** Handles user connections. Makes use of **crypto** and **comm**. diff --git a/src/cons/cons.cpp b/src/cons/cons.cpp index eb8ea0d3..b4d87c2d 100644 --- a/src/cons/cons.cpp +++ b/src/cons/cons.cpp @@ -9,7 +9,7 @@ #include "../p2p/peer_session_handler.hpp" #include "../hplog.hpp" #include "../crypto.hpp" -#include "../proc.hpp" +#include "../sc.hpp" #include "../hpfs/h32.hpp" #include "../hpfs/hpfs.hpp" #include "ledger_handler.hpp" @@ -735,9 +735,9 @@ namespace cons // Send any output from the previous consensus round to locally connected users. dispatch_user_outputs(cons_prop); - proc::contract_bufmap_t useriobufmap; + sc::contract_bufmap_t useriobufmap; - proc::contract_iobuf_pair nplbufpair; + sc::contract_iobuf_pair nplbufpair; nplbufpair.inputs.splice(nplbufpair.inputs.end(), ctx.candidate_npl_messages); feed_user_inputs_to_contract_bufmap(useriobufmap, cons_prop); @@ -826,12 +826,12 @@ namespace cons * @param bufmap The contract bufmap which needs to be populated with inputs. * @param cons_prop The proposal that achieved consensus. */ - void feed_user_inputs_to_contract_bufmap(proc::contract_bufmap_t &bufmap, const p2p::proposal &cons_prop) + void feed_user_inputs_to_contract_bufmap(sc::contract_bufmap_t &bufmap, const p2p::proposal &cons_prop) { // Populate the buf map with all currently connected users regardless of whether they have inputs or not. // This is in case the contract wanted to emit some data to a user without needing any input. for (const std::string &pubkey : cons_prop.users) - bufmap.try_emplace(pubkey, proc::contract_iobuf_pair()); + bufmap.try_emplace(pubkey, sc::contract_iobuf_pair()); for (const std::string &hash : cons_prop.hash_inputs) { @@ -852,7 +852,7 @@ namespace cons std::string inputtofeed; inputtofeed.swap(cand_input.input); - proc::contract_iobuf_pair &bufpair = bufmap[cand_input.userpubkey]; + sc::contract_iobuf_pair &bufpair = bufmap[cand_input.userpubkey]; bufpair.inputs.push_back(std::move(inputtofeed)); // Remove the input from the candidate set because we no longer need it. @@ -867,7 +867,7 @@ namespace cons * for the next consensus round. * @param bufmap The contract bufmap containing the outputs produced by the contract. */ - void extract_user_outputs_from_contract_bufmap(proc::contract_bufmap_t &bufmap) + void extract_user_outputs_from_contract_bufmap(sc::contract_bufmap_t &bufmap) { for (auto &[pubkey, bufpair] : bufmap) { @@ -902,12 +902,12 @@ namespace cons * @param time_now The time that must be passed on to the contract. * @param useriobufmap The contract bufmap which holds user I/O buffers. */ - int run_contract_binary(const int64_t time_now, proc::contract_bufmap_t &useriobufmap, proc::contract_iobuf_pair &nplbufpair) + int run_contract_binary(const int64_t time_now, sc::contract_bufmap_t &useriobufmap, sc::contract_iobuf_pair &nplbufpair) { // todo:implement exchange of hpsc bufs - proc::contract_iobuf_pair hpscbufpair; - return proc::exec_contract( - proc::contract_exec_args(time_now, useriobufmap, nplbufpair, hpscbufpair), + sc::contract_iobuf_pair hpscbufpair; + return sc::exec_contract( + sc::contract_exec_args(time_now, useriobufmap, nplbufpair, hpscbufpair), ctx.curr_state_hash); } diff --git a/src/cons/cons.hpp b/src/cons/cons.hpp index c84b621d..4eccda76 100644 --- a/src/cons/cons.hpp +++ b/src/cons/cons.hpp @@ -3,7 +3,7 @@ #include "../pchheader.hpp" #include "../util.hpp" -#include "../proc.hpp" +#include "../sc.hpp" #include "../p2p/p2p.hpp" #include "../usr/user_input.hpp" #include "../hpfs/h32.hpp" @@ -148,13 +148,13 @@ void dispatch_user_outputs(const p2p::proposal &cons_prop); void check_state(vote_counter &votes); -void feed_user_inputs_to_contract_bufmap(proc::contract_bufmap_t &bufmap, const p2p::proposal &cons_prop); +void feed_user_inputs_to_contract_bufmap(sc::contract_bufmap_t &bufmap, const p2p::proposal &cons_prop); -void extract_user_outputs_from_contract_bufmap(proc::contract_bufmap_t &bufmap); +void extract_user_outputs_from_contract_bufmap(sc::contract_bufmap_t &bufmap); void broadcast_npl_output(std::string &output); -int run_contract_binary(const int64_t time_now, proc::contract_bufmap_t &useriobufmap, proc::contract_iobuf_pair &nplbufpair); +int run_contract_binary(const int64_t time_now, sc::contract_bufmap_t &useriobufmap, sc::contract_iobuf_pair &nplbufpair); template void increment(std::map &counter, const T &candidate); diff --git a/src/main.cpp b/src/main.cpp index bc718766..83140166 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,7 @@ #include "util.hpp" #include "conf.hpp" #include "crypto.hpp" -#include "proc.hpp" +#include "sc.hpp" #include "hplog.hpp" #include "usr/usr.hpp" #include "p2p/p2p.hpp" @@ -67,7 +67,7 @@ void deinit() usr::deinit(); p2p::deinit(); cons::deinit(); - proc::deinit(); + sc::deinit(); hpfs::deinit(); hplog::deinit(); } diff --git a/src/proc.cpp b/src/sc.cpp similarity index 86% rename from src/proc.cpp rename to src/sc.cpp index e6f4b93c..e7b9e985 100644 --- a/src/proc.cpp +++ b/src/sc.cpp @@ -4,45 +4,12 @@ #include "fbschema/common_helpers.hpp" #include "fbschema/p2pmsg_container_generated.h" #include "fbschema/p2pmsg_content_generated.h" -#include "proc.hpp" +#include "sc.hpp" #include "hpfs/hpfs.hpp" -namespace proc +namespace sc { - constexpr size_t OUTPUT_READ_BUF_SIZE = 64 * 1024; //64KB - - // Enum used to differenciate pipe fds maintained for SC I/O pipes. - enum FDTYPE - { - // Used by Smart Contract to read input sent by Hot Pocket - SCREAD = 0, - // Used by Hot Pocket to write input to the smart contract. - HPWRITE = 1, - // Used by Hot Pocket to read output from the smart contract. - HPREAD = 2, - // Used by Smart Contract to write output back to Hot Pocket. - SCWRITE = 3 - }; - - // Map of user pipe fds (map key: user public key) - contract_fdmap_t userfds; - - // Pipe fds for NPL <--> messages. - std::vector nplfds; - - // Pipe fds for HP <--> messages. - std::vector hpscfds; - - // Holds the contract process id (if currently executing). - pid_t contract_pid; - - // Holds the hpfs rw process id (if currently executing). - pid_t hpfs_pid; - - // Thread to collect contract outputs while contract is running. - std::thread output_fetcher_thread; - - bool should_deinit = false; + execution_context ctx; /** * Executes the contract process and passes the specified arguments. @@ -55,34 +22,34 @@ namespace proc return -1; // Setup io pipes and feed all inputs to them. - create_iopipes_for_fdmap(userfds, args.userbufs); - create_iopipes(nplfds, !args.nplbuff.inputs.empty()); - create_iopipes(hpscfds, !args.hpscbufs.inputs.empty()); + create_iopipes_for_fdmap(ctx.userfds, args.userbufs); + create_iopipes(ctx.nplfds, !args.nplbuff.inputs.empty()); + create_iopipes(ctx.hpscfds, !args.hpscbufs.inputs.empty()); int ret = 0; const pid_t pid = fork(); if (pid > 0) { // HotPocket process. - contract_pid = pid; + ctx.contract_pid = pid; // Close all fds unused by HP process. close_unused_fds(true); // Start the contract output collection thread. - output_fetcher_thread = std::thread(fetch_outputs, std::ref(args)); + ctx.output_fetcher_thread = std::thread(fetch_outputs, std::ref(args)); // Write the inputs into the contract process. if (feed_inputs(args) != 0) goto failure; // Wait for child process (contract process) to complete execution. - const int presult = await_process_execution(contract_pid); - contract_pid = 0; + const int presult = await_process_execution(ctx.contract_pid); + ctx.contract_pid = 0; LOG_DBG << "Contract process ended."; // Wait for the output collection thread to gracefully stop. - output_fetcher_thread.join(); + ctx.output_fetcher_thread.join(); if (presult != 0) { @@ -139,9 +106,9 @@ namespace proc ret = -1; success: - cleanup_fdmap(userfds); - cleanup_vectorfds(hpscfds); - cleanup_vectorfds(nplfds); + cleanup_fdmap(ctx.userfds); + cleanup_vectorfds(ctx.hpscfds); + cleanup_vectorfds(ctx.nplfds); return ret; } @@ -168,10 +135,10 @@ namespace proc int start_hpfs_rw_session() { LOG_DBG << "Starting hpfs rw session..."; - if (hpfs::start_fs_session(hpfs_pid, conf::ctx.state_rw_dir, "rw", true) == -1) + if (hpfs::start_fs_session(ctx.hpfs_pid, conf::ctx.state_rw_dir, "rw", true) == -1) return -1; - LOG_DBG << "hpfs rw session started. pid:" << hpfs_pid; + LOG_DBG << "hpfs rw session started. pid:" << ctx.hpfs_pid; } /** @@ -183,11 +150,11 @@ namespace proc if (hpfs::get_hash(state_hash, conf::ctx.state_rw_dir, "/") == -1) return -1; - LOG_DBG << "Stopping hpfs rw session... pid:" << hpfs_pid; - if (util::kill_process(hpfs_pid, true) == -1) + LOG_DBG << "Stopping hpfs rw session... pid:" << ctx.hpfs_pid; + if (util::kill_process(ctx.hpfs_pid, true) == -1) return -1; - hpfs_pid = 0; + ctx.hpfs_pid = 0; LOG_DBG << "hpfs rw session stopped."; return 0; } @@ -215,12 +182,12 @@ namespace proc os << "{\"version\":\"" << util::HP_VERSION << "\",\"pubkey\":\"" << conf::cfg.pubkeyhex << "\",\"ts\":" << args.timestamp - << ",\"hpfd\":[" << hpscfds[FDTYPE::SCREAD] << "," << hpscfds[FDTYPE::SCWRITE] + << ",\"hpfd\":[" << ctx.hpscfds[FDTYPE::SCREAD] << "," << ctx.hpscfds[FDTYPE::SCWRITE] << "],\"usrfd\":{"; - fdmap_json_to_stream(userfds, os); + fdmap_json_to_stream(ctx.userfds, os); - os << "},\"nplfd\":[" << nplfds[FDTYPE::SCREAD] << "," << nplfds[FDTYPE::SCWRITE] + os << "},\"nplfd\":[" << ctx.nplfds[FDTYPE::SCREAD] << "," << ctx.nplfds[FDTYPE::SCWRITE] << "],\"unl\":["; for (auto nodepk = conf::cfg.unl.begin(); nodepk != conf::cfg.unl.end(); nodepk++) @@ -276,7 +243,7 @@ namespace proc } // Write any verified (consensus-reached) user inputs to user pipes. - if (write_contract_fdmap_inputs(userfds, args.userbufs) != 0) + if (write_contract_fdmap_inputs(ctx.userfds, args.userbufs) != 0) { LOG_ERR << "Failed to write user inputs to contract."; return -1; @@ -289,14 +256,14 @@ namespace proc { while (true) { - if (should_deinit) + if (ctx.should_deinit) break; const int hpsc_npl_res = read_contract_hp_npl_outputs(args); if (hpsc_npl_res == -1) return -1; - const int user_res = read_contract_fdmap_outputs(userfds, args.userbufs); + const int user_res = read_contract_fdmap_outputs(ctx.userfds, args.userbufs); if (user_res == -1) { LOG_ERR << "Error reading user outputs from the contract."; @@ -304,7 +271,7 @@ namespace proc } // If no bytes were read after contract finished execution, exit the read loop. - if (hpsc_npl_res == 0 && user_res == 0 && contract_pid == 0) + if (hpsc_npl_res == 0 && user_res == 0 && ctx.contract_pid == 0) break; util::sleep(20); @@ -319,13 +286,13 @@ namespace proc */ int write_contract_hp_npl_inputs(const contract_exec_args &args) { - if (write_iopipe(hpscfds, args.hpscbufs.inputs) != 0) + if (write_iopipe(ctx.hpscfds, args.hpscbufs.inputs) != 0) { LOG_ERR << "Error writing HP inputs to SC"; return -1; } - if (write_npl_iopipe(nplfds, args.nplbuff.inputs) != 0) + if (write_npl_iopipe(ctx.nplfds, args.nplbuff.inputs) != 0) { LOG_ERR << "Error writing NPL inputs to SC"; return -1; @@ -342,14 +309,14 @@ namespace proc */ int read_contract_hp_npl_outputs(const contract_exec_args &args) { - const int hpsc_res = read_iopipe(hpscfds, args.hpscbufs.output); + const int hpsc_res = read_iopipe(ctx.hpscfds, args.hpscbufs.output); if (hpsc_res == -1) { LOG_ERR << "Error reading HP output from the contract."; return -1; } - const int npl_res = read_iopipe(nplfds, args.nplbuff.output); + const int npl_res = read_iopipe(ctx.nplfds, args.nplbuff.output); if (npl_res == -1) { LOG_ERR << "Error reading NPL output from the contract."; @@ -361,7 +328,7 @@ namespace proc /** * Common helper function to write json output of fdmap to given ostream. - * @param fdmap Any pubkey->fdlist map. (eg. userfds, nplfds) + * @param fdmap Any pubkey->fdlist map. (eg. ctx.userfds, ctx.nplfds) * @param os An output stream. */ void fdmap_json_to_stream(const contract_fdmap_t &fdmap, std::ostringstream &os) @@ -456,7 +423,7 @@ namespace proc /** * Common function to close any open fds in the map after an error. - * @param fdmap Any pubkey->fdlist map. (eg. userfds, nplfds) + * @param fdmap Any pubkey->fdlist map. (eg. ctx.userfds, ctx.nplfds) */ void cleanup_fdmap(contract_fdmap_t &fdmap) { @@ -664,12 +631,12 @@ namespace proc void close_unused_fds(const bool is_hp) { - close_unused_vectorfds(is_hp, hpscfds); + close_unused_vectorfds(is_hp, ctx.hpscfds); - close_unused_vectorfds(is_hp, nplfds); + close_unused_vectorfds(is_hp, ctx.nplfds); // Loop through user fds. - for (auto &[pubkey, fds] : userfds) + for (auto &[pubkey, fds] : ctx.userfds) close_unused_vectorfds(is_hp, fds); } @@ -718,16 +685,16 @@ namespace proc */ void deinit() { - should_deinit = true; + ctx.should_deinit = true; - if (contract_pid > 0) - util::kill_process(contract_pid, true); + if (ctx.contract_pid > 0) + util::kill_process(ctx.contract_pid, true); - if (hpfs_pid > 0) - util::kill_process(hpfs_pid, true); + if (ctx.hpfs_pid > 0) + util::kill_process(ctx.hpfs_pid, true); - if (output_fetcher_thread.joinable()) - output_fetcher_thread.join(); + if (ctx.output_fetcher_thread.joinable()) + ctx.output_fetcher_thread.join(); } -} // namespace proc +} // namespace sc diff --git a/src/proc.hpp b/src/sc.hpp similarity index 75% rename from src/proc.hpp rename to src/sc.hpp index b45b04e5..2c1e1b79 100644 --- a/src/proc.hpp +++ b/src/sc.hpp @@ -9,9 +9,22 @@ /** * Contains helper functions regarding POSIX process execution and IPC between HP and SC. */ -namespace proc +namespace sc { + // Enum used to differenciate pipe fds maintained for SC I/O pipes. + enum FDTYPE + { + // Used by Smart Contract to read input sent by Hot Pocket + SCREAD = 0, + // Used by Hot Pocket to write input to the smart contract. + HPWRITE = 1, + // Used by Hot Pocket to read output from the smart contract. + HPREAD = 2, + // Used by Smart Contract to write output back to Hot Pocket. + SCWRITE = 3 + }; + /** * Represents list of inputs to the contract and the accumulated contract output for those inputs. */ @@ -66,6 +79,33 @@ namespace proc } }; + /** + * Holds context information relating to contract execution environment. + */ + struct execution_context + { + // Map of user pipe fds (map key: user public key) + contract_fdmap_t userfds; + + // Pipe fds for NPL <--> messages. + std::vector nplfds; + + // Pipe fds for HP <--> messages. + std::vector hpscfds; + + // Holds the contract process id (if currently executing). + pid_t contract_pid; + + // Holds the hpfs rw process id (if currently executing). + pid_t hpfs_pid; + + // Thread to collect contract outputs while contract is running. + std::thread output_fetcher_thread; + + // Indicates that the deinit procedure has begun. + bool should_deinit = false; + }; + int exec_contract(const contract_exec_args &args, hpfs::h32 &state_hash); void deinit(); @@ -114,6 +154,6 @@ namespace proc void cleanup_vectorfds(std::vector &fds); -} // namespace proc +} // namespace sc #endif \ No newline at end of file