diff --git a/.github/workflows/verify-generated-headers.yml b/.github/workflows/verify-generated-headers.yml index 4120f7b7f9..723e7181b9 100644 --- a/.github/workflows/verify-generated-headers.yml +++ b/.github/workflows/verify-generated-headers.yml @@ -18,12 +18,23 @@ jobs: generator: bash ./hook/generate_sfcodes.sh - target: hook/tts.h generator: ./hook/generate_tts.sh - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 + env: + CLANG_VERSION: 10 name: ${{ matrix.target }} steps: - name: Checkout repository uses: actions/checkout@v4 + - name: Download and install clang-format + run: | + sudo apt-get update -y + sudo apt-get install -y libtinfo5 + curl -LO https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.1/clang+llvm-10.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz + tar -xf clang+llvm-10.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz + sudo mv clang+llvm-10.0.1-x86_64-linux-gnu-ubuntu-16.04 /opt/clang-10 + sudo ln -s /opt/clang-10/bin/clang-format /usr/local/bin/clang-format-10 + - name: Verify ${{ matrix.target }} run: | set -euo pipefail diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index 74ff58f601..6a58c5e706 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -463,6 +463,7 @@ target_sources (rippled PRIVATE src/ripple/app/tx/impl/DepositPreauth.cpp src/ripple/app/tx/impl/Escrow.cpp src/ripple/app/tx/impl/GenesisMint.cpp + src/ripple/app/tx/impl/HookDefinitionUpdate.cpp src/ripple/app/tx/impl/Import.cpp src/ripple/app/tx/impl/InvariantCheck.cpp src/ripple/app/tx/impl/Invoke.cpp diff --git a/cfg/xahaud-example.cfg b/cfg/xahaud-example.cfg index 42c520d4b3..3ed13253d2 100644 --- a/cfg/xahaud-example.cfg +++ b/cfg/xahaud-example.cfg @@ -1502,6 +1502,15 @@ # Example: # owner_reserve = 2000000 # 2 XAH # +# hook_gas_price = +# +# The gas price for hooks is used to calculate the execution fee for hooks. +# +# If this parameter is unspecified, xahaud will use an internal +# default. Don't change this without understanding the consequences. +# +# Example: +# hook_gas_price = 1000000 #------------------------------------------------------------------------------- # # 9. Misc Settings diff --git a/hook/extern.h b/hook/extern.h index ee34af6ff8..667cec2a4c 100644 --- a/hook/extern.h +++ b/hook/extern.h @@ -82,7 +82,7 @@ sto_erase( uint32_t field_id); extern int64_t -etxn_burden(void); +etxn_burden(); extern int64_t etxn_details(uint32_t write_ptr, uint32_t write_len); @@ -94,7 +94,7 @@ extern int64_t etxn_reserve(uint32_t count); extern int64_t -etxn_generation(void); +etxn_generation(); extern int64_t etxn_nonce(uint32_t write_ptr, uint32_t write_len); @@ -149,7 +149,7 @@ extern int64_t float_divide(int64_t float1, int64_t float2); extern int64_t -float_one(void); +float_one(); extern int64_t float_mantissa(int64_t float1); @@ -167,13 +167,13 @@ extern int64_t float_root(int64_t float1, uint32_t n); extern int64_t -fee_base(void); +fee_base(); extern int64_t -ledger_seq(void); +ledger_seq(); extern int64_t -ledger_last_time(void); +ledger_last_time(); extern int64_t ledger_last_hash(uint32_t write_ptr, uint32_t write_len); @@ -213,13 +213,13 @@ hook_param( uint32_t read_len); extern int64_t -hook_again(void); +hook_again(); extern int64_t hook_skip(uint32_t read_ptr, uint32_t read_len, uint32_t flags); extern int64_t -hook_pos(void); +hook_pos(); extern int64_t slot(uint32_t write_ptr, uint32_t write_len, uint32_t slot); @@ -299,19 +299,19 @@ extern int64_t trace_float(uint32_t read_ptr, uint32_t read_len, int64_t float1); extern int64_t -otxn_burden(void); +otxn_burden(); extern int64_t otxn_field(uint32_t write_ptr, uint32_t write_len, uint32_t field_id); extern int64_t -otxn_generation(void); +otxn_generation(); extern int64_t otxn_id(uint32_t write_ptr, uint32_t write_len, uint32_t flags); extern int64_t -otxn_type(void); +otxn_type(); extern int64_t otxn_slot(uint32_t slot_no); diff --git a/hook/generate_extern.sh b/hook/generate_extern.sh index 2e9598ad02..0f05dac523 100755 --- a/hook/generate_extern.sh +++ b/hook/generate_extern.sh @@ -4,7 +4,7 @@ set -eu SCRIPT_DIR=$(dirname "$0") SCRIPT_DIR=$(cd "$SCRIPT_DIR" && pwd) -APPLY_HOOK="$SCRIPT_DIR/../src/ripple/app/hook/applyHook.h" +APPLY_HOOK="$SCRIPT_DIR/../src/ripple/app/hook/hook_api.macro" { echo '// For documentation please see: https://xrpl-hooks.readme.io/reference/' @@ -19,127 +19,36 @@ APPLY_HOOK="$SCRIPT_DIR/../src/ripple/app/hook/applyHook.h" return s; } - function emit(ret, name, argc, argt, argn) { - attr = (name == "_g") ? " __attribute__((noduplicate))" : ""; - if (!first) - printf("\n"); - first = 0; - printf("extern %s%s\n", ret, attr); - if (argc == 0) { - printf("%s(void);\n", name); - return; - } - if (argc <= 3) { - line = argt[1] " " argn[1]; - for (i = 2; i <= argc; ++i) - line = line ", " argt[i] " " argn[i]; - printf("%s(%s);\n", name, line); - return; - } - printf("%s(\n", name); - for (i = 1; i <= argc; ++i) { - sep = (i < argc) ? "," : ");"; - printf(" %s %s%s\n", argt[i], argn[i], sep); - } - } - - function process(buffer, kind, payload, parts, n, i, arg, tokens, argc, argt, argn) { - if (kind == "func") - sub(/^DECLARE_HOOK_FUNCTION[[:space:]]*\(/, "", buffer); - else - sub(/^DECLARE_HOOK_FUNCNARG[[:space:]]*\(/, "", buffer); - buffer = trim(buffer); - sub(/\)[[:space:]]*$/, "", buffer); - n = split(buffer, parts, ","); - for (i = 1; i <= n; ++i) - parts[i] = trim(parts[i]); - ret = parts[1]; - name = parts[2]; - argc = 0; - delete argt; - delete argn; - for (i = 3; i <= n; ++i) { - arg = parts[i]; - if (arg == "") - continue; - split(arg, tokens, /[[:space:]]+/); - if (length(tokens) < 2) - continue; - ++argc; - argt[argc] = tokens[1]; - argn[argc] = tokens[2]; - } - emit(ret, name, argc, argt, argn); - } - - BEGIN { - first = 1; - in_block = 0; - in_macro = 0; - } - { line = $0; - if (in_block) { - if (line ~ /\*\//) { - sub(/.*\*\//, "", line); - in_block = 0; - } - else - next; - } - while (line ~ /\/\*/) { - if (line ~ /\/\*.*\*\//) { - gsub(/\/\*.*\*\//, "", line); - } - else { - sub(/\/\*.*/, "", line); - in_block = 1; - break; - } - } - sub(/\/\/.*$/, "", line); - line = trim(line); - if (line == "") - next; - - if (!in_macro && line ~ /^DECLARE_HOOK_FUNCTION\(/) { - buffer = line; - kind = "func"; - if (line ~ /\);[[:space:]]*$/) { - sub(/\);[[:space:]]*$/, "", buffer); - process(buffer, kind); - } - else - in_macro = 1; + + # Skip block comments + if (line ~ /\/\*/) { next; } - if (!in_macro && line ~ /^DECLARE_HOOK_FUNCNARG\(/) { - buffer = line; - kind = "narg"; - if (line ~ /\);[[:space:]]*$/) { - sub(/\);[[:space:]]*$/, "", buffer); - process(buffer, kind); - } - else - in_macro = 1; - next; - } - if (in_macro) { - buffer = buffer " " line; - if (line ~ /\);[[:space:]]*$/) { - sub(/\);[[:space:]]*$/, "", buffer); - process(buffer, kind); - in_macro = 0; + + # Look for comment lines that start with // and contain function signature + if (line ~ /^[[:space:]]*\/\/[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]+[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*\(/) { + # Remove leading // and trim + sub(/^[[:space:]]*\/\/[[:space:]]*/, "", line); + line = trim(line); + + # Check if function name is "_g" to add attribute + if (line ~ /[[:space:]]+_g[[:space:]]*\(/) { + # Insert __attribute__((noduplicate)) before _g + sub(/[[:space:]]+_g/, " __attribute__((noduplicate)) _g", line); } + + # printf("\n"); + + printf("extern %s\n\n", line); } } - - END { - printf("\n"); - } ' "$APPLY_HOOK" echo '#define HOOK_EXTERN' echo '#endif // HOOK_EXTERN' -} +} | ( + cd "$SCRIPT_DIR/.." + clang-format --style=file - +) diff --git a/hook/sfcodes.h b/hook/sfcodes.h index 8c7bc48f49..100c480512 100644 --- a/hook/sfcodes.h +++ b/hook/sfcodes.h @@ -63,6 +63,7 @@ #define sfEmitGeneration ((2U << 16U) + 46U) #define sfLockCount ((2U << 16U) + 49U) #define sfFirstNFTokenSequence ((2U << 16U) + 50U) +#define sfHookGasPrice ((2U << 16U) + 92U) #define sfStartTime ((2U << 16U) + 93U) #define sfRepeatCount ((2U << 16U) + 94U) #define sfDelaySeconds ((2U << 16U) + 95U) @@ -87,6 +88,8 @@ #define sfHookInstructionCount ((3U << 16U) + 17U) #define sfHookReturnCode ((3U << 16U) + 18U) #define sfReferenceCount ((3U << 16U) + 19U) +#define sfHookCallbackCost ((3U << 16U) + 95U) +#define sfHookCost ((3U << 16U) + 96U) #define sfTouchCount ((3U << 16U) + 97U) #define sfAccountIndex ((3U << 16U) + 98U) #define sfAccountCount ((3U << 16U) + 99U) diff --git a/hook/tts.h b/hook/tts.h index bd03b03995..e2f564555a 100644 --- a/hook/tts.h +++ b/hook/tts.h @@ -34,6 +34,7 @@ #define ttURITOKEN_BUY 47 #define ttURITOKEN_CREATE_SELL_OFFER 48 #define ttURITOKEN_CANCEL_SELL_OFFER 49 +#define ttHOOK_DEFINITION_UPDATE 91 #define ttCRON 92 #define ttCRON_SET 93 #define ttREMARKS_SET 94 diff --git a/src/ripple/app/hook/Enum.h b/src/ripple/app/hook/Enum.h index 9728b10401..4ebb711e82 100644 --- a/src/ripple/app/hook/Enum.h +++ b/src/ripple/app/hook/Enum.h @@ -5,6 +5,28 @@ #include #ifndef HOOKENUM_INCLUDED #define HOOKENUM_INCLUDED 1 + +#ifndef GUARD_CHECKER_BUILD +#include +#include +#include +#else +// Override uint256, Feature and Rules for guard checker build +#define uint256 std::string +#define featureHooksUpdate1 "1" +#define fix20250131 "1" +namespace hook_api { +struct Rules +{ + constexpr bool + enabled(const uint256& feature) const + { + return true; + } +}; +} // namespace hook_api +#endif + namespace ripple { enum HookSetOperation : int8_t { hsoINVALID = -1, @@ -367,110 +389,75 @@ const uint8_t max_emit = 255; const uint8_t max_params = 16; const double fee_base_multiplier = 1.1f; -#define I32 0x7FU -#define I64 0x7EU - -#define HOOK_WRAP_PARAMS(...) __VA_ARGS__ -#define HOOK_API_DEFINITION(RETURN_TYPE, FUNCTION_NAME, PARAMS_TUPLE) \ - { \ -#FUNCTION_NAME, \ - { \ - RETURN_TYPE, HOOK_WRAP_PARAMS PARAMS_TUPLE \ - } \ - } - -using APIWhitelist = std::map>; +using APIWhitelist = + std::map, uint32_t>>; // RH NOTE: Find descriptions of api functions in ./impl/applyHook.cpp and // hookapi.h (include for hooks) this is a map of the api name to its return // code (vec[0] and its parameters vec[>0]) as wasm type codes -static const APIWhitelist import_whitelist{ - // clang-format off - HOOK_API_DEFINITION(I32, _g, (I32, I32)), - HOOK_API_DEFINITION(I64, accept, (I32, I32, I64)), - HOOK_API_DEFINITION(I64, rollback, (I32, I32, I64)), - HOOK_API_DEFINITION(I64, util_raddr, (I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, util_accid, (I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, util_verify, (I32, I32, I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, util_sha512h, (I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, util_keylet, (I32, I32, I32, I32, I32, I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, sto_validate, (I32, I32)), - HOOK_API_DEFINITION(I64, sto_subfield, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, sto_subarray, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, sto_emplace, (I32, I32, I32, I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, sto_erase, (I32, I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, etxn_burden, ()), - HOOK_API_DEFINITION(I64, etxn_details, (I32, I32)), - HOOK_API_DEFINITION(I64, etxn_fee_base, (I32, I32)), - HOOK_API_DEFINITION(I64, etxn_reserve, (I32)), - HOOK_API_DEFINITION(I64, etxn_generation, ()), - HOOK_API_DEFINITION(I64, etxn_nonce, (I32, I32)), - HOOK_API_DEFINITION(I64, emit, (I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, float_set, (I32, I64)), - HOOK_API_DEFINITION(I64, float_multiply, (I64, I64)), - HOOK_API_DEFINITION(I64, float_mulratio, (I64, I32, I32, I32)), - HOOK_API_DEFINITION(I64, float_negate, (I64)), - HOOK_API_DEFINITION(I64, float_compare, (I64, I64, I32)), - HOOK_API_DEFINITION(I64, float_sum, (I64, I64)), - HOOK_API_DEFINITION(I64, float_sto, (I32, I32, I32, I32, I32, I32, I64, I32)), - HOOK_API_DEFINITION(I64, float_sto_set, (I32, I32)), - HOOK_API_DEFINITION(I64, float_invert, (I64)), - HOOK_API_DEFINITION(I64, float_divide, (I64, I64)), - HOOK_API_DEFINITION(I64, float_one, ()), - HOOK_API_DEFINITION(I64, float_mantissa, (I64)), - HOOK_API_DEFINITION(I64, float_sign, (I64)), - HOOK_API_DEFINITION(I64, float_int, (I64, I32, I32)), - HOOK_API_DEFINITION(I64, float_log, (I64)), - HOOK_API_DEFINITION(I64, float_root, (I64, I32)), - HOOK_API_DEFINITION(I64, fee_base, ()), - HOOK_API_DEFINITION(I64, ledger_seq, ()), - HOOK_API_DEFINITION(I64, ledger_last_time, ()), - HOOK_API_DEFINITION(I64, ledger_last_hash, (I32, I32)), - HOOK_API_DEFINITION(I64, ledger_nonce, (I32, I32)), - HOOK_API_DEFINITION(I64, ledger_keylet, (I32, I32, I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, hook_account, (I32, I32)), - HOOK_API_DEFINITION(I64, hook_hash, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, hook_param_set, (I32, I32, I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, hook_param, (I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, hook_again, ()), - HOOK_API_DEFINITION(I64, hook_skip, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, hook_pos, ()), - HOOK_API_DEFINITION(I64, slot, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, slot_clear, (I32)), - HOOK_API_DEFINITION(I64, slot_count, (I32)), - HOOK_API_DEFINITION(I64, slot_set, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, slot_size, (I32)), - HOOK_API_DEFINITION(I64, slot_subarray, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, slot_subfield, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, slot_type, (I32, I32)), - HOOK_API_DEFINITION(I64, slot_float, (I32)), - HOOK_API_DEFINITION(I64, state_set, (I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, state_foreign_set, (I32, I32, I32, I32, I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, state, (I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, state_foreign, (I32, I32, I32, I32, I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, trace, (I32, I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, trace_num, (I32, I32, I64)), - HOOK_API_DEFINITION(I64, trace_float, (I32, I32, I64)), - HOOK_API_DEFINITION(I64, otxn_burden, ()), - HOOK_API_DEFINITION(I64, otxn_field, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, otxn_generation, ()), - HOOK_API_DEFINITION(I64, otxn_id, (I32, I32, I32)), - HOOK_API_DEFINITION(I64, otxn_type, ()), - HOOK_API_DEFINITION(I64, otxn_slot, (I32)), - HOOK_API_DEFINITION(I64, otxn_param, (I32, I32, I32, I32)), - HOOK_API_DEFINITION(I64, meta_slot, (I32)), - // clang-format on -}; +inline APIWhitelist +getImportWhitelist(Rules const& rules) +{ + APIWhitelist whitelist; -// featureHooks1 -static const APIWhitelist import_whitelist_1{ - // clang-format off - HOOK_API_DEFINITION(I64, xpop_slot, (I32, I32)), - // clang-format on -}; +#pragma push_macro("HOOK_API_DEFINITION") +#pragma push_macro("HOOK_API_COST") +#undef HOOK_API_DEFINITION +#undef HOOK_API_COST + +#define int64_t 0x7EU +#define int32_t 0x7FU +#define uint32_t 0x7FU + +#define HOOK_WRAP_PARAMS(...) __VA_ARGS__ + +#define HOOK_API_DEFINITION( \ + RETURN_TYPE, FUNCTION_NAME, PARAMS_TUPLE, AMENDMENT) \ + if (AMENDMENT == uint256{} || rules.enabled(AMENDMENT)) \ + whitelist[#FUNCTION_NAME] = { \ + {RETURN_TYPE, HOOK_WRAP_PARAMS PARAMS_TUPLE}, 0}; + +#define HOOK_API_COST(FUNCTION_NAME, COST, AMENDMENT) \ + if (AMENDMENT == uint256{} || rules.enabled(AMENDMENT)) \ + whitelist[#FUNCTION_NAME].second = COST; + +#include "hook_api.macro" + +#undef HOOK_API_COST +#undef HOOK_API_DEFINITION +#undef HOOK_WRAP_PARAMS +#undef int64_t +#undef int32_t +#undef uint32_t +#pragma pop_macro("HOOK_API_COST") +#pragma pop_macro("HOOK_API_DEFINITION") + + // all cost should > 0 + for (auto& [function, pair] : whitelist) + { + if (pair.second <= 0) + return {}; + } + + return whitelist; +} #undef HOOK_API_DEFINITION #undef I32 #undef I64 + +enum GuardRulesVersion : uint64_t { + GuardRuleFix20250131 = 0x00000001, +}; + +inline uint64_t +getGuardRulesVersion(Rules const& rules) +{ + uint64_t version = 0; + if (rules.enabled(fix20250131)) + version |= GuardRuleFix20250131; + return version; +} + }; // namespace hook_api #endif diff --git a/src/ripple/app/hook/Guard.h b/src/ripple/app/hook/Guard.h index f395af4481..53b78aebcc 100644 --- a/src/ripple/app/hook/Guard.h +++ b/src/ripple/app/hook/Guard.h @@ -145,6 +145,7 @@ struct WasmBlkInf uint32_t sanity_check; uint32_t iteration_bound; uint32_t instruction_count; + uint32_t execution_cost; WasmBlkInf* parent; std::vector children; uint32_t start_byte; @@ -194,7 +195,7 @@ struct WasmBlkInf level, \ " " \ " ", \ - worst_case_execution, \ + instruction_count, \ blk->start_byte, \ blk->instruction_count, \ blk->iteration_bound, \ @@ -203,20 +204,20 @@ struct WasmBlkInf &(blk->parent)); \ } // compute worst case execution time -inline uint64_t +inline std::pair compute_wce(const WasmBlkInf* blk, int level, bool* recursion_limit_reached) { if (level > 16) { *recursion_limit_reached = true; - return 0; + return {0, 0}; } if (blk->sanity_check != 0x1234ABCDU) { printf("!!! sanity check failed\n"); *recursion_limit_reached = true; - return (uint64_t)-1; + return {(uint64_t)-1, (uint64_t)-1}; } WasmBlkInf const* parent = blk->parent; @@ -225,23 +226,28 @@ compute_wce(const WasmBlkInf* blk, int level, bool* recursion_limit_reached) { printf("!!! parent sanity check failed\n"); *recursion_limit_reached = true; - return (uint64_t)-1; + return {(uint64_t)-1, (uint64_t)-1}; } - uint64_t worst_case_execution = blk->instruction_count; + uint64_t instruction_count = blk->instruction_count; + uint64_t execution_cost = blk->execution_cost; double multiplier = 1.0; if (blk->children.size() > 0) for (auto const& child : blk->children) - worst_case_execution += + { + auto [child_instruction_count, child_execution_cost] = compute_wce(child, level + 1, recursion_limit_reached); + instruction_count += child_instruction_count; + execution_cost += child_execution_cost; + } if (parent == 0 || parent->iteration_bound == 0) // this condtion should never occur [defensively programmed] { PRINT_WCE(1); - return worst_case_execution; + return {instruction_count, execution_cost}; } // if the block has a parent then the quotient of its guard and its parent's @@ -250,12 +256,12 @@ compute_wce(const WasmBlkInf* blk, int level, bool* recursion_limit_reached) multiplier = ((double)(blk->iteration_bound)) / ((double)(parent->iteration_bound)); - worst_case_execution *= multiplier; - if (worst_case_execution < 1.0) - worst_case_execution = 1.0; + instruction_count *= multiplier; + if (instruction_count < 1.0) + instruction_count = 1.0; PRINT_WCE(3); - return worst_case_execution; + return {instruction_count, execution_cost}; }; // checks the WASM binary for the appropriate required _g guard calls and @@ -263,12 +269,15 @@ compute_wce(const WasmBlkInf* blk, int level, bool* recursion_limit_reached) // expr under analysis begins and end_offset is where it ends returns {worst // case instruction count} if valid or {} if invalid may throw overflow_error, // length_error -inline std::optional +inline std::optional< + std::pair> // {instruction count, execution cost} check_guard( std::vector const& wasm, int codesec, int start_offset, int end_offset, + std::map, uint32_t>> + import_type_map, int guard_func_idx, int last_import_idx, GuardLog guardLog, @@ -333,6 +342,7 @@ check_guard( ADVANCE(1); current->instruction_count++; + current->execution_cost++; // unreachable and nop instructions if (instr == 0x00U || // unreachable @@ -512,6 +522,9 @@ check_guard( GUARD_ERROR("Too many guard calls! Limit is 1024"); } + uint32_t cost = import_type_map[callee_idx].second; + current->execution_cost += cost; + continue; } @@ -634,7 +647,7 @@ check_guard( } else if (fc_type == 10) // memory.copy { - if (rulesVersion & 0x02U) + if (rulesVersion & hook_api::GuardRuleFix20250131) GUARD_ERROR("Memory.copy instruction is not allowed."); REQUIRE(2); @@ -642,7 +655,7 @@ check_guard( } else if (fc_type == 11) // memory.fill { - if (rulesVersion & 0x02U) + if (rulesVersion & hook_api::GuardRuleFix20250131) GUARD_ERROR("Memory.fill instruction is not allowed."); ADVANCE(1); @@ -788,7 +801,8 @@ check_guard( } bool recursion_limit_reached = false; - uint64_t wce = compute_wce(&(*root), 0, &recursion_limit_reached); + auto [instruction_count, execution_cost] = + compute_wce(&(*root), 0, &recursion_limit_reached); if (recursion_limit_reached) { GUARDLOG(hook::log::NESTING_LIMIT) @@ -800,9 +814,9 @@ check_guard( GUARDLOG(hook::log::INSTRUCTION_COUNT) << "GuardCheck " - << "Total worse-case execution count: " << wce << "\n"; + << "Total worse-case execution count: " << instruction_count << "\n"; - if (wce >= 0xFFFFU) + if (instruction_count >= 0xFFFFU) { GUARDLOG(hook::log::INSTRUCTION_EXCESS) << "GuardCheck " @@ -812,7 +826,7 @@ check_guard( << "\n"; return {}; } - return wce; + return std::pair{instruction_count, execution_cost}; } // RH TODO: reprogram this function to use REQUIRE/ADVANCE @@ -826,6 +840,8 @@ validateGuards( std::vector const& wasm, GuardLog guardLog, std::string guardLogAccStr, + bool returnCost, + hook_api::APIWhitelist const import_whitelist, /* RH NOTE: * rules version is a bit field, so rule update 1 is 0x01, update 2 is 0x02 * and update 3 is 0x04 ideally at rule version 3 all bits so far are set @@ -835,7 +851,7 @@ validateGuards( * might have unforeseen consequences, without also rolling back further * changes that are fine. */ - uint64_t rulesVersion = 0) + uint64_t rulesVersion = 0x00) { uint64_t byteCount = wasm.size(); @@ -875,7 +891,9 @@ validateGuards( std::map func_type_map; std::map< int /* type idx */, - std::map> + std::pair< + std::map, + uint32_t /* cost */>> import_type_map; // now we check for guards... first check if _g is imported @@ -1020,40 +1038,39 @@ validateGuards( int type_idx = parseLeb128(wasm, i, &i); CHECK_SHORT_HOOK(); + uint32_t cost = 0; + + auto it = import_whitelist.find(import_name); + auto it_end = import_whitelist.end(); + bool found_in_whitelist = (it != it_end); + if (import_name == "_g") - { guard_import_number = func_upto; - } - else if ( - hook_api::import_whitelist.find(import_name) == - hook_api::import_whitelist.end()) + + if (found_in_whitelist) + cost = it->second.second; + else { - if (rulesVersion > 0 && - hook_api::import_whitelist_1.find(import_name) != - hook_api::import_whitelist_1.end()) - { - // PASS, this is a version 1 api - } - else - { - GUARDLOG(hook::log::IMPORT_ILLEGAL) - << "Malformed transaction. " - << "Hook attempted to import a function that does " - "not " - << "appear in the hook_api function set: `" - << import_name << "`" - << "\n"; - return {}; - } + GUARDLOG(hook::log::IMPORT_ILLEGAL) + << "Malformed transaction. " + << "Hook attempted to import a function that does " + "not " + << "appear in the hook_api function set: `" + << import_name << "`" + << "\n"; + return {}; } // add to import map if (import_type_map.find(type_idx) == import_type_map.end()) import_type_map[type_idx] = { - {func_upto, std::move(import_name)}}; + {{func_upto, std::move(import_name)}}, cost}; else - import_type_map[type_idx].emplace( + { + import_type_map[type_idx].first.emplace( func_upto, std::move(import_name)); + import_type_map[type_idx].second = cost; + } func_upto++; } @@ -1256,14 +1273,11 @@ validateGuards( if (auto const& usage = import_type_map.find(j); usage != import_type_map.end()) { - for (auto const& [import_idx, api_name] : usage->second) + for (auto const& [import_idx, api_name] : + usage->second.first) { auto const& api_signature = - hook_api::import_whitelist.find(api_name) != - hook_api::import_whitelist.end() - ? hook_api::import_whitelist.find(api_name)->second - : hook_api::import_whitelist_1.find(api_name) - ->second; + import_whitelist.find(api_name)->second.first; if (!first_signature) { @@ -1502,6 +1516,7 @@ validateGuards( j, i, code_end, + import_type_map, guard_import_number, last_import_number, guardLog, @@ -1512,9 +1527,15 @@ validateGuards( return {}; if (hook_func_idx && *hook_func_idx == j) - maxInstrCountHook = *valid; + if (!returnCost) + maxInstrCountHook = valid->first; + else + maxInstrCountHook = valid->second; else if (cbak_func_idx && *cbak_func_idx == j) - maxInstrCountCbak = *valid; + if (!returnCost) + maxInstrCountCbak = valid->first; + else + maxInstrCountCbak = valid->second; else { if (DEBUG_GUARD) diff --git a/src/ripple/app/hook/Macro.h b/src/ripple/app/hook/Macro.h index 5f309b2e31..ebf3499b24 100644 --- a/src/ripple/app/hook/Macro.h +++ b/src/ripple/app/hook/Macro.h @@ -25,7 +25,8 @@ _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, N, ...) \ N #define VA_NARGS(__drop, ...) \ - VA_NARGS_IMPL(__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) + VA_NARGS_IMPL( \ + __VA_OPT__(__VA_ARGS__, ) 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define FIRST(a, b) a #define SECOND(a, b) b #define STRIP_TYPES(...) FOR_VARS(SECOND, 0, __VA_ARGS__) @@ -88,30 +89,18 @@ #define WASM_VAL_TYPE(T, b) CAT2(TYP_, T) -#define DECLARE_HOOK_FUNCTION(R, F, ...) \ - R F(hook::HookContext& hookCtx, \ - WasmEdge_CallingFrameContext const& frameCtx, \ - __VA_ARGS__); \ - extern WasmEdge_Result WasmFunction##F( \ - void* data_ptr, \ - const WasmEdge_CallingFrameContext* frameCtx, \ - const WasmEdge_Value* in, \ - WasmEdge_Value* out); \ - extern WasmEdge_ValType WasmFunctionParams##F[]; \ - extern WasmEdge_ValType WasmFunctionResult##F[]; \ - extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \ - extern WasmEdge_String WasmFunctionName##F; - -#define DECLARE_HOOK_FUNCNARG(R, F) \ - R F(hook::HookContext& hookCtx, \ - WasmEdge_CallingFrameContext const& frameCtx); \ - extern WasmEdge_Result WasmFunction##F( \ - void* data_ptr, \ - const WasmEdge_CallingFrameContext* frameCtx, \ - const WasmEdge_Value* in, \ - WasmEdge_Value* out); \ - extern WasmEdge_ValType WasmFunctionResult##F[]; \ - extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \ +#define DECLARE_HOOK_FUNCTION(R, F, ...) \ + R F(hook::HookContext& hookCtx, \ + WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \ + COMMA __VA_ARGS__)); \ + extern WasmEdge_Result WasmFunction##F( \ + void* data_ptr, \ + const WasmEdge_CallingFrameContext* frameCtx, \ + const WasmEdge_Value* in, \ + WasmEdge_Value* out); \ + extern WasmEdge_ValType WasmFunctionParams##F[]; \ + extern WasmEdge_ValType WasmFunctionResult##F[]; \ + extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \ extern WasmEdge_String WasmFunctionName##F; #define DEFINE_HOOK_FUNCTION(R, F, ...) \ @@ -121,61 +110,35 @@ const WasmEdge_Value* in, \ WasmEdge_Value* out) \ { \ - int _stack = 0; \ - FOR_VARS(VAR_ASSIGN, 2, __VA_ARGS__); \ + __VA_OPT__(int _stack = 0;) \ + __VA_OPT__(FOR_VARS(VAR_ASSIGN, 2, __VA_ARGS__);) \ hook::HookContext* hookCtx = \ reinterpret_cast(data_ptr); \ R return_code = hook_api::F( \ *hookCtx, \ - *const_cast(frameCtx), \ - STRIP_TYPES(__VA_ARGS__)); \ + *const_cast(frameCtx) \ + __VA_OPT__(COMMA STRIP_TYPES(__VA_ARGS__))); \ if (return_code == RC_ROLLBACK || return_code == RC_ACCEPT) \ return WasmEdge_Result_Terminate; \ out[0] = RET_ASSIGN(R, return_code); \ return WasmEdge_Result_Success; \ }; \ WasmEdge_ValType hook_api::WasmFunctionParams##F[] = { \ - FOR_VARS(WASM_VAL_TYPE, 0, __VA_ARGS__)}; \ + __VA_OPT__(FOR_VARS(WASM_VAL_TYPE, 0, __VA_ARGS__))}; \ WasmEdge_ValType hook_api::WasmFunctionResult##F[1] = { \ WASM_VAL_TYPE(R, dummy)}; \ WasmEdge_FunctionTypeContext* hook_api::WasmFunctionType##F = \ WasmEdge_FunctionTypeCreate( \ WasmFunctionParams##F, \ - VA_NARGS(NULL, __VA_ARGS__), \ + VA_NARGS(NULL __VA_OPT__(, __VA_ARGS__)), \ WasmFunctionResult##F, \ 1); \ WasmEdge_String hook_api::WasmFunctionName##F = \ WasmEdge_StringCreateByCString(#F); \ R hook_api::F( \ hook::HookContext& hookCtx, \ - WasmEdge_CallingFrameContext const& frameCtx, \ - __VA_ARGS__) - -#define DEFINE_HOOK_FUNCNARG(R, F) \ - WasmEdge_Result hook_api::WasmFunction##F( \ - void* data_ptr, \ - const WasmEdge_CallingFrameContext* frameCtx, \ - const WasmEdge_Value* in, \ - WasmEdge_Value* out) \ - { \ - hook::HookContext* hookCtx = \ - reinterpret_cast(data_ptr); \ - R return_code = hook_api::F( \ - *hookCtx, *const_cast(frameCtx)); \ - if (return_code == RC_ROLLBACK || return_code == RC_ACCEPT) \ - return WasmEdge_Result_Terminate; \ - out[0] = CAT2(RET_, R(return_code)); \ - return WasmEdge_Result_Success; \ - }; \ - WasmEdge_ValType hook_api::WasmFunctionResult##F[1] = { \ - WASM_VAL_TYPE(R, dummy)}; \ - WasmEdge_FunctionTypeContext* hook_api::WasmFunctionType##F = \ - WasmEdge_FunctionTypeCreate({}, 0, WasmFunctionResult##F, 1); \ - WasmEdge_String hook_api::WasmFunctionName##F = \ - WasmEdge_StringCreateByCString(#F); \ - R hook_api::F( \ - hook::HookContext& hookCtx, \ - WasmEdge_CallingFrameContext const& frameCtx) + WasmEdge_CallingFrameContext const& frameCtx __VA_OPT__( \ + COMMA __VA_ARGS__)) #define HOOK_SETUP() \ try \ diff --git a/src/ripple/app/hook/applyHook.h b/src/ripple/app/hook/applyHook.h index 2c1f250bbf..8ba0505db6 100644 --- a/src/ripple/app/hook/applyHook.h +++ b/src/ripple/app/hook/applyHook.h @@ -61,365 +61,23 @@ namespace hook_api { if (HOOK_DBG) \ fprintf -DECLARE_HOOK_FUNCTION(int32_t, _g, uint32_t guard_id, uint32_t maxiter); - -DECLARE_HOOK_FUNCTION( - int64_t, - accept, - uint32_t read_ptr, - uint32_t read_len, - int64_t error_code); -DECLARE_HOOK_FUNCTION( - int64_t, - rollback, - uint32_t read_ptr, - uint32_t read_len, - int64_t error_code); - -DECLARE_HOOK_FUNCTION( - int64_t, - util_raddr, - uint32_t write_ptr, - uint32_t write_len, - uint32_t read_ptr, - uint32_t read_len); -DECLARE_HOOK_FUNCTION( - int64_t, - util_accid, - uint32_t write_ptr, - uint32_t write_len, - uint32_t read_ptr, - uint32_t read_len); -DECLARE_HOOK_FUNCTION( - int64_t, - util_verify, - uint32_t dread_ptr, - uint32_t dread_len, - uint32_t sread_ptr, - uint32_t sread_len, - uint32_t kread_ptr, - uint32_t kread_len); -DECLARE_HOOK_FUNCTION( - int64_t, - util_sha512h, - uint32_t write_ptr, - uint32_t write_len, - uint32_t read_ptr, - uint32_t read_len); -DECLARE_HOOK_FUNCTION( - int64_t, - util_keylet, - uint32_t write_ptr, - uint32_t write_len, - uint32_t keylet_type, - uint32_t a, - uint32_t b, - uint32_t c, - uint32_t d, - uint32_t e, - uint32_t f); - -DECLARE_HOOK_FUNCTION( - int64_t, - sto_validate, - uint32_t tread_ptr, - uint32_t tread_len); -DECLARE_HOOK_FUNCTION( - int64_t, - sto_subfield, - uint32_t read_ptr, - uint32_t read_len, - uint32_t field_id); -DECLARE_HOOK_FUNCTION( - int64_t, - sto_subarray, - uint32_t read_ptr, - uint32_t read_len, - uint32_t array_id); -DECLARE_HOOK_FUNCTION( - int64_t, - sto_emplace, - uint32_t write_ptr, - uint32_t write_len, - uint32_t sread_ptr, - uint32_t sread_len, - uint32_t fread_ptr, - uint32_t fread_len, - uint32_t field_id); -DECLARE_HOOK_FUNCTION( - int64_t, - sto_erase, - uint32_t write_ptr, - uint32_t write_len, - uint32_t read_ptr, - uint32_t read_len, - uint32_t field_id); - -DECLARE_HOOK_FUNCNARG(int64_t, etxn_burden); -DECLARE_HOOK_FUNCTION( - int64_t, - etxn_details, - uint32_t write_ptr, - uint32_t write_len); -DECLARE_HOOK_FUNCTION( - int64_t, - etxn_fee_base, - uint32_t read_ptr, - uint32_t read_len); -DECLARE_HOOK_FUNCTION(int64_t, etxn_reserve, uint32_t count); -DECLARE_HOOK_FUNCNARG(int64_t, etxn_generation); -DECLARE_HOOK_FUNCTION( - int64_t, - etxn_nonce, - uint32_t write_ptr, - uint32_t write_len); -DECLARE_HOOK_FUNCTION( - int64_t, - emit, - uint32_t write_ptr, - uint32_t write_len, - uint32_t read_ptr, - uint32_t read_len); - -DECLARE_HOOK_FUNCTION(int64_t, float_set, int32_t exponent, int64_t mantissa); -DECLARE_HOOK_FUNCTION(int64_t, float_multiply, int64_t float1, int64_t float2); -DECLARE_HOOK_FUNCTION( - int64_t, - float_mulratio, - int64_t float1, - uint32_t round_up, - uint32_t numerator, - uint32_t denominator); -DECLARE_HOOK_FUNCTION(int64_t, float_negate, int64_t float1); -DECLARE_HOOK_FUNCTION( - int64_t, - float_compare, - int64_t float1, - int64_t float2, - uint32_t mode); -DECLARE_HOOK_FUNCTION(int64_t, float_sum, int64_t float1, int64_t float2); -DECLARE_HOOK_FUNCTION( - int64_t, - float_sto, - uint32_t write_ptr, - uint32_t write_len, - uint32_t cread_ptr, - uint32_t cread_len, - uint32_t iread_ptr, - uint32_t iread_len, - int64_t float1, - uint32_t field_code); -DECLARE_HOOK_FUNCTION( - int64_t, - float_sto_set, - uint32_t read_ptr, - uint32_t read_len); -DECLARE_HOOK_FUNCTION(int64_t, float_invert, int64_t float1); -DECLARE_HOOK_FUNCTION(int64_t, float_divide, int64_t float1, int64_t float2); -DECLARE_HOOK_FUNCNARG(int64_t, float_one); -DECLARE_HOOK_FUNCTION(int64_t, float_mantissa, int64_t float1); -DECLARE_HOOK_FUNCTION(int64_t, float_sign, int64_t float1); -DECLARE_HOOK_FUNCTION( - int64_t, - float_int, - int64_t float1, - uint32_t decimal_places, - uint32_t abs); -DECLARE_HOOK_FUNCTION(int64_t, float_log, int64_t float1); -DECLARE_HOOK_FUNCTION(int64_t, float_root, int64_t float1, uint32_t n); - -DECLARE_HOOK_FUNCNARG(int64_t, fee_base); -DECLARE_HOOK_FUNCNARG(int64_t, ledger_seq); -DECLARE_HOOK_FUNCNARG(int64_t, ledger_last_time); -DECLARE_HOOK_FUNCTION( - int64_t, - ledger_last_hash, - uint32_t write_ptr, - uint32_t write_len); -DECLARE_HOOK_FUNCTION( - int64_t, - ledger_nonce, - uint32_t write_ptr, - uint32_t write_len); -DECLARE_HOOK_FUNCTION( - int64_t, - ledger_keylet, - uint32_t write_ptr, - uint32_t write_len, - uint32_t lread_ptr, - uint32_t lread_len, - uint32_t hread_ptr, - uint32_t hread_len); - -DECLARE_HOOK_FUNCTION( - int64_t, - hook_account, - uint32_t write_ptr, - uint32_t write_len); -DECLARE_HOOK_FUNCTION( - int64_t, - hook_hash, - uint32_t write_ptr, - uint32_t write_len, - int32_t hook_no); -DECLARE_HOOK_FUNCTION( - int64_t, - hook_param_set, - uint32_t read_ptr, - uint32_t read_len, - uint32_t kread_ptr, - uint32_t kread_len, - uint32_t hread_ptr, - uint32_t hread_len); -DECLARE_HOOK_FUNCTION( - int64_t, - hook_param, - uint32_t write_ptr, - uint32_t write_len, - uint32_t read_ptr, - uint32_t read_len); -DECLARE_HOOK_FUNCNARG(int64_t, hook_again); -DECLARE_HOOK_FUNCTION( - int64_t, - hook_skip, - uint32_t read_ptr, - uint32_t read_len, - uint32_t flags); -DECLARE_HOOK_FUNCNARG(int64_t, hook_pos); - -DECLARE_HOOK_FUNCTION( - int64_t, - slot, - uint32_t write_ptr, - uint32_t write_len, - uint32_t slot); -DECLARE_HOOK_FUNCTION(int64_t, slot_clear, uint32_t slot); -DECLARE_HOOK_FUNCTION(int64_t, slot_count, uint32_t slot); -DECLARE_HOOK_FUNCTION( - int64_t, - slot_set, - uint32_t read_ptr, - uint32_t read_len, - uint32_t slot); -DECLARE_HOOK_FUNCTION(int64_t, slot_size, uint32_t slot); -DECLARE_HOOK_FUNCTION( - int64_t, - slot_subarray, - uint32_t parent_slot, - uint32_t array_id, - uint32_t new_slot); -DECLARE_HOOK_FUNCTION( - int64_t, - slot_subfield, - uint32_t parent_slot, - uint32_t field_id, - uint32_t new_slot); -DECLARE_HOOK_FUNCTION(int64_t, slot_type, uint32_t slot_no, uint32_t flags); -DECLARE_HOOK_FUNCTION(int64_t, slot_float, uint32_t slot_no); - -DECLARE_HOOK_FUNCTION( - int64_t, - state_set, - uint32_t read_ptr, - uint32_t read_len, - uint32_t kread_ptr, - uint32_t kread_len); -DECLARE_HOOK_FUNCTION( - int64_t, - state_foreign_set, - uint32_t read_ptr, - uint32_t read_len, - uint32_t kread_ptr, - uint32_t kread_len, - uint32_t nread_ptr, - uint32_t nread_len, - uint32_t aread_ptr, - uint32_t aread_len); -DECLARE_HOOK_FUNCTION( - int64_t, - state, - uint32_t write_ptr, - uint32_t write_len, - uint32_t kread_ptr, - uint32_t kread_len); -DECLARE_HOOK_FUNCTION( - int64_t, - state_foreign, - uint32_t write_ptr, - uint32_t write_len, - uint32_t kread_ptr, - uint32_t kread_len, - uint32_t nread_ptr, - uint32_t nread_len, - uint32_t aread_ptr, - uint32_t aread_len); - -DECLARE_HOOK_FUNCTION( - int64_t, - trace, - uint32_t mread_ptr, - uint32_t mread_len, - uint32_t dread_ptr, - uint32_t dread_len, - uint32_t as_hex); -DECLARE_HOOK_FUNCTION( - int64_t, - trace_num, - uint32_t read_ptr, - uint32_t read_len, - int64_t number); -DECLARE_HOOK_FUNCTION( - int64_t, - trace_float, - uint32_t read_ptr, - uint32_t read_len, - int64_t float1); - -DECLARE_HOOK_FUNCNARG(int64_t, otxn_burden); -DECLARE_HOOK_FUNCTION( - int64_t, - otxn_field, - uint32_t write_ptr, - uint32_t write_len, - uint32_t field_id); -DECLARE_HOOK_FUNCNARG(int64_t, otxn_generation); -DECLARE_HOOK_FUNCTION( - int64_t, - otxn_id, - uint32_t write_ptr, - uint32_t write_len, - uint32_t flags); -DECLARE_HOOK_FUNCNARG(int64_t, otxn_type); -DECLARE_HOOK_FUNCTION(int64_t, otxn_slot, uint32_t slot_no); -DECLARE_HOOK_FUNCTION( - int64_t, - otxn_param, - uint32_t write_ptr, - uint32_t write_len, - uint32_t read_ptr, - uint32_t read_len); - -DECLARE_HOOK_FUNCTION(int64_t, meta_slot, uint32_t slot_no); -DECLARE_HOOK_FUNCTION( - int64_t, - xpop_slot, - uint32_t slot_no_tx, - uint32_t slot_no_meta); - -/* - DECLARE_HOOK_FUNCTION(int64_t, str_find, uint32_t hread_ptr, - uint32_t hread_len, uint32_t nread_ptr, uint32_t nread_len, uint32_t mode, - uint32_t n); DECLARE_HOOK_FUNCTION(int64_t, str_replace, uint32_t - write_ptr, uint32_t write_len, uint32_t hread_ptr, uint32_t hread_len, - uint32_t nread_ptr, - uint32_t nread_len, uint32_t rread_ptr, uint32_t rread_len, uint32_t mode, - uint32_t n); DECLARE_HOOK_FUNCTION(int64_t, str_compare, uint32_t - fread_ptr, uint32_t fread_len, uint32_t sread_ptr, uint32_t sread_len, - uint32_t mode); - DECLARE_HOOK_FUNCTION(int64_t, str_concat, uint32_t write_ptr, - uint32_t write_len, uint32_t read_ptr, uint32_t read_len, uint64_t operand, - uint32_t operand_type); -*/ +#pragma push_macro("HOOK_API_DEFINITION") +#undef HOOK_API_DEFINITION +#undef HOOK_API_COST + +#define HOOK_WRAP_PARAMS(...) __VA_ARGS__ +#define HOOK_API_DEFINITION(RETURN_TYPE, FUNCTION_NAME, PARAMS_TUPLE, ...) \ + DECLARE_HOOK_FUNCTION( \ + RETURN_TYPE, FUNCTION_NAME, HOOK_WRAP_PARAMS PARAMS_TUPLE); +#define HOOK_API_COST(...) + +#include + +#undef HOOK_API_COST +#undef HOOK_API_DEFINITION +#undef HOOK_WRAP_PARAMS +#pragma pop_macro("HOOK_API_DEFINITION") + } /* end namespace hook_api */ namespace hook { @@ -471,6 +129,18 @@ computeExecutionFee(uint64_t instructionCount); int64_t computeCreationFee(uint64_t byteCount); +constexpr uint32_t MICRO_DROPS_PER_DROP{1'000'000}; + +XRPAmount +hookCostToFee(ReadView const& view, uint64_t hookCost); + +std::optional> +doValidateGuards( + STTx const& tx, + Blob const& wasm, + Rules const& rules, + beast::Journal const& j); + struct HookResult { ripple::uint256 const hookSetTxnID; @@ -597,14 +267,14 @@ gatherHookParameters( beast::Journal const& j_); // RH TODO: call destruct for these on rippled shutdown -#define ADD_HOOK_FUNCTION(F, ctx) \ +#define ADD_HOOK_FUNCTION(F, ctx, cost) \ { \ WasmEdge_FunctionInstanceContext* hf = \ WasmEdge_FunctionInstanceCreate( \ hook_api::WasmFunctionType##F, \ hook_api::WasmFunction##F, \ (void*)(&ctx), \ - 0); \ + cost); \ WasmEdge_ModuleInstanceAddFunction( \ importObj, hook_api::WasmFunctionName##F, hf); \ } @@ -792,97 +462,21 @@ class HookExecutor WasmEdge_LogSetDebugLevel(); - ADD_HOOK_FUNCTION(_g, ctx); - ADD_HOOK_FUNCTION(accept, ctx); - ADD_HOOK_FUNCTION(rollback, ctx); - ADD_HOOK_FUNCTION(util_raddr, ctx); - ADD_HOOK_FUNCTION(util_accid, ctx); - ADD_HOOK_FUNCTION(util_verify, ctx); - ADD_HOOK_FUNCTION(util_sha512h, ctx); - ADD_HOOK_FUNCTION(sto_validate, ctx); - ADD_HOOK_FUNCTION(sto_subfield, ctx); - ADD_HOOK_FUNCTION(sto_subarray, ctx); - ADD_HOOK_FUNCTION(sto_emplace, ctx); - ADD_HOOK_FUNCTION(sto_erase, ctx); - ADD_HOOK_FUNCTION(util_keylet, ctx); - - ADD_HOOK_FUNCTION(emit, ctx); - ADD_HOOK_FUNCTION(etxn_burden, ctx); - ADD_HOOK_FUNCTION(etxn_fee_base, ctx); - ADD_HOOK_FUNCTION(etxn_details, ctx); - ADD_HOOK_FUNCTION(etxn_reserve, ctx); - ADD_HOOK_FUNCTION(etxn_generation, ctx); - ADD_HOOK_FUNCTION(etxn_nonce, ctx); - - ADD_HOOK_FUNCTION(float_set, ctx); - ADD_HOOK_FUNCTION(float_multiply, ctx); - ADD_HOOK_FUNCTION(float_mulratio, ctx); - ADD_HOOK_FUNCTION(float_negate, ctx); - ADD_HOOK_FUNCTION(float_compare, ctx); - ADD_HOOK_FUNCTION(float_sum, ctx); - ADD_HOOK_FUNCTION(float_sto, ctx); - ADD_HOOK_FUNCTION(float_sto_set, ctx); - ADD_HOOK_FUNCTION(float_invert, ctx); - - ADD_HOOK_FUNCTION(float_divide, ctx); - ADD_HOOK_FUNCTION(float_one, ctx); - ADD_HOOK_FUNCTION(float_mantissa, ctx); - ADD_HOOK_FUNCTION(float_sign, ctx); - ADD_HOOK_FUNCTION(float_int, ctx); - ADD_HOOK_FUNCTION(float_log, ctx); - ADD_HOOK_FUNCTION(float_root, ctx); - - ADD_HOOK_FUNCTION(otxn_burden, ctx); - ADD_HOOK_FUNCTION(otxn_generation, ctx); - ADD_HOOK_FUNCTION(otxn_field, ctx); - ADD_HOOK_FUNCTION(otxn_id, ctx); - ADD_HOOK_FUNCTION(otxn_type, ctx); - ADD_HOOK_FUNCTION(otxn_slot, ctx); - ADD_HOOK_FUNCTION(otxn_param, ctx); - - ADD_HOOK_FUNCTION(hook_account, ctx); - ADD_HOOK_FUNCTION(hook_hash, ctx); - ADD_HOOK_FUNCTION(hook_again, ctx); - ADD_HOOK_FUNCTION(fee_base, ctx); - ADD_HOOK_FUNCTION(ledger_seq, ctx); - ADD_HOOK_FUNCTION(ledger_last_hash, ctx); - ADD_HOOK_FUNCTION(ledger_last_time, ctx); - ADD_HOOK_FUNCTION(ledger_nonce, ctx); - ADD_HOOK_FUNCTION(ledger_keylet, ctx); - - ADD_HOOK_FUNCTION(hook_param, ctx); - ADD_HOOK_FUNCTION(hook_param_set, ctx); - ADD_HOOK_FUNCTION(hook_skip, ctx); - ADD_HOOK_FUNCTION(hook_pos, ctx); - - ADD_HOOK_FUNCTION(state, ctx); - ADD_HOOK_FUNCTION(state_foreign, ctx); - ADD_HOOK_FUNCTION(state_set, ctx); - ADD_HOOK_FUNCTION(state_foreign_set, ctx); - - ADD_HOOK_FUNCTION(slot, ctx); - ADD_HOOK_FUNCTION(slot_clear, ctx); - ADD_HOOK_FUNCTION(slot_count, ctx); - ADD_HOOK_FUNCTION(slot_set, ctx); - ADD_HOOK_FUNCTION(slot_size, ctx); - ADD_HOOK_FUNCTION(slot_subarray, ctx); - ADD_HOOK_FUNCTION(slot_subfield, ctx); - ADD_HOOK_FUNCTION(slot_type, ctx); - ADD_HOOK_FUNCTION(slot_float, ctx); - - ADD_HOOK_FUNCTION(trace, ctx); - ADD_HOOK_FUNCTION(trace_num, ctx); - ADD_HOOK_FUNCTION(trace_float, ctx); - - ADD_HOOK_FUNCTION(meta_slot, ctx); - ADD_HOOK_FUNCTION(xpop_slot, ctx); - - /* - ADD_HOOK_FUNCTION(str_find, ctx); - ADD_HOOK_FUNCTION(str_replace, ctx); - ADD_HOOK_FUNCTION(str_compare, ctx); - ADD_HOOK_FUNCTION(str_concat, ctx); - */ +#pragma push_macro("HOOK_API_DEFINITION") +#undef HOOK_API_DEFINITION +#undef HOOK_API_COST + +#define HOOK_WRAP_PARAMS(...) __VA_ARGS__ +#define HOOK_API_DEFINITION(RETURN_TYPE, FUNCTION_NAME, PARAMS_TUPLE, ...) \ + ADD_HOOK_FUNCTION(FUNCTION_NAME, ctx, 0); +#define HOOK_API_COST(...) + +#include + +#undef HOOK_API_DEFINITION +#undef HOOK_API_COST +#undef HOOK_WRAP_PARAMS +#pragma pop_macro("HOOK_API_DEFINITION") WasmEdge_TableInstanceContext* hostTable = WasmEdge_TableInstanceCreate(tableType); diff --git a/src/ripple/app/hook/guard_checker.cpp b/src/ripple/app/hook/guard_checker.cpp index f20d24617b..815c5f493e 100644 --- a/src/ripple/app/hook/guard_checker.cpp +++ b/src/ripple/app/hook/guard_checker.cpp @@ -1,3 +1,5 @@ +#define GUARD_CHECKER_BUILD +#include "Enum.h" #include "Guard.h" #include #include @@ -79,7 +81,16 @@ main(int argc, char** argv) close(fd); - auto result = validateGuards(hook, std::cout, "", 3); + // Dummy rules for guard checker build + hook_api::Rules rules; + + auto result = validateGuards( + hook, + std::cout, + "", + false, + hook_api::getImportWhitelist(rules), + hook_api::getGuardRulesVersion(rules)); if (!result) { diff --git a/src/ripple/app/hook/hook_api.macro b/src/ripple/app/hook/hook_api.macro new file mode 100644 index 0000000000..d87648f0e2 --- /dev/null +++ b/src/ripple/app/hook/hook_api.macro @@ -0,0 +1,443 @@ +// int32_t _g(uint32_t guard_id, uint32_t maxiter); +HOOK_API_DEFINITION( + int32_t, _g, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(_g, 100, uint256{}) + +// int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code); +HOOK_API_DEFINITION( + int64_t, accept, (uint32_t, uint32_t, int64_t), + uint256{}) +HOOK_API_COST(accept, 100, uint256{}) + +// int64_t rollback(uint32_t read_ptr, uint32_t read_len, int64_t error_code); +HOOK_API_DEFINITION( + int64_t, rollback, (uint32_t, uint32_t, int64_t), + uint256{}) +HOOK_API_COST(rollback, 100, uint256{}) + +// int64_t util_raddr(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len); +HOOK_API_DEFINITION( + int64_t, util_raddr, (uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(util_raddr, 100, uint256{}) + +// int64_t util_accid(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len); +HOOK_API_DEFINITION( + int64_t, util_accid, (uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(util_accid, 100, uint256{}) + +// int64_t util_verify(uint32_t dread_ptr, uint32_t dread_len, uint32_t sread_ptr, uint32_t sread_len, uint32_t kread_ptr, uint32_t kread_len); +HOOK_API_DEFINITION( + int64_t, util_verify, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(util_verify, 100, uint256{}) + +// int64_t util_sha512h(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len); +HOOK_API_DEFINITION( + int64_t, util_sha512h, (uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(util_sha512h, 100, uint256{}) + +// int64_t util_keylet(uint32_t write_ptr, uint32_t write_len, uint32_t keylet_type, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f); +HOOK_API_DEFINITION( + int64_t, util_keylet, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(util_keylet, 100, uint256{}) + +// int64_t sto_validate(uint32_t tread_ptr, uint32_t tread_len); +HOOK_API_DEFINITION( + int64_t, sto_validate, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(sto_validate, 100, uint256{}) + +// int64_t sto_subfield(uint32_t read_ptr, uint32_t read_len, uint32_t field_id); +HOOK_API_DEFINITION( + int64_t, sto_subfield, (uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(sto_subfield, 100, uint256{}) + +// int64_t sto_subarray(uint32_t read_ptr, uint32_t read_len, uint32_t array_id); +HOOK_API_DEFINITION( + int64_t, sto_subarray, (uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(sto_subarray, 100, uint256{}) + +// int64_t sto_emplace(uint32_t write_ptr, uint32_t write_len, uint32_t sread_ptr, uint32_t sread_len, uint32_t fread_ptr, uint32_t fread_len, uint32_t field_id); +HOOK_API_DEFINITION( + int64_t, sto_emplace, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(sto_emplace, 100, uint256{}) + +// int64_t sto_erase(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len, uint32_t field_id); +HOOK_API_DEFINITION( + int64_t, sto_erase, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(sto_erase, 100, uint256{}) + +// int64_t etxn_burden(); +HOOK_API_DEFINITION( + int64_t, etxn_burden, (), + uint256{}) +HOOK_API_COST(etxn_burden, 100, uint256{}) + +// int64_t etxn_details(uint32_t write_ptr, uint32_t write_len); +HOOK_API_DEFINITION( + int64_t, etxn_details, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(etxn_details, 100, uint256{}) + +// int64_t etxn_fee_base(uint32_t read_ptr, uint32_t read_len); +HOOK_API_DEFINITION( + int64_t, etxn_fee_base, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(etxn_fee_base, 100, uint256{}) + +// int64_t etxn_reserve(uint32_t count); +HOOK_API_DEFINITION( + int64_t, etxn_reserve, (uint32_t), + uint256{}) +HOOK_API_COST(etxn_reserve, 100, uint256{}) + +// int64_t etxn_generation(); +HOOK_API_DEFINITION( + int64_t, etxn_generation, (), + uint256{}) +HOOK_API_COST(etxn_generation, 100, uint256{}) + +// int64_t etxn_nonce(uint32_t write_ptr, uint32_t write_len); +HOOK_API_DEFINITION( + int64_t, etxn_nonce, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(etxn_nonce, 100, uint256{}) + +// int64_t emit(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len); +HOOK_API_DEFINITION( + int64_t, emit, (uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(emit, 100, uint256{}) + +// int64_t float_set(int32_t exponent, int64_t mantissa); +HOOK_API_DEFINITION( + int64_t, float_set, (int32_t, int64_t), + uint256{}) +HOOK_API_COST(float_set, 100, uint256{}) + +// int64_t float_multiply(int64_t float1, int64_t float2); +HOOK_API_DEFINITION( + int64_t, float_multiply, (int64_t, int64_t), + uint256{}) +HOOK_API_COST(float_multiply, 100, uint256{}) + +// int64_t float_mulratio(int64_t float1, uint32_t round_up, uint32_t numerator, uint32_t denominator); +HOOK_API_DEFINITION( + int64_t, float_mulratio, (int64_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(float_mulratio, 100, uint256{}) + +// int64_t float_negate(int64_t float1); +HOOK_API_DEFINITION( + int64_t, float_negate, (int64_t), + uint256{}) +HOOK_API_COST(float_negate, 100, uint256{}) + +// int64_t float_compare(int64_t float1, int64_t float2, uint32_t mode); +HOOK_API_DEFINITION( + int64_t, float_compare, (int64_t, int64_t, uint32_t), + uint256{}) +HOOK_API_COST(float_compare, 100, uint256{}) + +// int64_t float_sum(int64_t float1, int64_t float2); +HOOK_API_DEFINITION( + int64_t, float_sum, (int64_t, int64_t), + uint256{}) +HOOK_API_COST(float_sum, 100, uint256{}) + +// int64_t float_sto(uint32_t write_ptr, uint32_t write_len, uint32_t cread_ptr, uint32_t cread_len, uint32_t iread_ptr, uint32_t iread_len, int64_t float1, uint32_t field_code); +HOOK_API_DEFINITION( + int64_t, float_sto, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, int64_t, uint32_t), + uint256{}) +HOOK_API_COST(float_sto, 100, uint256{}) + +// int64_t float_sto_set(uint32_t read_ptr, uint32_t read_len); +HOOK_API_DEFINITION( + int64_t, float_sto_set, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(float_sto_set, 100, uint256{}) + +// int64_t float_invert(int64_t float1); +HOOK_API_DEFINITION( + int64_t, float_invert, (int64_t), + uint256{}) +HOOK_API_COST(float_invert, 100, uint256{}) + +// int64_t float_divide(int64_t float1, int64_t float2); +HOOK_API_DEFINITION( + int64_t, float_divide, (int64_t, int64_t), + uint256{}) +HOOK_API_COST(float_divide, 100, uint256{}) + +// int64_t float_one(); +HOOK_API_DEFINITION( + int64_t, float_one, (), + uint256{}) +HOOK_API_COST(float_one, 100, uint256{}) + +// int64_t float_mantissa(int64_t float1); +HOOK_API_DEFINITION( + int64_t, float_mantissa, (int64_t), + uint256{}) +HOOK_API_COST(float_mantissa, 100, uint256{}) + +// int64_t float_sign(int64_t float1); +HOOK_API_DEFINITION( + int64_t, float_sign, (int64_t), + uint256{}) +HOOK_API_COST(float_sign, 100, uint256{}) + +// int64_t float_int(int64_t float1, uint32_t decimal_places, uint32_t abs); +HOOK_API_DEFINITION( + int64_t, float_int, (int64_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(float_int, 100, uint256{}) + +// int64_t float_log(int64_t float1); +HOOK_API_DEFINITION( + int64_t, float_log, (int64_t), + uint256{}) +HOOK_API_COST(float_log, 100, uint256{}) + +// int64_t float_root(int64_t float1, uint32_t n); +HOOK_API_DEFINITION( + int64_t, float_root, (int64_t, uint32_t), + uint256{}) +HOOK_API_COST(float_root, 100, uint256{}) + +// int64_t fee_base(); +HOOK_API_DEFINITION( + int64_t, fee_base, (), + uint256{}) +HOOK_API_COST(fee_base, 100, uint256{}) + +// int64_t ledger_seq(); +HOOK_API_DEFINITION( + int64_t, ledger_seq, (), + uint256{}) +HOOK_API_COST(ledger_seq, 100, uint256{}) + +// int64_t ledger_last_time(); +HOOK_API_DEFINITION( + int64_t, ledger_last_time, (), + uint256{}) +HOOK_API_COST(ledger_last_time, 100, uint256{}) + +// int64_t ledger_last_hash(uint32_t write_ptr, uint32_t write_len); +HOOK_API_DEFINITION( + int64_t, ledger_last_hash, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(ledger_last_hash, 100, uint256{}) + +// int64_t ledger_nonce(uint32_t write_ptr, uint32_t write_len); +HOOK_API_DEFINITION( + int64_t, ledger_nonce, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(ledger_nonce, 100, uint256{}) + +// int64_t ledger_keylet(uint32_t write_ptr, uint32_t write_len, uint32_t lread_ptr, uint32_t lread_len, uint32_t hread_ptr, uint32_t hread_len); +HOOK_API_DEFINITION( + int64_t, ledger_keylet, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(ledger_keylet, 100, uint256{}) + +// int64_t hook_account(uint32_t write_ptr, uint32_t write_len); +HOOK_API_DEFINITION( + int64_t, hook_account, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(hook_account, 100, uint256{}) + +// int64_t hook_hash(uint32_t write_ptr, uint32_t write_len, int32_t hook_no); +HOOK_API_DEFINITION( + int64_t, hook_hash, (uint32_t, uint32_t, int32_t), + uint256{}) +HOOK_API_COST(hook_hash, 100, uint256{}) + +// int64_t hook_param_set(uint32_t read_ptr, uint32_t read_len, uint32_t kread_ptr, uint32_t kread_len, uint32_t hread_ptr, uint32_t hread_len); +HOOK_API_DEFINITION( + int64_t, hook_param_set, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(hook_param_set, 100, uint256{}) + +// int64_t hook_param(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len); +HOOK_API_DEFINITION( + int64_t, hook_param, (uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(hook_param, 100, uint256{}) + +// int64_t hook_again(); +HOOK_API_DEFINITION( + int64_t, hook_again, (), + uint256{}) +HOOK_API_COST(hook_again, 100, uint256{}) + +// int64_t hook_skip(uint32_t read_ptr, uint32_t read_len, uint32_t flags); +HOOK_API_DEFINITION( + int64_t, hook_skip, (uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(hook_skip, 100, uint256{}) + +// int64_t hook_pos(); +HOOK_API_DEFINITION( + int64_t, hook_pos, (), + uint256{}) +HOOK_API_COST(hook_pos, 100, uint256{}) + +// int64_t slot(uint32_t write_ptr, uint32_t write_len, uint32_t slot); +HOOK_API_DEFINITION( + int64_t, slot, (uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(slot, 100, uint256{}) + +// int64_t slot_clear(uint32_t slot); +HOOK_API_DEFINITION( + int64_t, slot_clear, (uint32_t), + uint256{}) +HOOK_API_COST(slot_clear, 100, uint256{}) + +// int64_t slot_count(uint32_t slot); +HOOK_API_DEFINITION( + int64_t, slot_count, (uint32_t), + uint256{}) +HOOK_API_COST(slot_count, 100, uint256{}) + +// int64_t slot_set(uint32_t read_ptr, uint32_t read_len, uint32_t slot); +HOOK_API_DEFINITION( + int64_t, slot_set, (uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(slot_set, 100, uint256{}) + +// int64_t slot_size(uint32_t slot); +HOOK_API_DEFINITION( + int64_t, slot_size, (uint32_t), + uint256{}) +HOOK_API_COST(slot_size, 100, uint256{}) + +// int64_t slot_subarray(uint32_t parent_slot, uint32_t array_id, uint32_t new_slot); +HOOK_API_DEFINITION( + int64_t, slot_subarray, (uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(slot_subarray, 100, uint256{}) + +// int64_t slot_subfield(uint32_t parent_slot, uint32_t field_id, uint32_t new_slot); +HOOK_API_DEFINITION( + int64_t, slot_subfield, (uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(slot_subfield, 100, uint256{}) + +// int64_t slot_type(uint32_t slot_no, uint32_t flags); +HOOK_API_DEFINITION( + int64_t, slot_type, (uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(slot_type, 100, uint256{}) + +// int64_t slot_float(uint32_t slot_no); +HOOK_API_DEFINITION( + int64_t, slot_float, (uint32_t), + uint256{}) +HOOK_API_COST(slot_float, 100, uint256{}) + +// int64_t state_set(uint32_t read_ptr, uint32_t read_len, uint32_t kread_ptr, uint32_t kread_len); +HOOK_API_DEFINITION( + int64_t, state_set, (uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(state_set, 100, uint256{}) + +// int64_t state_foreign_set(uint32_t read_ptr, uint32_t read_len, uint32_t kread_ptr, uint32_t kread_len, uint32_t nread_ptr, uint32_t nread_len, uint32_t aread_ptr, uint32_t aread_len); +HOOK_API_DEFINITION( + int64_t, state_foreign_set, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(state_foreign_set, 100, uint256{}) + +// int64_t state(uint32_t write_ptr, uint32_t write_len, uint32_t kread_ptr, uint32_t kread_len); +HOOK_API_DEFINITION( + int64_t, state, (uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(state, 100, uint256{}) + +// int64_t state_foreign(uint32_t write_ptr, uint32_t write_len, uint32_t kread_ptr, uint32_t kread_len, uint32_t nread_ptr, uint32_t nread_len, uint32_t aread_ptr, uint32_t aread_len); +HOOK_API_DEFINITION( + int64_t, state_foreign, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(state_foreign, 100, uint256{}) + +// int64_t trace(uint32_t mread_ptr, uint32_t mread_len, uint32_t dread_ptr, uint32_t dread_len, uint32_t as_hex); +HOOK_API_DEFINITION( + int64_t, trace, (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(trace, 100, uint256{}) + +// int64_t trace_num(uint32_t read_ptr, uint32_t read_len, int64_t number); +HOOK_API_DEFINITION( + int64_t, trace_num, (uint32_t, uint32_t, int64_t), + uint256{}) +HOOK_API_COST(trace_num, 100, uint256{}) + +// int64_t trace_float(uint32_t read_ptr, uint32_t read_len, int64_t float1); +HOOK_API_DEFINITION( + int64_t, trace_float, (uint32_t, uint32_t, int64_t), + uint256{}) +HOOK_API_COST(trace_float, 100, uint256{}) + +// int64_t otxn_burden(); +HOOK_API_DEFINITION( + int64_t, otxn_burden, (), + uint256{}) +HOOK_API_COST(otxn_burden, 100, uint256{}) + +// int64_t otxn_field(uint32_t write_ptr, uint32_t write_len, uint32_t field_id); +HOOK_API_DEFINITION( + int64_t, otxn_field, (uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(otxn_field, 100, uint256{}) + +// int64_t otxn_generation(); +HOOK_API_DEFINITION( + int64_t, otxn_generation, (), + uint256{}) +HOOK_API_COST(otxn_generation, 100, uint256{}) + +// int64_t otxn_id(uint32_t write_ptr, uint32_t write_len, uint32_t flags); +HOOK_API_DEFINITION( + int64_t, otxn_id, (uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(otxn_id, 100, uint256{}) + +// int64_t otxn_type(); +HOOK_API_DEFINITION( + int64_t, otxn_type, (), + uint256{}) +HOOK_API_COST(otxn_type, 100, uint256{}) + +// int64_t otxn_slot(uint32_t slot_no); +HOOK_API_DEFINITION( + int64_t, otxn_slot, (uint32_t), + uint256{}) +HOOK_API_COST(otxn_slot, 100, uint256{}) + +// int64_t otxn_param(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len); +HOOK_API_DEFINITION( + int64_t, otxn_param, (uint32_t, uint32_t, uint32_t, uint32_t), + uint256{}) +HOOK_API_COST(otxn_param, 100, uint256{}) + +// int64_t meta_slot(uint32_t slot_no); +HOOK_API_DEFINITION( + int64_t, meta_slot, (uint32_t), + uint256{}) +HOOK_API_COST(meta_slot, 100, uint256{}) + +// int64_t xpop_slot(uint32_t slot_no_tx, uint32_t slot_no_meta); +HOOK_API_DEFINITION( + int64_t, xpop_slot, (uint32_t, uint32_t), + featureHooksUpdate1) +HOOK_API_COST(xpop_slot, 100, uint256{}) diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index f27b7d23b3..fd7f6f236c 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -920,6 +921,84 @@ hook::computeCreationFee(uint64_t byteCount) return fee; } +XRPAmount +hook::hookCostToFee(ReadView const& view, uint64_t hookCost) +{ + XRPAmount fee{0}; + auto const HOOK_GAS_PRICE = view.fees().hookGasPrice; + double const gas_cost = + double(HOOK_GAS_PRICE) / double(hook::MICRO_DROPS_PER_DROP); + fee = uint64_t(hookCost * gas_cost); + return fee; +} + +std::optional> +hook::doValidateGuards( + STTx const& tx, + Blob const& wasm, + Rules const& rules, + beast::Journal const& j) +{ + // RH NOTE: validateGuards has a generic non-rippled specific + // interface so it can be used in other projects (i.e. tooling). + // As such the calling here is a bit convoluted. + + std::optional>> logger; + std::ostringstream loggerStream; + std::string hsacc{""}; + if (j.trace()) + { + logger = loggerStream; + std::stringstream ss; + +#define HS_ACC() tx.getAccountID(sfAccount) << "-" << tx.getTransactionID() + ss << HS_ACC(); +#undef HS_ACC + hsacc = ss.str(); + } + + auto result = validateGuards( + wasm, // wasm to verify + logger, + hsacc, + rules.enabled(featureHookFeeV2), + hook_api::getImportWhitelist(rules), + hook_api::getGuardRulesVersion(rules)); + + if (j.trace()) + { + // clunky but to get the stream to accept the output + // correctly we will split on new line and feed each line + // one by one into the trace stream beast::Journal should be + // updated to inherit from basic_ostream then this + // wouldn't be necessary. + + // is this a needless copy or does the compiler do copy + // elision here? + std::string s = loggerStream.str(); + + char* data = s.data(); + size_t len = s.size(); + + char* last = data; + size_t i = 0; + for (; i < len; ++i) + { + if (data[i] == '\n') + { + data[i] = '\0'; + j.trace() << last; + last = data + i; + } + } + + if (last < data + i) + j.trace() << last; + } + + return result; +} + // many datatypes can be encoded into an int64_t inline int64_t data_as_int64(void const* ptr_raw, uint32_t len) @@ -2275,7 +2354,7 @@ DEFINE_HOOK_FUNCTION( } // Return the tt (Transaction Type) numeric code of the originating transaction -DEFINE_HOOK_FUNCNARG(int64_t, otxn_type) +DEFINE_HOOK_FUNCTION(int64_t, otxn_type) { HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, // hookCtx on current stack @@ -2325,7 +2404,7 @@ DEFINE_HOOK_FUNCTION(int64_t, otxn_slot, uint32_t slot_into) // Return the burden of the originating transaction... this will be 1 unless the // originating transaction was itself an emitted transaction from a previous // hook invocation -DEFINE_HOOK_FUNCNARG(int64_t, otxn_burden) +DEFINE_HOOK_FUNCTION(int64_t, otxn_burden) { HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, // hookCtx on current stack @@ -2362,7 +2441,7 @@ DEFINE_HOOK_FUNCNARG(int64_t, otxn_burden) // Return the generation of the originating transaction... this will be 1 unless // the originating transaction was itself an emitted transaction from a previous // hook invocation -DEFINE_HOOK_FUNCNARG(int64_t, otxn_generation) +DEFINE_HOOK_FUNCTION(int64_t, otxn_generation) { HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, // hookCtx on current stack @@ -2394,14 +2473,14 @@ DEFINE_HOOK_FUNCNARG(int64_t, otxn_generation) } // Return the generation of a hypothetically emitted transaction from this hook -DEFINE_HOOK_FUNCNARG(int64_t, etxn_generation) +DEFINE_HOOK_FUNCTION(int64_t, etxn_generation) { // proxy only, no setup or teardown return otxn_generation(hookCtx, frameCtx) + 1; } // Return the current ledger sequence number -DEFINE_HOOK_FUNCNARG(int64_t, ledger_seq) +DEFINE_HOOK_FUNCTION(int64_t, ledger_seq) { HOOK_SETUP(); @@ -2431,7 +2510,7 @@ DEFINE_HOOK_FUNCTION( HOOK_TEARDOWN(); } -DEFINE_HOOK_FUNCNARG(int64_t, ledger_last_time) +DEFINE_HOOK_FUNCTION(int64_t, ledger_last_time) { HOOK_SETUP(); @@ -3889,7 +3968,7 @@ DEFINE_HOOK_FUNCTION(int64_t, etxn_reserve, uint32_t count) } // Compute the burden of an emitted transaction based on a number of factors -DEFINE_HOOK_FUNCNARG(int64_t, etxn_burden) +DEFINE_HOOK_FUNCTION(int64_t, etxn_burden) { HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, // hookCtx on current stack @@ -4711,7 +4790,7 @@ DEFINE_HOOK_FUNCTION( } // Return the current fee base of the current ledger (multiplied by a margin) -DEFINE_HOOK_FUNCNARG(int64_t, fee_base) +DEFINE_HOOK_FUNCTION(int64_t, fee_base) { HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, // hookCtx on current stack @@ -5634,7 +5713,7 @@ DEFINE_HOOK_FUNCTION(int64_t, float_divide, int64_t float1, int64_t float2) HOOK_TEARDOWN(); } -DEFINE_HOOK_FUNCNARG(int64_t, float_one) +DEFINE_HOOK_FUNCTION(int64_t, float_one) { return float_one_internal; } @@ -6030,12 +6109,12 @@ DEFINE_HOOK_FUNCTION( HOOK_TEARDOWN(); } -DEFINE_HOOK_FUNCNARG(int64_t, hook_pos) +DEFINE_HOOK_FUNCTION(int64_t, hook_pos) { return hookCtx.result.hookChainPosition; } -DEFINE_HOOK_FUNCNARG(int64_t, hook_again) +DEFINE_HOOK_FUNCTION(int64_t, hook_again) { HOOK_SETUP(); diff --git a/src/ripple/app/ledger/Ledger.cpp b/src/ripple/app/ledger/Ledger.cpp index 5faaff5880..b50e083494 100644 --- a/src/ripple/app/ledger/Ledger.cpp +++ b/src/ripple/app/ledger/Ledger.cpp @@ -246,6 +246,13 @@ Ledger::Ledger( sle->at(sfReserveIncrement) = *f; sle->at(sfReferenceFeeUnits) = Config::FEE_UNITS_DEPRECATED; } + + if (std::find(amendments.begin(), amendments.end(), featureHookFeeV2) != + amendments.end()) + { + // TODO: want to set sfHookGasPrice when running unittest. + sle->at(sfHookGasPrice) = config.FEES.hook_gas_price; + } rawInsert(sle); } @@ -671,6 +678,7 @@ Ledger::setup() { bool oldFees = false; bool newFees = false; + bool hookFees = false; { auto const baseFee = sle->at(~sfBaseFee); auto const reserveBase = sle->at(~sfReserveBase); @@ -704,12 +712,26 @@ Ledger::setup() assign(fees_.increment, reserveIncrementXRP); newFees = baseFeeXRP || reserveBaseXRP || reserveIncrementXRP; } + { + auto const hookGasPrice = sle->at(~sfHookGasPrice); + auto assign = [](std::uint32_t& dest, + std::optional const& src) { + if (src) + dest = src.value(); + }; + assign(fees_.hookGasPrice, hookGasPrice); + hookFees = !!hookGasPrice; + } if (oldFees && newFees) // Should be all of one or the other, but not both ret = false; if (!rules_.enabled(featureXRPFees) && newFees) // Can't populate the new fees before the amendment is enabled ret = false; + if (!rules_.enabled(featureHookFeeV2) && hookFees) + // Can't populate the Hook Fee V2 before the amendment is + // enabled + ret = false; } } catch (SHAMapMissingNode const&) @@ -728,13 +750,18 @@ Ledger::setup() void Ledger::defaultFees(Config const& config) { - assert(fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0); + assert( + fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0 && + fees_.hookGasPrice == 0); if (fees_.base == 0) fees_.base = config.FEES.reference_fee; if (fees_.reserve == 0) fees_.reserve = config.FEES.account_reserve; if (fees_.increment == 0) fees_.increment = config.FEES.owner_reserve; + + if (fees_.hookGasPrice == 0) + fees_.hookGasPrice = config.FEES.hook_gas_price; } std::shared_ptr diff --git a/src/ripple/app/misc/FeeVoteImpl.cpp b/src/ripple/app/misc/FeeVoteImpl.cpp index 0d60dc6b78..ab047baf4d 100644 --- a/src/ripple/app/misc/FeeVoteImpl.cpp +++ b/src/ripple/app/misc/FeeVoteImpl.cpp @@ -29,10 +29,10 @@ namespace ripple { namespace detail { +template class VotableValue { private: - using value_type = XRPAmount; value_type const current_; // The current setting value_type const target_; // The setting we want std::map voteMap_; @@ -67,8 +67,9 @@ class VotableValue getVotes() const; }; -auto -VotableValue::getVotes() const -> std::pair +template +std::pair +VotableValue::getVotes() const { value_type ourVote = current_; int weight = 0; @@ -191,6 +192,28 @@ FeeVoteImpl::doValidation( "reserve increment", sfReserveIncrement); } + + if (rules.enabled(featureHookFeeV2)) + { + auto vote = [&v, this]( + auto const current, + std::uint32_t target, + char const* name, + auto const& sfield) { + if (current != target) + { + JLOG(journal_.info()) + << "Voting for " << name << " of " << target; + + v[sfield] = target; + } + }; + vote( + lastFees.hookGasPrice, + target_.hook_gas_price, + "hook gas price", + sfHookGasPrice); + } } void @@ -211,11 +234,14 @@ FeeVoteImpl::doVoting( detail::VotableValue incReserveVote( lastClosedLedger->fees().increment, target_.owner_reserve); + detail::VotableValue hookGasPriceVote( + lastClosedLedger->fees().hookGasPrice, target_.hook_gas_price); + auto const& rules = lastClosedLedger->rules(); if (rules.enabled(featureXRPFees)) { auto doVote = [](std::shared_ptr const& val, - detail::VotableValue& value, + detail::VotableValue& value, SF_AMOUNT const& xrpField) { if (auto const field = ~val->at(~xrpField); field && field->native()) @@ -244,7 +270,7 @@ FeeVoteImpl::doVoting( else { auto doVote = [](std::shared_ptr const& val, - detail::VotableValue& value, + detail::VotableValue& value, auto const& valueField) { if (auto const field = val->at(~valueField)) { @@ -275,6 +301,23 @@ FeeVoteImpl::doVoting( doVote(val, incReserveVote, sfReserveIncrement); } } + if (rules.enabled(featureHookFeeV2)) + { + auto doVote = [](std::shared_ptr const& val, + detail::VotableValue& value, + SF_UINT32 const& valueField) { + if (auto const field = ~val->at(~valueField); field) + value.addVote(field.value()); + else + value.noVote(); + }; + for (auto const& val : set) + { + if (!val->isTrusted()) + continue; + doVote(val, hookGasPriceVote, sfHookGasPrice); + } + } // choose our positions // TODO: Use structured binding once LLVM issue @@ -283,15 +326,18 @@ FeeVoteImpl::doVoting( auto const baseFee = baseFeeVote.getVotes(); auto const baseReserve = baseReserveVote.getVotes(); auto const incReserve = incReserveVote.getVotes(); + auto const hookGasPrice = hookGasPriceVote.getVotes(); auto const seq = lastClosedLedger->info().seq + 1; // add transactions to our position - if (baseFee.second || baseReserve.second || incReserve.second) + if (baseFee.second || baseReserve.second || incReserve.second || + hookGasPrice.second) { JLOG(journal_.warn()) << "We are voting for a fee change: " << baseFee.first << "/" - << baseReserve.first << "/" << incReserve.first; + << baseReserve.first << "/" << incReserve.first << "/" + << hookGasPrice.first; STTx feeTx(ttFEE, [=, &rules](auto& obj) { obj[sfAccount] = AccountID(); @@ -315,6 +361,8 @@ FeeVoteImpl::doVoting( incReserveVote.current()); obj[sfReferenceFeeUnits] = Config::FEE_UNITS_DEPRECATED; } + if (rules.enabled(featureHookFeeV2)) + obj[sfHookGasPrice] = hookGasPrice.first; }); uint256 txID = feeTx.getTransactionID(); diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index 0e5b8ef5f5..1d092d9b47 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -2191,6 +2191,9 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) reserveIncXRP && reserveIncXRP->native()) jvObj[jss::reserve_inc] = reserveIncXRP->xrp().jsonClipped(); + if (auto const hookGasPrice = ~val->at(~sfHookGasPrice); hookGasPrice) + jvObj[jss::hook_gas_price] = *hookGasPrice; + for (auto i = mStreamMaps[sValidations].begin(); i != mStreamMaps[sValidations].end();) { @@ -2624,12 +2627,16 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) l[jss::seq] = Json::UInt(lpClosed->info().seq); l[jss::hash] = to_string(lpClosed->info().hash); + auto const hookFeeV2 = lpClosed->rules().enabled(featureHookFeeV2); + if (!human) { l[jss::base_fee] = baseFee.jsonClipped(); l[jss::reserve_base] = lpClosed->fees().accountReserve(0).jsonClipped(); l[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); + if (hookFeeV2) + l[jss::hook_gas_price] = lpClosed->fees().hookGasPrice; l[jss::close_time] = Json::Value::UInt( lpClosed->info().closeTime.time_since_epoch().count()); } @@ -2645,6 +2652,8 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) lpClosed->fees().accountReserve(0).decimalXRP(); l[jss::reserve_inc_native] = lpClosed->fees().increment.decimalXRP(); + if (hookFeeV2) + l[jss::hook_gas_price] = lpClosed->fees().hookGasPrice; auto const nowOffset = app_.timeKeeper().nowOffset(); if (std::abs(nowOffset.count()) >= 60) @@ -2958,6 +2967,9 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) jvObj[jss::reserve_inc] = lpAccepted->fees().increment.jsonClipped(); + if (lpAccepted->rules().enabled(featureHookFeeV2)) + jvObj[jss::hook_gas_price] = lpAccepted->fees().hookGasPrice; + jvObj[jss::txn_count] = Json::UInt(alpAccepted->size()); if (mMode >= OperatingMode::SYNCING) @@ -3982,6 +3994,8 @@ NetworkOPsImp::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) jvResult[jss::reserve_base] = lpClosed->fees().accountReserve(0).jsonClipped(); jvResult[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); + if (lpClosed->rules().enabled(featureHookFeeV2)) + jvResult[jss::hook_gas_price] = lpClosed->fees().hookGasPrice; } if ((mMode >= OperatingMode::SYNCING) && !isNeedNetworkLedger()) diff --git a/src/ripple/app/tx/impl/Change.cpp b/src/ripple/app/tx/impl/Change.cpp index 37a436feea..a7e3975ff3 100644 --- a/src/ripple/app/tx/impl/Change.cpp +++ b/src/ripple/app/tx/impl/Change.cpp @@ -17,6 +17,7 @@ */ //============================================================================== +#include #include #include #include @@ -150,6 +151,16 @@ Change::preclaim(PreclaimContext const& ctx) ctx.tx.isFieldPresent(sfReserveIncrementDrops)) return temDISABLED; } + if (ctx.view.rules().enabled(featureHookFeeV2)) + { + if (!ctx.tx.isFieldPresent(sfHookGasPrice)) + return temMALFORMED; + } + else + { + if (ctx.tx.isFieldPresent(sfHookGasPrice)) + return temDISABLED; + } return tesSUCCESS; case ttAMENDMENT: case ttUNL_MODIFY: @@ -605,8 +616,9 @@ Change::activateXahauGenesis() wasmBytes, // wasm to verify loggerStream, "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", - (ctx_.view().rules().enabled(featureHooksUpdate1) ? 1 : 0) + - (ctx_.view().rules().enabled(fix20250131) ? 2 : 0)); + false, + hook_api::getImportWhitelist(ctx_.view().rules()), + hook_api::getGuardRulesVersion(ctx_.view().rules())); if (!result) { @@ -1024,6 +1036,8 @@ Change::applyFee() set(feeObject, ctx_.tx, sfReserveBase); set(feeObject, ctx_.tx, sfReserveIncrement); } + if (view().rules().enabled(featureHookFeeV2)) + set(feeObject, ctx_.tx, sfHookGasPrice); view().update(feeObject); diff --git a/src/ripple/app/tx/impl/HookDefinitionUpdate.cpp b/src/ripple/app/tx/impl/HookDefinitionUpdate.cpp new file mode 100644 index 0000000000..d89d66c5e0 --- /dev/null +++ b/src/ripple/app/tx/impl/HookDefinitionUpdate.cpp @@ -0,0 +1,117 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 XRPL-Labs + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include + +namespace ripple { + +NotTEC +HookDefinitionUpdate::preflight(PreflightContext const& ctx) +{ + if (!ctx.rules.enabled(featureHookFeeV2)) + return temDISABLED; + + auto const& tx = ctx.tx; + auto const& j = ctx.j; + + if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) + return ret; + + // TODO: check flags + + return preflight2(ctx); +} + +TER +HookDefinitionUpdate::preclaim(PreclaimContext const& ctx) +{ + auto const& view = ctx.view; + auto const& tx = ctx.tx; + + auto exists = + view.exists(keylet::hookDefinition(tx.getFieldH256(sfHookHash))); + if (!exists) + return tecNO_ENTRY; + + return tesSUCCESS; +} + +TER +HookDefinitionUpdate::doApply() +{ + auto& view = ctx_.view(); + auto const& tx = ctx_.tx; + + auto hookDefSle = + view.peek(keylet::hookDefinition(tx.getFieldH256(sfHookHash))); + if (!hookDefSle) + return tefINTERNAL; + + Blob hook = hookDefSle->getFieldVL(sfCreateCode); + + auto const result = + hook::doValidateGuards(tx, hook, view.rules(), ctx_.journal); + if (!result) + // TODO: TEQU better error code + // For compatibility, if an already deployed HookDefinition fails with a + // new guard-checker, don't update/delete it + return tecINTERNAL; + + auto const [hookCost, callbackCost] = *result; + + { + // if result is same as old, don't update sle + if (hookCost == hookDefSle->getFieldU64(sfHookCost) && + callbackCost == hookDefSle->getFieldU64(sfHookCallbackCost)) + return tesSUCCESS; + } + + { + // set new cost fields + hookDefSle->setFieldU64(sfHookCost, hookCost); + if (callbackCost > 0) + hookDefSle->setFieldU64(sfHookCallbackCost, callbackCost); + else if (hookDefSle->isFieldPresent(sfHookCallbackCost)) + hookDefSle->makeFieldAbsent(sfHookCallbackCost); + } + + { + // remove old fee fields + if (hookDefSle->isFieldPresent(sfFee)) + hookDefSle->makeFieldAbsent(sfFee); + if (hookDefSle->isFieldPresent(sfHookCallbackFee)) + hookDefSle->makeFieldAbsent(sfHookCallbackFee); + } + + view.update(hookDefSle); + + return tesSUCCESS; +} + +XRPAmount +HookDefinitionUpdate::calculateBaseFee(ReadView const& view, STTx const& tx) +{ + auto const baseFee = Transactor::calculateBaseFee(view, tx); + + // TODO: add a cost based on the size of the hook code? + + return baseFee; +} + +} // namespace ripple diff --git a/src/ripple/app/tx/impl/HookDefinitionUpdate.h b/src/ripple/app/tx/impl/HookDefinitionUpdate.h new file mode 100644 index 0000000000..f7b265cfa9 --- /dev/null +++ b/src/ripple/app/tx/impl/HookDefinitionUpdate.h @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 XRPL-Labs + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_HOOKDEFINITIONUPDATE_H_INCLUDED +#define RIPPLE_TX_HOOKDEFINITIONUPDATE_H_INCLUDED + +#include +#include +#include + +namespace ripple { + +class HookDefinitionUpdate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit HookDefinitionUpdate(ApplyContext& ctx) : Transactor(ctx) + { + } + + static XRPAmount + calculateBaseFee(ReadView const& view, STTx const& tx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const&); + + TER + doApply() override; +}; + +} // namespace ripple + +#endif diff --git a/src/ripple/app/tx/impl/SetHook.cpp b/src/ripple/app/tx/impl/SetHook.cpp index 9ca8295571..51007ba598 100644 --- a/src/ripple/app/tx/impl/SetHook.cpp +++ b/src/ripple/app/tx/impl/SetHook.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -470,59 +469,8 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj) Blob hook = hookSetObj.getFieldVL(sfCreateCode); - // RH NOTE: validateGuards has a generic non-rippled specific - // interface so it can be used in other projects (i.e. tooling). - // As such the calling here is a bit convoluted. - - std::optional>> - logger; - std::ostringstream loggerStream; - std::string hsacc{""}; - if (ctx.j.trace()) - { - logger = loggerStream; - std::stringstream ss; - ss << HS_ACC(); - hsacc = ss.str(); - } - - auto result = validateGuards( - hook, // wasm to verify - logger, - hsacc, - (ctx.rules.enabled(featureHooksUpdate1) ? 1 : 0) + - (ctx.rules.enabled(fix20250131) ? 2 : 0)); - - if (ctx.j.trace()) - { - // clunky but to get the stream to accept the output - // correctly we will split on new line and feed each line - // one by one into the trace stream beast::Journal should be - // updated to inherit from basic_ostream then this - // wouldn't be necessary. - - // is this a needless copy or does the compiler do copy - // elision here? - std::string s = loggerStream.str(); - - char* data = s.data(); - size_t len = s.size(); - - char* last = data; - size_t i = 0; - for (; i < len; ++i) - { - if (data[i] == '\n') - { - data[i] = '\0'; - ctx.j.trace() << last; - last = data + i; - } - } - - if (last < data + i) - ctx.j.trace() << last; - } + auto const result = + hook::doValidateGuards(ctx.tx, hook, ctx.rules, ctx.j); if (!result) return false; @@ -1637,9 +1585,11 @@ SetHook::setHook() assert(false); // should never happen } - // otherwise assign instruction counts - std::tie(maxInstrCountHook, maxInstrCountCbak) = + auto const instrCounts = std::get>(valid); + + std::tie(maxInstrCountHook, maxInstrCountCbak) = + instrCounts; } catch (std::exception& e) { @@ -1680,15 +1630,26 @@ SetHook::setHook() newHookDef->setFieldH256( sfHookSetTxnID, ctx.tx.getTransactionID()); newHookDef->setFieldU64(sfReferenceCount, 1); - newHookDef->setFieldAmount( - sfFee, - XRPAmount{ - hook::computeExecutionFee(maxInstrCountHook)}); - if (maxInstrCountCbak > 0) + + if (view().rules().enabled(featureHookFeeV2)) + { + newHookDef->setFieldU64(sfHookCost, maxInstrCountHook); + if (maxInstrCountCbak > 0) + newHookDef->setFieldU64( + sfHookCallbackCost, maxInstrCountCbak); + } + else + { newHookDef->setFieldAmount( - sfHookCallbackFee, + sfFee, XRPAmount{ - hook::computeExecutionFee(maxInstrCountCbak)}); + hook::computeExecutionFee(maxInstrCountHook)}); + if (maxInstrCountCbak > 0) + newHookDef->setFieldAmount( + sfHookCallbackFee, + XRPAmount{hook::computeExecutionFee( + maxInstrCountCbak)}); + } if (flags) newHookDef->setFieldU32(sfFlags, newFlags); diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index 80646172a0..b0869c48d7 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -207,7 +207,7 @@ Transactor::Transactor(ApplyContext& ctx) // RH NOTE: this only computes one chain at a time, so if there is a receiving // side to a txn then it must seperately be computed by a second call here -XRPAmount +std::pair Transactor::calculateHookChainFee( ReadView const& view, STTx const& tx, @@ -216,9 +216,10 @@ Transactor::calculateHookChainFee( { std::shared_ptr hookSLE = view.read(hookKeylet); if (!hookSLE) - return XRPAmount{0}; + return std::make_pair(XRPAmount{0}, 0); XRPAmount fee{0}; + uint64_t hookCost{0}; auto const& hooks = hookSLE->getFieldArray(sfHooks); @@ -255,19 +256,35 @@ Transactor::calculateHookChainFee( if (hook::canHook(tx.getTxnType(), hookOn) && (!collectCallsOnly || (flags & hook::hsfCOLLECT))) { - XRPAmount const toAdd{hookDef->getFieldAmount(sfFee).xrp().drops()}; + if (hookDef->isFieldPresent(sfFee)) + { + XRPAmount const toAdd{ + hookDef->getFieldAmount(sfFee).xrp().drops()}; - // this overflow should never happen, if somehow it does - // fee is set to the largest possible valid xrp value to force - // fail the transaction - if (fee + toAdd < fee) - fee = XRPAmount{INITIAL_XRP.drops()}; + // this overflow should never happen, if somehow it does + // fee is set to the largest possible valid xrp value to force + // fail the transaction + if (fee + toAdd < fee) + fee = XRPAmount{INITIAL_XRP.drops()}; + else + fee += toAdd; + } else - fee += toAdd; + { + uint64_t const toAdd{hookDef->getFieldU64(sfHookCost)}; + + // this overflow should never happen, if somehow it does + // fee is set to the largest possible valid xrp value to force + // fail the transaction + if (hookCost + toAdd < hookCost) + hookCost = std::numeric_limits::max(); + else + hookCost += toAdd; + } } } - return fee; + return std::make_pair(fee, hookCost); } XRPAmount @@ -293,6 +310,7 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) tx.isFieldPresent(sfSigners) ? tx.getFieldArray(sfSigners).size() : 0; XRPAmount hookExecutionFee{0}; + uint64_t hookCost{0}; uint64_t burden{1}; if (view.rules().enabled(featureHooks)) { @@ -315,18 +333,32 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) std::shared_ptr hookDef = view.read(keylet::hookDefinition(callbackHookHash)); - if (hookDef && hookDef->isFieldPresent(sfHookCallbackFee)) + if (hookDef) { - XRPAmount const toAdd{ - hookDef->getFieldAmount(sfHookCallbackFee).xrp().drops()}; - - // this overflow should never happen, if somehow it does - // fee is set to the largest possible valid xrp value to force - // fail the transaction - if (hookExecutionFee + toAdd < hookExecutionFee) - hookExecutionFee = XRPAmount{INITIAL_XRP.drops()}; - else - hookExecutionFee += toAdd; + if (hookDef->isFieldPresent(sfHookCallbackFee)) + { + XRPAmount const toAdd{ + hookDef->getFieldAmount(sfHookCallbackFee) + .xrp() + .drops()}; + + // this overflow should never happen, if somehow it does + // fee is set to the largest possible valid xrp value to + // force fail the transaction + if (hookExecutionFee + toAdd < hookExecutionFee) + hookExecutionFee = XRPAmount{INITIAL_XRP.drops()}; + else + hookExecutionFee += toAdd; + } + if (hookDef->isFieldPresent(sfHookCallbackCost)) + { + uint64_t const toAdd{ + hookDef->getFieldU64(sfHookCallbackCost)}; + if (hookCost + toAdd < hookCost) + hookCost = std::numeric_limits::max(); + else + hookCost += toAdd; + } } assert(emitDetails.isFieldPresent(sfEmitBurden)); @@ -334,8 +366,12 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) burden = emitDetails.getFieldU64(sfEmitBurden); } else - hookExecutionFee += calculateHookChainFee( + { + auto const hookChainFee = calculateHookChainFee( view, tx, keylet::hook(tx.getAccountID(sfAccount))); + hookExecutionFee += hookChainFee.first; + hookCost += hookChainFee.second; + } // find any additional stakeholders whose hooks will be executed and // charged to this transaction @@ -344,8 +380,12 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) for (auto& [tshAcc, canRollback] : tsh) if (canRollback) - hookExecutionFee += + { + auto const hookChainFee = calculateHookChainFee(view, tx, keylet::hook(tshAcc)); + hookExecutionFee += hookChainFee.first; + hookCost += hookChainFee.second; + } } XRPAmount accumulator = baseFee; @@ -399,6 +439,8 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) // transaction. do { + hookExecutionFee += hook::hookCostToFee(view, hookCost); + if (accumulator * burden < accumulator) break; @@ -1260,7 +1302,8 @@ Transactor::executeHookChain( return tecINTERNAL; } - bool hasCallback = hookDef->isFieldPresent(sfHookCallbackFee); + bool hasCallback = hookDef->isFieldPresent(sfHookCallbackFee) || + hookDef->isFieldPresent(sfHookCallbackCost); try { @@ -1365,7 +1408,8 @@ Transactor::doHookCallback( return; } - if (!hookDef->isFieldPresent(sfHookCallbackFee)) + if (!hookDef->isFieldPresent(sfHookCallbackFee) && + !hookDef->isFieldPresent(sfHookCallbackCost)) { JLOG(j_.trace()) << "HookInfo[" << callbackAccountID << "]: Callback specified by emitted txn " @@ -1587,8 +1631,11 @@ Transactor::doTSH( continue; // compute and deduct fees for the TSH if applicable - XRPAmount tshFeeDrops = + auto const tshFee = calculateHookChainFee(view, ctx_.tx, klTshHook, !canRollback); + XRPAmount tshFeeDrops{tshFee.first}; + + tshFeeDrops += hook::hookCostToFee(view, tshFee.second); // no hooks to execute, skip tsh if (tshFeeDrops == 0) @@ -1730,7 +1777,8 @@ Transactor::doAgainAsWeak( stateMap, ctx_, hookAccountID, - hookDef->isFieldPresent(sfHookCallbackFee), + hookDef->isFieldPresent(sfHookCallbackFee) || + hookDef->isFieldPresent(sfHookCallbackCost), false, false, 2UL, // param 2 = aaw diff --git a/src/ripple/app/tx/impl/Transactor.h b/src/ripple/app/tx/impl/Transactor.h index 7a2882e86c..7ad69aa767 100644 --- a/src/ripple/app/tx/impl/Transactor.h +++ b/src/ripple/app/tx/impl/Transactor.h @@ -174,7 +174,7 @@ class Transactor // Hooks - static XRPAmount + static std::pair calculateHookChainFee( ReadView const& view, STTx const& tx, diff --git a/src/ripple/app/tx/impl/applySteps.cpp b/src/ripple/app/tx/impl/applySteps.cpp index a18f78e1c2..a4deee7699 100644 --- a/src/ripple/app/tx/impl/applySteps.cpp +++ b/src/ripple/app/tx/impl/applySteps.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -187,6 +188,8 @@ invoke_preflight(PreflightContext const& ctx) return invoke_preflight_helper(ctx); case ttCRON: return invoke_preflight_helper(ctx); + case ttHOOK_DEFINITION_UPDATE: + return invoke_preflight_helper(ctx); default: assert(false); return {temUNKNOWN, TxConsequences{temUNKNOWN}}; @@ -316,6 +319,8 @@ invoke_preclaim(PreclaimContext const& ctx) return invoke_preclaim(ctx); case ttCRON: return invoke_preclaim(ctx); + case ttHOOK_DEFINITION_UPDATE: + return invoke_preclaim(ctx); default: assert(false); return temUNKNOWN; @@ -407,6 +412,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx) return CronSet::calculateBaseFee(view, tx); case ttCRON: return Cron::calculateBaseFee(view, tx); + case ttHOOK_DEFINITION_UPDATE: + return HookDefinitionUpdate::calculateBaseFee(view, tx); default: return XRPAmount{0}; } @@ -608,6 +615,10 @@ invoke_apply(ApplyContext& ctx) Cron p(ctx); return p(); } + case ttHOOK_DEFINITION_UPDATE: { + HookDefinitionUpdate p(ctx); + return p(); + } default: assert(false); return {temUNKNOWN, false}; diff --git a/src/ripple/core/Config.h b/src/ripple/core/Config.h index 0909f88ac1..1995f9b723 100644 --- a/src/ripple/core/Config.h +++ b/src/ripple/core/Config.h @@ -79,6 +79,9 @@ struct FeeSetup /** The per-owned item reserve requirement in drops. */ XRPAmount owner_reserve{2 * DROPS_PER_XRP}; + /** The gas price for hooks in drops. */ + std::uint32_t hook_gas_price{1'000'000}; // TODO: default value + /* (Remember to update the example cfg files when changing any of these * values.) */ }; diff --git a/src/ripple/core/impl/Config.cpp b/src/ripple/core/impl/Config.cpp index 9fd23f33e0..451e54d286 100644 --- a/src/ripple/core/impl/Config.cpp +++ b/src/ripple/core/impl/Config.cpp @@ -1079,6 +1079,13 @@ setup_FeeVote(Section const& section) if (set(temp, "owner_reserve", section)) setup.owner_reserve = temp; } + { + std::uint32_t temp; + if (set(temp, "hook_gas_price", section)) + { + setup.hook_gas_price = temp; + } + } return setup; } } // namespace ripple diff --git a/src/ripple/ledger/ReadView.h b/src/ripple/ledger/ReadView.h index e019d602f0..c634a858ba 100644 --- a/src/ripple/ledger/ReadView.h +++ b/src/ripple/ledger/ReadView.h @@ -52,6 +52,8 @@ struct Fees XRPAmount reserve{0}; // Reserve base (drops) XRPAmount increment{0}; // Reserve increment (drops) + std::uint32_t hookGasPrice{0}; // price per cost unit for hooks + explicit Fees() = default; Fees(Fees const&) = default; Fees& diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index 2766859dc7..58149fc14e 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -74,7 +74,7 @@ namespace detail { // Feature.cpp. Because it's only used to reserve storage, and determine how // large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than // the actual number of amendments. A LogicError on startup will verify this. -static constexpr std::size_t numFeatures = 90; +static constexpr std::size_t numFeatures = 91; /** Amendments that this server supports and the default voting behavior. Whether they are enabled depends on the Rules defined in the validated @@ -378,6 +378,8 @@ extern uint256 const fixInvalidTxFlags; extern uint256 const featureExtendedHookState; extern uint256 const fixCronStacking; extern uint256 const fixHookAPI20251128; +extern uint256 const featureHookFeeV2; + } // namespace ripple #endif diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index f507b22326..99a5256327 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -414,6 +414,7 @@ extern SF_UINT32 const sfXahauActivationLgrSeq; extern SF_UINT32 const sfDelaySeconds; extern SF_UINT32 const sfRepeatCount; extern SF_UINT32 const sfStartTime; +extern SF_UINT32 const sfHookGasPrice; // 64-bit integers (common) extern SF_UINT64 const sfIndexNext; @@ -438,6 +439,8 @@ extern SF_UINT64 const sfRewardAccumulator; extern SF_UINT64 const sfAccountCount; extern SF_UINT64 const sfAccountIndex; extern SF_UINT64 const sfTouchCount; +extern SF_UINT64 const sfHookCost; +extern SF_UINT64 const sfHookCallbackCost; // 128-bit extern SF_UINT128 const sfEmailHash; diff --git a/src/ripple/protocol/TxFormats.h b/src/ripple/protocol/TxFormats.h index bea5905cbf..590da4e16f 100644 --- a/src/ripple/protocol/TxFormats.h +++ b/src/ripple/protocol/TxFormats.h @@ -149,6 +149,9 @@ enum TxType : std::uint16_t ttURITOKEN_CREATE_SELL_OFFER = 48, ttURITOKEN_CANCEL_SELL_OFFER = 49, + /** This transaction updates a hook definition. */ + ttHOOK_DEFINITION_UPDATE = 91, + /* A pseudo-txn alarm signal for invoking a hook, emitted by validators after alarm set conditions are met */ ttCRON = 92, diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index 24383f896c..cdfef50b85 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -484,6 +484,7 @@ REGISTER_FIX (fixInvalidTxFlags, Supported::yes, VoteBehavior::De REGISTER_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixCronStacking, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FIX (fixHookAPI20251128, Supported::yes, VoteBehavior::DefaultYes); +REGISTER_FEATURE(HookFeeV2, Supported::yes, VoteBehavior::DefaultNo); // The following amendments are obsolete, but must remain supported // because they could potentially get enabled. diff --git a/src/ripple/protocol/impl/LedgerFormats.cpp b/src/ripple/protocol/impl/LedgerFormats.cpp index 5f51976be3..066b1790c7 100644 --- a/src/ripple/protocol/impl/LedgerFormats.cpp +++ b/src/ripple/protocol/impl/LedgerFormats.cpp @@ -185,6 +185,8 @@ LedgerFormats::LedgerFormats() {sfXahauActivationLgrSeq, soeOPTIONAL}, {sfAccountCount, soeOPTIONAL}, {sfNetworkID, soeOPTIONAL}, + // Hooks Cost + {sfHookGasPrice, soeOPTIONAL}, }, commonFields); @@ -236,8 +238,10 @@ LedgerFormats::LedgerFormats() {sfCreateCode, soeREQUIRED}, {sfHookSetTxnID, soeREQUIRED}, {sfReferenceCount, soeREQUIRED}, - {sfFee, soeREQUIRED}, - {sfHookCallbackFee, soeOPTIONAL} + {sfFee, soeOPTIONAL}, + {sfHookCallbackFee, soeOPTIONAL}, + {sfHookCost, soeOPTIONAL}, + {sfHookCallbackCost, soeOPTIONAL}, }, commonFields); diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index c4f2ef85a5..f02ae0f2c9 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -158,6 +158,7 @@ CONSTRUCT_TYPED_SFIELD(sfLockCount, "LockCount", UINT32, CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50); +CONSTRUCT_TYPED_SFIELD(sfHookGasPrice, "HookGasPrice", UINT32, 92); CONSTRUCT_TYPED_SFIELD(sfStartTime, "StartTime", UINT32, 93); CONSTRUCT_TYPED_SFIELD(sfRepeatCount, "RepeatCount", UINT32, 94); CONSTRUCT_TYPED_SFIELD(sfDelaySeconds, "DelaySeconds", UINT32, 95); @@ -187,6 +188,8 @@ CONSTRUCT_TYPED_SFIELD(sfEmitBurden, "EmitBurden", UINT64, CONSTRUCT_TYPED_SFIELD(sfHookInstructionCount, "HookInstructionCount", UINT64, 17); CONSTRUCT_TYPED_SFIELD(sfHookReturnCode, "HookReturnCode", UINT64, 18); CONSTRUCT_TYPED_SFIELD(sfReferenceCount, "ReferenceCount", UINT64, 19); +CONSTRUCT_TYPED_SFIELD(sfHookCallbackCost, "HookCallbackCost", UINT64, 95); +CONSTRUCT_TYPED_SFIELD(sfHookCost, "HookCost", UINT64, 96); CONSTRUCT_TYPED_SFIELD(sfTouchCount, "TouchCount", UINT64, 97); CONSTRUCT_TYPED_SFIELD(sfAccountIndex, "AccountIndex", UINT64, 98); CONSTRUCT_TYPED_SFIELD(sfAccountCount, "AccountCount", UINT64, 99); diff --git a/src/ripple/protocol/impl/STValidation.cpp b/src/ripple/protocol/impl/STValidation.cpp index e62a81733b..460c149894 100644 --- a/src/ripple/protocol/impl/STValidation.cpp +++ b/src/ripple/protocol/impl/STValidation.cpp @@ -66,6 +66,8 @@ STValidation::validationFormat() {sfBaseFeeDrops, soeOPTIONAL}, {sfReserveBaseDrops, soeOPTIONAL}, {sfReserveIncrementDrops, soeOPTIONAL}, + // HookCost + {sfHookGasPrice, soeOPTIONAL}, }; // clang-format on diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index 789de36f2d..d741d5de2c 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -196,6 +196,8 @@ TxFormats::TxFormats() {sfBaseFeeDrops, soeOPTIONAL}, {sfReserveBaseDrops, soeOPTIONAL}, {sfReserveIncrementDrops, soeOPTIONAL}, + // HookCost + {sfHookGasPrice, soeOPTIONAL}, }, commonFields); @@ -490,6 +492,13 @@ TxFormats::TxFormats() {sfStartTime, soeOPTIONAL}, }, commonFields); + + add(jss::HookDefinitionUpdate, + ttHOOK_DEFINITION_UPDATE, + { + {sfHookHash, soeREQUIRED}, + }, + commonFields); } TxFormats const& diff --git a/src/ripple/protocol/jss.h b/src/ripple/protocol/jss.h index 3160372689..2e1c57210b 100644 --- a/src/ripple/protocol/jss.h +++ b/src/ripple/protocol/jss.h @@ -80,21 +80,22 @@ JSS(GenesisMint); // tt JSS(GenesisMints); JSS(GovernanceMarks); JSS(GovernanceFlags); -JSS(HookApiVersion); // field -JSS(HookCanEmit); // field -JSS(HookHash); // field -JSS(HookNamespace); // field -JSS(HookOn); // field -JSS(Hooks); // field -JSS(HookGrants); // field -JSS(HookParameters); // field -JSS(HookParameterName); // field -JSS(HookParameterValue); // field -JSS(HookParameter); // field -JSS(HookGrant); // field -JSS(isSerialized); // out: RPC server_definitions -JSS(isSigningField); // out: RPC server_definitions -JSS(isVLEncoded); // out: RPC server_definitions +JSS(HookApiVersion); // field +JSS(HookCanEmit); // field +JSS(HookHash); // field +JSS(HookNamespace); // field +JSS(HookOn); // field +JSS(Hooks); // field +JSS(HookGrants); // field +JSS(HookParameters); // field +JSS(HookParameterName); // field +JSS(HookParameterValue); // field +JSS(HookParameter); // field +JSS(HookGrant); // field +JSS(HookDefinitionUpdate); // transaction type. +JSS(isSerialized); // out: RPC server_definitions +JSS(isSigningField); // out: RPC server_definitions +JSS(isVLEncoded); // out: RPC server_definitions JSS(Import); JSS(ImportVLSequence); JSS(Invalid); // @@ -368,6 +369,7 @@ JSS(highest_ticket); // out: AccountInfo JSS(historical_perminute); // historical_perminute. JSS(hook); // in: LedgerEntry JSS(hook_definition); // in: LedgerEntry +JSS(hook_gas_price); // JSS(hook_state); // in: LedgerEntry JSS(hostid); // out: NetworkOPs JSS(hotwallet); // in: GatewayBalances diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index ad38aefb20..f3932007d3 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -37,36 +37,42 @@ class FeeVote_test : public beast::unit_test::suite BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT(setup.hook_gas_price == defaultSetup.hook_gas_price); } { Section config; config.append( {"reference_fee = 50", "account_reserve = 1234567", - "owner_reserve = 1234"}); + "owner_reserve = 1234", + "hook_gas_price = 12345"}); auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == 50); BEAST_EXPECT(setup.account_reserve == 1234567); BEAST_EXPECT(setup.owner_reserve == 1234); + BEAST_EXPECT(setup.hook_gas_price == 12345); } { Section config; config.append( {"reference_fee = blah", "account_reserve = yada", - "owner_reserve = foo"}); + "owner_reserve = foo", + "hook_gas_price = baa"}); // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT(setup.hook_gas_price == defaultSetup.hook_gas_price); } { Section config; config.append( {"reference_fee = -50", "account_reserve = -1234567", - "owner_reserve = -1234"}); + "owner_reserve = -1234", + "hook_gas_price = -12345"}); // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); @@ -74,6 +80,8 @@ class FeeVote_test : public beast::unit_test::suite setup.account_reserve == static_cast(-1234567)); BEAST_EXPECT( setup.owner_reserve == static_cast(-1234)); + BEAST_EXPECT( + setup.hook_gas_price == static_cast(-12345)); } { const auto big64 = std::to_string( @@ -84,12 +92,14 @@ class FeeVote_test : public beast::unit_test::suite config.append( {"reference_fee = " + big64, "account_reserve = " + big64, - "owner_reserve = " + big64}); + "owner_reserve = " + big64, + "hook_gas_price = " + big64}); // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT(setup.hook_gas_price == defaultSetup.hook_gas_price); } } diff --git a/src/test/app/GenesisMint_test.cpp b/src/test/app/GenesisMint_test.cpp index 9f183f0c69..f92c6d2e1b 100644 --- a/src/test/app/GenesisMint_test.cpp +++ b/src/test/app/GenesisMint_test.cpp @@ -24,6 +24,23 @@ namespace ripple { namespace test { struct GenesisMint_test : public beast::unit_test::suite { + // Close the ledger until doVoting for FeatureHookFeeV2 is called + void + incLgrSeqForGasPriceEnabled(jtx::Env& env) + { + if (!env.current()->rules().enabled(featureHookFeeV2)) + return; + + BEAST_EXPECT(!env.le(keylet::fees())->isFieldPresent(sfHookGasPrice)); + + auto const seq = env.current()->info().seq; + BEAST_EXPECT(seq <= 256); + for (int i = seq; i <= 256; ++i) + env.close(); + env.close(); + BEAST_EXPECT(env.le(keylet::fees())->getFieldU32(sfHookGasPrice) > 0); + } + void validateEmittedTxn(jtx::Env& env, std::string result, uint64_t lineno) { @@ -95,6 +112,8 @@ struct GenesisMint_test : public beast::unit_test::suite using namespace std::literals::chrono_literals; Env env{*this, envconfig(), features}; + incLgrSeqForGasPriceEnabled(env); + auto const alice = Account("alice"); auto const bob = Account("bob"); auto const invoker = Account("invoker"); @@ -139,6 +158,8 @@ struct GenesisMint_test : public beast::unit_test::suite features, nullptr, beast::severities::kDisabled}; + incLgrSeqForGasPriceEnabled(env); + auto const alice = Account("alice"); auto const bob = Account("bob"); auto const invoker = Account("invoker"); @@ -233,14 +254,20 @@ struct GenesisMint_test : public beast::unit_test::suite auto acc = env.le(keylet::account(carol.id())); BEAST_EXPECT( acc->getFieldAmount(sfBalance).xrp().drops() == 67890000000ULL); - BEAST_EXPECT(acc->getFieldU32(sfSequence) == 60); + if (env.current()->rules().enabled(featureHookFeeV2)) + BEAST_EXPECT(acc->getFieldU32(sfSequence) == 2610); + else + BEAST_EXPECT(acc->getFieldU32(sfSequence) == 60); } { auto acc = env.le(keylet::account(david.id())); BEAST_EXPECT( acc->getFieldAmount(sfBalance).xrp().drops() == 12345000000ULL); - BEAST_EXPECT(acc->getFieldU32(sfSequence) == 60); + if (env.current()->rules().enabled(featureHookFeeV2)) + BEAST_EXPECT(acc->getFieldU32(sfSequence) == 2610); + else + BEAST_EXPECT(acc->getFieldU32(sfSequence) == 60); } // lots of entries @@ -646,6 +673,8 @@ struct GenesisMint_test : public beast::unit_test::suite using namespace std::literals::chrono_literals; Env env{*this, envconfig(), features}; + incLgrSeqForGasPriceEnabled(env); + auto const alice = Account("alice"); auto const bob = Account("bob"); env.fund(XRP(10000), alice, bob); @@ -668,6 +697,8 @@ struct GenesisMint_test : public beast::unit_test::suite using namespace std::literals::chrono_literals; Env env{*this, envconfig(), features}; + incLgrSeqForGasPriceEnabled(env); + auto const alice = Account("alice"); auto const bob = Account("bob"); env.fund(XRP(10000), alice, bob); @@ -700,6 +731,7 @@ struct GenesisMint_test : public beast::unit_test::suite testWithFeats(sa); testWithFeats(sa - fixXahauV1); testWithFeats(sa - fixHookAPI20251128); + testWithFeats(sa - featureHookFeeV2); } }; diff --git a/src/test/app/Import_test.cpp b/src/test/app/Import_test.cpp index 0579528362..5e5e69b9d8 100644 --- a/src/test/app/Import_test.cpp +++ b/src/test/app/Import_test.cpp @@ -5786,7 +5786,14 @@ class Import_test : public beast::unit_test::suite test::jtx::Env env{ *this, network::makeGenesisConfig( - features, 21337, keys, "10", "1000000", "200000", 1999998), + features, + 21337, + keys, + "10", + "1000000", + "200000", + "1000000", + 1999998), features}; // confirm total coins header @@ -5830,7 +5837,14 @@ class Import_test : public beast::unit_test::suite test::jtx::Env env{ *this, network::makeGenesisConfig( - features, 21337, keys, "10", "1000000", "200000", 1999998), + features, + 21337, + keys, + "10", + "1000000", + "200000", + "1000000", + 1999998), features}; // confirm total coins header @@ -5875,7 +5889,14 @@ class Import_test : public beast::unit_test::suite test::jtx::Env env{ *this, network::makeGenesisConfig( - features, 21337, keys, "10", "1000000", "200000", 1999998), + features, + 21337, + keys, + "10", + "1000000", + "200000", + "1000000", + 1999998), features}; // confirm total coins header @@ -5921,7 +5942,14 @@ class Import_test : public beast::unit_test::suite test::jtx::Env env{ *this, network::makeGenesisConfig( - features, 21337, keys, "10", "1000000", "200000", 4999999), + features, + 21337, + keys, + "10", + "1000000", + "200000", + "1000000", + 4999999), features}; // confirm total coins header @@ -5965,7 +5993,14 @@ class Import_test : public beast::unit_test::suite test::jtx::Env env{ *this, network::makeGenesisConfig( - features, 21337, keys, "10", "1000000", "200000", 19999999), + features, + 21337, + keys, + "10", + "1000000", + "200000", + "1000000", + 19999999), features}; // confirm total coins header @@ -6009,7 +6044,14 @@ class Import_test : public beast::unit_test::suite test::jtx::Env env{ *this, network::makeGenesisConfig( - features, 21337, keys, "10", "1000000", "200000", 29999998), + features, + 21337, + keys, + "10", + "1000000", + "200000", + "1000000", + 29999998), features}; // confirm total coins header @@ -6053,7 +6095,14 @@ class Import_test : public beast::unit_test::suite test::jtx::Env env{ *this, network::makeGenesisConfig( - features, 21337, keys, "10", "1000000", "200000", 29999998), + features, + 21337, + keys, + "10", + "1000000", + "200000", + "1000000", + 29999998), features}; // confirm total coins header @@ -6096,7 +6145,14 @@ class Import_test : public beast::unit_test::suite test::jtx::Env env{ *this, network::makeGenesisConfig( - features, 21337, keys, "10", "1000000", "200000", 50000000), + features, + 21337, + keys, + "10", + "1000000", + "200000", + "1000000", + 50000000), features}; // confirm total coins header diff --git a/src/test/app/SetHookTSH_test.cpp b/src/test/app/SetHookTSH_test.cpp index 5df238a986..880b11fa51 100644 --- a/src/test/app/SetHookTSH_test.cpp +++ b/src/test/app/SetHookTSH_test.cpp @@ -629,6 +629,23 @@ struct SetHookTSH0_test : public beast::unit_test::suite 0x61U, 0x72U, 0x74U, 0x2EU, 0x22U, 0x00U, 0x22U, 0x74U, 0x73U, 0x68U, 0x2EU, 0x63U, 0x3AU, 0x20U, 0x45U, 0x6EU, 0x64U, 0x2EU, 0x22U}; + // Close the ledger until doVoting for FeatureHookFeeV2 is called + void + incLgrSeqForGasPriceEnabled(jtx::Env& env) + { + if (!env.current()->rules().enabled(featureHookFeeV2)) + return; + + BEAST_EXPECT(!env.le(keylet::fees())->isFieldPresent(sfHookGasPrice)); + + auto const seq = env.current()->info().seq; + BEAST_EXPECT(seq <= 256); + for (int i = seq; i <= 256; ++i) + env.close(); + env.close(); + BEAST_EXPECT(env.le(keylet::fees())->getFieldU32(sfHookGasPrice) > 0); + } + void addWeakTSH(jtx::Env& env, jtx::Account const& account) { @@ -789,6 +806,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -830,6 +848,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const bene = Account("bob"); @@ -882,6 +901,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -917,6 +937,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -962,6 +983,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -997,6 +1019,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1041,6 +1064,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1073,6 +1097,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1114,6 +1139,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1151,6 +1177,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1188,6 +1215,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -1256,6 +1284,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = Account("issuer"); @@ -1289,6 +1318,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = Account("issuer"); @@ -1331,6 +1361,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("gw"); auto const holder = Account("bob"); @@ -1368,6 +1399,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("gw"); auto const holder = Account("bob"); @@ -1420,6 +1452,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const authed = Account("alice"); auto const account = Account("bob"); @@ -1453,6 +1486,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const authed = Account("alice"); auto const account = Account("bob"); @@ -1509,6 +1543,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1553,6 +1588,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -1607,6 +1643,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1651,6 +1688,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1695,6 +1733,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -1766,6 +1805,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1817,6 +1857,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -1878,6 +1919,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1929,6 +1971,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -1984,6 +2027,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -2062,6 +2106,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2099,6 +2144,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2136,6 +2182,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -2200,6 +2247,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2241,6 +2289,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2282,6 +2331,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2323,6 +2373,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2364,6 +2415,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -2432,6 +2484,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2480,6 +2533,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2532,6 +2586,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2580,6 +2635,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -2632,6 +2688,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -2715,6 +2772,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = env.master; @@ -2766,6 +2824,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = env.master; @@ -2818,6 +2877,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = env.master; @@ -2905,6 +2965,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite network::makeNetworkVLConfig( 21337, keys, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = Account("bob"); @@ -2945,6 +3006,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite network::makeNetworkVLConfig( 21337, keys, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = Account("bob"); @@ -3002,6 +3064,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -3035,6 +3098,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -3082,6 +3146,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const gw = Account{"gateway"}; @@ -3131,6 +3196,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const gw = Account{"gateway"}; @@ -3168,6 +3234,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const cross = Account("bob"); @@ -3212,6 +3279,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("carol"); auto const cross = Account("bob"); @@ -3272,6 +3340,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3302,6 +3371,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3332,6 +3402,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const cross = Account("bob"); @@ -3373,6 +3444,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const cross = Account("bob"); @@ -3451,6 +3523,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3496,6 +3569,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3542,6 +3616,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3590,6 +3665,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3638,6 +3714,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -3709,6 +3786,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3743,6 +3821,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3777,6 +3856,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -3837,6 +3917,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3877,6 +3958,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -3918,6 +4000,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -3987,6 +4070,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -4033,6 +4117,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -4063,6 +4148,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -4106,6 +4192,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const signer1 = Account{"bob"}; @@ -4139,6 +4226,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const signer1 = Account{"bob"}; @@ -4201,6 +4289,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -4243,6 +4332,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = Account{"gw"}; @@ -4274,6 +4364,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = Account{"gw"}; @@ -4334,6 +4425,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const buyer = Account("carol"); @@ -4373,6 +4465,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const buyer = Account("carol"); @@ -4416,6 +4509,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const buyer = Account("carol"); @@ -4456,6 +4550,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const buyer = Account("carol"); @@ -4509,6 +4604,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -4557,6 +4653,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -4614,6 +4711,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -4663,6 +4761,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -4720,6 +4819,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -4778,6 +4878,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -4836,6 +4937,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -4897,6 +4999,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -4957,6 +5060,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5016,6 +5120,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5075,6 +5180,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -5145,6 +5251,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5201,6 +5308,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -5267,6 +5375,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -5334,6 +5443,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5400,6 +5510,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5453,6 +5564,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5507,6 +5619,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5560,6 +5673,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5613,6 +5727,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5667,6 +5782,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const issuer = Account("alice"); auto const owner = Account("bob"); @@ -5724,6 +5840,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -5817,6 +5934,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = Account{"gw"}; @@ -5848,6 +5966,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = Account{"gw"}; @@ -5877,6 +5996,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -5914,6 +6034,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const issuer = Account{"gw"}; @@ -5955,6 +6076,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -6015,6 +6137,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -6045,6 +6168,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account{"bob"}; @@ -6075,6 +6199,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -6110,6 +6235,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); bool const withIOUIssuerWeakTSH = env.current()->rules().enabled(featureIOUIssuerWeakTSH); @@ -6163,6 +6289,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -6219,6 +6346,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); auto const dest = Account("bob"); @@ -6284,6 +6412,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -6328,6 +6457,7 @@ struct SetHookTSH0_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + incLgrSeqForGasPriceEnabled(env); auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -6531,11 +6661,13 @@ struct SetHookTSH0_test : public beast::unit_test::suite using namespace test::jtx; static FeatureBitset const all{supported_amendments()}; - static std::array const feats{ + static std::array const feats{ all, - all - fixXahauV1 - fixXahauV2 - featureIOUIssuerWeakTSH, - all - fixXahauV2 - featureIOUIssuerWeakTSH, - all - featureIOUIssuerWeakTSH, + all - fixXahauV1 - fixXahauV2 - featureIOUIssuerWeakTSH - + featureHookFeeV2, + all - fixXahauV2 - featureIOUIssuerWeakTSH - featureHookFeeV2, + all - featureIOUIssuerWeakTSH - featureHookFeeV2, + all - featureHookFeeV2, }; if (BEAST_EXPECT(instance < feats.size())) @@ -6566,12 +6698,14 @@ struct SetHookTSH0_test : public beast::unit_test::suite SETHOOKTSH_TEST(1, false) SETHOOKTSH_TEST(2, false) -SETHOOKTSH_TEST(3, true) +SETHOOKTSH_TEST(3, false) +SETHOOKTSH_TEST(4, true) BEAST_DEFINE_TESTSUITE_PRIO(SetHookTSH0, app, ripple, 2); BEAST_DEFINE_TESTSUITE_PRIO(SetHookTSH1, app, ripple, 2); BEAST_DEFINE_TESTSUITE_PRIO(SetHookTSH2, app, ripple, 2); BEAST_DEFINE_TESTSUITE_PRIO(SetHookTSH3, app, ripple, 2); +BEAST_DEFINE_TESTSUITE_PRIO(SetHookTSH4, app, ripple, 2); } // namespace test } // namespace ripple diff --git a/src/test/app/SetHook_test.cpp b/src/test/app/SetHook_test.cpp index 0c7be29e81..fc477896c2 100644 --- a/src/test/app/SetHook_test.cpp +++ b/src/test/app/SetHook_test.cpp @@ -84,6 +84,23 @@ class SetHook0_test : public beast::unit_test::suite jv[jss::Flags] = hsfOVERRIDE; } + // Close the ledger until doVoting for FeatureHookFeeV2 is called + void + incLgrSeqForGasPriceEnabled(jtx::Env& env) + { + if (!env.current()->rules().enabled(featureHookFeeV2)) + return; + + BEAST_EXPECT(!env.le(keylet::fees())->isFieldPresent(sfHookGasPrice)); + + auto const seq = env.current()->info().seq; + BEAST_EXPECT(seq <= 256); + for (int i = seq; i <= 256; ++i) + env.close(); + env.close(); + BEAST_EXPECT(env.le(keylet::fees())->getFieldU32(sfHookGasPrice) > 0); + } + public: // This is a large fee, large enough that we can set most small test hooks // without running into fee issues we only want to test fee code specifically in @@ -98,6 +115,7 @@ class SetHook0_test : public beast::unit_test::suite std::string fee, std::string a_res, std::string o_res, + std::string g_res, uint32_t ledgerID) { using namespace jtx; @@ -224,9 +242,13 @@ class SetHook0_test : public beast::unit_test::suite config.append( {"reference_fee = " + fee, "account_reserve = " + a_res, - "owner_reserve = " + o_res}); + "owner_reserve = " + o_res, + "hook_gas_price = 1000"}); auto setup = setup_FeeVote(config); cfg->FEES = setup; + cfg->section("voting") = config; + // override hook gas price for doVoting + cfg->section("voting").set("hook_gas_price", g_res); return cfg; }); } @@ -239,6 +261,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const gw = Account{"gateway"}; @@ -868,6 +891,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Checks malformed nsdelete operation"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); bool const fixNS = env.current()->rules().enabled(fixNSDelete); bool const hasHookCanEmit = @@ -1152,6 +1176,7 @@ class SetHook0_test : public beast::unit_test::suite 0x6aU, 0x24U, 0x00U, 0x0bU}; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); bool const fixNS = env.current()->rules().enabled(fixNSDelete); auto const bob = Account{"bob"}; @@ -1237,8 +1262,10 @@ class SetHook0_test : public beast::unit_test::suite test::jtx::Env env{ *this, - makePageCapConfig(features, 21337, "10", "1000000", "200000", 0), + makePageCapConfig( + features, 21337, "10", "1000000", "200000", "1000000", 0), features}; + incLgrSeqForGasPriceEnabled(env); bool const hasFix = env.current()->rules().enabled(fixPageCap); @@ -2320,6 +2347,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test accept() hookapi"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -2341,6 +2369,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test rollback() hookapi"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -2365,6 +2394,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test guards"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -2570,12 +2600,88 @@ class SetHook0_test : public beast::unit_test::suite } } + void + testGuardCost(FeatureBitset features) + { + testcase("Test guard cost"); + using namespace jtx; + + auto const alice = Account{"alice"}; + auto const bob = Account{"bob"}; + + for (auto withCost : {true, false}) + { + Env env{ + *this, + withCost ? features | featureHookFeeV2 + : features - featureHookFeeV2}; + incLgrSeqForGasPriceEnabled(env); + + env.fund(XRP(10000), alice); + env.fund(XRP(10000), bob); + TestHook hook_wasm = wasm[R"[test.hook]( + #include + extern int32_t _g (uint32_t id, uint32_t maxiter); + #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) + extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); + extern int64_t hook_account (uint32_t, uint32_t); + int64_t hook(uint32_t reserved) + { + uint8_t acc[20]; + for (int i = 0; i < 10; ++i) + { + _g(1, 11); + for (int j = 0; j < 2; ++j) + { + _g(2, 30); + for (int k = 0; k < 5; ++k) + { + _g(3, 120); + hook_account(acc, 20); + } + for (int k = 0; k < 5; ++k) + { + _g(4, 120); + hook_account(acc, 20); + } + } + } + return accept(0,0,2); + } + )[test.hook]"]; + + HASH_WASM(hook); + + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), + HSFEE); + env.close(); + + auto const hookDef = env.le(hook_keylet); + + BEAST_EXPECT(hookDef); + if (withCost) + { + auto const hookCost = hookDef->getFieldU64(sfHookCost); + BEAST_EXPECT(hookCost == 4494); + BEAST_EXPECT(!hookDef->isFieldPresent(sfFee)); + } + else + { + auto const hookFee = hookDef->getFieldAmount(sfFee); + BEAST_EXPECT(hookFee == XRPAmount{1944}); + BEAST_EXPECT(!hookDef->isFieldPresent(sfHookCost)); + } + } + } + void test_emit(FeatureBitset features) { testcase("Test emit"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -3080,6 +3186,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -3198,6 +3305,7 @@ class SetHook0_test : public beast::unit_test::suite f = f - fixHookAPI20251128; Env env{*this, f}; + incLgrSeqForGasPriceEnabled(env); env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); @@ -3239,6 +3347,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -3303,6 +3412,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -3352,6 +3462,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -3392,6 +3503,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_compare"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -3514,7 +3626,7 @@ class SetHook0_test : public beast::unit_test::suite HSFEE); env.close(); - env(pay(bob, alice, XRP(1)), M("test float_compare"), fee(XRP(1))); + env(pay(bob, alice, XRP(1)), M("test float_compare"), HSFEE); env.close(); } } @@ -3525,6 +3637,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_divide"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -3724,6 +3837,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_int"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -3842,7 +3956,7 @@ class SetHook0_test : public beast::unit_test::suite HSFEE); env.close(); - env(pay(bob, alice, XRP(1)), M("test float_int"), fee(XRP(1))); + env(pay(bob, alice, XRP(1)), M("test float_int"), HSFEE); env.close(); } } @@ -3853,6 +3967,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_invert"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -3941,6 +4056,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_log"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -4024,6 +4140,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_mantissa"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -4153,6 +4270,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_mulratio"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -4308,6 +4426,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_multiply"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -4607,6 +4726,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_negate"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -4678,6 +4798,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_one"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -4719,6 +4840,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_root"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -4797,6 +4919,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_set"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -4870,6 +4993,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_sign"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -4983,6 +5107,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_sto"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -5188,6 +5313,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_sto_set"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -5330,6 +5456,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test float_sum"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -5512,6 +5639,7 @@ class SetHook0_test : public beast::unit_test::suite auto const test = [&](Account alice) -> void { Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; env.fund(XRP(10000), alice); @@ -5633,6 +5761,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test hook_again"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -5711,6 +5840,7 @@ class SetHook0_test : public beast::unit_test::suite auto const test = [&](Account alice) -> void { Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; env.fund(XRP(10000), alice); @@ -5870,6 +6000,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test hook_param"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -5997,6 +6128,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test hook_param_set"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -6206,6 +6338,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test hook_pos"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -6260,6 +6393,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test hook_skip"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -6382,6 +6516,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test ledger_keylet"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -6484,6 +6619,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -6558,6 +6694,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test ledger_last_time"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -6626,6 +6763,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -6724,6 +6862,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test ledger_seq"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -6783,6 +6922,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test meta_slot"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -6872,7 +7012,11 @@ class SetHook0_test : public beast::unit_test::suite std::vector const keys = { "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC" "1"}; - Env env{*this, network::makeNetworkVLConfig(21337, keys)}; + Env env{ + *this, + network::makeNetworkVLConfig(21337, keys), + features - featureHooksUpdate1}; + incLgrSeqForGasPriceEnabled(env); auto const master = Account("masterpassphrase"); env(noop(master), fee(10'000'000'000), ter(tesSUCCESS)); @@ -6950,6 +7094,16 @@ class SetHook0_test : public beast::unit_test::suite } )[test.hook]"]; + // before featureHooksUpdate1 + env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + M("set xpop_slot (disabled)"), + HSFEE, + ter(temMALFORMED)); + env.close(); + + env.enableFeature(featureHooksUpdate1); + env.close(); + // install the hook on alice env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), M("set xpop_slot"), @@ -6988,6 +7142,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test otxn_field"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7058,6 +7213,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test otxn_id"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7141,6 +7297,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test otxn_slot"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7223,6 +7380,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test otxn_type"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7294,6 +7452,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test otxn_param"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7417,6 +7576,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test slot"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7530,6 +7690,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test slot_clear"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7588,6 +7749,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test slot_count"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7653,6 +7815,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test slot_float"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7728,6 +7891,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test slot_set"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7838,6 +8002,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test slot_size"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); Account const alice{"alice"}; Account const bob{"bob"}; @@ -7890,7 +8055,7 @@ class SetHook0_test : public beast::unit_test::suite ASSERT(s > 0); // pull the object out into a buffer, check the number of bytes written is correct - uint8_t buf[4096]; + uint8_t buf[8210]; // ltSkip(3)+FLS(6)+LLS(6)+ Hashes(3+8,192(32*256)) ASSERT(slot(SBUF(buf), 1) == s); // check the object is valid @@ -7918,6 +8083,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -8058,6 +8224,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -8159,6 +8326,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -8298,6 +8466,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -8439,6 +8608,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -8659,6 +8829,7 @@ class SetHook0_test : public beast::unit_test::suite 0x6eU, 0x74U, 0x32U}; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -8709,7 +8880,11 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test state_foreign_set"); using namespace jtx; - Env env{*this, features}; + Env env{ + *this, + network::makeNetworkConfig(21137, "10", "200000000", "50000000"), + features}; + incLgrSeqForGasPriceEnabled(env); auto const david = Account("david"); // grantee generic auto const cho = Account{"cho"}; // invoker @@ -9174,7 +9349,11 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test state_set"); using namespace jtx; - Env env{*this, features}; + Env env{ + *this, + network::makeNetworkConfig(21137, "10", "200000000", "50000000"), + features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -10168,6 +10347,7 @@ class SetHook0_test : public beast::unit_test::suite { Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); @@ -10395,6 +10575,7 @@ class SetHook0_test : public beast::unit_test::suite for (auto f : {features, features - fixHookAPI20251128}) { Env env{*this, f}; + incLgrSeqForGasPriceEnabled(env); bool const hasFix = env.current()->rules().enabled(fixHookAPI20251128); @@ -10438,6 +10619,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -10578,6 +10760,7 @@ class SetHook0_test : public beast::unit_test::suite auto const alice = Account{"alice"}; { Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); @@ -10682,6 +10865,7 @@ class SetHook0_test : public beast::unit_test::suite *this, isfixHookAPI20251128 ? features | fixHookAPI20251128 : features - fixHookAPI20251128}; + incLgrSeqForGasPriceEnabled(env); env.fund(XRP(10000), alice, bob); env.close(); @@ -10738,6 +10922,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -10822,6 +11007,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const bob = Account{"bob"}; auto const alice = Account{"alice"}; @@ -10905,6 +11091,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -10955,6 +11142,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -10999,6 +11187,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -11041,6 +11230,7 @@ class SetHook0_test : public beast::unit_test::suite { using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -11311,6 +11501,7 @@ class SetHook0_test : public beast::unit_test::suite using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -11898,6 +12089,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test util_raddr"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -12351,6 +12543,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test util_sha512h"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -12724,6 +12917,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("Test util_verify"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -12838,6 +13032,7 @@ class SetHook0_test : public beast::unit_test::suite testcase("test HookCanEmit"); using namespace jtx; Env env{*this, features}; + incLgrSeqForGasPriceEnabled(env); auto const caller = Account{"caller"}; auto const alice = Account{"alice"}; @@ -13354,7 +13549,7 @@ class SetHook0_test : public beast::unit_test::suite // invoke the hook env(pay(caller, acc, XRP(1)), M("test hookcanemit 3"), - fee(XRP(1))); + fee(XRP(10))); env.close(); auto meta = env.meta(); @@ -13407,6 +13602,7 @@ class SetHook0_test : public beast::unit_test::suite test_rollback(features); testGuards(features); + testGuardCost(features); test_emit(features); // // test_etxn_burden(features); // tested above @@ -13501,16 +13697,18 @@ class SetHook0_test : public beast::unit_test::suite using namespace test::jtx; static FeatureBitset const all{supported_amendments()}; - static std::array const feats{ + static std::array const feats{ all, - all - fixXahauV2, - all - fixXahauV1 - fixXahauV2, - all - fixXahauV1 - fixXahauV2 - fixNSDelete, - all - fixXahauV1 - fixXahauV2 - fixNSDelete - fixPageCap, + all - featureHookFeeV2, + all - fixXahauV2 - featureHookFeeV2, + all - fixXahauV1 - fixXahauV2 - featureHookFeeV2, + all - fixXahauV1 - fixXahauV2 - fixNSDelete - featureHookFeeV2, + all - fixXahauV1 - fixXahauV2 - fixNSDelete - fixPageCap - + featureHookFeeV2, all - fixXahauV1 - fixXahauV2 - fixNSDelete - fixPageCap - - featureHookCanEmit, + featureHookCanEmit - featureHookFeeV2, all - fixXahauV1 - fixXahauV2 - fixNSDelete - fixPageCap - - featureExtendedHookState, + featureExtendedHookState - featureHookFeeV2, }; if (BEAST_EXPECT(instance < feats.size())) @@ -13679,7 +13877,8 @@ SETHOOK_TEST(2, false) SETHOOK_TEST(3, false) SETHOOK_TEST(4, false) SETHOOK_TEST(5, false) -SETHOOK_TEST(6, true) +SETHOOK_TEST(6, false) +SETHOOK_TEST(7, true) BEAST_DEFINE_TESTSUITE_PRIO(SetHook0, app, ripple, 2); BEAST_DEFINE_TESTSUITE_PRIO(SetHook1, app, ripple, 2); @@ -13688,6 +13887,7 @@ BEAST_DEFINE_TESTSUITE_PRIO(SetHook3, app, ripple, 2); BEAST_DEFINE_TESTSUITE_PRIO(SetHook4, app, ripple, 2); BEAST_DEFINE_TESTSUITE_PRIO(SetHook5, app, ripple, 2); BEAST_DEFINE_TESTSUITE_PRIO(SetHook6, app, ripple, 2); +BEAST_DEFINE_TESTSUITE_PRIO(SetHook7, app, ripple, 2); } // namespace test } // namespace ripple #undef M diff --git a/src/test/app/SetHook_wasm.h b/src/test/app/SetHook_wasm.h index 73294c9fb0..e913e43821 100644 --- a/src/test/app/SetHook_wasm.h +++ b/src/test/app/SetHook_wasm.h @@ -543,6 +543,110 @@ std::map> wasm = { }}, /* ==== WASM: 6 ==== */ + {R"[test.hook]( + #include + extern int32_t _g (uint32_t id, uint32_t maxiter); + #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) + extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); + extern int64_t hook_account (uint32_t, uint32_t); + int64_t hook(uint32_t reserved) + { + uint8_t acc[20]; + for (int i = 0; i < 10; ++i) + { + _g(1, 11); + for (int j = 0; j < 2; ++j) + { + _g(2, 30); + for (int k = 0; k < 5; ++k) + { + _g(3, 120); + hook_account(acc, 20); + } + for (int k = 0; k < 5; ++k) + { + _g(4, 120); + hook_account(acc, 20); + } + } + } + return accept(0,0,2); + } + )[test.hook]", + { + 0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U, 0x19U, + 0x04U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x02U, 0x7FU, + 0x7FU, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U, 0x7EU, + 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U, 0x2AU, 0x03U, 0x03U, 0x65U, + 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U, 0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, + 0x76U, 0x0CU, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x61U, 0x63U, 0x63U, + 0x6FU, 0x75U, 0x6EU, 0x74U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, + 0x06U, 0x61U, 0x63U, 0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x02U, 0x03U, + 0x02U, 0x01U, 0x03U, 0x05U, 0x03U, 0x01U, 0x00U, 0x02U, 0x06U, 0x21U, + 0x05U, 0x7FU, 0x01U, 0x41U, 0x80U, 0x88U, 0x04U, 0x0BU, 0x7FU, 0x00U, + 0x41U, 0x80U, 0x08U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, + 0x7FU, 0x00U, 0x41U, 0x80U, 0x88U, 0x04U, 0x0BU, 0x7FU, 0x00U, 0x41U, + 0x80U, 0x08U, 0x0BU, 0x07U, 0x08U, 0x01U, 0x04U, 0x68U, 0x6FU, 0x6FU, + 0x6BU, 0x00U, 0x03U, 0x0AU, 0xB3U, 0x84U, 0x00U, 0x01U, 0xAFU, 0x84U, + 0x00U, 0x02U, 0x02U, 0x7FU, 0x01U, 0x7EU, 0x23U, 0x80U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x41U, 0x20U, 0x6BU, 0x22U, 0x01U, 0x24U, 0x80U, 0x80U, + 0x80U, 0x80U, 0x00U, 0x41U, 0x0AU, 0x21U, 0x02U, 0x03U, 0x40U, 0x41U, + 0x01U, 0x41U, 0x0BU, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, + 0x41U, 0x02U, 0x41U, 0x1EU, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, + 0x1AU, 0x41U, 0x03U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, + 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x03U, 0x41U, 0xF8U, 0x00U, 0x10U, + 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, + 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x03U, 0x41U, + 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, + 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, + 0x41U, 0x03U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x1AU, 0x41U, 0x03U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, + 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x04U, 0x41U, 0xF8U, + 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, + 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, + 0x04U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, + 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x41U, 0x04U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, + 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x04U, 0x41U, 0xF8U, 0x00U, + 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, + 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x04U, + 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, + 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, + 0x1AU, 0x41U, 0x02U, 0x41U, 0x1EU, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x41U, 0x03U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, + 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x03U, 0x41U, 0xF8U, 0x00U, + 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, + 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x03U, + 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, + 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, + 0x1AU, 0x41U, 0x03U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, + 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x03U, 0x41U, 0xF8U, 0x00U, 0x10U, + 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, + 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x04U, 0x41U, + 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, + 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, + 0x41U, 0x04U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x1AU, 0x41U, 0x04U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, + 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0x04U, 0x41U, 0xF8U, + 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, + 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, + 0x04U, 0x41U, 0xF8U, 0x00U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, + 0x1AU, 0x20U, 0x01U, 0x41U, 0x14U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x20U, 0x02U, 0x41U, 0x7FU, 0x6AU, 0x22U, 0x02U, 0x0DU, + 0x00U, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x02U, 0x10U, 0x82U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x21U, 0x03U, 0x20U, 0x01U, 0x41U, 0x20U, + 0x6AU, 0x24U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x20U, 0x03U, 0x0BU, + }}, + + /* ==== WASM: 7 ==== */ {R"[test.hook]( #include extern int32_t _g(uint32_t, uint32_t); @@ -1159,7 +1263,7 @@ std::map> wasm = { 0x78U, 0x29U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, }}, - /* ==== WASM: 7 ==== */ + /* ==== WASM: 8 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -1264,7 +1368,7 @@ std::map> wasm = { 0x00U, }}, - /* ==== WASM: 8 ==== */ + /* ==== WASM: 9 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -1386,7 +1490,7 @@ std::map> wasm = { 0x58U, 0x4EU, 0x00U, }}, - /* ==== WASM: 9 ==== */ + /* ==== WASM: 10 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -1509,7 +1613,7 @@ std::map> wasm = { 0x4EU, 0x59U, 0x5FU, 0x4EU, 0x4FU, 0x4EU, 0x43U, 0x45U, 0x53U, 0x00U, }}, - /* ==== WASM: 10 ==== */ + /* ==== WASM: 11 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -1589,7 +1693,7 @@ std::map> wasm = { 0x54U, 0x00U, }}, - /* ==== WASM: 11 ==== */ + /* ==== WASM: 12 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -1634,7 +1738,7 @@ std::map> wasm = { 0x30U, 0x00U, }}, - /* ==== WASM: 12 ==== */ + /* ==== WASM: 13 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -1953,7 +2057,7 @@ std::map> wasm = { 0x80U, 0x80U, 0x80U, 0x00U, 0x0BU, }}, - /* ==== WASM: 13 ==== */ + /* ==== WASM: 14 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -2739,7 +2843,7 @@ std::map> wasm = { 0x37U, 0x36U, 0x33U, 0x4CU, 0x4CU, 0x20U, 0x29U, 0x00U, }}, - /* ==== WASM: 14 ==== */ + /* ==== WASM: 15 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -3129,7 +3233,7 @@ std::map> wasm = { 0x80U, 0x80U, 0x80U, 0x00U, 0x0BU, }}, - /* ==== WASM: 15 ==== */ + /* ==== WASM: 16 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -3310,7 +3414,7 @@ std::map> wasm = { 0x38U, 0x4CU, 0x4CU, 0x29U, 0x00U, }}, - /* ==== WASM: 16 ==== */ + /* ==== WASM: 17 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -3499,7 +3603,7 @@ std::map> wasm = { 0x29U, 0x00U, }}, - /* ==== WASM: 17 ==== */ + /* ==== WASM: 18 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -3738,7 +3842,7 @@ std::map> wasm = { 0x00U, 0x42U, 0x00U, 0x10U, 0x85U, 0x80U, 0x80U, 0x80U, 0x00U, 0x0BU, }}, - /* ==== WASM: 18 ==== */ + /* ==== WASM: 19 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -4440,7 +4544,7 @@ std::map> wasm = { 0x38U, 0x35U, 0x35U, 0x32U, 0x55U, 0x29U, 0x00U, }}, - /* ==== WASM: 19 ==== */ + /* ==== WASM: 20 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -5785,7 +5889,7 @@ std::map> wasm = { 0x20U, 0x29U, 0x00U, }}, - /* ==== WASM: 20 ==== */ + /* ==== WASM: 21 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -5887,7 +5991,7 @@ std::map> wasm = { 0x84U, 0x80U, 0x80U, 0x80U, 0x00U, 0x0BU, }}, - /* ==== WASM: 21 ==== */ + /* ==== WASM: 22 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -5930,7 +6034,7 @@ std::map> wasm = { 0x00U, 0x0BU, }}, - /* ==== WASM: 22 ==== */ + /* ==== WASM: 23 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -6078,7 +6182,7 @@ std::map> wasm = { 0x34U, 0x34U, 0x4CU, 0x4CU, 0x2CU, 0x20U, 0x33U, 0x29U, 0x00U, }}, - /* ==== WASM: 23 ==== */ + /* ==== WASM: 24 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -6406,7 +6510,7 @@ std::map> wasm = { 0x38U, 0x34U, 0x39U, 0x30U, 0x4CU, 0x4CU, 0x00U, }}, - /* ==== WASM: 24 ==== */ + /* ==== WASM: 25 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -6611,7 +6715,7 @@ std::map> wasm = { 0x10U, 0x85U, 0x80U, 0x80U, 0x80U, 0x00U, 0x0BU, }}, - /* ==== WASM: 25 ==== */ + /* ==== WASM: 26 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -7295,7 +7399,7 @@ std::map> wasm = { 0x20U, 0x3DU, 0x3DU, 0x20U, 0x30U, 0x00U, }}, - /* ==== WASM: 26 ==== */ + /* ==== WASM: 27 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -7590,7 +7694,7 @@ std::map> wasm = { 0x32U, 0x34U, 0x31U, 0x36U, 0x55U, 0x4CU, 0x4CU, 0x00U, }}, - /* ==== WASM: 27 ==== */ + /* ==== WASM: 28 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -8303,7 +8407,7 @@ std::map> wasm = { 0x31U, 0x33U, 0x33U, 0x38U, 0x20U, 0x29U, 0x00U, }}, - /* ==== WASM: 28 ==== */ + /* ==== WASM: 29 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -8388,7 +8492,7 @@ std::map> wasm = { 0x30U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x32U, 0x30U, 0x00U, }}, - /* ==== WASM: 29 ==== */ + /* ==== WASM: 30 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -8450,7 +8554,7 @@ std::map> wasm = { 0x80U, 0x80U, 0x00U, 0x0BU, }}, - /* ==== WASM: 30 ==== */ + /* ==== WASM: 31 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -8527,7 +8631,7 @@ std::map> wasm = { 0x31U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, }}, - /* ==== WASM: 31 ==== */ + /* ==== WASM: 32 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -8604,7 +8708,7 @@ std::map> wasm = { 0x31U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, }}, - /* ==== WASM: 32 ==== */ + /* ==== WASM: 33 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -8861,7 +8965,7 @@ std::map> wasm = { 0x00U, 0x00U, }}, - /* ==== WASM: 33 ==== */ + /* ==== WASM: 34 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -9019,7 +9123,7 @@ std::map> wasm = { 0x04U, 0x00U, 0x00U, }}, - /* ==== WASM: 34 ==== */ + /* ==== WASM: 35 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -9372,7 +9476,7 @@ std::map> wasm = { 0x00U, 0x2AU, 0x04U, 0x00U, 0x00U, 0x31U, 0x04U, 0x00U, 0x00U, }}, - /* ==== WASM: 35 ==== */ + /* ==== WASM: 36 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -9405,7 +9509,7 @@ std::map> wasm = { 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x0BU, }}, - /* ==== WASM: 36 ==== */ + /* ==== WASM: 37 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -9632,7 +9736,7 @@ std::map> wasm = { 0x00U, }}, - /* ==== WASM: 37 ==== */ + /* ==== WASM: 38 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -9663,7 +9767,7 @@ std::map> wasm = { 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x0BU, }}, - /* ==== WASM: 38 ==== */ + /* ==== WASM: 39 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -9974,7 +10078,7 @@ std::map> wasm = { 0x00U, }}, - /* ==== WASM: 39 ==== */ + /* ==== WASM: 40 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -10051,7 +10155,7 @@ std::map> wasm = { 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, }}, - /* ==== WASM: 40 ==== */ + /* ==== WASM: 41 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -10085,7 +10189,7 @@ std::map> wasm = { 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x0BU, }}, - /* ==== WASM: 41 ==== */ + /* ==== WASM: 42 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -10172,7 +10276,7 @@ std::map> wasm = { 0x32U, 0x00U, }}, - /* ==== WASM: 42 ==== */ + /* ==== WASM: 43 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -10206,7 +10310,7 @@ std::map> wasm = { 0x0BU, }}, - /* ==== WASM: 43 ==== */ + /* ==== WASM: 44 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -10346,7 +10450,7 @@ std::map> wasm = { 0x54U, 0x5FU, 0x4DU, 0x45U, 0x54U, 0x00U, }}, - /* ==== WASM: 44 ==== */ + /* ==== WASM: 45 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -10536,7 +10640,7 @@ std::map> wasm = { 0x31U, 0x34U, 0x00U, }}, - /* ==== WASM: 45 ==== */ + /* ==== WASM: 46 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -10690,7 +10794,7 @@ std::map> wasm = { 0x00U, }}, - /* ==== WASM: 46 ==== */ + /* ==== WASM: 47 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -10851,7 +10955,7 @@ std::map> wasm = { 0x00U, }}, - /* ==== WASM: 47 ==== */ + /* ==== WASM: 48 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -11008,7 +11112,7 @@ std::map> wasm = { 0x53U, 0x4CU, 0x4FU, 0x54U, 0x53U, 0x00U, }}, - /* ==== WASM: 48 ==== */ + /* ==== WASM: 49 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -11093,7 +11197,7 @@ std::map> wasm = { 0x74U, 0x79U, 0x70U, 0x65U, 0x28U, 0x29U, 0x00U, }}, - /* ==== WASM: 49 ==== */ + /* ==== WASM: 50 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -11350,7 +11454,7 @@ std::map> wasm = { 0x00U, 0x00U, }}, - /* ==== WASM: 50 ==== */ + /* ==== WASM: 51 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -11588,7 +11692,7 @@ std::map> wasm = { 0x3EU, 0x20U, 0x30U, 0x00U, }}, - /* ==== WASM: 51 ==== */ + /* ==== WASM: 52 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -11684,7 +11788,7 @@ std::map> wasm = { 0x53U, 0x54U, 0x00U, }}, - /* ==== WASM: 52 ==== */ + /* ==== WASM: 53 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -11794,7 +11898,7 @@ std::map> wasm = { 0x20U, 0x31U, 0x00U, }}, - /* ==== WASM: 53 ==== */ + /* ==== WASM: 54 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -11924,7 +12028,7 @@ std::map> wasm = { 0x30U, 0x30U, 0x30U, 0x4CU, 0x4CU, 0x00U, }}, - /* ==== WASM: 54 ==== */ + /* ==== WASM: 55 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -12198,7 +12302,7 @@ std::map> wasm = { 0x30U, 0x00U, }}, - /* ==== WASM: 55 ==== */ + /* ==== WASM: 56 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -12245,7 +12349,7 @@ std::map> wasm = { ASSERT(s > 0); // pull the object out into a buffer, check the number of bytes written is correct - uint8_t buf[4096]; + uint8_t buf[8210]; // ltSkip(3)+FLS(6)+LLS(6)+ Hashes(3+8,192(32*256)) ASSERT(slot(SBUF(buf), 1) == s); // check the object is valid @@ -12276,66 +12380,67 @@ std::map> wasm = { 0x41U, 0x80U, 0x08U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0xE0U, 0x89U, 0x04U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0x07U, 0x08U, 0x01U, 0x04U, 0x68U, 0x6FU, 0x6FU, - 0x6BU, 0x00U, 0x07U, 0x0AU, 0xCCU, 0x82U, 0x00U, 0x01U, 0xC8U, 0x82U, + 0x6BU, 0x00U, 0x07U, 0x0AU, 0xCFU, 0x82U, 0x00U, 0x01U, 0xCBU, 0x82U, 0x00U, 0x02U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x23U, 0x80U, 0x80U, 0x80U, - 0x80U, 0x00U, 0x41U, 0x80U, 0x20U, 0x6BU, 0x22U, 0x01U, 0x24U, 0x80U, - 0x80U, 0x80U, 0x80U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U, 0x80U, - 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x02U, 0x40U, 0x41U, 0x01U, 0x10U, - 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x7BU, 0x51U, 0x0DU, 0x00U, - 0x41U, 0xA2U, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x1DU, 0x42U, 0x21U, - 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, - 0x41U, 0x80U, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x22U, 0x41U, 0x01U, - 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x01U, 0x51U, 0x0DU, - 0x00U, 0x41U, 0xBFU, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x1EU, 0x42U, - 0x24U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, + 0x80U, 0x00U, 0x41U, 0xA0U, 0xC0U, 0x00U, 0x6BU, 0x22U, 0x01U, 0x24U, + 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U, + 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x02U, 0x40U, 0x41U, 0x01U, + 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x7BU, 0x51U, 0x0DU, + 0x00U, 0x41U, 0xA2U, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x1DU, 0x42U, + 0x21U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, 0x41U, 0x80U, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x22U, 0x41U, - 0xFFU, 0x01U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0xFFU, - 0x01U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xDDU, 0x88U, 0x80U, 0x80U, 0x00U, - 0x41U, 0x22U, 0x42U, 0x25U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, - 0x1AU, 0x0BU, 0x02U, 0x40U, 0x41U, 0x01U, 0x10U, 0x81U, 0x80U, 0x80U, - 0x80U, 0x00U, 0x41U, 0xFFU, 0x01U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, - 0x00U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xFFU, 0x88U, 0x80U, 0x80U, 0x00U, - 0x41U, 0x1FU, 0x42U, 0x28U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, - 0x1AU, 0x0BU, 0x02U, 0x40U, 0x41U, 0x01U, 0x10U, 0x81U, 0x80U, 0x80U, - 0x80U, 0x00U, 0x22U, 0x02U, 0x42U, 0x00U, 0x55U, 0x0DU, 0x00U, 0x41U, - 0x9EU, 0x89U, 0x80U, 0x80U, 0x00U, 0x41U, 0x06U, 0x42U, 0x2CU, 0x10U, + 0x01U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x01U, 0x51U, + 0x0DU, 0x00U, 0x41U, 0xBFU, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x1EU, + 0x42U, 0x24U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, + 0x02U, 0x40U, 0x41U, 0x80U, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x22U, + 0x41U, 0xFFU, 0x01U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, + 0xFFU, 0x01U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xDDU, 0x88U, 0x80U, 0x80U, + 0x00U, 0x41U, 0x22U, 0x42U, 0x25U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, 0x41U, 0x01U, 0x10U, 0x81U, 0x80U, + 0x80U, 0x80U, 0x00U, 0x41U, 0xFFU, 0x01U, 0x10U, 0x81U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xFFU, 0x88U, 0x80U, 0x80U, + 0x00U, 0x41U, 0x1FU, 0x42U, 0x28U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, 0x41U, 0x01U, 0x10U, 0x81U, 0x80U, + 0x80U, 0x80U, 0x00U, 0x22U, 0x02U, 0x42U, 0x00U, 0x55U, 0x0DU, 0x00U, + 0x41U, 0x9EU, 0x89U, 0x80U, 0x80U, 0x00U, 0x41U, 0x06U, 0x42U, 0x2CU, + 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, + 0x20U, 0x01U, 0x41U, 0x92U, 0xC0U, 0x00U, 0x41U, 0x01U, 0x10U, 0x84U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x20U, 0x02U, 0x51U, 0x0DU, 0x00U, 0x41U, + 0xA4U, 0x89U, 0x80U, 0x80U, 0x00U, 0x41U, 0x18U, 0x42U, 0x30U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, 0x20U, - 0x01U, 0x41U, 0x80U, 0x20U, 0x41U, 0x01U, 0x10U, 0x84U, 0x80U, 0x80U, - 0x80U, 0x00U, 0x20U, 0x02U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xA4U, 0x89U, - 0x80U, 0x80U, 0x00U, 0x41U, 0x18U, 0x42U, 0x30U, 0x10U, 0x82U, 0x80U, - 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, 0x20U, 0x01U, 0x20U, - 0x02U, 0xA7U, 0x10U, 0x85U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x01U, - 0x51U, 0x0DU, 0x00U, 0x41U, 0xBCU, 0x89U, 0x80U, 0x80U, 0x00U, 0x41U, - 0x1AU, 0x42U, 0x33U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, - 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x00U, 0x10U, 0x86U, 0x80U, - 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0x80U, 0x20U, 0x6AU, - 0x24U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x20U, 0x02U, 0x0BU, 0x0BU, - 0xE4U, 0x01U, 0x02U, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0x22U, 0x00U, - 0x68U, 0xB4U, 0x97U, 0x9AU, 0x36U, 0xCDU, 0xC7U, 0xF3U, 0xD3U, 0xD5U, - 0xC3U, 0x1AU, 0x4EU, 0xAEU, 0x2AU, 0xC7U, 0xD7U, 0x20U, 0x9DU, 0xDAU, - 0x87U, 0x75U, 0x88U, 0xB9U, 0xAFU, 0xC6U, 0x67U, 0x99U, 0x69U, 0x2AU, - 0xB0U, 0xD6U, 0x6BU, 0x00U, 0x41U, 0xA2U, 0x08U, 0x0BU, 0xB4U, 0x01U, - 0x73U, 0x6CU, 0x6FU, 0x74U, 0x5FU, 0x73U, 0x69U, 0x7AU, 0x65U, 0x28U, - 0x31U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x44U, 0x4FU, 0x45U, 0x53U, - 0x4EU, 0x54U, 0x5FU, 0x45U, 0x58U, 0x49U, 0x53U, 0x54U, 0x00U, 0x73U, - 0x6CU, 0x6FU, 0x74U, 0x5FU, 0x73U, 0x65U, 0x74U, 0x28U, 0x53U, 0x42U, - 0x55U, 0x46U, 0x28U, 0x6BU, 0x6CU, 0x5FU, 0x73U, 0x6BU, 0x29U, 0x2CU, - 0x20U, 0x31U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x31U, 0x00U, 0x73U, - 0x6CU, 0x6FU, 0x74U, 0x5FU, 0x73U, 0x65U, 0x74U, 0x28U, 0x53U, 0x42U, - 0x55U, 0x46U, 0x28U, 0x6BU, 0x6CU, 0x5FU, 0x73U, 0x6BU, 0x29U, 0x2CU, - 0x20U, 0x32U, 0x35U, 0x35U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x32U, - 0x35U, 0x35U, 0x00U, 0x73U, 0x6CU, 0x6FU, 0x74U, 0x5FU, 0x73U, 0x69U, - 0x7AU, 0x65U, 0x28U, 0x31U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x73U, - 0x6CU, 0x6FU, 0x74U, 0x5FU, 0x73U, 0x69U, 0x7AU, 0x65U, 0x28U, 0x32U, - 0x35U, 0x35U, 0x29U, 0x00U, 0x73U, 0x20U, 0x3EU, 0x20U, 0x30U, 0x00U, - 0x73U, 0x6CU, 0x6FU, 0x74U, 0x28U, 0x53U, 0x42U, 0x55U, 0x46U, 0x28U, - 0x62U, 0x75U, 0x66U, 0x29U, 0x2CU, 0x20U, 0x31U, 0x29U, 0x20U, 0x3DU, - 0x3DU, 0x20U, 0x73U, 0x00U, 0x73U, 0x74U, 0x6FU, 0x5FU, 0x76U, 0x61U, - 0x6CU, 0x69U, 0x64U, 0x61U, 0x74U, 0x65U, 0x28U, 0x62U, 0x75U, 0x66U, - 0x2CU, 0x20U, 0x73U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x31U, 0x00U, + 0x01U, 0x20U, 0x02U, 0xA7U, 0x10U, 0x85U, 0x80U, 0x80U, 0x80U, 0x00U, + 0x42U, 0x01U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xBCU, 0x89U, 0x80U, 0x80U, + 0x00U, 0x41U, 0x1AU, 0x42U, 0x33U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x00U, 0x10U, + 0x86U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0xA0U, + 0xC0U, 0x00U, 0x6AU, 0x24U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x20U, + 0x02U, 0x0BU, 0x0BU, 0xE4U, 0x01U, 0x02U, 0x00U, 0x41U, 0x80U, 0x08U, + 0x0BU, 0x22U, 0x00U, 0x68U, 0xB4U, 0x97U, 0x9AU, 0x36U, 0xCDU, 0xC7U, + 0xF3U, 0xD3U, 0xD5U, 0xC3U, 0x1AU, 0x4EU, 0xAEU, 0x2AU, 0xC7U, 0xD7U, + 0x20U, 0x9DU, 0xDAU, 0x87U, 0x75U, 0x88U, 0xB9U, 0xAFU, 0xC6U, 0x67U, + 0x99U, 0x69U, 0x2AU, 0xB0U, 0xD6U, 0x6BU, 0x00U, 0x41U, 0xA2U, 0x08U, + 0x0BU, 0xB4U, 0x01U, 0x73U, 0x6CU, 0x6FU, 0x74U, 0x5FU, 0x73U, 0x69U, + 0x7AU, 0x65U, 0x28U, 0x31U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x44U, + 0x4FU, 0x45U, 0x53U, 0x4EU, 0x54U, 0x5FU, 0x45U, 0x58U, 0x49U, 0x53U, + 0x54U, 0x00U, 0x73U, 0x6CU, 0x6FU, 0x74U, 0x5FU, 0x73U, 0x65U, 0x74U, + 0x28U, 0x53U, 0x42U, 0x55U, 0x46U, 0x28U, 0x6BU, 0x6CU, 0x5FU, 0x73U, + 0x6BU, 0x29U, 0x2CU, 0x20U, 0x31U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, + 0x31U, 0x00U, 0x73U, 0x6CU, 0x6FU, 0x74U, 0x5FU, 0x73U, 0x65U, 0x74U, + 0x28U, 0x53U, 0x42U, 0x55U, 0x46U, 0x28U, 0x6BU, 0x6CU, 0x5FU, 0x73U, + 0x6BU, 0x29U, 0x2CU, 0x20U, 0x32U, 0x35U, 0x35U, 0x29U, 0x20U, 0x3DU, + 0x3DU, 0x20U, 0x32U, 0x35U, 0x35U, 0x00U, 0x73U, 0x6CU, 0x6FU, 0x74U, + 0x5FU, 0x73U, 0x69U, 0x7AU, 0x65U, 0x28U, 0x31U, 0x29U, 0x20U, 0x3DU, + 0x3DU, 0x20U, 0x73U, 0x6CU, 0x6FU, 0x74U, 0x5FU, 0x73U, 0x69U, 0x7AU, + 0x65U, 0x28U, 0x32U, 0x35U, 0x35U, 0x29U, 0x00U, 0x73U, 0x20U, 0x3EU, + 0x20U, 0x30U, 0x00U, 0x73U, 0x6CU, 0x6FU, 0x74U, 0x28U, 0x53U, 0x42U, + 0x55U, 0x46U, 0x28U, 0x62U, 0x75U, 0x66U, 0x29U, 0x2CU, 0x20U, 0x31U, + 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x73U, 0x00U, 0x73U, 0x74U, 0x6FU, + 0x5FU, 0x76U, 0x61U, 0x6CU, 0x69U, 0x64U, 0x61U, 0x74U, 0x65U, 0x28U, + 0x62U, 0x75U, 0x66U, 0x2CU, 0x20U, 0x73U, 0x29U, 0x20U, 0x3DU, 0x3DU, + 0x20U, 0x31U, 0x00U, }}, - /* ==== WASM: 56 ==== */ + /* ==== WASM: 57 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -12629,7 +12734,7 @@ std::map> wasm = { 0x5FU, 0x53U, 0x4CU, 0x4FU, 0x54U, 0x53U, 0x00U, }}, - /* ==== WASM: 57 ==== */ + /* ==== WASM: 58 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -12863,7 +12968,7 @@ std::map> wasm = { 0x5FU, 0x53U, 0x4CU, 0x4FU, 0x54U, 0x53U, 0x00U, }}, - /* ==== WASM: 58 ==== */ + /* ==== WASM: 59 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -13159,7 +13264,7 @@ std::map> wasm = { 0x2CU, 0x20U, 0x31U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x30U, 0x00U, }}, - /* ==== WASM: 59 ==== */ + /* ==== WASM: 60 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -13363,7 +13468,7 @@ std::map> wasm = { 0x6EU, 0x74U, 0x32U, 0x22U, 0x20U, 0x2BU, 0x20U, 0x69U, 0x29U, 0x00U, }}, - /* ==== WASM: 60 ==== */ + /* ==== WASM: 61 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -13480,7 +13585,7 @@ std::map> wasm = { 0x69U, 0x29U, 0x00U, }}, - /* ==== WASM: 61 ==== */ + /* ==== WASM: 62 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -13589,7 +13694,7 @@ std::map> wasm = { 0x6EU, 0x74U, 0x65U, 0x6EU, 0x74U, 0x32U, 0x22U, 0x29U, 0x00U, }}, - /* ==== WASM: 62 ==== */ + /* ==== WASM: 63 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -13862,7 +13967,7 @@ std::map> wasm = { 0x00U, }}, - /* ==== WASM: 63 ==== */ + /* ==== WASM: 64 ==== */ {R"[test.hook]( #include #define sfInvoiceID ((5U << 16U) + 17U) @@ -14081,7 +14186,7 @@ std::map> wasm = { 0x30U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, }}, - /* ==== WASM: 64 ==== */ + /* ==== WASM: 65 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -14193,7 +14298,7 @@ std::map> wasm = { 0x58U, 0x49U, 0x53U, 0x54U, 0x00U, }}, - /* ==== WASM: 65 ==== */ + /* ==== WASM: 66 ==== */ {R"[test.hook]( #include #define sfInvoiceID ((5U << 16U) + 17U) @@ -14329,7 +14434,7 @@ std::map> wasm = { 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, }}, - /* ==== WASM: 66 ==== */ + /* ==== WASM: 67 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -14465,7 +14570,7 @@ std::map> wasm = { 0x49U, 0x47U, 0x00U, }}, - /* ==== WASM: 67 ==== */ + /* ==== WASM: 68 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -14604,7 +14709,7 @@ std::map> wasm = { 0x66U, 0x28U, 0x64U, 0x61U, 0x74U, 0x61U, 0x32U, 0x29U, 0x00U, }}, - /* ==== WASM: 68 ==== */ + /* ==== WASM: 69 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -14716,7 +14821,7 @@ std::map> wasm = { 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x30U, 0x00U, }}, - /* ==== WASM: 69 ==== */ + /* ==== WASM: 70 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -14810,7 +14915,7 @@ std::map> wasm = { 0x61U, 0x64U, 0x5BU, 0x69U, 0x5DU, 0x00U, }}, - /* ==== WASM: 70 ==== */ + /* ==== WASM: 71 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -14943,7 +15048,7 @@ std::map> wasm = { 0x64U, 0x5BU, 0x69U, 0x5DU, 0x00U, }}, - /* ==== WASM: 71 ==== */ + /* ==== WASM: 72 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -15031,7 +15136,7 @@ std::map> wasm = { 0x61U, 0x74U, 0x61U, 0x29U, 0x00U, }}, - /* ==== WASM: 72 ==== */ + /* ==== WASM: 73 ==== */ {R"[test.hook]( #include #define sfInvoiceID ((5U << 16U) + 17U) @@ -15142,7 +15247,7 @@ std::map> wasm = { 0x20U, 0x33U, 0x32U, 0x00U, }}, - /* ==== WASM: 73 ==== */ + /* ==== WASM: 74 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -15353,7 +15458,7 @@ std::map> wasm = { 0x00U, }}, - /* ==== WASM: 74 ==== */ + /* ==== WASM: 75 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -15461,7 +15566,7 @@ std::map> wasm = { 0x20U, 0x22U, 0x32U, 0x22U, 0x2CU, 0x20U, 0x31U, 0x29U, 0x00U, }}, - /* ==== WASM: 75 ==== */ + /* ==== WASM: 76 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -16073,7 +16178,7 @@ std::map> wasm = { 0x00U, }}, - /* ==== WASM: 76 ==== */ + /* ==== WASM: 77 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -16260,7 +16365,7 @@ std::map> wasm = { 0x63U, 0x65U, 0x29U, 0x20U, 0x3EU, 0x20U, 0x30U, 0x00U, }}, - /* ==== WASM: 77 ==== */ + /* ==== WASM: 78 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -16610,7 +16715,7 @@ std::map> wasm = { 0x20U, 0x30U, 0x00U, }}, - /* ==== WASM: 78 ==== */ + /* ==== WASM: 79 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -16746,7 +16851,7 @@ std::map> wasm = { 0x00U, }}, - /* ==== WASM: 79 ==== */ + /* ==== WASM: 80 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -16805,7 +16910,7 @@ std::map> wasm = { 0x64U, 0xE1U, 0xF1U, }}, - /* ==== WASM: 79 ==== */ + /* ==== WASM: 81 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -16978,7 +17083,7 @@ std::map> wasm = { 0x54U, 0x5FU, 0x45U, 0x58U, 0x49U, 0x53U, 0x54U, 0x00U, }}, - /* ==== WASM: 80 ==== */ + /* ==== WASM: 82 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -17126,7 +17231,7 @@ std::map> wasm = { 0x30U, 0x00U, 0x22U, 0x00U, 0x00U, 0x00U, 0x00U, }}, - /* ==== WASM: 81 ==== */ + /* ==== WASM: 83 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -17223,7 +17328,7 @@ std::map> wasm = { 0x3DU, 0x20U, 0x30U, 0x00U, }}, - /* ==== WASM: 82 ==== */ + /* ==== WASM: 84 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -17282,7 +17387,7 @@ std::map> wasm = { 0x4FU, 0x46U, 0x5FU, 0x42U, 0x4FU, 0x55U, 0x4EU, 0x44U, 0x53U, 0x00U, }}, - /* ==== WASM: 83 ==== */ + /* ==== WASM: 85 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -17341,7 +17446,7 @@ std::map> wasm = { 0x4EU, 0x44U, 0x53U, 0x00U, }}, - /* ==== WASM: 84 ==== */ + /* ==== WASM: 86 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -19170,7 +19275,7 @@ std::map> wasm = { 0x53U, 0x4DU, 0x41U, 0x4CU, 0x4CU, 0x00U, }}, - /* ==== WASM: 85 ==== */ + /* ==== WASM: 87 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -20512,7 +20617,7 @@ std::map> wasm = { 0x20U, 0x30U, 0x2CU, 0x20U, 0x30U, 0x29U, 0x29U, 0x00U, }}, - /* ==== WASM: 86 ==== */ + /* ==== WASM: 88 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -23445,7 +23550,7 @@ std::map> wasm = { 0x4FU, 0x4FU, 0x5FU, 0x53U, 0x4DU, 0x41U, 0x4CU, 0x4CU, 0x00U, }}, - /* ==== WASM: 87 ==== */ + /* ==== WASM: 89 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -25410,7 +25515,7 @@ std::map> wasm = { 0x54U, 0x4FU, 0x4FU, 0x5FU, 0x53U, 0x4DU, 0x41U, 0x4CU, 0x4CU, 0x00U, }}, - /* ==== WASM: 88 ==== */ + /* ==== WASM: 90 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -25695,7 +25800,7 @@ std::map> wasm = { 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x30U, 0x00U, }}, - /* ==== WASM: 89 ==== */ + /* ==== WASM: 91 ==== */ {R"[test.hook]( #include extern int32_t _g(uint32_t, uint32_t); @@ -26282,7 +26387,7 @@ std::map> wasm = { 0x4EU, 0x5FU, 0x46U, 0x41U, 0x49U, 0x4CU, 0x55U, 0x52U, 0x45U, 0x00U, }}, - /* ==== WASM: 90 ==== */ + /* ==== WASM: 92 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -26311,7 +26416,7 @@ std::map> wasm = { 0x0BU, }}, - /* ==== WASM: 91 ==== */ + /* ==== WASM: 93 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -26343,7 +26448,7 @@ std::map> wasm = { 0x20U, 0x52U, 0x65U, 0x6AU, 0x65U, 0x63U, 0x74U, 0x65U, 0x64U, 0x00U, }}, - /* ==== WASM: 92 ==== */ + /* ==== WASM: 94 ==== */ {R"[test.hook]( (module (type (;0;) (func (param i32 i32 i64) (result i64))) @@ -26370,7 +26475,7 @@ std::map> wasm = { 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x00U, 0x10U, 0x00U, 0x0BU, }}, - /* ==== WASM: 93 ==== */ + /* ==== WASM: 95 ==== */ {R"[test.hook]( (module (type (;0;) (func (param i32 i32) (result i32))) @@ -26423,7 +26528,7 @@ std::map> wasm = { 0x00U, 0x1AU, 0x0BU, }}, - /* ==== WASM: 94 ==== */ + /* ==== WASM: 96 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -33066,7 +33171,7 @@ std::map> wasm = { 0x39U, 0x30U, 0x31U, 0x32U, 0x33U, 0x00U, }}, - /* ==== WASM: 95 ==== */ + /* ==== WASM: 97 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -33112,7 +33217,7 @@ std::map> wasm = { 0x0BU, 0x06U, 0x76U, 0x61U, 0x6CU, 0x75U, 0x65U, 0x00U, }}, - /* ==== WASM: 96 ==== */ + /* ==== WASM: 98 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); diff --git a/src/test/app/Touch_test.cpp b/src/test/app/Touch_test.cpp index b5df8d2b92..cb24bfbfa7 100644 --- a/src/test/app/Touch_test.cpp +++ b/src/test/app/Touch_test.cpp @@ -424,6 +424,14 @@ struct Touch_test : public beast::unit_test::suite *this, network::makeNetworkConfig(21337, "10", "1000000", "200000"), features}; + if (env.current()->rules().enabled(featureHookFeeV2)) + { + for (int i = 0; i < 257; ++i) + env.close(); + env.close(); + BEAST_EXPECT( + env.le(keylet::fees())->getFieldU32(sfHookGasPrice) > 0); + } auto const alice = Account("alice"); auto const issuer = env.master; @@ -1403,6 +1411,7 @@ struct Touch_test : public beast::unit_test::suite testAllTxns(sa); testAllTxns(sa - fixHookAPI20251128); testAllTxns(sa - featureTouch - fixHookAPI20251128); + testAllTxns(sa - featureHookFeeV2); } }; diff --git a/src/test/app/Wildcard_test.cpp b/src/test/app/Wildcard_test.cpp index fc330b449f..03bedff90c 100644 --- a/src/test/app/Wildcard_test.cpp +++ b/src/test/app/Wildcard_test.cpp @@ -35,7 +35,8 @@ class Wildcard_test : public beast::unit_test::suite config.append( {"reference_fee = 10", "account_reserve = 1000000", - "owner_reserve = 200000"}); + "owner_reserve = 200000", + "hook_gas_price = 1000000"}); auto setup = setup_FeeVote(config); cfg->FEES = setup; return cfg; diff --git a/src/test/app/XahauGenesis_test.cpp b/src/test/app/XahauGenesis_test.cpp index 8353a3ce9a..e32d69235d 100644 --- a/src/test/app/XahauGenesis_test.cpp +++ b/src/test/app/XahauGenesis_test.cpp @@ -4818,6 +4818,7 @@ struct XahauGenesis_test : public beast::unit_test::suite std::string fee, std::string a_res, std::string o_res, + std::string g_res, uint32_t ledgerID) { using namespace jtx; @@ -4925,7 +4926,8 @@ struct XahauGenesis_test : public beast::unit_test::suite config.append( {"reference_fee = " + fee, "account_reserve = " + a_res, - "owner_reserve = " + o_res}); + "owner_reserve = " + o_res, + "hook_gas_price = " + g_res}); auto setup = setup_FeeVote(config); cfg->FEES = setup; @@ -4963,6 +4965,7 @@ struct XahauGenesis_test : public beast::unit_test::suite "10", "1000000", "200000", + "1000000", 0), features - featureXahauGenesis}; diff --git a/src/test/jtx/impl/envconfig.cpp b/src/test/jtx/impl/envconfig.cpp index ce2c2ae60a..1195f18de7 100644 --- a/src/test/jtx/impl/envconfig.cpp +++ b/src/test/jtx/impl/envconfig.cpp @@ -48,6 +48,9 @@ setupConfigForUnitTests(Config& cfg) cfg.FEES.reference_fee = 10; cfg.FEES.account_reserve = XRP(200).value().xrp().drops(); cfg.FEES.owner_reserve = XRP(50).value().xrp().drops(); + cfg.FEES.hook_gas_price = 1'000'000; + + cfg["voting"].set("hook_gas_price", to_string(10'000)); cfg.overwrite(ConfigSection::nodeDatabase(), "type", "rwdb"); cfg.overwrite(ConfigSection::nodeDatabase(), "path", "main"); diff --git a/src/test/jtx/impl/network.cpp b/src/test/jtx/impl/network.cpp index e08976ca35..231e7a04ac 100644 --- a/src/test/jtx/impl/network.cpp +++ b/src/test/jtx/impl/network.cpp @@ -37,7 +37,8 @@ makeNetworkConfig( uint32_t networkID, std::string fee, std::string a_res, - std::string o_res) + std::string o_res, + std::string g_res) { using namespace jtx; return envconfig([&](std::unique_ptr cfg) { @@ -46,9 +47,13 @@ makeNetworkConfig( config.append( {"reference_fee = " + fee, "account_reserve = " + a_res, - "owner_reserve = " + o_res}); + "owner_reserve = " + o_res, + "hook_gas_price = 1"}); auto setup = setup_FeeVote(config); cfg->FEES = setup; + cfg->section("voting") = config; + // override hook gas price for doVoting + cfg->section("voting").set("hook_gas_price", g_res); return cfg; }); } @@ -59,7 +64,8 @@ makeNetworkVLConfig( std::vector keys, std::string fee, std::string a_res, - std::string o_res) + std::string o_res, + std::string g_res) { using namespace jtx; return envconfig([&](std::unique_ptr cfg) { @@ -68,7 +74,8 @@ makeNetworkVLConfig( config.append( {"reference_fee = " + fee, "account_reserve = " + a_res, - "owner_reserve = " + o_res}); + "owner_reserve = " + o_res, + "hook_gas_price = " + g_res}); auto setup = setup_FeeVote(config); cfg->FEES = setup; @@ -98,6 +105,7 @@ makeGenesisConfig( std::string fee, std::string a_res, std::string o_res, + std::string g_res, uint32_t ledgerID) { using namespace jtx; @@ -129,7 +137,8 @@ makeGenesisConfig( config.append( {"reference_fee = " + fee, "account_reserve = " + a_res, - "owner_reserve = " + o_res}); + "owner_reserve = " + o_res, + "hook_gas_price = " + g_res}); auto setup = setup_FeeVote(config); cfg->FEES = setup; diff --git a/src/test/jtx/network.h b/src/test/jtx/network.h index b43367cdee..e726654aae 100644 --- a/src/test/jtx/network.h +++ b/src/test/jtx/network.h @@ -35,7 +35,8 @@ makeNetworkConfig( uint32_t networkID, std::string fee = "10", std::string a_res = "1000000", - std::string o_res = "200000"); + std::string o_res = "200000", + std::string g_res = "100000"); std::unique_ptr makeNetworkVLConfig( @@ -43,7 +44,8 @@ makeNetworkVLConfig( std::vector keys, std::string fee = "10", std::string a_res = "1000000", - std::string o_res = "200000"); + std::string o_res = "200000", + std::string g_res = "100000"); std::unique_ptr makeGenesisConfig( @@ -53,6 +55,7 @@ makeGenesisConfig( std::string fee = "10", std::string a_res = "1000000", std::string o_res = "200000", + std::string g_res = "100000", uint32_t ledgerID = 0); } // namespace network diff --git a/src/test/rpc/AccountNamespace_test.cpp b/src/test/rpc/AccountNamespace_test.cpp index aeeb040493..5538193df7 100644 --- a/src/test/rpc/AccountNamespace_test.cpp +++ b/src/test/rpc/AccountNamespace_test.cpp @@ -34,6 +34,14 @@ class AccountNamespace_test : public beast::unit_test::suite using namespace jtx; Env env(*this); + if (env.current()->rules().enabled(featureHookFeeV2)) + { + for (int i = 0; i < 257; ++i) + env.close(); + env.close(); + BEAST_EXPECT( + env.le(keylet::fees())->getFieldU32(sfHookGasPrice) > 0); + } Account const alice{"alice"}; Account const bob{"bob"}; @@ -225,6 +233,7 @@ class AccountNamespace_test : public beast::unit_test::suite using namespace test::jtx; FeatureBitset const all{supported_amendments()}; testErrors(all); + testErrors(all - featureHookFeeV2); } }; diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index 0f4b1c8237..c048d8e5d4 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -1104,6 +1104,14 @@ class LedgerRPC_test : public beast::unit_test::suite testcase("ledger_entry Request Hook State"); using namespace test::jtx; Env env{*this}; + if (env.current()->rules().enabled(featureHookFeeV2)) + { + for (int i = 0; i < 257; ++i) + env.close(); + env.close(); + BEAST_EXPECT( + env.le(keylet::fees())->getFieldU32(sfHookGasPrice) > 0); + } Account const alice{"alice"}; Account const bob{"bob"}; env.fund(XRP(10000), alice, bob);