User outputs round limit. (#241)

Implemented user outputs round limit and upgraded the contract libraries to support the configs.
This commit is contained in:
Ravin Perera
2021-02-15 14:23:47 +05:30
committed by GitHub
parent 033b5fa7bc
commit 0de7983504
8 changed files with 267 additions and 404 deletions

View File

@@ -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();

View File

@@ -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

View File

@@ -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();

View File

@@ -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);
});
}
}

View File

@@ -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();

View File

@@ -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.";
}
}

View File

@@ -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;

View File

@@ -43,6 +43,9 @@ namespace sc
// List of outputs from the contract.
std::list<contract_output> 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);