diff --git a/examples/c_contract/echo_contract.c b/examples/c_contract/echo_contract.c index 3d122bd9..494f4ae4 100644 --- a/examples/c_contract/echo_contract.c +++ b/examples/c_contract/echo_contract.c @@ -53,29 +53,15 @@ int main(int argc, char **argv) // printf("Received %.*s from %.*s", len, msg, HP_KEY_SIZE, sender); // free(msg); - // // Test code segment - Config file will be updated with below values. - // struct hp_config config = {}; - // config.version = "2.0"; - // config.consensus = "public"; - // config.npl = "public"; - // config.appbill.bin_args = "123"; - // struct hp_unl_node unl[1] = {"ed726f9f536904b125bdca10bbdd1e66591b274799b92ac8bcfc75bf45d7da4c0f"}; - // config.unl.list = unl; - // config.unl.count = 1; - // config.roundtime = 1000; - // hp_update_config(&config); - - // // Test code segment - Get current config file values. - // struct hp_config *current_config = hp_get_config(); - // if (current_config != NULL) - // { - // printf("\"version\": \"%s\"\n", current_config->version); - // printf("\"consensus\": \"%s\"\n", current_config->consensus); - // printf("\"npl\": \"%s\"\n", current_config->npl); - // printf("\"appbill_bin_args\": \"%s\"\n", current_config->appbill.bin_args); - // } - // // Returned hp_config struct memory should be freed after it's been used. - // hp_free_config(current_config); + // Config update example: + // struct hp_config *config = hp_get_config(); + // hp_set_config_string(&config->version, "2.0", 4); + // config->round_limits.user_input_bytes = 1024; + // struct hp_unl_node new_unl[2] = {{"ed726f9f536904b125bdca10bbdd1e66591b274799b92ac8bcfc75bf45d7da4c0f"}, + // {"ed3e63992d62804ea0c182e5b22fe43c4b652fbbf068ec7520f3020f4c3771416a"}}; + // hp_set_config_unl(config, new_unl, 2); + // hp_update_config(config); + // hp_free_config(config); hp_deinit_user_input_mmap(); hp_deinit_contract(); diff --git a/examples/c_contract/hotpocket_contract.h b/examples/c_contract/hotpocket_contract.h index 2214ee09..abd1c9c8 100644 --- a/examples/c_contract/hotpocket_contract.h +++ b/examples/c_contract/hotpocket_contract.h @@ -81,26 +81,10 @@ ptr = NULL; \ } -#define __HP_UPDATE_CONFIG_ERROR(msg) \ - { \ - fprintf(stderr, "%s\n", msg); \ - if (fd) \ - close(fd); \ - if (config) \ - __hp_free_patch_config(existing_patch); \ - \ - return -1; \ - } - -/** - * dest - Destination pointer. - * src - Source pointer. -*/ -#define __HP_STRING_COPY(dest, src) \ - { \ - __HP_FREE(dest); \ - dest = (char *)malloc(strlen(src) + 1); \ - memcpy(dest, src, strlen(src) + 1); \ +#define __HP_UPDATE_CONFIG_ERROR(msg) \ + { \ + fprintf(stderr, "%s\n", msg); \ + return -1; \ } struct hp_user_input @@ -149,6 +133,12 @@ struct hp_appbill_config char *bin_args; }; +struct hp_round_limits_config +{ + size_t user_input_bytes; + size_t user_output_bytes; +}; + struct hp_config { char *version; @@ -159,6 +149,7 @@ struct hp_config char *consensus; char *npl; struct hp_appbill_config appbill; + struct hp_round_limits_config round_limits; }; struct hp_contract_context @@ -191,8 +182,10 @@ int hp_writev_user_msg(const struct hp_user *user, const struct iovec *bufs, con int hp_write_npl_msg(const void *buf, const uint32_t len); int hp_writev_npl_msg(const struct iovec *bufs, const int buf_count); int hp_read_npl_msg(void *msg_buf, char *pubkey_buf, const int timeout); -int hp_update_config(const struct hp_config *config); struct hp_config *hp_get_config(); +int hp_update_config(const struct hp_config *config); +void hp_set_config_string(char **config_str, const char *value, const size_t value_size); +void hp_set_config_unl(struct hp_config *config, const struct hp_unl_node *new_unl, const size_t new_unl_count); void hp_free_config(struct hp_config *config); void __hp_parse_args_json(const struct json_object_s *object); @@ -200,7 +193,6 @@ int __hp_write_control_msg(const void *buf, const uint32_t len); void __hp_populate_patch_from_json_object(struct hp_config *config, const struct json_object_s *object); int __hp_write_to_patch_file(const int fd, const struct hp_config *config); struct hp_config *__hp_read_from_patch_file(const int fd); -void __hp_free_patch_config(struct hp_config *patch_config); static struct __hp_contract __hpc = {}; @@ -436,125 +428,6 @@ int hp_read_npl_msg(void *msg_buf, char *pubkey_buf, const int timeout) return 0; } -/** - * Update the params of the existing config file. - * @param config Pointer to the updated config struct. -*/ -int hp_update_config(const struct hp_config *config) -{ - struct hp_contract_context *cctx = __hpc.cctx; - - if (cctx->readonly) - { - fprintf(stderr, "Config update not allowed in readonly mode.\n"); - return -1; - } - const int fd = open(PATCH_FILE_PATH, O_RDWR); - if (fd == -1) - { - fprintf(stderr, "Error opening patch.cfg file.\n"); - return -1; - } - - struct hp_config *existing_patch = __hp_read_from_patch_file(fd); - if (existing_patch != NULL) - { - if (config->version) - { - if (strlen(config->version) != 0) - { - __HP_STRING_COPY(existing_patch->version, config->version); - } - else - { - __HP_UPDATE_CONFIG_ERROR("Version cannot be empty."); - } - } - - if (config->unl.count) - { - for (size_t i = 0; i < config->unl.count; i++) - { - const size_t pubkey_len = strlen(config->unl.list[i].pubkey); - if (pubkey_len == 0) - { - __HP_UPDATE_CONFIG_ERROR("Unl pubkey cannot be empty."); - } - - if (pubkey_len != HP_KEY_SIZE) - { - __HP_UPDATE_CONFIG_ERROR("Unl pubkey invalid. Invalid length."); - } - - if (config->unl.list[i].pubkey[0] != 'e' || config->unl.list[i].pubkey[1] != 'd') - { - __HP_UPDATE_CONFIG_ERROR("Unl pubkey invalid. Invalid format."); - } - // Checking the validity of hexadecimal portion. (without 'ed'). - for (size_t j = 2; j < HP_KEY_SIZE; j++) - { - const char current_char = config->unl.list[i].pubkey[j]; - if ((current_char < 'A' || current_char > 'F') && (current_char < 'a' || current_char > 'f') && (current_char < '0' || current_char > '9')) - { - __HP_UPDATE_CONFIG_ERROR("Unl pubkey invalid. Invalid character."); - } - } - } - __HP_FREE(existing_patch->unl.list); - existing_patch->unl.list = config->unl.count ? (struct hp_unl_node *)malloc(sizeof(struct hp_unl_node) * config->unl.count) : NULL; - memcpy(existing_patch->unl.list, config->unl.list, sizeof(struct hp_unl_node) * config->unl.count); - existing_patch->unl.count = config->unl.count; - } - - if (config->bin_path) - __HP_STRING_COPY(existing_patch->bin_path, config->bin_path); - - if (config->bin_args) - __HP_STRING_COPY(existing_patch->bin_args, config->bin_args); - - // if (config->roundtime) - // existing_patch->roundtime = config->roundtime; - - if (config->consensus) - { - if (strlen(config->consensus) == 0 || (strcmp(config->consensus, "public") != 0 && strcmp(config->consensus, "private") != 0)) - { - __HP_UPDATE_CONFIG_ERROR("Invalid consensus flag. Valid values: public|private"); - } - __HP_STRING_COPY(existing_patch->consensus, config->consensus); - } - - if (config->npl) - { - if (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"); - } - __HP_STRING_COPY(existing_patch->npl, config->npl); - } - - if (config->appbill.mode) - __HP_STRING_COPY(existing_patch->appbill.mode, config->appbill.mode); - - if (config->appbill.bin_args) - __HP_STRING_COPY(existing_patch->appbill.bin_args, config->appbill.bin_args); - - if (__hp_write_to_patch_file(fd, existing_patch) == -1) - __HP_UPDATE_CONFIG_ERROR("Error writing updated config to patch.cfg file."); - - __hp_free_patch_config(existing_patch); - } - else - { - fprintf(stderr, "Error reading patch.cfg file.\n"); - close(fd); - return -1; - } - - close(fd); - return 0; -} - /** * Get the existing config file values. * @return returns a pointer to a config structure, returns NULL on error. @@ -576,13 +449,119 @@ struct hp_config *hp_get_config() return config; } +/** + * Update the params of the existing config file. + * @param config Pointer to the updated config struct. +*/ +int hp_update_config(const struct hp_config *config) +{ + struct hp_contract_context *cctx = __hpc.cctx; + + if (cctx->readonly) + { + fprintf(stderr, "Config update not allowed in readonly mode.\n"); + return -1; + } + + // Validate fields. + + if (!config->version || strlen(config->version) == 0) + __HP_UPDATE_CONFIG_ERROR("Version cannot be empty."); + + if (config->unl.count) + { + for (size_t i = 0; i < config->unl.count; i++) + { + const size_t pubkey_len = strlen(config->unl.list[i].pubkey); + if (pubkey_len == 0) + __HP_UPDATE_CONFIG_ERROR("Unl pubkey cannot be empty."); + + if (pubkey_len != HP_KEY_SIZE) + __HP_UPDATE_CONFIG_ERROR("Unl pubkey invalid. Invalid length."); + + if (config->unl.list[i].pubkey[0] != 'e' || config->unl.list[i].pubkey[1] != 'd') + __HP_UPDATE_CONFIG_ERROR("Unl pubkey invalid. Invalid format."); + + // Checking the validity of hexadecimal portion. (without 'ed'). + for (size_t j = 2; j < HP_KEY_SIZE; j++) + { + const char current_char = config->unl.list[i].pubkey[j]; + if ((current_char < 'A' || current_char > 'F') && (current_char < 'a' || current_char > 'f') && (current_char < '0' || current_char > '9')) + __HP_UPDATE_CONFIG_ERROR("Unl pubkey invalid. Invalid character."); + } + } + } + + if (!config->bin_path || strlen(config->bin_path) == 0) + __HP_UPDATE_CONFIG_ERROR("Binary path cannot be empty."); + + if (config->roundtime <= 0) + __HP_UPDATE_CONFIG_ERROR("Round time must be higher than 0."); + + if (!config->consensus || strlen(config->consensus) == 0 || (strcmp(config->consensus, "public") != 0 && strcmp(config->consensus, "private") != 0)) + __HP_UPDATE_CONFIG_ERROR("Invalid consensus flag. Valid values: public|private"); + + 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) + __HP_UPDATE_CONFIG_ERROR("Invalid round limits."); + + const int fd = open(PATCH_FILE_PATH, O_RDWR); + if (fd == -1) + __HP_UPDATE_CONFIG_ERROR("Error opening patch.cfg file."); + + if (__hp_write_to_patch_file(fd, config) == -1) + { + close(fd); + __HP_UPDATE_CONFIG_ERROR("Error writing updated config to patch.cfg file."); + } + + close(fd); + return 0; +} + +/** + * Assigns the given string value to the specified config string field. + * @param config_str Pointer to the string field to populate the new value to. + * @param value New string value. + * @param value_size String length of the new value. + */ +void hp_set_config_string(char **config_str, const char *value, const size_t value_size) +{ + *config_str = realloc(*config_str, value_size); + strncpy(*config_str, value, value_size); +} + +/** + * Populates the config unl list with the specified values. + * @param config The config struct to populate the unl to. + * @param new_unl Pointer to the new unl node array. + * @param new_unl_count No. of entries in the new unl node array. + */ +void hp_set_config_unl(struct hp_config *config, const struct hp_unl_node *new_unl, const size_t new_unl_count) +{ + const size_t mem_size = sizeof(struct hp_unl_node) * new_unl_count; + config->unl.list = realloc(config->unl.list, mem_size); + memcpy(config->unl.list, new_unl, mem_size); + config->unl.count = new_unl_count; +} + /** * Frees the memory allocated for the config structure. * @param config Pointer to the config to be freed. */ void hp_free_config(struct hp_config *config) { - __hp_free_patch_config(config); + __HP_FREE(config->version); + __HP_FREE(config->unl.list); + __HP_FREE(config->bin_path); + __HP_FREE(config->bin_args); + __HP_FREE(config->consensus); + __HP_FREE(config->npl); + __HP_FREE(config->appbill.mode); + __HP_FREE(config->appbill.bin_args); + __HP_FREE(config); } /** @@ -622,7 +601,7 @@ struct hp_config *__hp_read_from_patch_file(const int fd) */ int __hp_write_to_patch_file(const int fd, const struct hp_config *config) { - struct iovec iov_vec[4]; + struct iovec iov_vec[5]; // {version: + newline + 4 spaces => 21; const size_t version_len = 21 + strlen(config->version); char version_buf[version_len]; @@ -652,28 +631,44 @@ int __hp_write_to_patch_file(const int fd, const struct hp_config *config) iov_vec[1].iov_base = unl_buf; iov_vec[1].iov_len = unl_buf_size; - char *json_string = " \"bin_path\": \"%s\",\n \"bin_args\": \"%s\",\n \"roundtime\": %d,\n \"consensus\": \"%s\",\n \"npl\": \"%s\",\n"; + // Top-level field values. + + const char *json_string = " \"bin_path\": \"%s\",\n \"bin_args\": \"%s\",\n \"roundtime\": %s,\n \"consensus\": \"%s\",\n \"npl\": \"%s\",\n"; - // Calculating number of digits in roundtime for json length. - // String length is taken after converting to string. char roundtime_str[16]; sprintf(roundtime_str, "%d", config->roundtime); const size_t json_string_len = 95 + strlen(config->bin_path) + strlen(config->bin_args) + strlen(roundtime_str) + strlen(config->consensus) + strlen(config->npl); char json_buf[json_string_len]; - sprintf(json_buf, json_string, config->bin_path, config->bin_args, config->roundtime, config->consensus, config->npl); + sprintf(json_buf, json_string, config->bin_path, config->bin_args, roundtime_str, config->consensus, config->npl); iov_vec[2].iov_base = json_buf; iov_vec[2].iov_len = json_string_len; - char *appbill_json = " \"appbill\": {\n \"mode\": \"%s\",\n \"bin_args\": \"%s\"\n }\n}"; + // Appbill field valiues. + + const char *appbill_json = " \"appbill\": {\n \"mode\": \"%s\",\n \"bin_args\": \"%s\"\n },\n"; const size_t appbill_json_len = 67 + strlen(config->appbill.mode) + strlen(config->appbill.bin_args); char appbill_buf[appbill_json_len]; sprintf(appbill_buf, appbill_json, config->appbill.mode, config->appbill.bin_args); iov_vec[3].iov_base = appbill_buf; iov_vec[3].iov_len = appbill_json_len; + // Round limits field valies. + + const char *round_limits_json = " \"round_limits\": {\n \"user_input_bytes\": %s,\n \"user_output_bytes\": %s\n }\n}"; + + char user_input_bytes_str[20], user_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); + + const size_t round_limits_json_len = 89 + strlen(user_input_bytes_str) + strlen(user_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); + iov_vec[4].iov_base = round_limits_buf; + iov_vec[4].iov_len = round_limits_json_len; + if (ftruncate(fd, 0) == -1 || // Clear any previous content in the file. - pwritev(fd, iov_vec, 4, 0) == -1) // Start writing from begining. + pwritev(fd, iov_vec, 5, 0) == -1) // Start writing from begining. return -1; return 0; @@ -754,6 +749,23 @@ void __hp_populate_patch_from_json_object(struct hp_config *config, const struct sub_ele = sub_ele->next; } while (sub_ele); } + else if (strcmp(k->string, "round_limits") == 0) + { + struct json_object_s *object = (struct json_object_s *)elem->value->payload; + struct json_object_element_s *sub_ele = object->start; + do + { + if (strcmp(sub_ele->name->string, "user_input_bytes") == 0) + { + __HP_ASSIGN_UINT64(config->round_limits.user_input_bytes, sub_ele); + } + else if (strcmp(sub_ele->name->string, "user_output_bytes") == 0) + { + __HP_ASSIGN_UINT64(config->round_limits.user_output_bytes, sub_ele); + } + sub_ele = sub_ele->next; + } while (sub_ele); + } elem = elem->next; } while (elem); @@ -882,21 +894,4 @@ int __hp_write_control_msg(const void *buf, const uint32_t len) return write(__hpc.control_fd, buf, len); } -/** - * Free memory allocated for the given patch config struct. - * @param patch_config Pointer of patch config struct to be freed. -*/ -void __hp_free_patch_config(struct hp_config *patch_config) -{ - __HP_FREE(patch_config->version); - __HP_FREE(patch_config->unl.list); - __HP_FREE(patch_config->bin_path); - __HP_FREE(patch_config->bin_args); - __HP_FREE(patch_config->consensus); - __HP_FREE(patch_config->npl); - __HP_FREE(patch_config->appbill.mode); - __HP_FREE(patch_config->appbill.bin_args); - __HP_FREE(patch_config); -} - #endif \ No newline at end of file diff --git a/examples/js_client/browser-example.html b/examples/js_client/browser-example.html index 67921773..864ee936 100644 --- a/examples/js_client/browser-example.html +++ b/examples/js_client/browser-example.html @@ -80,7 +80,10 @@ console.log('HotPocket Connected.'); hpc.sendContractReadRequest("Hello"); - hpc.sendContractInput("World!") + hpc.sendContractInput("World!").then(status => { + if (status != "ok") + console.log(status); + }); // When we need to close HP connection: // await hpc.close(); diff --git a/examples/js_client/text-client.js b/examples/js_client/text-client.js index 6ed4071d..97faec99 100644 --- a/examples/js_client/text-client.js +++ b/examples/js_client/text-client.js @@ -93,7 +93,10 @@ async function main() { if (inp.startsWith("read ")) hpc.sendContractReadRequest(inp.substr(5)); else { - hpc.sendContractInput(inp); + hpc.sendContractInput(inp).then(status => { + if (status != "ok") + console.log(status); + }); } } diff --git a/examples/nodejs_contract/echo_contract.js b/examples/nodejs_contract/echo_contract.js index 1ce1d503..7252c33d 100644 --- a/examples/nodejs_contract/echo_contract.js +++ b/examples/nodejs_contract/echo_contract.js @@ -50,13 +50,9 @@ const echoContract = async (ctx) => { // } // Update patch config - // await ctx.updateConfig({ - // version: "2.0", - // unl: [ - // "edf3f3bff36e22d0e1c7abf791ca4900e717754443b8e861dcfbf1cd2bbd0f6159" - // ], - // consensus: "private" - // }); + // const config = await ctx.getConfig(); + // config.unl.push("edf3f3bff36e22d0e1c7abf791ca4900e717754443b8e861dcfbf1cd2bbd0f6159"); + // await ctx.updateConfig(config); } const hpc = new HotPocket.Contract(); diff --git a/examples/nodejs_contract/hp-contract-lib.js b/examples/nodejs_contract/hp-contract-lib.js index 6f56ae7e..bd9e8ce0 100644 --- a/examples/nodejs_contract/hp-contract-lib.js +++ b/examples/nodejs_contract/hp-contract-lib.js @@ -15,6 +15,8 @@ const clientProtocols = { } Object.freeze(clientProtocols); +const PATCH_CONFIG_PATH = "../patch.cfg"; + class HotPocketContract { #controlChannel = null; @@ -66,140 +68,6 @@ class HotPocketContract { } } -// Represents patch config. -class PatchConfig { - #patchConfigPath = "../patch.cfg"; - #version = null; - #binPath = null; - #binArgs = null; - #roundtime = null; - #consensus = null; - #npl = null; - #unl = null; - #appbillMode = null; - #appbillBinArgs = null; - - // Loads the config value if there's a patch config file. Otherwise throw error. - readConfig() { - if (!fs.existsSync(this.#patchConfigPath)) - throw "Patch config file does not exist."; - - const fileContent = fs.readFileSync(this.#patchConfigPath); - const config = JSON.parse(fileContent); - this.#version = config.version; - this.#binPath = config.bin_path; - this.#binArgs = config.bin_args; - this.#roundtime = +config.roundtime; - this.#consensus = config.consensus; - this.#npl = config.npl; - this.#unl = config.unl; - this.#appbillMode = config.appbill.mode; - this.#appbillBinArgs = config.appbill.bin_args; - } - - setVersion(version) { - if (!version) - throw "Contract version is not specified."; - this.#version = version; - } - - setUnl(unl) { - if (!unl || !unl.length) - throw "UNL list cannot be empty."; - - let updatedUnl = []; - for (let pubKey of unl) { - // Pubkeys are validated against length, ed prefix and hex characters. - if (!pubKey.length) - throw "UNL pubKey not specified."; - else if (!(/^(e|E)(d|D)[0-9a-fA-F]{64}$/g.test(pubKey))) - throw "Invalid UNL pubKey specified."; - - updatedUnl.push(pubKey); - } - this.#unl = updatedUnl; - } - - setBinPath(binPath) { - if (!binPath.length) - throw "Binary path cannot be empty."; - this.#binPath = binPath; - } - - setBinArgs(binArgs) { - this.#binArgs = binArgs; - } - - setRoundtime(roundtime) { - if (roundtime == 0) - throw "Round time cannot be zero." - this.#roundtime = roundtime; - } - - setConsensus(consensus) { - if (consensus != "public" && consensus != "private") - throw "Invalid consensus flag configured in patch file. Valid values: public|private"; - this.#consensus = consensus; - } - - setNpl(npl) { - if (npl != "public" && npl != "private") - throw "Invalid npl flag configured in patch file. Valid values: public|private"; - this.#npl = npl; - } - - setAppbillMode(appbillMode) { - this.#appbillMode = appbillMode; - } - - setAppbillBinArgs(appbillBinArgs) { - this.#appbillBinArgs = appbillBinArgs; - } - - // Saves the config changes to tha patch config. - saveChanges() { - // Property order is simmilar to the property order of the patch.cfg created by HP at the startup. - const config = { - version: this.#version, - unl: this.#unl, - bin_path: this.#binPath, - bin_args: this.#binArgs ? this.#binArgs : "", - roundtime: this.#roundtime, - consensus: this.#consensus, - npl: this.#npl, - appbill: { - mode: this.#appbillMode ? this.#appbillMode : "", - bin_args: this.#appbillBinArgs ? this.#appbillBinArgs : "" - } - }; - - return new Promise((resolve, reject) => { - // Format json to match with the patch.cfg json format created by HP at the startup. - fs.writeFile(this.#patchConfigPath, JSON.stringify(config, null, 4), (err) => { - if (err) reject(err); - else resolve(); - }); - }); - } - - // Returns patch config as a JSON object. - getConfig() { - return { - version: this.#version, - unl: this.#unl, - roundtime: this.#roundtime, - consensus: this.#consensus, - npl: this.#npl, - binPath: this.#binPath, - binArgs: this.#binArgs, - appbill: { - mode: this.#appbillMode, - binArgs: this.#appbillBinArgs - } - } - } -} - class ContractContext { #controlChannel = null; @@ -215,75 +83,69 @@ class ContractContext { this.#patchConfig = new PatchConfig(); } - // Updates the config with given parameters and save the patch config. - // Params, - // { - // version: contract version as string, - // unl: list of unl pubkeys. Expecting "ed" prefixed (ed22519 algorithm) hex pubkeys, - // consensus: consensus private|public, - // npl: npl private|public, - // binPath: contract binary path string, - // binArgs: contract binary args string, - // appbillMode: appbill mode string, - // appbillBinArgs: appbill binary args string - // } - async updateConfig(params) { - if (this.readonly) - throw "Config update not allowed in readonly mode."; - - // Read current patch config values before update. - this.#patchConfig.readConfig(); - - if (params.hasOwnProperty('version')) { - this.#patchConfig.setVersion(params.version); - } - if (params.hasOwnProperty('unl')) { - this.#patchConfig.setUnl(params.unl); - } - // Commented and disabled updating until roundtime sniffing heuristics is implemented in HP. - // if (params.hasOwnProperty('roundtime')) { - // this.#patchConfig.setRoundtime(params.roundtime); - // } - if (params.hasOwnProperty('consensus')) { - this.#patchConfig.setConsensus(params.consensus); - } - if (params.hasOwnProperty('npl')) { - this.#patchConfig.setNpl(params.npl); - } - if (params.hasOwnProperty('binPath')) { - this.#patchConfig.setBinPath(params.binPath); - } - if (params.hasOwnProperty('binArgs')) { - this.#patchConfig.setBinArgs(params.binArgs); - } - if (params.hasOwnProperty('appbillMode')) { - this.#patchConfig.setAppbillMode(params.appbillMode); - } - if (params.hasOwnProperty('appbillBinArgs')) { - this.#patchConfig.setAppbillBinArgs(params.appbillBinArgs); - } - await this.#patchConfig.saveChanges(); + // Returns the config values in patch config. + getConfig() { + return this.#patchConfig.getConfig(); } - // Returns the config values in patch config. - // Returns, - // { - // version: contract version as string, - // unl: list of unl pubkeys. "ed" prefixed (ed22519 algorithm) hex pubkeys, - // roundtime: roundtime as Number, - // consensus: consensus private|public, - // npl: npl private|public, - // binPath: contract binary path string, - // binArgs: contract binary args string, - // appbill: { - // mode: appbill mode string, - // binArgs: appbill binary args string - // } - // } + // Updates the config with given config object and save the patch config. + updateConfig(config) { + return this.#patchConfig.updateConfig(config); + } +} + +// Handles patch config manipulation. +class PatchConfig { + + // Loads the config value if there's a patch config file. Otherwise throw error. getConfig() { - // Read current patch config values. - this.#patchConfig.readConfig() - return this.#patchConfig.getConfig(); + if (!fs.existsSync(PATCH_CONFIG_PATH)) + throw "Patch config file does not exist."; + + return new Promise((resolve, reject) => { + fs.readFile(PATCH_CONFIG_PATH, 'utf8', function (err, data) { + if (err) reject(err); + else resolve(JSON.parse(data)); + }); + }); + } + + updateConfig(config) { + + this.validateConfig(config); + + return new Promise((resolve, reject) => { + // Format json to match with the patch.cfg json format created by HP at the startup. + fs.writeFile(PATCH_CONFIG_PATH, JSON.stringify(config, null, 4), (err) => { + if (err) reject(err); + else resolve(); + }); + }); + } + + validateConfig(config) { + // Validate all config fields. + if (!config.version) + throw "Contract version is not specified."; + if (!config.unl || !config.unl.length) + throw "UNL list cannot be empty."; + for (let pubKey of config.unl) { + // Pubkeys are validated against length, ed prefix and hex characters. + if (!pubKey.length) + throw "UNL pubKey not specified."; + else if (!(/^(e|E)(d|D)[0-9a-fA-F]{64}$/g.test(pubKey))) + throw "Invalid UNL pubKey specified."; + } + if (!config.bin_path || !config.bin_path.length) + throw "Binary path cannot be empty."; + if (config.roundtime <= 0) + throw "Round time must be higher than zero." + if (config.consensus != "public" && config.consensus != "private") + 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) + throw "Invalid round limits."; } } diff --git a/src/sc/sc.cpp b/src/sc/sc.cpp index 32e7df0a..814be588 100644 --- a/src/sc/sc.cpp +++ b/src/sc/sc.cpp @@ -589,7 +589,7 @@ namespace sc * @param bufmap A map which has a public key and input/output buffer pair for that public key. * @return 0 if no bytes were read. 1 if bytes were read. */ - int read_contract_fdmap_outputs(contract_fdmap_t &fdmap, const pollfd *pfds, contract_bufmap_t &bufmap) + int read_contract_fdmap_outputs(contract_fdmap_t &fdmap, pollfd *pfds, contract_bufmap_t &bufmap) { bool bytes_read = false; int i = 0; @@ -600,7 +600,7 @@ namespace sc fd_pair &fds = fdmap[pubkey]; // This returns the total bytes read from the socket. - const int total_bytes_read = read_iosocket(true, pfds[i++], output); + const int total_bytes_read = (pfds[i].fd == -1) ? 0 : read_iosocket(true, pfds[i], output); if (total_bytes_read == -1) { @@ -649,14 +649,29 @@ namespace sc possible_read_len = total_bytes_read - pos; } // Extract the message chunk from the buffer. - std::string msgBuf = output.substr(pos, possible_read_len); + std::string msg_buf = output.substr(pos, possible_read_len); pos += possible_read_len; // Append the extracted message chunk to the current message. - current_output.message += msgBuf; + current_output.message += msg_buf; } - bytes_read = true; + // Increment total collected output len for this user. + bufs.total_output_len += total_bytes_read; + + // If total outputs exceeds limit for this user, close the user's out fd. + if (conf::cfg.contract.round_limits.user_output_bytes > 0 && + bufs.total_output_len > conf::cfg.contract.round_limits.user_output_bytes) + { + close(pfds[i].fd); + pfds[i].fd = -1; + } + else + { + bytes_read = true; + } } + + i++; } return bytes_read ? 1 : 0; diff --git a/src/sc/sc.hpp b/src/sc/sc.hpp index 7e1f06b1..079e8ca1 100644 --- a/src/sc/sc.hpp +++ b/src/sc/sc.hpp @@ -43,6 +43,9 @@ namespace sc // List of outputs from the contract. std::list outputs; + + // Total output bytes accumulated so far. + size_t total_output_len = 0; }; // Common typedef for a map of pubkey->fdpair. @@ -164,7 +167,7 @@ namespace sc int create_iosockets_for_fdmap(contract_fdmap_t &fdmap, contract_bufmap_t &bufmap); - int read_contract_fdmap_outputs(contract_fdmap_t &fdmap, const pollfd *pfds, contract_bufmap_t &bufmap); + int read_contract_fdmap_outputs(contract_fdmap_t &fdmap, pollfd *pfds, contract_bufmap_t &bufmap); int create_contract_log_files(execution_context &ctx);