Compare commits

...
Author SHA1 Message Date
tequ b3e7d4ae05 HookFeeV3: Support for execution costs per Hook APIs 2026-10-05 10:42:39 +09:00
tequ 697149aaa9 HookFeeV2: charge hook execution by cost units 2026-10-05 10:42:37 +09:00
54 changed files with 1902 additions and 351 deletions

No files matched your search

+9
View File
@@ -1308,6 +1308,15 @@
# Example:
# owner_reserve = 2000000 # 2 XAH
#
# hook_gas_price = <uint32_t>
#
# 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
+3
View File
@@ -72,6 +72,7 @@
#define sfLockCount ((2U << 16U) + 49U)
#define sfFirstNFTokenSequence ((2U << 16U) + 50U)
#define sfOracleDocumentID ((2U << 16U) + 51U)
#define sfHookGasPrice ((2U << 16U) + 92U)
#define sfStartTime ((2U << 16U) + 93U)
#define sfRepeatCount ((2U << 16U) + 94U)
#define sfDelaySeconds ((2U << 16U) + 95U)
@@ -105,6 +106,8 @@
#define sfMPTAmount ((3U << 16U) + 26U)
#define sfIssuerNode ((3U << 16U) + 27U)
#define sfSubjectNode ((3U << 16U) + 28U)
#define sfHookCost ((3U << 16U) + 95U)
#define sfHookCallbackCost ((3U << 16U) + 96U)
#define sfTouchCount ((3U << 16U) + 97U)
#define sfAccountIndex ((3U << 16U) + 98U)
#define sfAccountCount ((3U << 16U) + 99U)
+1
View File
@@ -61,6 +61,7 @@
#define ttNFTOKEN_MODIFY 70
#define ttPERMISSIONED_DOMAIN_SET 71
#define ttPERMISSIONED_DOMAIN_DELETE 72
#define ttHOOK_DEFINITION_UPDATE 90
#define ttMANIFEST_SET 91
#define ttCRON 92
#define ttCRON_SET 93
+4
View File
@@ -115,3 +115,7 @@ enum AMMClawbackFlags : uint32_t {
enum BridgeModifyFlags : uint32_t {
tfClearAccountCreateAmount = 0x00010000,
};
enum HookDefinitionUpdateFlags : uint32_t {
tfValidateGuards = 0x00000001,
};
+6
View File
@@ -0,0 +1,6 @@
!guard_checker.cpp
# binary
guard_checker
# directory
guard_checker*/**
+32 -2
View File
@@ -17,6 +17,7 @@
#define featureHooksUpdate2 "1"
#define fix20250131 "1"
#define fixGuardDepth32 "1"
#define featureHookFeeV3 "1"
namespace hook_api {
struct Rules
{
@@ -402,7 +403,17 @@ const uint8_t max_emit = 255;
const uint8_t max_params = 16;
const double fee_base_multiplier = 1.1f;
using APIWhitelist = std::map<std::string, std::vector<uint8_t>>;
// HookFeeV2 execution cost units: every wasm instruction costs one unit and
// every hook api call costs api_call_cost units on top of its call
// instruction. cost_units_per_drop units make one drop, so an instruction
// costs 0.1 drop and an api call 10 drops. HookFeeV3 replaces the flat
// api_call_cost with the per-api HOOK_API_COST table and prices the units
// with the ledger's HookGasPrice.
const uint32_t api_call_cost = 100;
const uint32_t cost_units_per_drop = 10;
using APIWhitelist =
std::map<std::string, std::pair<std::vector<uint8_t>, 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
@@ -413,7 +424,9 @@ getImportWhitelist(Rules const& rules)
APIWhitelist whitelist;
#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
@@ -425,17 +438,34 @@ getImportWhitelist(Rules const& rules)
RETURN_TYPE, FUNCTION_NAME, PARAMS_TUPLE, AMENDMENT) \
if (AMENDMENT == uint256{} || rules.enabled(AMENDMENT)) \
whitelist[#FUNCTION_NAME] = { \
RETURN_TYPE, HOOK_WRAP_PARAMS PARAMS_TUPLE};
{RETURN_TYPE, HOOK_WRAP_PARAMS PARAMS_TUPLE}, api_call_cost};
// per-api costs only apply under HookFeeV3; use find() so a cost line never
// whitelists an api whose definition is gated off
#define HOOK_API_COST(FUNCTION_NAME, COST, AMENDMENT) \
if (rules.enabled(featureHookFeeV3) && \
(AMENDMENT == uint256{} || rules.enabled(AMENDMENT))) \
if (auto it = whitelist.find(#FUNCTION_NAME); it != whitelist.end()) \
it->second.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;
}
+74 -30
View File
@@ -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<WasmBlkInf*> children;
uint32_t start_byte;
@@ -159,6 +160,7 @@ struct WasmBlkInf
: sanity_check(0x1234ABCDU)
, iteration_bound(iteration_bound_)
, instruction_count(instruction_count_)
, execution_cost(0)
, parent(parent_)
, children({})
, start_byte(start_byte_)
@@ -194,7 +196,7 @@ struct WasmBlkInf
level, \
" " \
" ", \
worst_case_execution, \
instruction_count, \
blk->start_byte, \
blk->instruction_count, \
blk->iteration_bound, \
@@ -203,7 +205,7 @@ struct WasmBlkInf
&(blk->parent)); \
}
// compute worst case execution time
inline uint64_t
inline std::pair<uint64_t, uint64_t>
compute_wce(
const WasmBlkInf* blk,
int level,
@@ -213,14 +215,14 @@ compute_wce(
if (level > max_level)
{
*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;
@@ -229,23 +231,28 @@ compute_wce(
{
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 += compute_wce(
{
auto [child_instruction_count, child_execution_cost] = compute_wce(
child, level + 1, max_level, 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
@@ -254,12 +261,18 @@ compute_wce(
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;
// the loop multiplier must apply to the execution cost as well, otherwise
// an api call inside a loop is only ever charged for a single iteration
execution_cost *= multiplier;
if (execution_cost < 1.0)
execution_cost = 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
@@ -267,16 +280,19 @@ compute_wce(
// 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<uint64_t>
inline std::optional<uint64_t> // count or cost depending on returnCost
check_guard(
std::vector<uint8_t> const& wasm,
int codesec,
int start_offset,
int end_offset,
std::map<int, std::map<int, std::string>> import_type_map,
std::map<int, uint32_t> const& import_cost_map,
int guard_func_idx,
int last_import_idx,
GuardLog guardLog,
std::string guardLogAccStr,
bool returnCost,
/* 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
@@ -337,6 +353,7 @@ check_guard(
ADVANCE(1);
current->instruction_count++;
current->execution_cost++;
// unreachable and nop instructions
if (instr == 0x00U || // unreachable
@@ -516,6 +533,10 @@ check_guard(
GUARD_ERROR("Too many guard calls! Limit is 1024");
}
if (auto const it = import_cost_map.find(callee_idx);
it != import_cost_map.end())
current->execution_cost += it->second;
continue;
}
@@ -795,7 +816,7 @@ check_guard(
int max_level = 16;
if (rulesVersion & hook_api::GuardRuleDepth32)
max_level = 32;
uint64_t wce =
auto [instruction_count, execution_cost] =
compute_wce(&(*root), 0, max_level, &recursion_limit_reached);
if (recursion_limit_reached)
{
@@ -807,11 +828,7 @@ check_guard(
return {};
}
GUARDLOG(hook::log::INSTRUCTION_COUNT)
<< "GuardCheck "
<< "Total worse-case execution count: " << wce << "\n";
if (wce >= 0xFFFFU)
if (instruction_count >= 0xFFFFU)
{
GUARDLOG(hook::log::INSTRUCTION_EXCESS)
<< "GuardCheck "
@@ -821,7 +838,10 @@ check_guard(
<< "\n";
return {};
}
return wce;
if (returnCost)
return execution_cost;
else
return instruction_count;
}
// RH TODO: reprogram this function to use REQUIRE/ADVANCE
@@ -835,6 +855,7 @@ validateGuards(
std::vector<uint8_t> 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
@@ -883,11 +904,16 @@ validateGuards(
// this maps function ids to type ids, used for looking up the type of cbak
// and hook as established inside the wasm binary.
std::map<int, int> func_type_map;
// used to check that every import sharing a type has the same signature
std::map<
int /* type idx */,
std::map<int /* import index */, std::string /* api name */>>
import_type_map;
// api costs must be keyed by function index, which is the index space a
// call instruction refers to, not by type index
std::map<int /* import func idx */, uint32_t /* cost */> import_cost_map;
// now we check for guards... first check if _g is imported
int guard_import_number = -1;
int last_import_number = -1;
@@ -1030,15 +1056,18 @@ 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;
}
if (!found_in_whitelist)
if (found_in_whitelist)
cost = it->second.second;
else
{
GUARDLOG(hook::log::IMPORT_ILLEGAL)
<< "Malformed transaction. "
@@ -1050,13 +1079,11 @@ validateGuards(
return {};
}
import_cost_map[func_upto] = cost;
// 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)}};
else
import_type_map[type_idx].emplace(
func_upto, std::move(import_name));
import_type_map[type_idx].emplace(
func_upto, std::move(import_name));
func_upto++;
}
@@ -1262,7 +1289,7 @@ validateGuards(
for (auto const& [import_idx, api_name] : usage->second)
{
auto const& api_signature =
import_whitelist.find(api_name)->second;
import_whitelist.find(api_name)->second.first;
if (!first_signature)
{
@@ -1501,19 +1528,36 @@ validateGuards(
j,
i,
code_end,
import_type_map,
import_cost_map,
guard_import_number,
last_import_number,
guardLog,
guardLogAccStr,
returnCost,
rulesVersion);
if (!valid)
return {};
if (hook_func_idx && *hook_func_idx == j)
{
GUARDLOG(hook::log::INSTRUCTION_COUNT)
<< "GuardCheck "
<< "Total hook worse-case execution "
<< (returnCost ? "cost: " : "count: ") << *valid
<< "\n";
maxInstrCountHook = *valid;
}
else if (cbak_func_idx && *cbak_func_idx == j)
{
GUARDLOG(hook::log::INSTRUCTION_COUNT)
<< "GuardCheck "
<< "Total cbak worse-case execution "
<< (returnCost ? "cost: " : "count: ") << *valid
<< "\n";
maxInstrCountCbak = *valid;
}
else
{
if (DEBUG_GUARD)
+1
View File
@@ -88,6 +88,7 @@ main(int argc, char** argv)
hook,
std::cout,
"",
true,
hook_api::getImportWhitelist(rules),
hook_api::getGuardRulesVersion(rules));
+75
View File
@@ -2,373 +2,448 @@
HOOK_API_DEFINITION(
int32_t, _g, (uint32_t, uint32_t),
uint256{})
HOOK_API_COST(_g, 70, 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, 80, 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, 80, 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, 120, 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, 110, 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, 4300, 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, 950, 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, 120, 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, 90, 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, 80, 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, 80, uint256{})
// int64_t etxn_burden();
HOOK_API_DEFINITION(
int64_t, etxn_burden, (),
uint256{})
HOOK_API_COST(etxn_burden, 70, 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, 140, 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, 800, uint256{})
// int64_t etxn_reserve(uint32_t count);
HOOK_API_DEFINITION(
int64_t, etxn_reserve, (uint32_t),
uint256{})
HOOK_API_COST(etxn_reserve, 90, uint256{})
// int64_t etxn_generation();
HOOK_API_DEFINITION(
int64_t, etxn_generation, (),
uint256{})
HOOK_API_COST(etxn_generation, 60, 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, 110, 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, 1800, 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, 80, 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, 80, 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, 70, uint256{})
// int64_t float_negate(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_negate, (int64_t),
uint256{})
HOOK_API_COST(float_negate, 60, 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, 70, 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, 70, 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, 60, 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, 60, uint256{})
// int64_t float_invert(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_invert, (int64_t),
uint256{})
HOOK_API_COST(float_invert, 60, 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, 60, uint256{})
// int64_t float_one();
HOOK_API_DEFINITION(
int64_t, float_one, (),
uint256{})
HOOK_API_COST(float_one, 50, uint256{})
// int64_t float_mantissa(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_mantissa, (int64_t),
uint256{})
HOOK_API_COST(float_mantissa, 60, uint256{})
// int64_t float_sign(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_sign, (int64_t),
uint256{})
HOOK_API_COST(float_sign, 60, 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, 60, uint256{})
// int64_t float_log(int64_t float1);
HOOK_API_DEFINITION(
int64_t, float_log, (int64_t),
uint256{})
HOOK_API_COST(float_log, 60, 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, 70, uint256{})
// int64_t fee_base();
HOOK_API_DEFINITION(
int64_t, fee_base, (),
uint256{})
HOOK_API_COST(fee_base, 60, uint256{})
// int64_t ledger_seq();
HOOK_API_DEFINITION(
int64_t, ledger_seq, (),
uint256{})
HOOK_API_COST(ledger_seq, 60, uint256{})
// int64_t ledger_last_time();
HOOK_API_DEFINITION(
int64_t, ledger_last_time, (),
uint256{})
HOOK_API_COST(ledger_last_time, 60, 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, 60, 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, 90, 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, 140, 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, 60, 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, 70, 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, 90, 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, 70, uint256{})
// int64_t hook_again();
HOOK_API_DEFINITION(
int64_t, hook_again, (),
uint256{})
HOOK_API_COST(hook_again, 80, 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, 70, uint256{})
// int64_t hook_pos();
HOOK_API_DEFINITION(
int64_t, hook_pos, (),
uint256{})
HOOK_API_COST(hook_pos, 50, 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, 120, uint256{})
// int64_t slot_clear(uint32_t slot);
HOOK_API_DEFINITION(
int64_t, slot_clear, (uint32_t),
uint256{})
HOOK_API_COST(slot_clear, 80, uint256{})
// int64_t slot_count(uint32_t slot);
HOOK_API_DEFINITION(
int64_t, slot_count, (uint32_t),
uint256{})
HOOK_API_COST(slot_count, 60, 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, 320, uint256{})
// int64_t slot_size(uint32_t slot);
HOOK_API_DEFINITION(
int64_t, slot_size, (uint32_t),
uint256{})
HOOK_API_COST(slot_size, 90, 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, 60, 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, 60, 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, 60, uint256{})
// int64_t slot_float(uint32_t slot_no);
HOOK_API_DEFINITION(
int64_t, slot_float, (uint32_t),
uint256{})
HOOK_API_COST(slot_float, 70, 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, 650, 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, 6000, 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, 410, 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, 430, 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, 240, 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, 90, 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, 70, 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, 70, uint256{})
// int64_t otxn_generation();
HOOK_API_DEFINITION(
int64_t, otxn_generation, (),
uint256{})
HOOK_API_COST(otxn_generation, 70, 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, 60, uint256{})
// int64_t otxn_type();
HOOK_API_DEFINITION(
int64_t, otxn_type, (),
uint256{})
HOOK_API_COST(otxn_type, 60, uint256{})
// int64_t otxn_slot(uint32_t slot_no);
HOOK_API_DEFINITION(
int64_t, otxn_slot, (uint32_t),
uint256{})
HOOK_API_COST(otxn_slot, 120, 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, 80, uint256{})
// int64_t meta_slot(uint32_t slot_no);
HOOK_API_DEFINITION(
int64_t, meta_slot, (uint32_t),
uint256{})
HOOK_API_COST(meta_slot, 60, 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, 5000, uint256{})
// int64_t prepare(uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
HOOK_API_DEFINITION(
int64_t, prepare, (uint32_t, uint32_t, uint32_t, uint32_t),
featureHooksUpdate2)
HOOK_API_COST(prepare, 5600, uint256{})
+2
View File
@@ -35,6 +35,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&
+6
View File
@@ -274,6 +274,12 @@ enum BridgeModifyFlags : uint32_t {
tfClearAccountCreateAmount = 0x00010000,
};
constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversal | tfClearAccountCreateAmount);
// HookDefinitionUpdate flags:
enum HookDefinitionUpdateFlags : uint32_t {
tfValidateGuards = 0x00000001,
};
constexpr std::uint32_t tfHookDefinitionUpdateMask = ~(tfUniversal | tfValidateGuards);
// clang-format on
} // namespace ripple
@@ -34,6 +34,8 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FEATURE(HookFeeV3, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(HookFeeV2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(EscrowDestinationCancel, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (20260929, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(HookOnV2_1, Supported::yes, VoteBehavior::DefaultNo)
@@ -103,8 +103,10 @@ LEDGER_ENTRY(ltHOOK_DEFINITION, 'D', HookDefinition, hook_definition, ({
{sfCreateCode, soeREQUIRED},
{sfHookSetTxnID, soeREQUIRED},
{sfReferenceCount, soeREQUIRED},
{sfFee, soeREQUIRED},
{sfFee, soeOPTIONAL},
{sfHookCallbackFee, soeOPTIONAL},
{sfHookCost, soeOPTIONAL},
{sfHookCallbackCost, soeOPTIONAL},
{sfPreviousTxnID, soeOPTIONAL},
{sfPreviousTxnLgrSeq, soeOPTIONAL},
}))
@@ -420,6 +422,7 @@ LEDGER_ENTRY(ltFEE_SETTINGS, 0x0073, FeeSettings, fee, ({
{sfXahauActivationLgrSeq, soeOPTIONAL},
{sfAccountCount, soeOPTIONAL},
{sfNetworkID, soeOPTIONAL},
{sfHookGasPrice, soeOPTIONAL},
{sfPreviousTxnID, soeOPTIONAL},
{sfPreviousTxnLgrSeq, soeOPTIONAL},
}))
@@ -115,6 +115,7 @@ TYPED_SFIELD(sfLockCount, UINT32, 49)
TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50)
TYPED_SFIELD(sfOracleDocumentID, UINT32, 51)
TYPED_SFIELD(sfHookGasPrice, UINT32, 92)
TYPED_SFIELD(sfStartTime, UINT32, 93)
TYPED_SFIELD(sfRepeatCount, UINT32, 94)
TYPED_SFIELD(sfDelaySeconds, UINT32, 95)
@@ -153,6 +154,8 @@ TYPED_SFIELD(sfOutstandingAmount, UINT64, 25, SField::sMD_BaseTen|SFie
TYPED_SFIELD(sfMPTAmount, UINT64, 26, SField::sMD_BaseTen|SField::sMD_Default)
TYPED_SFIELD(sfIssuerNode, UINT64, 27)
TYPED_SFIELD(sfSubjectNode, UINT64, 28)
TYPED_SFIELD(sfHookCost, UINT64, 95)
TYPED_SFIELD(sfHookCallbackCost, UINT64, 96)
TYPED_SFIELD(sfTouchCount, UINT64, 97)
TYPED_SFIELD(sfAccountIndex, UINT64, 98)
TYPED_SFIELD(sfAccountCount, UINT64, 99)
@@ -500,6 +500,11 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 72, PermissionedDomainDelete, ({
{sfDomainID, soeREQUIRED},
}))
/** This transaction type updates a hook definition */
TRANSACTION(ttHOOK_DEFINITION_UPDATE, 90, HookDefinitionUpdate, ({
{sfHookHash, soeREQUIRED},
}))
/* sfAccount is supplied by TxFormats::commonFields; listing it here as well
makes the SOTemplate reject the format at construction. */
TRANSACTION(ttMANIFEST_SET, 91, SetManifest, ({
@@ -590,6 +595,8 @@ TRANSACTION(ttFEE, 101, SetFee, ({
{sfBaseFeeDrops, soeOPTIONAL},
{sfReserveBaseDrops, soeOPTIONAL},
{sfReserveIncrementDrops, soeOPTIONAL},
// HookCost
{sfHookGasPrice, soeOPTIONAL},
}))
/** This system-generated transaction type is used to update the network's negative UNL
+1
View File
@@ -358,6 +358,7 @@ JSS(highest_sequence); // out: AccountInfo
JSS(highest_ticket); // out: AccountInfo
JSS(historical_perminute); // historical_perminute.
JSS(holders); // out: MPTHolders
JSS(hook_gas_price); //
JSS(hostid); // out: NetworkOPs
JSS(hotwallet); // in: GatewayBalances
JSS(id); // websocket.
+2
View File
@@ -66,6 +66,8 @@ STValidation::validationFormat()
{sfBaseFeeDrops, soeOPTIONAL},
{sfReserveBaseDrops, soeOPTIONAL},
{sfReserveIncrementDrops, soeOPTIONAL},
// HookCost
{sfHookGasPrice, soeOPTIONAL},
};
// clang-format on
+5 -2
View File
@@ -1407,7 +1407,8 @@ private:
{
using namespace jtx;
FeatureBitset const all{
supported_amendments() | featureAMM | featureAMMClawback};
(supported_amendments() | featureAMM | featureAMMClawback) -
featureHookFeeV3};
testRmFundedOffer(all);
testEnforceNoRipple(all);
testFillModes(all);
@@ -3217,7 +3218,9 @@ private:
testcase("RippleState Freeze");
using namespace test::jtx;
Env env(*this, features - featureXahauGenesis - featureTouch);
Env env(
*this,
features - featureXahauGenesis - featureTouch - featureHookFeeV3);
Account const G1{"G1"};
Account const alice{"alice"};
+14 -4
View File
@@ -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<std::uint32_t>(-1234567));
BEAST_EXPECT(
setup.owner_reserve == static_cast<std::uint32_t>(-1234));
BEAST_EXPECT(
setup.hook_gas_price == static_cast<std::uint32_t>(-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);
}
}
+1
View File
@@ -682,6 +682,7 @@ public:
auto const sa = supported_amendments();
testWithFeats(sa);
testWithFeats(sa - fixHookAPI20251128);
testWithFeats(sa - featureHookFeeV2);
}
};
+166
View File
@@ -0,0 +1,166 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 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 S
OFTWARE 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 <test/jtx.h>
#include <test/jtx/genesis.h>
#include <test/jtx/hook.h>
#include <xrpl/protocol/jss.h>
namespace ripple {
namespace test {
class HookDefinitionUpdate_test : public beast::unit_test::suite
{
static Json::Value
hookDefUpdateTx(
jtx::Account const& account,
std::string const& hookHash,
uint32_t flags = tfValidateGuards)
{
using namespace jtx;
Json::Value jv;
jv[jss::TransactionType] = jss::HookDefinitionUpdate;
jv[jss::Account] = account.human();
jv[jss::HookHash] = hookHash;
jv[jss::Flags] = flags;
return jv;
}
void
testInvalidPreflight(FeatureBitset features)
{
testcase("hook definition update invalid preflight");
using namespace test::jtx;
auto const alice = Account("alice");
// temDISABLED
{
Env env{*this, features - featureHookFeeV2};
env.fund(XRP(1000), alice);
env.close();
env(hookDefUpdateTx(alice, ""), ter(temDISABLED));
}
// temINVALID_FLAG
{
Env env{*this, features};
env.fund(XRP(1000), alice);
env.close();
env(hookDefUpdateTx(alice, "", 0x00000002), ter(temINVALID_FLAG));
}
}
void
testInvalidPreclaim(FeatureBitset features)
{
testcase("hook definition update invalid preclaim");
using namespace test::jtx;
auto const alice = Account("alice");
// tecNO_ENTRY
{
Env env{*this};
env.fund(XRP(1000), alice);
env.close();
env(hookDefUpdateTx(alice, ""), ter(tecNO_ENTRY));
}
}
void
testDoApply(FeatureBitset features)
{
testcase("hook definition update do apply");
using namespace test::jtx;
auto const alice = Account("alice");
Env env{*this, features - featureHookFeeV2};
env.fund(XRP(1000), alice);
env.close();
auto const hookCode = jtx::genesis::AcceptHook;
uint256 const hash = ripple::sha512Half_s(
ripple::Slice(hookCode.data(), hookCode.size()));
// prepare: Create Hook Definition without HookFeeV2
auto hookObj = hso(hookCode);
env(hook(alice, {{hookObj}}, 0), fee(XRP(1)));
env.close();
// prepare: enable HookFeeV2
env.enableFeature(featureHookFeeV2);
env.close();
// 1. Update Hook Definition (Fee -> Cost)
{
auto leb = env.le(keylet::hookDefinition(hash));
BEAST_EXPECT(leb->isFieldPresent(sfFee));
BEAST_EXPECT(!leb->isFieldPresent(sfHookCost));
env(hookDefUpdateTx(alice, to_string(hash)), fee(XRP(1)));
env.close();
auto lea = env.le(keylet::hookDefinition(hash));
BEAST_EXPECT(lea->isFieldPresent(sfHookCost));
BEAST_EXPECT(!lea->isFieldPresent(sfFee));
}
// 2. Update Hook Definition (Cost -> Cost)
{
env(hookDefUpdateTx(alice, to_string(hash)), fee(XRP(1)));
env.close();
auto const meta = env.meta();
BEAST_EXPECT(meta->isFieldPresent(sfAffectedNodes));
auto& nodes = meta->getFieldArray(sfAffectedNodes);
BEAST_EXPECT(nodes.size() == 1);
// no update for ltHookDefinition
BEAST_EXPECT(
nodes[0].getFieldU16(sfLedgerEntryType) == ltACCOUNT_ROOT);
}
// TODO: test callback cost
}
public:
void
run() override
{
using namespace test::jtx;
FeatureBitset const all{supported_amendments()};
testWithFeats(all);
}
void
testWithFeats(FeatureBitset features)
{
testInvalidPreflight(features);
testInvalidPreclaim(features);
testDoApply(features);
}
};
BEAST_DEFINE_TESTSUITE(HookDefinitionUpdate, app, ripple);
} // namespace test
} // namespace ripple
+64 -8
View File
@@ -6072,7 +6072,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
@@ -6116,7 +6123,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
@@ -6161,7 +6175,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
@@ -6207,7 +6228,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
@@ -6251,7 +6279,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
@@ -6295,7 +6330,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
@@ -6339,7 +6381,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
@@ -6382,7 +6431,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
+21 -6
View File
@@ -4913,9 +4913,11 @@ private:
env.close(std::chrono::seconds(300));
Oracle oracle(
env,
{.owner = account,
.series = {{"XRP", "USD", 740, 1}},
.fee = 10000});
{
.owner = account,
.series = {{"XRP", "USD", 740, 1}},
.fee = 1'000'000,
});
// verify tsh hook triggered
testTSHStrongWeak(env, tshSTRONG, __LINE__);
@@ -4952,6 +4954,7 @@ private:
{
.owner = account,
.series = {{"XRP", "USD", 740, 1}},
.fee = 1'000'000,
});
// set tsh collect
@@ -4964,7 +4967,7 @@ private:
// delete oracle
oracle.remove(oracle::RemoveArg{
.documentID = oracle.documentID(),
.fee = 10000,
.fee = 1'000'000,
});
// verify tsh hook triggered
@@ -8248,6 +8251,15 @@ private:
testTSHStrongWeak(env, tx->getTransactionID(), expected, __LINE__);
}
}
void
testHookDefinitionUpdateTSH(FeatureBitset features)
{
testcase("hook definition update tsh");
// TODO
BEAST_EXPECT(true);
}
void
testEmissionOrdering(FeatureBitset features)
{
@@ -8535,9 +8547,10 @@ public:
using namespace test::jtx;
static FeatureBitset const all{supported_amendments()};
static std::array<FeatureBitset, 3> const feats{
static std::array<FeatureBitset, 4> const feats{
all,
all - featureEscrowDestinationCancel,
all - featureHookFeeV2,
all - featureIOUIssuerWeakTSH,
};
@@ -8568,11 +8581,13 @@ public:
};
SETHOOKTSH_TEST(1, false)
SETHOOKTSH_TEST(2, true)
SETHOOKTSH_TEST(2, false)
SETHOOKTSH_TEST(3, 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);
} // namespace test
} // namespace ripple
File diff suppressed because it is too large. Load diff
+259 -141
View File
@@ -594,6 +594,109 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
0x6AU, 0x24U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x20U, 0x05U, 0x0BU,
}},
{R"[test.hook](
#include <stdint.h>
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,
}},
{R"[test.hook](
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
@@ -1669,13 +1772,21 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
etxn_reserve(1);
ASSERT(etxn_details((uint32_t)det, 116) == 116);
uint8_t expected1[49] = {
uint8_t expected1[16] = {
0xEDU, 0x20U, 0x2EU, 0x00U, 0x00U, 0x00U, 0x01U, 0x3DU, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU, 0xBCU, 0x09U, 0x95U,
0xFDU, 0x03U, 0x0FU, 0xF1U, 0x85U, 0xF4U, 0xECU, 0xACU, 0x94U, 0xDBU,
0x02U, 0x19U, 0x26U, 0xFEU, 0x58U, 0xD4U, 0x8DU, 0x48U, 0x21U, 0xCCU,
0x82U, 0x2EU, 0x4AU, 0x6AU, 0xC9U, 0xC8U, 0xC6U, 0x7CU, 0x88U
};
// 0x5B
// EmitParentTxnID 32bytes
uint8_t expected_emit_parent_txn_id[32] = {
0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,
0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,
0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,
0xFFU, 0xFFU
};
// 0x5CU
// EmitNonce 32bytes
uint8_t expected_emit_nonce[32] = {
@@ -1697,10 +1808,14 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
// current hook hash
ASSERT(hook_hash((uint32_t)expected_hook_hash, 32, -1) == 32);
for (int i = 0; GUARD(49), i < sizeof(expected1); ++i)
for (int i = 0; GUARD(16), i < sizeof(expected1); ++i)
ASSERT(det[i] == expected1[i]);
ASSERT(det[16] == 0x5BU);
// TODO: need to test EmitParentTxnID
// for (int i = 0; GUARD(32), i < sizeof(expected_emit_parent_txn_id); ++i)
// ASSERT(det[17 + i] == expected_emit_parent_txn_id[i]);
ASSERT(det[49] == 0x5CU);
// TODO: need to test this
// TODO: need to test EmitNonce
// for (int i = 0; GUARD(32), i < sizeof(expected_emit_nonce); ++i)
// ASSERT(det[50 + i] == expected_emit_nonce[i]);
ASSERT(det[82] == 0x5DU);
@@ -1727,11 +1842,11 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
0x68U, 0x00U, 0x04U, 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, 0xD0U, 0x8BU, 0x04U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0xC6U, 0x0BU,
0x41U, 0xC0U, 0x8BU, 0x04U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0xB6U, 0x0BU,
0x0BU, 0x7FU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0x7FU, 0x00U, 0x41U,
0xD0U, 0x8BU, 0x04U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU,
0xC0U, 0x8BU, 0x04U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU,
0x07U, 0x08U, 0x01U, 0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x06U,
0x0AU, 0xF4U, 0x84U, 0x00U, 0x01U, 0xF0U, 0x84U, 0x00U, 0x02U, 0x03U,
0x0AU, 0x97U, 0x85U, 0x00U, 0x01U, 0x93U, 0x85U, 0x00U, 0x02U, 0x03U,
0x7FU, 0x01U, 0x7EU, 0x23U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x41U,
0xA0U, 0x01U, 0x6BU, 0x22U, 0x01U, 0x24U, 0x80U, 0x80U, 0x80U, 0x80U,
0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U,
@@ -1760,87 +1875,89 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
0x7FU, 0x37U, 0x03U, 0x00U, 0x20U, 0x01U, 0x42U, 0x7FU, 0x37U, 0x03U,
0x08U, 0x20U, 0x01U, 0x42U, 0x7FU, 0x37U, 0x03U, 0x00U, 0x02U, 0x40U,
0x20U, 0x01U, 0x41U, 0x20U, 0x41U, 0x7FU, 0x10U, 0x84U, 0x80U, 0x80U,
0x80U, 0x00U, 0x42U, 0x20U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xA1U, 0x8AU,
0x80U, 0x80U, 0x00U, 0x41U, 0x36U, 0x42U, 0x3AU, 0x10U, 0x82U, 0x80U,
0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x41U, 0xBCU, 0x80U, 0x80U, 0x80U,
0x78U, 0x41U, 0x32U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU,
0x41U, 0x00U, 0x21U, 0x02U, 0x03U, 0x40U, 0x41U, 0xBCU, 0x80U, 0x80U,
0x80U, 0x78U, 0x41U, 0x32U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U,
0x1AU, 0x02U, 0x40U, 0x20U, 0x01U, 0x41U, 0x20U, 0x6AU, 0x20U, 0x02U,
0x6AU, 0x2DU, 0x00U, 0x00U, 0x20U, 0x02U, 0x41U, 0xF0U, 0x89U, 0x80U,
0x80U, 0x00U, 0x6AU, 0x2DU, 0x00U, 0x00U, 0x46U, 0x0DU, 0x00U, 0x41U,
0xD7U, 0x8AU, 0x80U, 0x80U, 0x00U, 0x41U, 0x17U, 0x42U, 0x3DU, 0x10U,
0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x20U, 0x02U, 0x41U,
0x01U, 0x6AU, 0x22U, 0x02U, 0x41U, 0x31U, 0x47U, 0x0DU, 0x00U, 0x0BU,
0x02U, 0x40U, 0x20U, 0x01U, 0x2DU, 0x00U, 0x51U, 0x41U, 0xDCU, 0x00U,
0x46U, 0x0DU, 0x00U, 0x41U, 0xEEU, 0x8AU, 0x80U, 0x80U, 0x00U, 0x41U,
0x11U, 0x42U, 0x3EU, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU,
0x0BU, 0x02U, 0x40U, 0x20U, 0x01U, 0x2DU, 0x00U, 0x72U, 0x41U, 0xDDU,
0x00U, 0x46U, 0x0DU, 0x00U, 0x41U, 0xFFU, 0x8AU, 0x80U, 0x80U, 0x00U,
0x41U, 0x11U, 0x42U, 0xC2U, 0x00U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U,
0x00U, 0x1AU, 0x0BU, 0x41U, 0xC3U, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U,
0x21U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U,
0x41U, 0xF3U, 0x00U, 0x6AU, 0x21U, 0x03U, 0x41U, 0x00U, 0x21U, 0x02U,
0x03U, 0x40U, 0x41U, 0xC3U, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U,
0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x02U, 0x40U, 0x20U,
0x03U, 0x20U, 0x02U, 0x6AU, 0x2DU, 0x00U, 0x00U, 0x20U, 0x01U, 0x20U,
0x02U, 0x6AU, 0x2DU, 0x00U, 0x00U, 0x46U, 0x0DU, 0x00U, 0x41U, 0x90U,
0x8BU, 0x80U, 0x80U, 0x00U, 0x41U, 0x25U, 0x42U, 0xC4U, 0x00U, 0x10U,
0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x20U, 0x02U, 0x41U,
0x01U, 0x6AU, 0x22U, 0x02U, 0x41U, 0x20U, 0x47U, 0x0DU, 0x00U, 0x0BU,
0x02U, 0x40U, 0x20U, 0x01U, 0x2DU, 0x00U, 0x93U, 0x01U, 0x41U, 0xE1U,
0x01U, 0x46U, 0x0DU, 0x00U, 0x41U, 0xB5U, 0x8BU, 0x80U, 0x80U, 0x00U,
0x41U, 0x11U, 0x42U, 0xC5U, 0x00U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U,
0x00U, 0x1AU, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x00U, 0x10U,
0x85U, 0x80U, 0x80U, 0x80U, 0x00U, 0x21U, 0x04U, 0x20U, 0x01U, 0x41U,
0xA0U, 0x01U, 0x6AU, 0x24U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x20U,
0x04U, 0x0BU, 0x0BU, 0xCEU, 0x03U, 0x01U, 0x00U, 0x41U, 0x80U, 0x08U,
0x0BU, 0xC6U, 0x03U, 0x65U, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x64U, 0x65U,
0x74U, 0x61U, 0x69U, 0x6CU, 0x73U, 0x28U, 0x31U, 0x30U, 0x30U, 0x30U,
0x30U, 0x30U, 0x30U, 0x2CU, 0x20U, 0x31U, 0x31U, 0x36U, 0x29U, 0x20U,
0x3DU, 0x3DU, 0x20U, 0x4FU, 0x55U, 0x54U, 0x5FU, 0x4FU, 0x46U, 0x5FU,
0x42U, 0x4FU, 0x55U, 0x4EU, 0x44U, 0x53U, 0x00U, 0x65U, 0x74U, 0x78U,
0x80U, 0x00U, 0x42U, 0x20U, 0x51U, 0x0DU, 0x00U, 0x41U, 0x80U, 0x8AU,
0x80U, 0x80U, 0x00U, 0x41U, 0x36U, 0x42U, 0xC2U, 0x00U, 0x10U, 0x82U,
0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x41U, 0xC4U, 0x80U, 0x80U,
0x80U, 0x78U, 0x41U, 0x11U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U,
0x1AU, 0x41U, 0x00U, 0x21U, 0x02U, 0x03U, 0x40U, 0x41U, 0xC4U, 0x80U,
0x80U, 0x80U, 0x78U, 0x41U, 0x11U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U,
0x00U, 0x1AU, 0x02U, 0x40U, 0x20U, 0x01U, 0x41U, 0x20U, 0x6AU, 0x20U,
0x02U, 0x6AU, 0x2DU, 0x00U, 0x00U, 0x20U, 0x02U, 0x41U, 0xF0U, 0x89U,
0x80U, 0x80U, 0x00U, 0x6AU, 0x2DU, 0x00U, 0x00U, 0x46U, 0x0DU, 0x00U,
0x41U, 0xB6U, 0x8AU, 0x80U, 0x80U, 0x00U, 0x41U, 0x17U, 0x42U, 0xC5U,
0x00U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x20U,
0x02U, 0x41U, 0x01U, 0x6AU, 0x22U, 0x02U, 0x41U, 0x10U, 0x47U, 0x0DU,
0x00U, 0x0BU, 0x02U, 0x40U, 0x20U, 0x01U, 0x2DU, 0x00U, 0x30U, 0x41U,
0xDBU, 0x00U, 0x46U, 0x0DU, 0x00U, 0x41U, 0xCDU, 0x8AU, 0x80U, 0x80U,
0x00U, 0x41U, 0x11U, 0x42U, 0xC6U, 0x00U, 0x10U, 0x82U, 0x80U, 0x80U,
0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, 0x20U, 0x01U, 0x2DU, 0x00U,
0x51U, 0x41U, 0xDCU, 0x00U, 0x46U, 0x0DU, 0x00U, 0x41U, 0xDEU, 0x8AU,
0x80U, 0x80U, 0x00U, 0x41U, 0x11U, 0x42U, 0xCAU, 0x00U, 0x10U, 0x82U,
0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, 0x20U, 0x01U,
0x2DU, 0x00U, 0x72U, 0x41U, 0xDDU, 0x00U, 0x46U, 0x0DU, 0x00U, 0x41U,
0xEFU, 0x8AU, 0x80U, 0x80U, 0x00U, 0x41U, 0x11U, 0x42U, 0xCEU, 0x00U,
0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x41U, 0xCFU,
0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U, 0x10U, 0x80U, 0x80U, 0x80U,
0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0xF3U, 0x00U, 0x6AU, 0x21U,
0x03U, 0x41U, 0x00U, 0x21U, 0x02U, 0x03U, 0x40U, 0x41U, 0xCFU, 0x80U,
0x80U, 0x80U, 0x78U, 0x41U, 0x21U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U,
0x00U, 0x1AU, 0x02U, 0x40U, 0x20U, 0x03U, 0x20U, 0x02U, 0x6AU, 0x2DU,
0x00U, 0x00U, 0x20U, 0x01U, 0x20U, 0x02U, 0x6AU, 0x2DU, 0x00U, 0x00U,
0x46U, 0x0DU, 0x00U, 0x41U, 0x80U, 0x8BU, 0x80U, 0x80U, 0x00U, 0x41U,
0x25U, 0x42U, 0xD0U, 0x00U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U,
0x1AU, 0x0BU, 0x20U, 0x02U, 0x41U, 0x01U, 0x6AU, 0x22U, 0x02U, 0x41U,
0x20U, 0x47U, 0x0DU, 0x00U, 0x0BU, 0x02U, 0x40U, 0x20U, 0x01U, 0x2DU,
0x00U, 0x93U, 0x01U, 0x41U, 0xE1U, 0x01U, 0x46U, 0x0DU, 0x00U, 0x41U,
0xA5U, 0x8BU, 0x80U, 0x80U, 0x00U, 0x41U, 0x11U, 0x42U, 0xD1U, 0x00U,
0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x41U, 0x00U,
0x41U, 0x00U, 0x42U, 0x00U, 0x10U, 0x85U, 0x80U, 0x80U, 0x80U, 0x00U,
0x21U, 0x04U, 0x20U, 0x01U, 0x41U, 0xA0U, 0x01U, 0x6AU, 0x24U, 0x80U,
0x80U, 0x80U, 0x80U, 0x00U, 0x20U, 0x04U, 0x0BU, 0x0BU, 0xBEU, 0x03U,
0x01U, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0xB6U, 0x03U, 0x65U, 0x74U,
0x78U, 0x6EU, 0x5FU, 0x64U, 0x65U, 0x74U, 0x61U, 0x69U, 0x6CU, 0x73U,
0x28U, 0x31U, 0x30U, 0x30U, 0x30U, 0x30U, 0x30U, 0x30U, 0x2CU, 0x20U,
0x31U, 0x31U, 0x36U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x4FU, 0x55U,
0x54U, 0x5FU, 0x4FU, 0x46U, 0x5FU, 0x42U, 0x4FU, 0x55U, 0x4EU, 0x44U,
0x53U, 0x00U, 0x65U, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x64U, 0x65U, 0x74U,
0x61U, 0x69U, 0x6CU, 0x73U, 0x28U, 0x30U, 0x2CU, 0x20U, 0x31U, 0x30U,
0x30U, 0x30U, 0x30U, 0x30U, 0x30U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U,
0x4FU, 0x55U, 0x54U, 0x5FU, 0x4FU, 0x46U, 0x5FU, 0x42U, 0x4FU, 0x55U,
0x4EU, 0x44U, 0x53U, 0x00U, 0x65U, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x64U,
0x65U, 0x74U, 0x61U, 0x69U, 0x6CU, 0x73U, 0x28U, 0x28U, 0x75U, 0x69U,
0x6EU, 0x74U, 0x33U, 0x32U, 0x5FU, 0x74U, 0x29U, 0x64U, 0x65U, 0x74U,
0x2CU, 0x20U, 0x31U, 0x31U, 0x35U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U,
0x54U, 0x4FU, 0x4FU, 0x5FU, 0x53U, 0x4DU, 0x41U, 0x4CU, 0x4CU, 0x00U,
0x65U, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x64U, 0x65U, 0x74U, 0x61U, 0x69U,
0x6CU, 0x73U, 0x28U, 0x28U, 0x75U, 0x69U, 0x6EU, 0x74U, 0x33U, 0x32U,
0x5FU, 0x74U, 0x29U, 0x64U, 0x65U, 0x74U, 0x2CU, 0x20U, 0x31U, 0x31U,
0x36U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x50U, 0x52U, 0x45U, 0x52U,
0x45U, 0x51U, 0x55U, 0x49U, 0x53U, 0x49U, 0x54U, 0x45U, 0x5FU, 0x4EU,
0x4FU, 0x54U, 0x5FU, 0x4DU, 0x45U, 0x54U, 0x00U, 0x65U, 0x74U, 0x78U,
0x6EU, 0x5FU, 0x64U, 0x65U, 0x74U, 0x61U, 0x69U, 0x6CU, 0x73U, 0x28U,
0x30U, 0x2CU, 0x20U, 0x31U, 0x30U, 0x30U, 0x30U, 0x30U, 0x30U, 0x30U,
0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x4FU, 0x55U, 0x54U, 0x5FU, 0x4FU,
0x46U, 0x5FU, 0x42U, 0x4FU, 0x55U, 0x4EU, 0x44U, 0x53U, 0x00U, 0x65U,
0x74U, 0x78U, 0x6EU, 0x5FU, 0x64U, 0x65U, 0x74U, 0x61U, 0x69U, 0x6CU,
0x73U, 0x28U, 0x28U, 0x75U, 0x69U, 0x6EU, 0x74U, 0x33U, 0x32U, 0x5FU,
0x74U, 0x29U, 0x64U, 0x65U, 0x74U, 0x2CU, 0x20U, 0x31U, 0x31U, 0x35U,
0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x54U, 0x4FU, 0x4FU, 0x5FU, 0x53U,
0x4DU, 0x41U, 0x4CU, 0x4CU, 0x00U, 0x65U, 0x74U, 0x78U, 0x6EU, 0x5FU,
0x64U, 0x65U, 0x74U, 0x61U, 0x69U, 0x6CU, 0x73U, 0x28U, 0x28U, 0x75U,
0x69U, 0x6EU, 0x74U, 0x33U, 0x32U, 0x5FU, 0x74U, 0x29U, 0x64U, 0x65U,
0x74U, 0x2CU, 0x20U, 0x31U, 0x31U, 0x36U, 0x29U, 0x20U, 0x3DU, 0x3DU,
0x20U, 0x50U, 0x52U, 0x45U, 0x52U, 0x45U, 0x51U, 0x55U, 0x49U, 0x53U,
0x49U, 0x54U, 0x45U, 0x5FU, 0x4EU, 0x4FU, 0x54U, 0x5FU, 0x4DU, 0x45U,
0x54U, 0x00U, 0x65U, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x64U, 0x65U, 0x74U,
0x61U, 0x69U, 0x6CU, 0x73U, 0x28U, 0x28U, 0x75U, 0x69U, 0x6EU, 0x74U,
0x33U, 0x32U, 0x5FU, 0x74U, 0x29U, 0x64U, 0x65U, 0x74U, 0x2CU, 0x20U,
0x31U, 0x31U, 0x36U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x31U, 0x31U,
0x36U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0xEDU, 0x20U, 0x2EU, 0x00U, 0x00U, 0x00U, 0x01U,
0x3DU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x5BU,
0xBCU, 0x09U, 0x95U, 0xFDU, 0x03U, 0x0FU, 0xF1U, 0x85U, 0xF4U, 0xECU,
0xACU, 0x94U, 0xDBU, 0x02U, 0x19U, 0x26U, 0xFEU, 0x58U, 0xD4U, 0x8DU,
0x48U, 0x21U, 0xCCU, 0x82U, 0x2EU, 0x4AU, 0x6AU, 0xC9U, 0xC8U, 0xC6U,
0x7CU, 0x88U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U, 0x61U, 0x73U,
0x68U, 0x28U, 0x28U, 0x75U, 0x69U, 0x6EU, 0x74U, 0x33U, 0x32U, 0x5FU,
0x74U, 0x29U, 0x65U, 0x78U, 0x70U, 0x65U, 0x63U, 0x74U, 0x65U, 0x64U,
0x5FU, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U, 0x61U, 0x73U, 0x68U,
0x2CU, 0x20U, 0x33U, 0x32U, 0x2CU, 0x20U, 0x2DU, 0x31U, 0x29U, 0x20U,
0x3DU, 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, 0x64U, 0x65U, 0x74U, 0x5BU,
0x69U, 0x5DU, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x65U, 0x78U, 0x70U, 0x65U,
0x63U, 0x74U, 0x65U, 0x64U, 0x31U, 0x5BU, 0x69U, 0x5DU, 0x00U, 0x64U,
0x65U, 0x74U, 0x5BU, 0x34U, 0x39U, 0x5DU, 0x20U, 0x3DU, 0x3DU, 0x20U,
0x30U, 0x78U, 0x35U, 0x43U, 0x55U, 0x00U, 0x64U, 0x65U, 0x74U, 0x5BU,
0x38U, 0x32U, 0x5DU, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x30U, 0x78U, 0x35U,
0x44U, 0x55U, 0x00U, 0x64U, 0x65U, 0x74U, 0x5BU, 0x38U, 0x33U, 0x20U,
0x2BU, 0x20U, 0x69U, 0x5DU, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x65U, 0x78U,
0x70U, 0x65U, 0x63U, 0x74U, 0x65U, 0x64U, 0x5FU, 0x68U, 0x6FU, 0x6FU,
0x6BU, 0x5FU, 0x68U, 0x61U, 0x73U, 0x68U, 0x5BU, 0x69U, 0x5DU, 0x00U,
0x64U, 0x65U, 0x74U, 0x5BU, 0x31U, 0x31U, 0x35U, 0x5DU, 0x20U, 0x3DU,
0x3DU, 0x20U, 0x30U, 0x78U, 0x45U, 0x31U, 0x00U,
0x28U, 0x75U, 0x69U, 0x6EU, 0x74U, 0x33U, 0x32U, 0x5FU, 0x74U, 0x29U,
0x64U, 0x65U, 0x74U, 0x2CU, 0x20U, 0x31U, 0x31U, 0x36U, 0x29U, 0x20U,
0x3DU, 0x3DU, 0x20U, 0x31U, 0x31U, 0x36U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xEDU, 0x20U,
0x2EU, 0x00U, 0x00U, 0x00U, 0x01U, 0x3DU, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x01U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U,
0x61U, 0x73U, 0x68U, 0x28U, 0x28U, 0x75U, 0x69U, 0x6EU, 0x74U, 0x33U,
0x32U, 0x5FU, 0x74U, 0x29U, 0x65U, 0x78U, 0x70U, 0x65U, 0x63U, 0x74U,
0x65U, 0x64U, 0x5FU, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x68U, 0x61U,
0x73U, 0x68U, 0x2CU, 0x20U, 0x33U, 0x32U, 0x2CU, 0x20U, 0x2DU, 0x31U,
0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, 0x64U, 0x65U,
0x74U, 0x5BU, 0x69U, 0x5DU, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x65U, 0x78U,
0x70U, 0x65U, 0x63U, 0x74U, 0x65U, 0x64U, 0x31U, 0x5BU, 0x69U, 0x5DU,
0x00U, 0x64U, 0x65U, 0x74U, 0x5BU, 0x31U, 0x36U, 0x5DU, 0x20U, 0x3DU,
0x3DU, 0x20U, 0x30U, 0x78U, 0x35U, 0x42U, 0x55U, 0x00U, 0x64U, 0x65U,
0x74U, 0x5BU, 0x34U, 0x39U, 0x5DU, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x30U,
0x78U, 0x35U, 0x43U, 0x55U, 0x00U, 0x64U, 0x65U, 0x74U, 0x5BU, 0x38U,
0x32U, 0x5DU, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x30U, 0x78U, 0x35U, 0x44U,
0x55U, 0x00U, 0x64U, 0x65U, 0x74U, 0x5BU, 0x38U, 0x33U, 0x20U, 0x2BU,
0x20U, 0x69U, 0x5DU, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x65U, 0x78U, 0x70U,
0x65U, 0x63U, 0x74U, 0x65U, 0x64U, 0x5FU, 0x68U, 0x6FU, 0x6FU, 0x6BU,
0x5FU, 0x68U, 0x61U, 0x73U, 0x68U, 0x5BU, 0x69U, 0x5DU, 0x00U, 0x64U,
0x65U, 0x74U, 0x5BU, 0x31U, 0x31U, 0x35U, 0x5DU, 0x20U, 0x3DU, 0x3DU,
0x20U, 0x30U, 0x78U, 0x45U, 0x31U, 0x00U,
}},
{R"[test.hook](
@@ -12786,7 +12903,7 @@ std::map<std::string, std::vector<uint8_t>> 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
@@ -12817,63 +12934,64 @@ std::map<std::string, std::vector<uint8_t>> 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,
}},
{R"[test.hook](
+1
View File
@@ -1410,6 +1410,7 @@ public:
testAllTxns(sa);
testAllTxns(sa - fixHookAPI20251128);
testAllTxns(sa - featureTouch - fixHookAPI20251128);
testAllTxns(sa - featureHookFeeV2);
}
};
+4 -2
View File
@@ -5068,7 +5068,8 @@ public:
// fragile: hardcoded ordering by txID XOR parentHash
// parentHash < txTree Hash < txMeta < PreviousTxnID
testTieBreaking(
all - fixProvisionalDoubleThreading - fixHookAPI20251128);
all - fixProvisionalDoubleThreading - fixHookAPI20251128 -
featureHookFeeV3);
testAcctTxnID(all);
testMaximum(all);
testUnexpectedBalanceChange(all);
@@ -5089,7 +5090,8 @@ public:
// fragile: hardcoded ordering by txID XOR parentHash
// parentHash < txTree Hash < txMeta < PreviousTxnID
testFullQueueGapFill(
all - fixProvisionalDoubleThreading - fixHookAPI20251128);
all - fixProvisionalDoubleThreading - fixHookAPI20251128 -
featureHookFeeV3);
testSignAndSubmitSequence(all);
testAccountInfo(all);
testServerInfo(all);
+2 -1
View File
@@ -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;
+4 -1
View File
@@ -4830,6 +4830,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;
@@ -4937,7 +4938,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;
@@ -4975,6 +4977,7 @@ struct XahauGenesis_test : public beast::unit_test::suite
"10",
"1000000",
"200000",
"1000000",
0),
features - featureXahauGenesis};
+16 -5
View File
@@ -154,6 +154,17 @@ private:
AppBundle bundle_;
// Features must be in the config before the Application is built so
// that the genesis ledger's Rules (and its FeeSettings) reflect them.
static std::unique_ptr<Config>
withFeatures(std::unique_ptr<Config> config, FeatureBitset const& features)
{
foreachFeature(features, [&config](uint256 const& f) {
config->features.insert(f);
});
return config;
}
public:
beast::Journal const journal;
@@ -183,15 +194,15 @@ public:
std::unique_ptr<Logs> logs = nullptr,
beast::severities::Severity thresh = beast::severities::kError)
: test(suite_)
, bundle_(suite_, std::move(config), std::move(logs), thresh)
, bundle_(
suite_,
withFeatures(std::move(config), features),
std::move(logs),
thresh)
, journal{bundle_.app->journal("Env")}
{
memoize(Account::master);
Pathfinder::initPathTable();
foreachFeature(
features, [&appFeats = app().config().features](uint256 const& f) {
appFeats.insert(f);
});
}
/**
+1
View File
@@ -36,6 +36,7 @@ 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 = 10'000;
// The Beta API (currently v2) is always available to tests
cfg.BETA_RPC_API = true;
+12 -5
View File
@@ -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<Config> cfg) {
@@ -46,9 +47,11 @@ makeNetworkConfig(
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;
cfg->section("voting") = config;
return cfg;
});
}
@@ -59,7 +62,8 @@ makeNetworkVLConfig(
std::vector<std::string> 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<Config> cfg) {
@@ -68,7 +72,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 +103,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 +135,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;
+5 -2
View File
@@ -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<Config>
makeNetworkVLConfig(
@@ -43,7 +44,8 @@ makeNetworkVLConfig(
std::vector<std::string> keys,
std::string fee = "10",
std::string a_res = "1000000",
std::string o_res = "200000");
std::string o_res = "200000",
std::string g_res = "10000");
std::unique_ptr<Config>
makeGenesisConfig(
@@ -53,6 +55,7 @@ makeGenesisConfig(
std::string fee = "10",
std::string a_res = "1000000",
std::string o_res = "200000",
std::string g_res = "10000",
uint32_t ledgerID = 0);
} // namespace network
+1
View File
@@ -225,6 +225,7 @@ public:
using namespace test::jtx;
FeatureBitset const all{supported_amendments()};
testErrors(all);
testErrors(all - featureHookFeeV3);
}
};
+2 -1
View File
@@ -117,7 +117,8 @@ class AccountTx_test : public beast::unit_test::suite
Env env(
*this,
supported_amendments() - featureXahauGenesis - fixHookAPI20251128);
supported_amendments() - featureXahauGenesis - fixHookAPI20251128 -
featureHookFeeV3);
Account A1{"A1"};
env.fund(XRP(10000), A1);
env.close();
+2 -1
View File
@@ -3392,7 +3392,8 @@ public:
return cfg;
}),
supported_amendments() - featureXahauGenesis -
fixProvisionalDoubleThreading - fixHookAPI20251128};
fixProvisionalDoubleThreading - fixHookAPI20251128 -
featureHookFeeV3};
Json::Value jv;
jv[jss::ledger_index] = "current";
+7 -4
View File
@@ -310,10 +310,13 @@ public:
{
using namespace test::jtx;
using namespace std::chrono_literals;
Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
cfg->NODE_SIZE = 0;
return cfg;
})};
Env env{
*this,
envconfig([](std::unique_ptr<Config> cfg) {
cfg->NODE_SIZE = 0;
return cfg;
}),
supported_amendments() - featureHookFeeV3};
Account const gw{"gateway"};
auto const USD = gw["USD"];
env.fund(XRP(100000), gw);
+3 -1
View File
@@ -316,7 +316,9 @@ public:
using namespace std::chrono_literals;
using namespace jtx;
Env env(
*this, supported_amendments() - featureXahauGenesis - featureTouch);
*this,
supported_amendments() - featureXahauGenesis - featureTouch -
featureHookFeeV3);
auto wsc = makeWSClient(env.app().config());
Json::Value stream{Json::objectValue};
+1 -1
View File
@@ -153,7 +153,7 @@ class TransactionEntry_test : public beast::unit_test::suite
Env env{
*this,
supported_amendments() - featureXahauGenesis - featureTouch -
fixHookAPI20251128,
fixHookAPI20251128 - featureHookFeeV3,
};
auto check_tx = [this, &env, apiVersion](
+3 -2
View File
@@ -922,7 +922,8 @@ public:
forAllApiVersions(std::bind_front(
&Transaction_test::testBinaryRequest,
this,
supported_amendments() - featureXahauGenesis - fixHookAPI20251128));
supported_amendments() - featureXahauGenesis - fixHookAPI20251128 -
featureHookFeeV3));
return;
FeatureBitset const all{supported_amendments()};
testWithFeats(all);
@@ -938,7 +939,7 @@ public:
forAllApiVersions(std::bind_front(
&Transaction_test::testRequest,
this,
features - featureXahauGenesis - featureTouch));
features - featureXahauGenesis - featureTouch - featureHookFeeV3));
}
};
+21 -3
View File
@@ -65,14 +65,17 @@ namespace hook_api {
#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 <xrpl/hook/hook_api.macro>
#undef HOOK_API_COST
#undef HOOK_API_DEFINITION
#undef HOOK_WRAP_PARAMS
#pragma pop_macro("HOOK_API_DEFINITION")
@@ -137,6 +140,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<std::pair<uint64_t, uint64_t>>
doValidateGuards(
STTx const& tx,
Blob const& wasm,
Rules const& rules,
beast::Journal const& j);
struct HookResult
{
ripple::uint256 const hookSetTxnID;
@@ -276,14 +291,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); \
}
@@ -475,14 +490,17 @@ public:
#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);
ADD_HOOK_FUNCTION(FUNCTION_NAME, ctx, 0);
#define HOOK_API_COST(...)
#include <xrpl/hook/hook_api.macro>
#undef HOOK_API_DEFINITION
#undef HOOK_API_COST
#undef HOOK_WRAP_PARAMS
#pragma pop_macro("HOOK_API_DEFINITION")
+91
View File
@@ -9,6 +9,7 @@
#include <xrpld/app/tx/detail/NFTokenUtils.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/hook/Guard.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/PublicKey.h>
@@ -553,6 +554,9 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
case ttREMARKS_SET: {
break;
}
case ttHOOK_DEFINITION_UPDATE: {
break;
}
// pseudo transactions
case ttAMENDMENT:
case ttFEE:
@@ -691,6 +695,93 @@ hook::computeCreationFee(uint64_t byteCount)
return fee;
}
XRPAmount
hook::hookCostToFee(ReadView const& view, uint64_t hookCost)
{
if (!view.rules().enabled(featureHookFeeV3))
{
// HookFeeV2: fixed price, rounded up. The quotient of a uint64 by 10
// always fits in an int64.
uint64_t const drops = hookCost / hook_api::cost_units_per_drop +
(hookCost % hook_api::cost_units_per_drop != 0);
return XRPAmount{static_cast<XRPAmount::value_type>(drops)};
}
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<std::pair<uint64_t, uint64_t>>
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<std::reference_wrapper<std::basic_ostream<char>>> 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<char> 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 std::variant<uint64_t, hook_api::hook_return_code>
data_as_int64(void const* ptr_raw, uint32_t len)
+29 -1
View File
@@ -244,6 +244,15 @@ Ledger::Ledger(
sle->at(sfReserveIncrement) = *f;
sle->at(sfReferenceFeeUnits) = Config::FEE_UNITS_DEPRECATED;
}
// rules_ covers config.features (unit tests), amendments covers
// a FRESH start where the desired amendments are pre-enabled.
if (rules_.enabled(featureHookFeeV3) ||
std::find(amendments.begin(), amendments.end(), featureHookFeeV3) !=
amendments.end())
{
sle->at(sfHookGasPrice) = config.FEES.hook_gas_price;
}
rawInsert(sle);
}
@@ -662,6 +671,7 @@ Ledger::setup()
{
bool oldFees = false;
bool newFees = false;
bool hookFees = false;
{
auto const baseFee = sle->at(~sfBaseFee);
auto const reserveBase = sle->at(~sfReserveBase);
@@ -695,12 +705,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<std::uint32_t> 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(featureHookFeeV3) && hookFees)
// Can't populate the Hook Fee V3 before the amendment is
// enabled
ret = false;
}
}
catch (SHAMapMissingNode const&)
@@ -720,7 +744,8 @@ void
Ledger::defaultFees(Config const& config)
{
XRPL_ASSERT(
fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0,
fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0 &&
fees_.hookGasPrice == 0,
"ripple::Ledger::defaultFees : zero fees");
if (fees_.base == 0)
fees_.base = config.FEES.reference_fee;
@@ -728,6 +753,9 @@ Ledger::defaultFees(Config const& config)
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<SLE>
+55 -7
View File
@@ -29,10 +29,10 @@ namespace ripple {
namespace detail {
template <typename value_type>
class VotableValue
{
private:
using value_type = XRPAmount;
value_type const current_; // The current setting
value_type const target_; // The setting we want
std::map<value_type, int> voteMap_;
@@ -67,8 +67,9 @@ public:
getVotes() const;
};
auto
VotableValue::getVotes() const -> std::pair<value_type, bool>
template <typename value_type>
std::pair<value_type, bool>
VotableValue<value_type>::getVotes() const
{
value_type ourVote = current_;
int weight = 0;
@@ -191,6 +192,28 @@ FeeVoteImpl::doValidation(
"reserve increment",
sfReserveIncrement);
}
if (rules.enabled(featureHookFeeV3))
{
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
@@ -213,11 +236,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<STValidation> const& val,
detail::VotableValue& value,
detail::VotableValue<XRPAmount>& value,
SF_AMOUNT const& xrpField) {
if (auto const field = ~val->at(~xrpField);
field && field->native())
@@ -246,7 +272,7 @@ FeeVoteImpl::doVoting(
else
{
auto doVote = [](std::shared_ptr<STValidation> const& val,
detail::VotableValue& value,
detail::VotableValue<XRPAmount>& value,
auto const& valueField) {
if (auto const field = val->at(~valueField))
{
@@ -277,6 +303,23 @@ FeeVoteImpl::doVoting(
doVote(val, incReserveVote, sfReserveIncrement);
}
}
if (rules.enabled(featureHookFeeV3))
{
auto doVote = [](std::shared_ptr<STValidation> const& val,
detail::VotableValue<std::uint32_t>& 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 16 is the minimum supported
@@ -285,15 +328,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();
@@ -317,6 +363,8 @@ FeeVoteImpl::doVoting(
incReserveVote.current());
obj[sfReferenceFeeUnits] = Config::FEE_UNITS_DEPRECATED;
}
if (rules.enabled(featureHookFeeV3))
obj[sfHookGasPrice] = hookGasPrice.first;
});
uint256 txID = feeTx.getTransactionID();
+14
View File
@@ -2453,6 +2453,9 @@ NetworkOPsImp::pubValidation(std::shared_ptr<STValidation> 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;
// NOTE Use MultiApiJson to publish two slightly different JSON objects
// for consumers supporting different API versions
MultiApiJson multiObj{jvObj};
@@ -2920,12 +2923,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 hookFeeV3 = lpClosed->rules().enabled(featureHookFeeV3);
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 (hookFeeV3)
l[jss::hook_gas_price] = lpClosed->fees().hookGasPrice;
l[jss::close_time] = Json::Value::UInt(
lpClosed->info().closeTime.time_since_epoch().count());
}
@@ -2941,6 +2948,8 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters)
lpClosed->fees().accountReserve(0).decimalXRP();
l[jss::reserve_inc_native] =
lpClosed->fees().increment.decimalXRP();
if (hookFeeV3)
l[jss::hook_gas_price] = lpClosed->fees().hookGasPrice;
if (auto const closeOffset = app_.timeKeeper().closeOffset();
std::abs(closeOffset.count()) >= 60)
@@ -3131,6 +3140,9 @@ NetworkOPsImp::pubLedger(std::shared_ptr<ReadView const> const& lpAccepted)
jvObj[jss::reserve_inc] =
lpAccepted->fees().increment.jsonClipped();
if (lpAccepted->rules().enabled(featureHookFeeV3))
jvObj[jss::hook_gas_price] = lpAccepted->fees().hookGasPrice;
jvObj[jss::txn_count] = Json::UInt(alpAccepted->size());
if (mMode >= OperatingMode::SYNCING)
@@ -4185,6 +4197,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(featureHookFeeV3))
jvResult[jss::hook_gas_price] = lpClosed->fees().hookGasPrice;
}
if ((mMode >= OperatingMode::SYNCING) && !isNeedNetworkLedger())
+13
View File
@@ -152,6 +152,16 @@ Change::preclaim(PreclaimContext const& ctx)
ctx.tx.isFieldPresent(sfReserveIncrementDrops))
return temDISABLED;
}
if (ctx.view.rules().enabled(featureHookFeeV3))
{
if (!ctx.tx.isFieldPresent(sfHookGasPrice))
return temMALFORMED;
}
else
{
if (ctx.tx.isFieldPresent(sfHookGasPrice))
return temDISABLED;
}
return tesSUCCESS;
case ttAMENDMENT:
case ttUNL_MODIFY:
@@ -605,6 +615,7 @@ Change::activateXahauGenesis()
wasmBytes, // wasm to verify
loggerStream,
"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
false,
hook_api::getImportWhitelist(ctx_.view().rules()),
hook_api::getGuardRulesVersion(ctx_.view().rules()));
@@ -1034,6 +1045,8 @@ Change::applyFee()
set(feeObject, ctx_.tx, sfReserveBase);
set(feeObject, ctx_.tx, sfReserveIncrement);
}
if (view().rules().enabled(featureHookFeeV3))
set(feeObject, ctx_.tx, sfHookGasPrice);
view().update(feeObject);
@@ -0,0 +1,127 @@
//------------------------------------------------------------------------------
/*
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 <xrpld/app/tx/detail/HookDefinitionUpdate.h>
#include <xrpl/protocol/TxFlags.h>
namespace ripple {
NotTEC
HookDefinitionUpdate::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureHookFeeV2))
return temDISABLED;
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;
auto const& tx = ctx.tx;
if (tx.getFlags() & tfHookDefinitionUpdateMask)
return temINVALID_FLAG;
return preflight2(ctx);
}
TER
HookDefinitionUpdate::preclaim(PreclaimContext const& ctx)
{
auto const& view = ctx.view;
auto const& tx = ctx.tx;
if (!view.exists(keylet::hookDefinition(tx.getFieldH256(sfHookHash))))
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; // LCOV_EXCL_LINE
Blob hook = hookDefSle->getFieldVL(sfCreateCode);
if (!tx.isFlag(tfValidateGuards))
return tecINTERNAL; // LCOV_EXCL_LINE
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);
auto const& hookDefSle =
view.read(keylet::hookDefinition(tx.getFieldH256(sfHookHash)));
if (!hookDefSle || !hookDefSle->isFieldPresent(sfCreateCode))
// if hook definition is not present, doValidateGuards will not call, so
// no additional fee is needed
return baseFee;
auto const hookCodeSize = hookDefSle->getFieldVL(sfCreateCode).size();
return baseFee + XRPAmount{hook::computeCreationFee(hookCodeSize)};
}
} // namespace ripple
@@ -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 <xrpld/app/tx/detail/Transactor.h>
#include <xrpl/basics/Log.h>
#include <xrpl/protocol/Indexes.h>
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
+24 -62
View File
@@ -556,59 +556,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<std::reference_wrapper<std::basic_ostream<char>>>
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,
hook_api::getImportWhitelist(ctx.rules),
hook_api::getGuardRulesVersion(ctx.rules));
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<char> 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;
@@ -1871,9 +1820,11 @@ SetHook::setHook()
"SetHook::hsoCREATE : should never happen");
}
// otherwise assign instruction counts
std::tie(maxInstrCountHook, maxInstrCountCbak) =
auto const instrCounts =
std::get<std::pair<uint64_t, uint64_t>>(valid);
std::tie(maxInstrCountHook, maxInstrCountCbak) =
instrCounts;
}
catch (std::exception& e)
{
@@ -1927,15 +1878,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);
+74 -26
View File
@@ -251,7 +251,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<XRPAmount, uint64_t>
Transactor::calculateHookChainFee(
ReadView const& view,
STTx const& tx,
@@ -261,9 +261,10 @@ Transactor::calculateHookChainFee(
{
std::shared_ptr<SLE const> 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);
@@ -302,19 +303,35 @@ Transactor::calculateHookChainFee(
if (hook::canHook(tx, hookOn, hookName) &&
(!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<uint64_t>::max();
else
hookCost += toAdd;
}
}
}
return fee;
return std::make_pair(fee, hookCost);
}
XRPAmount
@@ -340,6 +357,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))
{
@@ -362,18 +380,32 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
std::shared_ptr<SLE const> hookDef =
view.read(keylet::hookDefinition(callbackHookHash));
if (hookDef && hookDef->isFieldPresent(sfHookCallbackFee))
if (hookDef)
{
XRPAmount const toAdd{
hookDef->getFieldAmount(sfHookCallbackFee).xrp().drops()};
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;
// 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<uint64_t>::max();
else
hookCost += toAdd;
}
}
XRPL_ASSERT(
@@ -383,8 +415,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)), true);
hookExecutionFee += hookChainFee.first;
hookCost += hookChainFee.second;
}
// find any additional stakeholders whose hooks will be executed and
// charged to this transaction
@@ -393,8 +429,12 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
for (auto& [tshAcc, canRollback] : tsh)
if (canRollback)
hookExecutionFee += calculateHookChainFee(
{
auto const hookChainFee = calculateHookChainFee(
view, tx, keylet::hook(tshAcc), false);
hookExecutionFee += hookChainFee.first;
hookCost += hookChainFee.second;
}
}
XRPAmount accumulator = baseFee;
@@ -448,6 +488,8 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
// transaction.
do
{
hookExecutionFee += hook::hookCostToFee(view, hookCost);
if (accumulator * burden < accumulator)
break;
@@ -1408,7 +1450,8 @@ Transactor::executeHookChain(
return tecINTERNAL;
}
bool hasCallback = hookDef->isFieldPresent(sfHookCallbackFee);
bool hasCallback = hookDef->isFieldPresent(sfHookCallbackFee) ||
hookDef->isFieldPresent(sfHookCallbackCost);
try
{
@@ -1513,7 +1556,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 "
@@ -1732,8 +1776,11 @@ Transactor::doTSH(
continue;
// compute and deduct fees for the TSH if applicable
XRPAmount tshFeeDrops = calculateHookChainFee(
auto const tshFee = calculateHookChainFee(
view, ctx_.tx, klTshHook, false, !canRollback);
XRPAmount tshFeeDrops{tshFee.first};
tshFeeDrops += hook::hookCostToFee(view, tshFee.second);
// no hooks to execute, skip tsh
if (tshFeeDrops == 0)
@@ -1888,7 +1935,8 @@ Transactor::doAgainAsWeak(
stateMap,
ctx_,
hookAccountID,
hookDef->isFieldPresent(sfHookCallbackFee),
hookDef->isFieldPresent(sfHookCallbackFee) ||
hookDef->isFieldPresent(sfHookCallbackCost),
false,
false,
2UL, // param 2 = aaw
+1 -1
View File
@@ -174,7 +174,7 @@ public:
// Hooks
static XRPAmount
static std::pair<XRPAmount, uint64_t>
calculateHookChainFee(
ReadView const& view,
STTx const& tx,
+1
View File
@@ -44,6 +44,7 @@
#include <xrpld/app/tx/detail/DepositPreauth.h>
#include <xrpld/app/tx/detail/Escrow.h>
#include <xrpld/app/tx/detail/GenesisMint.h>
#include <xrpld/app/tx/detail/HookDefinitionUpdate.h>
#include <xrpld/app/tx/detail/Import.h>
#include <xrpld/app/tx/detail/Invoke.h>
#include <xrpld/app/tx/detail/LedgerStateFix.h>
+3
View File
@@ -82,6 +82,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.) */
};
+7
View File
@@ -1194,6 +1194,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