From 51a9c0ff59c8fcd891e83631d737832b7626d602 Mon Sep 17 00:00:00 2001 From: Olek <115580134+oleks-rip@users.noreply.github.com> Date: Thu, 12 Jun 2025 15:54:49 -0400 Subject: [PATCH] Host function gas cost (#5488) * Update Wamr to 2.3.1 * Add gas cost per host-function * Fix windows build * Fix wasm test * Add no import test --- .github/actions/dependencies/action.yml | 2 +- .github/workflows/nix.yml | 2 +- conanfile.py | 2 +- external/wamr/conandata.yml | 2 +- external/wamr/conanfile.py | 5 +- external/wamr/patches/ripp_metering.patch | 510 ++++++++++++---------- src/test/app/Wasm_test.cpp | 371 +++++++++------- src/xrpld/app/misc/WamrVM.cpp | 16 +- src/xrpld/app/misc/WamrVM.h | 4 +- src/xrpld/app/misc/WasmParamsHelper.h | 5 +- 10 files changed, 516 insertions(+), 403 deletions(-) diff --git a/.github/actions/dependencies/action.yml b/.github/actions/dependencies/action.yml index c251daa936..4f7e238333 100644 --- a/.github/actions/dependencies/action.yml +++ b/.github/actions/dependencies/action.yml @@ -17,7 +17,7 @@ runs: conan export external/rocksdb rocksdb/9.7.3@ conan export external/soci soci/4.0.3@ conan export external/nudb nudb/2.0.8@ - conan export -k external/wamr wamr/2.2.0@ + conan export external/wamr wamr/2.3.1@ - name: add Ripple Conan remote shell: bash run: | diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 76597a68e0..3b95fbe1d8 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -408,7 +408,7 @@ jobs: conan profile update 'conf.tools.build:cxxflags+=["-DBOOST_ASIO_DISABLE_CONCEPTS"]' default conan export external/snappy snappy/1.1.10@ conan export external/soci soci/4.0.3@ - conan export -k external/wamr wamr/2.2.0@ + conan export external/wamr wamr/2.3.1@ - name: build dependencies run: | diff --git a/conanfile.py b/conanfile.py index cc9ce866ec..01c7ccfc9b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -31,7 +31,7 @@ class Xrpl(ConanFile): 'openssl/1.1.1v', 'soci/4.0.3', 'zlib/1.3.1', - 'wamr/2.2.0', + 'wamr/2.3.1', ] tool_requires = [ diff --git a/external/wamr/conandata.yml b/external/wamr/conandata.yml index 6c49946eca..b145981630 100644 --- a/external/wamr/conandata.yml +++ b/external/wamr/conandata.yml @@ -1,5 +1,5 @@ patches: - 2.2.0: + 2.3.1: - patch_description: add metering to iwasm interpreter patch_file: patches/ripp_metering.patch patch_type: conan diff --git a/external/wamr/conanfile.py b/external/wamr/conanfile.py index ae83e8c13c..1fa3281c1e 100644 --- a/external/wamr/conanfile.py +++ b/external/wamr/conanfile.py @@ -13,7 +13,7 @@ required_conan_version = ">=1.55.0" class WamrConan(ConanFile): name = "wamr" - version = "2.2.0" + version = "2.3.1" license = "Apache License v2.0" url = "https://github.com/bytecodealliance/wasm-micro-runtime.git" description = "Webassembly micro runtime" @@ -42,7 +42,7 @@ class WamrConan(ConanFile): git = tools.Git() git.clone( "https://github.com/bytecodealliance/wasm-micro-runtime.git", - "c883fafead005e87ad3122b05409886f507c1cb0", + "2a303861cc916dc182b7fecaa0aacc1b797e7ac6", shallow=True, ) # get(self, **self.conan_data["sources"][self.version], strip_root=True) @@ -89,3 +89,4 @@ class WamrConan(ConanFile): self.cpp_info.libs = ["iwasm"] self.cpp_info.names["cmake_find_package"] = "wamr" self.cpp_info.names["cmake_find_package_multi"] = "wamr" + diff --git a/external/wamr/patches/ripp_metering.patch b/external/wamr/patches/ripp_metering.patch index a2abf37a57..e9ca69338f 100644 --- a/external/wamr/patches/ripp_metering.patch +++ b/external/wamr/patches/ripp_metering.patch @@ -1,5 +1,5 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 551991f8..5f48a0b8 100644 +index 88a1642b..aeb29912 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ @@ -11,97 +11,127 @@ index 551991f8..5f48a0b8 100644 option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) -diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake -index 1cb50235..bd103022 100644 ---- a/build-scripts/config_common.cmake -+++ b/build-scripts/config_common.cmake -@@ -669,6 +669,10 @@ if (WAMR_BUILD_AOT_VALIDATOR EQUAL 1) - message (" AOT validator enabled") - add_definitions (-DWASM_ENABLE_AOT_VALIDATOR=1) - endif () -+if (WAMR_BUILD_INSTRUCTION_METERING EQUAL 1) -+ message (" Instruction metering enabled") -+ add_definitions (-DWASM_ENABLE_INSTRUCTION_METERING=1) -+endif () - - ######################################## - # Show Phase4 Wasm proposals status. -diff --git a/core/config.h b/core/config.h -index cb1189c9..a4e1499e 100644 ---- a/core/config.h -+++ b/core/config.h -@@ -716,4 +716,8 @@ unless used elsewhere */ - #define WASM_ENABLE_AOT_VALIDATOR 0 - #endif - -+#ifndef WASM_ENABLE_INSTRUCTION_METERING -+#define WASM_ENABLE_INSTRUCTION_METERING 0 -+#endif -+ - #endif /* end of _CONFIG_H_ */ diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c -index 269ec577..bc6fd01b 100644 +index 269ec577..34eb7c34 100644 --- a/core/iwasm/common/wasm_c_api.c +++ b/core/iwasm/common/wasm_c_api.c -@@ -5389,3 +5389,8 @@ wasm_instance_get_wasm_func_exec_time(const wasm_instance_t *instance, +@@ -3242,10 +3242,20 @@ wasm_func_copy(const wasm_func_t *func) + + cloned->func_idx_rt = func->func_idx_rt; + cloned->inst_comm_rt = func->inst_comm_rt; ++ cloned->gas = func->gas; + + RETURN_OBJ(cloned, wasm_func_delete) + } + ++uint32_t ++wasm_func_set_gas(wasm_func_t *func, uint32_t gas) ++{ ++ if(!func) return 0; ++ ++ func->gas = gas; ++ return gas; ++} ++ + own wasm_functype_t * + wasm_func_type(const wasm_func_t *func) + { +@@ -4998,11 +5008,11 @@ wasm_instance_new_with_args_ex(wasm_store_t *store, const wasm_module_t *module, + goto failed; + } + ++ WASMModuleInstance *wasm_module_inst = NULL; + /* create the c-api func import list */ + #if WASM_ENABLE_INTERP != 0 + if (instance->inst_comm_rt->module_type == Wasm_Module_Bytecode) { +- WASMModuleInstance *wasm_module_inst = +- (WASMModuleInstance *)instance->inst_comm_rt; ++ wasm_module_inst = (WASMModuleInstance *)instance->inst_comm_rt; + p_func_imports = &(wasm_module_inst->c_api_func_imports); + import_func_count = MODULE_INTERP(module)->import_function_count; + } +@@ -5052,6 +5062,13 @@ wasm_instance_new_with_args_ex(wasm_store_t *store, const wasm_module_t *module, + } + bh_assert(func_import->func_ptr_linked); + ++ // fill gas ++ if(wasm_module_inst) { ++ WASMFunctionInstance *fi = wasm_module_inst->e->functions + func_host->func_idx_rt; ++ if(fi) fi->gas = func_host->gas; ++ } ++ ++ + func_import++; + } + +@@ -5389,3 +5406,8 @@ wasm_instance_get_wasm_func_exec_time(const wasm_instance_t *instance, return -1.0; #endif } + -+wasm_exec_env_t wasm_instance_exec_env(const wasm_instance_t*instance) ++wasm_exec_env_t wasm_instance_exec_env(const wasm_instance_t *instance) +{ + return wasm_runtime_get_exec_env_singleton(instance->inst_comm_rt); +} +diff --git a/core/iwasm/common/wasm_c_api_internal.h b/core/iwasm/common/wasm_c_api_internal.h +index 49a17a96..19a85980 100644 +--- a/core/iwasm/common/wasm_c_api_internal.h ++++ b/core/iwasm/common/wasm_c_api_internal.h +@@ -142,6 +142,10 @@ struct wasm_func_t { + void (*finalizer)(void *); + } cb_env; + } u; ++ ++ // gas cost for import func ++ uint32 gas; ++ + /* + * an index in both functions runtime instance lists + * of interpreter mode and aot mode diff --git a/core/iwasm/common/wasm_exec_env.c b/core/iwasm/common/wasm_exec_env.c -index e33fd9f3..d1ff9c41 100644 +index 47752950..d5821c4f 100644 --- a/core/iwasm/common/wasm_exec_env.c +++ b/core/iwasm/common/wasm_exec_env.c -@@ -85,6 +85,12 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, - wasm_runtime_dump_exec_env_mem_consumption(exec_env); +@@ -86,7 +86,9 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, #endif -+#if WASM_ENABLE_INSTRUCTION_METERING != 0 -+ exec_env->instructions_to_execute = -1; + #if WASM_ENABLE_INSTRUCTION_METERING != 0 +- exec_env->instructions_to_execute = -1; ++ exec_env->instructions_to_execute = INT64_MAX; + for(int i = 0; i < 256; ++i) + exec_env->instructions_schedule[i] = 1; -+#endif -+ - return exec_env; - - #ifdef OS_ENABLE_HW_BOUND_CHECK -diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h -index ce0c1fa7..2713a092 100644 ---- a/core/iwasm/common/wasm_exec_env.h -+++ b/core/iwasm/common/wasm_exec_env.h -@@ -87,6 +87,12 @@ typedef struct WASMExecEnv { - uint8 *bottom; - } wasm_stack; - -+#if WASM_ENABLE_INSTRUCTION_METERING != 0 -+ /* instructions to execute */ -+ int64 instructions_to_execute; -+ int64 instructions_schedule[256]; -+#endif -+ - #if WASM_ENABLE_FAST_JIT != 0 - /** - * Cache for -diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c -index d33c0272..21bfdf29 100644 ---- a/core/iwasm/common/wasm_runtime_common.c -+++ b/core/iwasm/common/wasm_runtime_common.c -@@ -2285,6 +2285,31 @@ wasm_runtime_access_exce_check_guard_page() - } #endif -+#if WASM_ENABLE_INSTRUCTION_METERING != 0 -+ -+void -+wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, + return exec_env; +diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h +index 5d80312f..2713a092 100644 +--- a/core/iwasm/common/wasm_exec_env.h ++++ b/core/iwasm/common/wasm_exec_env.h +@@ -89,7 +89,8 @@ typedef struct WASMExecEnv { + + #if WASM_ENABLE_INSTRUCTION_METERING != 0 + /* instructions to execute */ +- int instructions_to_execute; ++ int64 instructions_to_execute; ++ int64 instructions_schedule[256]; + #endif + + #if WASM_ENABLE_FAST_JIT != 0 +diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c +index dcee0aea..2559be21 100644 +--- a/core/iwasm/common/wasm_runtime_common.c ++++ b/core/iwasm/common/wasm_runtime_common.c +@@ -2288,10 +2288,26 @@ wasm_runtime_access_exce_check_guard_page() + #if WASM_ENABLE_INSTRUCTION_METERING != 0 + void + wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, +- int instructions_to_execute) + int64 instructions_to_execute) -+{ -+ exec_env->instructions_to_execute = instructions_to_execute; -+} + { ++ if(instructions_to_execute == -1) ++ instructions_to_execute = INT64_MAX; + exec_env->instructions_to_execute = instructions_to_execute; + } + +int64 +wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env) @@ -116,13 +146,10 @@ index d33c0272..21bfdf29 100644 + for(int i = 0; i < 256; ++i) + exec_env->instructions_schedule[i] = instructions_schedule[i]; +} -+ -+#endif -+ + #endif + WASMFuncType * - wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function, - uint32 module_type) -@@ -7792,13 +7817,14 @@ wasm_runtime_get_module_name(wasm_module_t module) +@@ -7805,13 +7821,14 @@ wasm_runtime_get_module_name(wasm_module_t module) bool wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env) { @@ -138,7 +165,7 @@ index d33c0272..21bfdf29 100644 uint32 page_size = os_getpagesize(); uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; boundary = boundary + page_size * guard_page_count; -@@ -7808,6 +7834,7 @@ wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env) +@@ -7821,6 +7838,7 @@ wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env) "native stack overflow"); return false; } @@ -146,7 +173,7 @@ index d33c0272..21bfdf29 100644 return true; } -@@ -7830,7 +7857,7 @@ wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env, +@@ -7843,7 +7861,7 @@ wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env, boundary = boundary - WASM_STACK_GUARD_SIZE + requested_size; if ((uint8 *)&boundary < boundary) { wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), @@ -156,19 +183,14 @@ index d33c0272..21bfdf29 100644 } return true; diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h -index 8ac032bf..5ca5d489 100644 +index 64a6cd79..f4c55e2f 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h -@@ -791,9 +791,25 @@ WASM_RUNTIME_API_EXTERN void - wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env, - uint8 *native_stack_boundary); - --#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 -+#if WASM_ENABLE_INSTRUCTION_METERING != 0 -+ +@@ -795,7 +795,14 @@ wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env, /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN void -+wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, + wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, +- int instructions_to_execute); + int64 instructions_to_execute); +WASM_RUNTIME_API_EXTERN int64 +wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env); @@ -177,17 +199,11 @@ index 8ac032bf..5ca5d489 100644 +wasm_runtime_set_instruction_schedule(WASMExecEnv *exec_env, + int64 const *instructions_schedule); + -+#endif -+ -+#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 -+/* See wasm_export.h for description */ -+WASM_RUNTIME_API_EXTERN -+void - wasm_runtime_set_bounds_checks(WASMModuleInstanceCommon *module_inst, - bool enable); + #endif + #if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h -index 241a0eec..9eb0dde1 100644 +index 241a0eec..1141744c 100644 --- a/core/iwasm/include/wasm_c_api.h +++ b/core/iwasm/include/wasm_c_api.h @@ -19,8 +19,10 @@ @@ -202,7 +218,16 @@ index 241a0eec..9eb0dde1 100644 #endif #else #define WASM_API_EXTERN -@@ -701,6 +703,11 @@ WASM_API_EXTERN double wasm_instance_sum_wasm_exec_time(const wasm_instance_t*); +@@ -592,6 +594,8 @@ WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*); + WASM_API_EXTERN own wasm_trap_t* wasm_func_call( + const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results); + ++WASM_API_EXTERN own uint32_t wasm_func_set_gas(wasm_func_t*, uint32_t); ++ + + // Global Instances + +@@ -701,6 +705,11 @@ WASM_API_EXTERN double wasm_instance_sum_wasm_exec_time(const wasm_instance_t*); // func_name. If the function is not found, return 0. WASM_API_EXTERN double wasm_instance_get_wasm_func_exec_time(const wasm_instance_t*, const char *); @@ -215,7 +240,7 @@ index 241a0eec..9eb0dde1 100644 // Convenience diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h -index b73a0364..3fd0949f 100644 +index b4ab34be..3fd0949f 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -20,8 +20,10 @@ @@ -230,22 +255,11 @@ index b73a0364..3fd0949f 100644 #endif #elif defined(__GNUC__) || defined(__clang__) #define WASM_RUNTIME_API_EXTERN __attribute__((visibility("default"))) -@@ -1821,6 +1823,27 @@ WASM_RUNTIME_API_EXTERN void - wasm_runtime_set_native_stack_boundary(wasm_exec_env_t exec_env, - uint8_t *native_stack_boundary); - -+/** -+ * Set the instruction count limit to the execution environment. -+ * By default the instruction count limit is -1, which means no limit. -+ * However, if the instruction count limit is set to a positive value, -+ * the execution will be terminated when the instruction count reaches -+ * the limit. -+ * -+ * @param exec_env the execution environment -+ * @param instruction_count the instruction count limit -+ */ -+WASM_RUNTIME_API_EXTERN void -+wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env, +@@ -1833,7 +1835,14 @@ wasm_runtime_set_native_stack_boundary(wasm_exec_env_t exec_env, + */ + WASM_RUNTIME_API_EXTERN void + wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env, +- int instruction_count); + int64_t instruction_count); + +WASM_RUNTIME_API_EXTERN int64_t @@ -254,132 +268,122 @@ index b73a0364..3fd0949f 100644 +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_instruction_schedule(wasm_exec_env_t exec_env, + int64_t const *instructions_schedule); -+ + /** * Dump runtime memory consumption, including: - * Exec env memory consumption diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c -index 41ac4c72..bd8f714a 100644 +index 1e98b0fa..ae24ff8b 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c -@@ -1516,10 +1516,13 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, - } \ - os_mutex_unlock(&exec_env->wait_lock); \ - } \ -+ CHECK_INSTRUCTION_LIMIT(); \ - goto *handle_table[*frame_ip++]; \ - } while (0) - #else --#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH() -+#define HANDLE_OP_END() \ -+ CHECK_INSTRUCTION_LIMIT(); \ -+ FETCH_OPCODE_AND_DISPATCH() - #endif - - #else /* else of WASM_ENABLE_LABELS_AS_VALUES */ -@@ -1542,9 +1545,12 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, - } \ - os_mutex_unlock(&exec_env->wait_lock); \ - } \ -+ CHECK_INSTRUCTION_LIMIT(); \ - continue; - #else --#define HANDLE_OP_END() continue -+#define HANDLE_OP_END() \ -+ CHECK_INSTRUCTION_LIMIT(); \ -+ continue; - #endif - - #endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ -@@ -1562,6 +1568,20 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global) - #endif +@@ -1569,13 +1569,14 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global) } -+#if WASM_ENABLE_INSTRUCTION_METERING != 0 -+#define CHECK_INSTRUCTION_LIMIT() \ -+ if (instructions_to_execute >= 0) \ -+ { \ -+ instructions_to_execute -= instructions_schedule[opcode];\ -+ if (instructions_to_execute < 0) { \ -+ wasm_set_exception(module, "instruction limit exceeded"); \ -+ goto got_exception; \ -+ } \ -+ } -+#else -+#define CHECK_INSTRUCTION_LIMIT() (void)0 -+#endif -+ - static void - wasm_interp_call_func_bytecode(WASMModuleInstance *module, - WASMExecEnv *exec_env, -@@ -1605,6 +1625,17 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, - uint32 local_idx, local_offset, global_idx; - uint8 local_type, *global_addr; - uint32 cache_index, type_index, param_cell_num, cell_num; -+ -+#if WASM_ENABLE_INSTRUCTION_METERING != 0 -+ int64 instructions_to_execute = -1; -+ int64 const *instructions_schedule = NULL; -+ if(exec_env) -+ { -+ instructions_to_execute = exec_env->instructions_to_execute; -+ instructions_schedule = exec_env->instructions_schedule; -+ } -+#endif -+ - #if WASM_ENABLE_EXCE_HANDLING != 0 - int32_t exception_tag_index; + #if WASM_ENABLE_INSTRUCTION_METERING != 0 +-#define CHECK_INSTRUCTION_LIMIT() \ +- if (instructions_left == 0) { \ +- wasm_set_exception(module, "instruction limit exceeded"); \ +- goto got_exception; \ +- } \ +- else if (instructions_left > 0) \ +- instructions_left--; ++#define CHECK_INSTRUCTION_LIMIT() \ ++ do { \ ++ instructions_left -= instructions_schedule[opcode]; \ ++ if (instructions_left < 0) { \ ++ wasm_set_exception(module, "instruction limit exceeded"); \ ++ goto got_exception; \ ++ } \ ++ } while (0) + #else + #define CHECK_INSTRUCTION_LIMIT() (void)0 #endif -@@ -6859,6 +6890,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, +@@ -1625,9 +1626,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, + uint32 cache_index, type_index, param_cell_num, cell_num; + + #if WASM_ENABLE_INSTRUCTION_METERING != 0 +- int instructions_left = -1; ++ int64 instructions_left = INT64_MAX; ++ int64 const *instructions_schedule = NULL; + if (exec_env) { + instructions_left = exec_env->instructions_to_execute; ++ instructions_schedule = exec_env->instructions_schedule; + } + #endif + +@@ -6885,6 +6888,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, FREE_FRAME(exec_env, frame); wasm_exec_env_set_cur_frame(exec_env, prev_frame); +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + if(exec_env) -+ exec_env->instructions_to_execute = instructions_to_execute; ++ exec_env->instructions_to_execute = instructions_left; +#endif + if (!prev_frame->ip) { /* Called from native. */ return; -@@ -6899,6 +6935,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, +@@ -6925,6 +6933,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, } #endif SYNC_ALL_TO_FRAME(); + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + if(exec_env) -+ exec_env->instructions_to_execute = instructions_to_execute; ++ exec_env->instructions_to_execute = instructions_left; +#endif + return; #if WASM_ENABLE_LABELS_AS_VALUES == 0 diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c -index f33ad60e..9cbf2010 100644 +index 4e5edf41..04c39e1f 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c -@@ -105,6 +105,19 @@ typedef float64 CellType_F64; - goto unaligned_atomic; \ +@@ -106,14 +106,14 @@ typedef float64 CellType_F64; } while (0) -+#if WASM_ENABLE_INSTRUCTION_METERING != 0 + #if WASM_ENABLE_INSTRUCTION_METERING != 0 +-#define CHECK_INSTRUCTION_LIMIT() \ +- if (instructions_left == 0) { \ +- wasm_set_exception(module, "instruction limit exceeded"); \ +- goto got_exception; \ +- } \ +- else if (instructions_left > 0) \ +- instructions_left--; +- +#define CHECK_INSTRUCTION_LIMIT() \ -+ if (instructions_to_execute >= 0) { \ -+ instructions_to_execute -= instructions_schedule[opcode]; \ -+ if (instructions_to_execute < 0) { \ ++ do { \ ++ instructions_left -= instructions_schedule[opcode]; \ ++ if (instructions_left < 0) { \ + wasm_set_exception(module, "instruction limit exceeded"); \ + goto got_exception; \ + } \ -+ } -+#else -+#define CHECK_INSTRUCTION_LIMIT() (void)0 -+#endif -+ - static inline uint32 - rotl32(uint32 n, uint32 c) - { -@@ -1466,12 +1479,14 @@ wasm_interp_dump_op_count() ++ } while (0) + #else + #define CHECK_INSTRUCTION_LIMIT() (void)0 + #endif +@@ -1454,7 +1454,6 @@ wasm_interp_dump_op_count() + do { \ + const void *p_label_addr = *(void **)frame_ip; \ + frame_ip += sizeof(void *); \ +- CHECK_INSTRUCTION_LIMIT(); \ + goto *p_label_addr; \ + } while (0) + #else +@@ -1466,7 +1465,6 @@ wasm_interp_dump_op_count() + /* int32 relative offset was emitted in 64-bit target */ \ + p_label_addr = label_base + (int32)LOAD_U32_WITH_2U16S(frame_ip); \ + frame_ip += sizeof(int32); \ +- CHECK_INSTRUCTION_LIMIT(); \ + goto *p_label_addr; \ + } while (0) + #else +@@ -1477,17 +1475,18 @@ wasm_interp_dump_op_count() + /* uint32 label address was emitted in 32-bit target */ \ + p_label_addr = (void *)(uintptr_t)LOAD_U32_WITH_2U16S(frame_ip); \ + frame_ip += sizeof(int32); \ +- CHECK_INSTRUCTION_LIMIT(); \ + goto *p_label_addr; \ } while (0) #endif #endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ @@ -396,46 +400,91 @@ index f33ad60e..9cbf2010 100644 #endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ -@@ -1538,6 +1553,16 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, - uint8 *maddr = NULL; - uint32 local_idx, local_offset, global_idx; +@@ -1508,6 +1507,25 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global) + #endif + } + ++static int64 const def_instructions_schedule[256] = { ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ++ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ++}; ++ + static void + wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, +@@ -1556,9 +1574,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint8 opcode = 0, local_type, *global_addr; -+ -+#if WASM_ENABLE_INSTRUCTION_METERING != 0 -+ int64 instructions_to_execute = -1; -+ int64 const *instructions_schedule = NULL; -+ if (exec_env) { -+ instructions_to_execute = exec_env->instructions_to_execute; + + #if WASM_ENABLE_INSTRUCTION_METERING != 0 +- int instructions_left = -1; ++ int64 instructions_left = INT64_MAX; ++ int64 const *instructions_schedule = def_instructions_schedule; + if (exec_env) { + instructions_left = exec_env->instructions_to_execute; + instructions_schedule = exec_env->instructions_schedule; -+ } -+#endif -+ + } + #endif #if !defined(OS_ENABLE_HW_BOUND_CHECK) \ - || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 - #if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 -@@ -7761,6 +7786,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, +@@ -7694,6 +7714,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, + { + wasm_interp_call_func_native(module, exec_env, cur_func, + prev_frame); ++ instructions_left -= cur_func->gas; + } + + #if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 +@@ -7806,6 +7827,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, FREE_FRAME(exec_env, frame); wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame *)prev_frame); +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + if (exec_env) -+ exec_env->instructions_to_execute = instructions_to_execute; ++ exec_env->instructions_to_execute = instructions_left; +#endif + if (!prev_frame->ip) /* Called from native. */ return; -@@ -7789,6 +7819,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, +@@ -7834,6 +7860,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, got_exception: SYNC_ALL_TO_FRAME(); +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + if (exec_env) -+ exec_env->instructions_to_execute = instructions_to_execute; ++ exec_env->instructions_to_execute = instructions_left; +#endif return; #if WASM_ENABLE_LABELS_AS_VALUES == 0 +diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h +index 8d38c883..a687ab89 100644 +--- a/core/iwasm/interpreter/wasm_runtime.h ++++ b/core/iwasm/interpreter/wasm_runtime.h +@@ -228,6 +228,10 @@ struct WASMFunctionInstance { + WASMFunctionImport *func_import; + WASMFunction *func; + } u; ++ ++ // gas cost for import func ++ uint32 gas; ++ + #if WASM_ENABLE_MULTI_MODULE != 0 + WASMModuleInstance *import_module_inst; + WASMFunctionInstance *import_func_inst; diff --git a/core/shared/platform/include/platform_wasi_types.h b/core/shared/platform/include/platform_wasi_types.h index ac1a95ea..e23b500e 100644 --- a/core/shared/platform/include/platform_wasi_types.h @@ -451,20 +500,5 @@ index ac1a95ea..e23b500e 100644 + #define assert_wasi_layout(expr, message) static_assert(expr, message) + #endif #endif - + assert_wasi_layout(_Alignof(int8_t) == 1, "non-wasi data layout"); -diff --git a/doc/build_wamr.md b/doc/build_wamr.md -index 6425450b..94dd9628 100644 ---- a/doc/build_wamr.md -+++ b/doc/build_wamr.md -@@ -327,6 +327,10 @@ And the wasm app can calls below APIs to allocate/free memory from/to the shared - - **WAMR_BUILD_SHRUNK_MEMORY**=1/0, default to enable if not set - > Note: When enabled, this feature will reduce memory usage by decreasing the size of the linear memory, particularly when the `memory.grow` opcode is not used and memory usage is somewhat predictable. - -+## **Instruction metering** -+- **WAMR_BUILD_INSTRUCTION_METERING**=1/0, default to disable if not set -+> Note: Enabling this feature allows limiting the number of instructions a wasm module instance can execute. Use the `wasm_runtime_set_instruction_count_limit(...)` API before calling `wasm_runtime_call_*(...)` APIs to enforce this limit. -+ - ## **Combination of configurations:** - - We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command: diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp index 3156a98a86..c5bd2278df 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #ifdef _DEBUG // #define DEBUG_OUTPUT 1 @@ -71,97 +71,6 @@ getLedgerSqn_wrap(void* env, wasm_val_vec_t const*, wasm_val_vec_t* results) return nullptr; } -struct TestHostFunctionsOld : public HostFunctions -{ - test::jtx::Env* env_; - Bytes accountID_; - Bytes data_; - int clock_drift_ = 0; - test::StreamSink sink_; - beast::Journal jlog_; - void const* rt_ = nullptr; - -public: - explicit TestHostFunctionsOld(test::jtx::Env* env, int cd = 0) - : env_(env) - , clock_drift_(cd) - , sink_(beast::severities::kDebug) - , jlog_(sink_) - { - std::string s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; - accountID_ = Bytes{s.begin(), s.end()}; - std::string t = "10000"; - data_ = Bytes{t.begin(), t.end()}; - } - - virtual void - setRT(void const* rt) override - { - rt_ = rt; - } - - virtual void const* - getRT() const override - { - return rt_; - } - - test::StreamSink& - getSink() - { - return sink_; - } - - beast::Journal - getJournal() override - { - return jlog_; - } - - int32_t - getLedgerSqn() override - { - return (int32_t)env_->current()->seq(); - } - - int32_t - getParentLedgerTime() override - { - return env_->current()->parentCloseTime().time_since_epoch().count() + - clock_drift_; - } - - Expected - getTxField(SField const& fname) override - { - return accountID_; - } - - Expected - getLedgerObjField(int32_t cacheIdx, SField const& fname) override - { - return data_; - } - - Expected - getCurrentLedgerObjField(SField const& fname) override - { - if (fname.getName() == "Destination" || fname.getName() == "Account") - return accountID_; - else if (fname.getName() == "Data") - return data_; - else if (fname.getName() == "FinishAfter") - { - auto t = - env_->current()->parentCloseTime().time_since_epoch().count(); - std::string s = std::to_string(t); - return Bytes{s.begin(), s.end()}; - } - - return Unexpected(-1); - } -}; - struct TestHostFunctions : public HostFunctions { test::jtx::Env& env_; @@ -171,7 +80,7 @@ struct TestHostFunctions : public HostFunctions void const* rt_ = nullptr; public: - explicit TestHostFunctions(test::jtx::Env& env, int cd = 0) + TestHostFunctions(test::jtx::Env& env, int cd = 0) : env_(env), clock_drift_(cd) { auto opt = parseBase58("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); @@ -212,6 +121,12 @@ public: clock_drift_; } + Hash + getParentLedgerHash() override + { + return env_.current()->info().parentHash; + } + virtual int32_t cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx) override { @@ -238,29 +153,6 @@ public: return Bytes(); } - Expected - getTxNestedField(Slice const& locator) override - { - uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41, - 0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34, - 0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee, - 0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00}; - return Bytes(&a[0], &a[sizeof(a)]); - } - - Expected - getLedgerObjField(int32_t cacheIdx, SField const& fname) override - { - // auto const& sn = fname.getName(); - if (fname == sfBalance) - { - int64_t x = 10'000; - uint8_t const* p = reinterpret_cast(&x); - return Bytes{p, p + sizeof(x)}; - } - return data_; - } - Expected getCurrentLedgerObjField(SField const& fname) override { @@ -280,24 +172,97 @@ public: return Unexpected(-1); } + Expected + getLedgerObjField(int32_t cacheIdx, SField const& fname) override + { + // auto const& sn = fname.getName(); + if (fname == sfBalance) + { + int64_t x = 10'000; + uint8_t const* p = reinterpret_cast(&x); + return Bytes{p, p + sizeof(x)}; + } + return data_; + } + + Expected + getTxNestedField(Slice const& locator) override + { + uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41, + 0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34, + 0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee, + 0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00}; + return Bytes(&a[0], &a[sizeof(a)]); + } + + Expected + getCurrentLedgerObjNestedField(Slice const& locator) override + { + uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41, + 0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34, + 0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee, + 0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00}; + return Bytes(&a[0], &a[sizeof(a)]); + } + + Expected + getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override + { + uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41, + 0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34, + 0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee, + 0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00}; + return Bytes(&a[0], &a[sizeof(a)]); + } + int32_t getTxArrayLen(SField const& fname) override { return 32; } + int32_t + getCurrentLedgerObjArrayLen(SField const& fname) override + { + return 32; + } + + int32_t + getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override + { + return 32; + } + int32_t getTxNestedArrayLen(Slice const& locator) override { return 32; } + int32_t + getCurrentLedgerObjNestedArrayLen(Slice const& locator) override + { + return 32; + } + + int32_t + getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override + { + return 32; + } + int32_t updateData(Bytes const& data) override { return 0; } + Hash + computeSha512HalfHash(Bytes const& data) override + { + return env_.current()->info().parentHash; + } + Expected accountKeylet(AccountID const& account) override { @@ -307,6 +272,49 @@ public: return Bytes{keylet.key.begin(), keylet.key.end()}; } + Expected + credentialKeylet( + AccountID const& subject, + AccountID const& issuer, + Bytes const& credentialType) override + { + if (!subject || !issuer || credentialType.empty()) + return Unexpected(HF_ERR_INVALID_ACCOUNT); + auto const keylet = + keylet::credential(subject, issuer, makeSlice(credentialType)); + return Bytes{keylet.key.begin(), keylet.key.end()}; + } + + Expected + escrowKeylet(AccountID const& account, std::uint32_t seq) override + { + if (!account) + return Unexpected(HF_ERR_INVALID_ACCOUNT); + auto const keylet = keylet::escrow(account, seq); + return Bytes{keylet.key.begin(), keylet.key.end()}; + } + + Expected + oracleKeylet(AccountID const& account, std::uint32_t documentId) override + { + if (!account) + return Unexpected(HF_ERR_INVALID_ACCOUNT); + auto const keylet = keylet::oracle(account, documentId); + return Bytes{keylet.key.begin(), keylet.key.end()}; + } + + Expected + getNFT(AccountID const& account, uint256 const& nftId) override + { + if (!account || !nftId) + { + return Unexpected(HF_ERR_INVALID_PARAMS); + } + + std::string s = "https://ripple.com"; + return Bytes(s.begin(), s.end()); + } + int32_t trace(std::string const& msg, Bytes const& data, bool asHex) override { @@ -350,6 +358,33 @@ public: } }; +struct TestHostFunctionsSink : public TestHostFunctions +{ + test::StreamSink sink_; + beast::Journal jlog_; + void const* rt_ = nullptr; + +public: + explicit TestHostFunctionsSink(test::jtx::Env& env, int cd = 0) + : TestHostFunctions(env, cd) + , sink_(beast::severities::kDebug) + , jlog_(sink_) + { + } + + test::StreamSink& + getSink() + { + return sink_; + } + + beast::Journal + getJournal() override + { + return jlog_; + } +}; + struct Wasm_test : public beast::unit_test::suite { void @@ -361,9 +396,9 @@ struct Wasm_test : public beast::unit_test::suite Bytes const wasm(ws.begin(), ws.end()); auto& engine = WasmEngine::instance(); - auto const r = engine.run(wasm, "fib", wasmParams(10)); + auto const re = engine.run(wasm, "fib", wasmParams(10)); - BEAST_EXPECT(r.has_value() && (r->result == 55)); + BEAST_EXPECT(re.has_value() && (re->result == 55) && (re->cost == 755)); } void @@ -375,10 +410,11 @@ struct Wasm_test : public beast::unit_test::suite Bytes const wasm(ws.begin(), ws.end()); auto& engine = WasmEngine::instance(); - auto const r = + auto const re = engine.run(wasm, "sha512_process", wasmParams(sha512PureHex)); - BEAST_EXPECT(r.has_value() && (r->result == 34432)); + BEAST_EXPECT( + re.has_value() && (re->result == 34432) && (re->cost == 157'452)); } void @@ -396,9 +432,9 @@ struct Wasm_test : public beast::unit_test::suite static_cast(512), static_cast(b58Hex.size())); auto const s = std::string_view(b58Hex.c_str(), minsz); - auto const r = engine.run(wasm, "b58enco", wasmParams(outb, s)); + auto const re = engine.run(wasm, "b58enco", wasmParams(outb, s)); - BEAST_EXPECT(r.has_value() && r->result); + BEAST_EXPECT(re.has_value() && re->result && (re->cost == 3'066'129)); } void @@ -409,9 +445,10 @@ struct Wasm_test : public beast::unit_test::suite Bytes const wasm(ws.begin(), ws.end()); auto& engine = WasmEngine::instance(); - auto const r = engine.run(wasm, "sp1_groth16_verifier"); + auto const re = engine.run(wasm, "sp1_groth16_verifier"); - BEAST_EXPECT(r.has_value() && r->result); + BEAST_EXPECT( + re.has_value() && re->result && (re->cost == 4'191'711'969ll)); } void @@ -422,9 +459,9 @@ struct Wasm_test : public beast::unit_test::suite Bytes const wasm(ws.begin(), ws.end()); auto& engine = WasmEngine::instance(); - auto const r = engine.run(wasm, "bellman_groth16_test"); + auto const re = engine.run(wasm, "bellman_groth16_test"); - BEAST_EXPECT(r.has_value() && r->result); + BEAST_EXPECT(re.has_value() && re->result && (re->cost == 332'205'984)); } void @@ -442,24 +479,29 @@ struct Wasm_test : public beast::unit_test::suite std::string const funcName("finish"); std::vector imports; - WASM_IMPORT_FUNC(imports, getLedgerSqn, &ledgerDataProvider); + WASM_IMPORT_FUNC(imports, getLedgerSqn, &ledgerDataProvider, 33); auto& engine = WasmEngine::instance(); - auto r = engine.run( + auto re = engine.run( wasm, funcName, {}, imports, nullptr, 1'000'000, env.journal); - if (BEAST_EXPECT(r.has_value())) - BEAST_EXPECT(!r->result); + + // code takes 4 gas + 1 getLedgerSqn call + if (BEAST_EXPECT(re.has_value())) + BEAST_EXPECT(!re->result && (re->cost == 37)); + env.close(); env.close(); env.close(); env.close(); - // empty module - run the same module - r = engine.run( + // empty module - run the same instance + re = engine.run( {}, funcName, {}, imports, nullptr, 1'000'000, env.journal); - if (BEAST_EXPECT(r.has_value())) - BEAST_EXPECT(r->result); + + // code takes 8 gas + 2 getLedgerSqn calls + if (BEAST_EXPECT(re.has_value())) + BEAST_EXPECT(re->result && (re->cost == 74)); } void @@ -479,7 +521,7 @@ struct Wasm_test : public beast::unit_test::suite auto re = runEscrowWasm( wasm, funcName, wasmParams(data), nullptr, -1, env.journal); if (BEAST_EXPECT(re.has_value())) - BEAST_EXPECT(re.value().result); + BEAST_EXPECT(re.value().result && (re->cost == 838)); } { std::string str = "rHb9CJAWyB4rj91VRWn96DkukG4bwdty00"; @@ -487,7 +529,7 @@ struct Wasm_test : public beast::unit_test::suite auto re = runEscrowWasm( wasm, funcName, wasmParams(data), nullptr, -1, env.journal); if (BEAST_EXPECT(re.has_value())) - BEAST_EXPECT(!re.value().result); + BEAST_EXPECT(!re.value().result && (re->cost == 822)); } } @@ -513,7 +555,7 @@ struct Wasm_test : public beast::unit_test::suite -1, env.journal); if (BEAST_EXPECT(re.has_value())) - BEAST_EXPECT(re.value().result); + BEAST_EXPECT(re.value().result && (re->cost == 42'212)); std::vector const lo_data2(lo_js2.begin(), lo_js2.end()); re = runEscrowWasm( @@ -524,7 +566,7 @@ struct Wasm_test : public beast::unit_test::suite -1, env.journal); if (BEAST_EXPECT(re.has_value())) - BEAST_EXPECT(!re.value().result); + BEAST_EXPECT(!re.value().result && (re->cost == 41'496)); } void @@ -566,11 +608,11 @@ struct Wasm_test : public beast::unit_test::suite WasmImpFunc( imports, "func-add", reinterpret_cast(&Add)); - auto res = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports); + auto re = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports); // if (res) printf("invokeAdd get the result: %d\n", res.value()); - BEAST_EXPECT(res.has_value() && res->result == 6912); + BEAST_EXPECT(re.has_value() && re->result == 6912 && (re->cost == 2)); } void @@ -587,7 +629,7 @@ struct Wasm_test : public beast::unit_test::suite std::vector wasm(wasmStr.begin(), wasmStr.end()); std::string funcName("mock_escrow"); auto re = runEscrowWasm(wasm, funcName, {}, &hfs, 15, env.journal); - BEAST_EXPECT(re.error()); + BEAST_EXPECT(!re && re.error()); } void @@ -616,10 +658,10 @@ struct Wasm_test : public beast::unit_test::suite { TestHostFunctions nfs(env, 0); std::string funcName("finish"); - auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000); + auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000); if (BEAST_EXPECT(re.has_value())) { - BEAST_EXPECT(re.value().result); + BEAST_EXPECT(!re->result && (re->cost == 487)); // std::cout << "good case result " << re.value().result // << " cost: " << re.value().cost << std::endl; } @@ -636,7 +678,7 @@ struct Wasm_test : public beast::unit_test::suite auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000); if (BEAST_EXPECT(re.has_value())) { - BEAST_EXPECT(!re.value().result); + BEAST_EXPECT(!re->result && (re->cost == 487)); // std::cout << "bad case (current time < escrow_finish_after " // "time) result " // << re.value().result << " cost: " << @@ -654,13 +696,13 @@ struct Wasm_test : public beast::unit_test::suite Expected getTxField(SField const& fname) override { - return Unexpected(-1); + return Unexpected(HF_ERR_FIELD_NOT_FOUND); } }; BadTestHostFunctions nfs(env); std::string funcName("finish"); auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000); - BEAST_EXPECT(re.error()); + BEAST_EXPECT(re.has_value() && !re->result && (re->cost == 23)); // std::cout << "bad case (access nonexistent field) result " // << re.error() << std::endl; } @@ -680,7 +722,7 @@ struct Wasm_test : public beast::unit_test::suite BadTestHostFunctions nfs(env); std::string funcName("finish"); auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000); - BEAST_EXPECT(!re); + BEAST_EXPECT(re.has_value() && !re->result && (re->cost == 23)); // std::cout << "bad case (more than MAX_PAGES) result " // << re.error() << std::endl; } @@ -690,10 +732,10 @@ struct Wasm_test : public beast::unit_test::suite auto wasmStr = boost::algorithm::unhex(std::string(wasmHex)); std::vector wasm(wasmStr.begin(), wasmStr.end()); - TestHostFunctionsOld nfs(&env); + TestHostFunctionsSink nfs(env); std::string funcName("recursive"); auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 1000'000'000); - BEAST_EXPECT(re.error()); + BEAST_EXPECT(!re && re.error()); // std::cout << "bad case (deep recursion) result " << re.error() // << std::endl; @@ -712,13 +754,32 @@ struct Wasm_test : public beast::unit_test::suite BEAST_EXPECT( countSubstr( - sink.messages().str(), "WAMR error: failed to call func") == + sink.messages().str(), "WAMR Error: failed to call func") == 1); BEAST_EXPECT( countSubstr( sink.messages().str(), "WAMR Exception: wasm operand stack overflow") == 1); } + + { + auto wasmStr = boost::algorithm::unhex(ledgerSqnHex); + Bytes wasm(wasmStr.begin(), wasmStr.end()); + std::string const funcName("finish"); + TestLedgerDataProvider ledgerDataProvider(&env); + + std::vector imports; + WASM_IMPORT_FUNC2( + imports, getLedgerSqn, "get_ledger_sqn2", &ledgerDataProvider); + + auto& engine = WasmEngine::instance(); + + auto re = engine.run( + wasm, funcName, {}, imports, nullptr, 1'000'000, env.journal); + + // expected import not provided + BEAST_EXPECT(!re); + } } void @@ -741,7 +802,7 @@ struct Wasm_test : public beast::unit_test::suite auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000); if (BEAST_EXPECT(re.has_value())) { - BEAST_EXPECT(re->result); + BEAST_EXPECT(re->result && (re->cost == 80)); // std::cout << "good case result " << re.value().result // << " cost: " << re.value().cost << std::endl; } @@ -763,7 +824,7 @@ struct Wasm_test : public beast::unit_test::suite auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000); if (BEAST_EXPECT(re.has_value())) { - BEAST_EXPECT(re->result); + BEAST_EXPECT(re->result && (re->cost == 138)); // std::cout << "good case result " << re.value().result // << " cost: " << re.value().cost << std::endl; } @@ -787,10 +848,10 @@ struct Wasm_test : public beast::unit_test::suite // runing too long // testWasmSP1Verifier(); - // testWasmBG16Verifier(); + testWasmBG16Verifier(); - // TODO: needs fix for new host functions interface - // testEscrowWasmDN1(); + // TODO: fix result + testEscrowWasmDN1(); testEscrowWasmDN2(); } }; diff --git a/src/xrpld/app/misc/WamrVM.cpp b/src/xrpld/app/misc/WamrVM.cpp index 8f7c9b1c36..b44c4bac2e 100644 --- a/src/xrpld/app/misc/WamrVM.cpp +++ b/src/xrpld/app/misc/WamrVM.cpp @@ -529,6 +529,15 @@ ModuleWrapper::buildImports( reinterpret_cast(imp.wrap), imp.udata, nullptr); + if (!func) + throw std::runtime_error( + "can't create import function " + + imp.name); // LCOV_EXCL_LINE + + if (imp.gas && !wasm_func_set_gas(func, imp.gas)) + throw std::runtime_error( + "can't set gas for import function " + + imp.name); // LCOV_EXCL_LINE wimports.data[i] = wasm_func_as_extern(func); ++impCnt; @@ -862,6 +871,10 @@ WamrEngine::runHlp( // auto j = j_.debug(); // #endif + // currently only 1 module support, possible parallel UT run + static std::mutex m; + std::lock_guard lg(m); + // Create and instantiate the module. if (!wasmCode.empty()) { @@ -894,7 +907,8 @@ WamrEngine::runHlp( } assert(res.r.data[0].kind == WASM_I32); - + if (gas == -1) + gas = std::numeric_limits::max(); WasmResult const ret{res.r.data[0].of.i32, gas - module->getGas()}; // j << "WAMR Res: " << ret.result << " cost: " << ret.cost << std::endl; diff --git a/src/xrpld/app/misc/WamrVM.h b/src/xrpld/app/misc/WamrVM.h index 635673fb9b..57c65504e1 100644 --- a/src/xrpld/app/misc/WamrVM.h +++ b/src/xrpld/app/misc/WamrVM.h @@ -20,8 +20,8 @@ #include -#include -#include +#include +#include namespace ripple { diff --git a/src/xrpld/app/misc/WasmParamsHelper.h b/src/xrpld/app/misc/WasmParamsHelper.h index af2db08ebc..224d7431d3 100644 --- a/src/xrpld/app/misc/WasmParamsHelper.h +++ b/src/xrpld/app/misc/WasmParamsHelper.h @@ -62,6 +62,7 @@ struct WasmImportFunc void* udata = nullptr; // wasm_func_callback_with_env_t void* wrap = nullptr; + uint32_t gas = 0; }; #define WASM_IMPORT_FUNC(v, f, ...) \ @@ -139,12 +140,14 @@ WasmImpFunc( std::vector& v, std::string_view imp_name, void* f_wrap, - void* data = nullptr) + void* data = nullptr, + uint32_t gas = 0) { WasmImportFunc e; e.name = imp_name; e.udata = data; e.wrap = f_wrap; + e.gas = gas; WasmImpFuncHelper(e); v.push_back(std::move(e)); }