diff --git a/examples/c_contract/hotpocket_contract.h b/examples/c_contract/hotpocket_contract.h index beee9491..d357c4f8 100644 --- a/examples/c_contract/hotpocket_contract.h +++ b/examples/c_contract/hotpocket_contract.h @@ -154,9 +154,10 @@ struct hp_config struct hp_unl_collection unl; char *bin_path; char *bin_args; - u_int16_t roundtime; + uint32_t roundtime; char *consensus; char *npl; + uint16_t max_input_ledger_offset; struct hp_appbill_config appbill; struct hp_round_limits_config round_limits; }; @@ -505,6 +506,9 @@ int hp_update_config(const struct hp_config *config) if (config->roundtime <= 0) __HP_UPDATE_CONFIG_ERROR("Round time must be higher than 0."); + + if (config->max_input_ledger_offset < 0) + __HP_UPDATE_CONFIG_ERROR("Invalid max input ledger offset."); 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"); @@ -643,14 +647,17 @@ int __hp_write_to_patch_file(const int fd, const struct hp_config *config) // 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"; + " \"consensus\": \"%s\",\n \"npl\": \"%s\",\n \"max_input_ledger_offset\": %s,\n"; 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 max_input_ledger_offset_str[16]; + sprintf(max_input_ledger_offset_str, "%d", config->max_input_ledger_offset); + + const size_t json_string_len = 128 + strlen(config->bin_path) + strlen(config->bin_args) + strlen(roundtime_str) + strlen(config->consensus) + strlen(config->npl) + strlen(max_input_ledger_offset_str); char json_buf[json_string_len]; - sprintf(json_buf, json_string, config->bin_path, config->bin_args, roundtime_str, config->consensus, config->npl); + sprintf(json_buf, json_string, config->bin_path, config->bin_args, roundtime_str, config->consensus, config->npl, max_input_ledger_offset_str); iov_vec[2].iov_base = json_buf; iov_vec[2].iov_len = json_string_len; @@ -746,6 +753,11 @@ void __hp_populate_patch_from_json_object(struct hp_config *config, const struct const struct json_number_s *value = (struct json_number_s *)elem->value->payload; config->roundtime = strtol(value->number, NULL, 0); } + else if (strcmp(k->string, "max_input_ledger_offset") == 0) + { + const struct json_number_s *value = (struct json_number_s *)elem->value->payload; + config->max_input_ledger_offset = strtoul(value->number, NULL, 0); + } else if (strcmp(k->string, "consensus") == 0) { __HP_ASSIGN_CHAR_PTR(config->consensus, elem); diff --git a/examples/nodejs_contract/hp-contract-lib.js b/examples/nodejs_contract/hp-contract-lib.js index 8a45d6a9..b36bf7ee 100644 --- a/examples/nodejs_contract/hp-contract-lib.js +++ b/examples/nodejs_contract/hp-contract-lib.js @@ -149,6 +149,8 @@ class PatchConfig { if (config.round_limits.user_input_bytes < 0 || config.round_limits.user_output_bytes < 0 || config.round_limits.npl_output_bytes < 0 || config.round_limits.proc_cpu_seconds < 0 || config.round_limits.proc_mem_bytes < 0 || config.round_limits.proc_ofd_count < 0) throw "Invalid round limits."; + if (config.max_input_ledger_offset < 0) + throw "Invalid max input ledger offset"; } } diff --git a/src/conf.cpp b/src/conf.cpp index 5e888a67..0b7f2cff 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -150,6 +150,7 @@ namespace conf cfg.contract.roundtime = 1000; cfg.contract.is_consensus_public = false; cfg.contract.is_npl_public = false; + cfg.contract.max_input_ledger_offset = 10; cfg.mesh.port = 22860; cfg.mesh.msg_forwarding = true; @@ -516,12 +517,12 @@ namespace conf // We always save the startup role to config. Not the current role which might get changed dynamically during syncing. node_config.insert_or_assign("role", cfg.node.role == ROLE::OBSERVER ? ROLE_OBSERVER : ROLE_VALIDATOR); node_config.insert_or_assign("history", cfg.node.history == HISTORY::FULL ? HISTORY_FULL : HISTORY_CUSTOM); - + jsoncons::ojson history_config; history_config.insert_or_assign("max_primary_shards", cfg.node.history_config.max_primary_shards); history_config.insert_or_assign("max_blob_shards", cfg.node.history_config.max_blob_shards); node_config.insert_or_assign("history_config", history_config); - + d.insert_or_assign("node", node_config); } @@ -905,6 +906,7 @@ namespace conf jdoc.insert_or_assign("roundtime", contract.roundtime.load()); jdoc.insert_or_assign("consensus", contract.is_consensus_public ? PUBLIC : PRIVATE); jdoc.insert_or_assign("npl", contract.is_npl_public ? PUBLIC : PRIVATE); + jdoc.insert_or_assign("max_input_ledger_offset", contract.max_input_ledger_offset); jsoncons::ojson appbill; appbill.insert_or_assign("mode", contract.appbill.mode); @@ -995,6 +997,7 @@ namespace conf return -1; } contract.is_npl_public = jdoc["npl"] == PUBLIC; + contract.max_input_ledger_offset = jdoc["max_input_ledger_offset"].as(); jpath = "contract.appbill"; contract.appbill.mode = jdoc["appbill"]["mode"].as(); diff --git a/src/conf.hpp b/src/conf.hpp index 989bb66a..6e460160 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -56,7 +56,7 @@ namespace conf struct history_configuration { uint64_t max_primary_shards; // Maximum number of shards for primary shards. - uint64_t max_blob_shards; // Maximum number of shards for blob shards. + uint64_t max_blob_shards; // Maximum number of shards for blob shards. }; // Log severity levels used in Hot Pocket. @@ -122,6 +122,7 @@ namespace conf std::atomic roundtime = 0; // Consensus round time in ms bool is_consensus_public = false; // If true, consensus are broadcasted to non-unl nodes as well. bool is_npl_public = false; // If true, npl messages are broadcasted to non-unl nodes as well. + uint16_t max_input_ledger_offset; // Maximum ledger sequence number offset that can be specified in the input. appbill_config appbill; round_limits_config round_limits; diff --git a/src/msg/usrmsg_common.hpp b/src/msg/usrmsg_common.hpp index 21fe05c2..a16a7c83 100644 --- a/src/msg/usrmsg_common.hpp +++ b/src/msg/usrmsg_common.hpp @@ -56,6 +56,7 @@ namespace msg::usrmsg constexpr const char *REASON_BAD_SIG = "bad_sig"; constexpr const char *REASON_APPBILL_BALANCE_EXCEEDED = "appbill_balance_exceeded"; constexpr const char *REASON_MAX_LEDGER_EXPIRED = "max_ledger_expired"; + constexpr const char *REASON_MAX_LEDGER_OFFSET_EXCEEDED = "max_ledger_offset_exceeded"; constexpr const char *REASON_NONCE_EXPIRED = "nonce_expired"; constexpr const char *REASON_ALREADY_SUBMITTED = "already_submitted"; constexpr const char *REASON_NONCE_OVERFLOW = "nonce_overflow"; diff --git a/src/usr/usr.cpp b/src/usr/usr.cpp index 26262707..f9f5f8fd 100644 --- a/src/usr/usr.cpp +++ b/src/usr/usr.cpp @@ -176,6 +176,14 @@ namespace usr uint64_t max_lcl_seq_no; if (parser.extract_input_container(input_data, nonce, max_lcl_seq_no, input_container) != -1) { + const p2p::sequence_hash lcl_id = ledger::ctx.get_lcl_id(); + // Ignore the input if the max ledger seq number specified is beyond the max offeset. + if (conf::cfg.contract.max_input_ledger_offset != 0 && max_lcl_seq_no > lcl_id.seq_no + conf::cfg.contract.max_input_ledger_offset) + { + send_input_status(parser, user.session, msg::usrmsg::STATUS_REJECTED, msg::usrmsg::REASON_MAX_LEDGER_OFFSET_EXCEEDED, sig); + return -1; + } + // Check for max nonce size. if (nonce.size() > MAX_INPUT_NONCE_SIZE) { @@ -384,6 +392,13 @@ namespace usr const char *validate_user_input_submission(const std::string &user_pubkey, const usr::extracted_user_input &extracted_input, const uint64_t lcl_seq_no, size_t &total_input_size, std::string &hash, util::buffer_view &input) { + // Ignore the input if the max ledger seq number specified is beyond the max offeset. + if (conf::cfg.contract.max_input_ledger_offset != 0 && extracted_input.max_lcl_seq_no > lcl_seq_no + conf::cfg.contract.max_input_ledger_offset) + { + LOG_DEBUG << "User input bad max ledger seq beyond the max offset."; + return msg::usrmsg::REASON_MAX_LEDGER_OFFSET_EXCEEDED; + } + // Ignore the input if our ledger has passed the input TTL. if (extracted_input.max_lcl_seq_no <= lcl_seq_no) {