diff --git a/examples/c_contract/hotpocket_contract.h b/examples/c_contract/hotpocket_contract.h index abd1c9c8..190f0a04 100644 --- a/examples/c_contract/hotpocket_contract.h +++ b/examples/c_contract/hotpocket_contract.h @@ -137,6 +137,7 @@ struct hp_round_limits_config { size_t user_input_bytes; size_t user_output_bytes; + size_t npl_output_bytes; }; struct hp_config @@ -504,7 +505,7 @@ int hp_update_config(const struct hp_config *config) if (!config->npl || strlen(config->npl) == 0 || (strcmp(config->npl, "public") != 0 && strcmp(config->npl, "private")) != 0) __HP_UPDATE_CONFIG_ERROR("Invalid npl flag. Valid values: public|private"); - if (config->round_limits.user_input_bytes < 0 || config->round_limits.user_output_bytes < 0) + if (config->round_limits.user_input_bytes < 0 || config->round_limits.user_output_bytes < 0 || config->round_limits.npl_output_bytes < 0) __HP_UPDATE_CONFIG_ERROR("Invalid round limits."); const int fd = open(PATCH_FILE_PATH, O_RDWR); @@ -655,17 +656,18 @@ int __hp_write_to_patch_file(const int fd, const struct hp_config *config) // Round limits field valies. - const char *round_limits_json = " \"round_limits\": {\n \"user_input_bytes\": %s,\n \"user_output_bytes\": %s\n }\n}"; + const char *round_limits_json = " \"round_limits\": {\n \"user_input_bytes\": %s,\n \"user_output_bytes\": %s,\n \"npl_output_bytes\": %s\n }\n}"; - char user_input_bytes_str[20], user_output_bytes_str[20]; + char user_input_bytes_str[20], user_output_bytes_str[20], npl_output_bytes_str[20]; sprintf(user_input_bytes_str, "%" PRIu64, config->round_limits.user_input_bytes); sprintf(user_output_bytes_str, "%" PRIu64, config->round_limits.user_output_bytes); + sprintf(npl_output_bytes_str, "%" PRIu64, config->round_limits.npl_output_bytes); - const size_t round_limits_json_len = 89 + strlen(user_input_bytes_str) + strlen(user_output_bytes_str); + const size_t round_limits_json_len = 119 + strlen(user_input_bytes_str) + strlen(user_output_bytes_str) + strlen(npl_output_bytes_str); char round_limits_buf[round_limits_json_len]; - sprintf(round_limits_buf, round_limits_json, user_input_bytes_str, user_output_bytes_str); + sprintf(round_limits_buf, round_limits_json, user_input_bytes_str, user_output_bytes_str, npl_output_bytes_str); iov_vec[4].iov_base = round_limits_buf; - iov_vec[4].iov_len = round_limits_json_len; + iov_vec[4].iov_len = strlen(round_limits_buf); if (ftruncate(fd, 0) == -1 || // Clear any previous content in the file. pwritev(fd, iov_vec, 5, 0) == -1) // Start writing from begining. @@ -763,6 +765,10 @@ void __hp_populate_patch_from_json_object(struct hp_config *config, const struct { __HP_ASSIGN_UINT64(config->round_limits.user_output_bytes, sub_ele); } + else if (strcmp(sub_ele->name->string, "npl_output_bytes") == 0) + { + __HP_ASSIGN_UINT64(config->round_limits.npl_output_bytes, sub_ele); + } sub_ele = sub_ele->next; } while (sub_ele); } diff --git a/examples/nodejs_contract/hp-contract-lib.js b/examples/nodejs_contract/hp-contract-lib.js index bd9e8ce0..19649f89 100644 --- a/examples/nodejs_contract/hp-contract-lib.js +++ b/examples/nodejs_contract/hp-contract-lib.js @@ -144,7 +144,7 @@ class PatchConfig { throw "Invalid consensus flag configured in patch file. Valid values: public|private"; if (config.npl != "public" && config.npl != "private") throw "Invalid npl flag configured in patch file. Valid values: public|private"; - if (config.round_limits.user_input_bytes < 0 || config.round_limits.user_output_bytes < 0) + if (config.round_limits.user_input_bytes < 0 || config.round_limits.user_output_bytes < 0 || config.round_limits.npl_output_bytes < 0) throw "Invalid round limits."; } } diff --git a/src/conf.cpp b/src/conf.cpp index 335becbc..50dcae51 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -854,6 +854,7 @@ namespace conf jsoncons::ojson round_limits; round_limits.insert_or_assign("user_input_bytes", contract.round_limits.user_input_bytes); round_limits.insert_or_assign("user_output_bytes", contract.round_limits.user_output_bytes); + round_limits.insert_or_assign("npl_output_bytes", contract.round_limits.npl_output_bytes); jdoc.insert_or_assign("round_limits", round_limits); } @@ -945,6 +946,7 @@ namespace conf contract.round_limits.user_input_bytes = jdoc["round_limits"]["user_input_bytes"].as(); contract.round_limits.user_output_bytes = jdoc["round_limits"]["user_output_bytes"].as(); + contract.round_limits.npl_output_bytes = jdoc["round_limits"]["npl_output_bytes"].as(); } catch (const std::exception &e) { diff --git a/src/conf.hpp b/src/conf.hpp index 49ae0d7e..878da1ae 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -89,6 +89,7 @@ namespace conf { size_t user_input_bytes = 0; // Max contract input bytes per user per round. size_t user_output_bytes = 0; // Max contract output bytes per user per round. + size_t npl_output_bytes = 0; // Max npl output bytes per round. }; struct contract_config diff --git a/src/sc/sc.cpp b/src/sc/sc.cpp index 814be588..48cb06cc 100644 --- a/src/sc/sc.cpp +++ b/src/sc/sc.cpp @@ -3,12 +3,12 @@ #include "../consensus.hpp" #include "../hplog.hpp" #include "../ledger.hpp" -#include "sc.hpp" #include "../msg/fbuf/p2pmsg_helpers.hpp" #include "../msg/controlmsg_common.hpp" #include "../msg/controlmsg_parser.hpp" #include "../unl.hpp" #include "contract_serve.hpp" +#include "sc.hpp" namespace sc { @@ -351,7 +351,7 @@ namespace sc // Atempt to read messages from contract (regardless of contract terminated or not). const int control_read_res = read_control_outputs(ctx, out_fds[control_fd_idx]); - const int npl_read_res = ctx.args.readonly ? 0 : read_npl_outputs(ctx, out_fds[npl_fd_idx]); + const int npl_read_res = ctx.args.readonly ? 0 : read_npl_outputs(ctx, &out_fds[npl_fd_idx]); const int user_read_res = read_contract_fdmap_outputs(ctx.user_fds, out_fds, ctx.args.userbufs); if (ctx.termination_signaled || ctx.contract_pid == 0) @@ -500,10 +500,10 @@ namespace sc * @param ctx contract execution context. * @return 0 if no bytes were read. 1 if bytes were read. */ - int read_npl_outputs(execution_context &ctx, const pollfd pfd) + int read_npl_outputs(execution_context &ctx, pollfd *pfd) { std::string output; - const int res = read_iosocket(false, pfd, output); + const int res = read_iosocket(false, *pfd, output); if (res == -1) { @@ -511,8 +511,18 @@ namespace sc } else if (res > 0) { - // Broadcast npl messages once contract npl output is collected. - broadcast_npl_output(output); + ctx.total_npl_output_size += output.size(); + if (conf::cfg.contract.round_limits.npl_output_bytes > 0 && + ctx.total_npl_output_size > conf::cfg.contract.round_limits.npl_output_bytes) + { + close(pfd->fd); + pfd->fd = -1; + } + else + { + // Broadcast npl messages once contract npl output is collected. + broadcast_npl_output(output); + } } return (res > 0) ? 1 : 0; diff --git a/src/sc/sc.hpp b/src/sc/sc.hpp index 079e8ca1..b34231f9 100644 --- a/src/sc/sc.hpp +++ b/src/sc/sc.hpp @@ -119,6 +119,8 @@ namespace sc // Thread to collect contract inputs and outputs and feed npl messages while contract is running. std::thread contract_monitor_thread; + size_t total_npl_output_size = 0; + // Indicates that the contract has sent termination control message. bool termination_signaled = false; @@ -157,7 +159,7 @@ namespace sc int read_control_outputs(execution_context &ctx, const pollfd pfd); - int read_npl_outputs(execution_context &ctx, const pollfd pfd); + int read_npl_outputs(execution_context &ctx, pollfd *pfd); void broadcast_npl_output(std::string_view output);