diff --git a/.github/actions/dependencies/action.yml b/.github/actions/dependencies/action.yml index afce1557d3..c251daa936 100644 --- a/.github/actions/dependencies/action.yml +++ b/.github/actions/dependencies/action.yml @@ -17,6 +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@ - name: add Ripple Conan remote shell: bash run: | diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index d9a67a63e9..2ea9b32544 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -351,6 +351,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@ - name: build dependencies run: | diff --git a/BUILD.md b/BUILD.md index fd985dce81..d348f2d670 100644 --- a/BUILD.md +++ b/BUILD.md @@ -204,6 +204,17 @@ It fixes some source files to add missing `#include`s. conan export --version 2.0.8 external/nudb ``` +Export our [Conan recipe for WAMR](./external/wamr). +It add metering and expose some internal structures. + + + ``` + # Conan 1.x + conan export external/wamr wamr/2.2.0@ + # Conan 2.x + conan export --version 2.2.0 external/wamr + ``` + ### Build and Test 1. Create a build directory and move into it. diff --git a/CMakeLists.txt b/CMakeLists.txt index 28cfd2322a..702cb41a0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,7 +103,7 @@ endif() find_package(nudb REQUIRED) find_package(date REQUIRED) find_package(xxHash REQUIRED) -find_package(wasmedge REQUIRED) +find_package(wamr REQUIRED) target_link_libraries(ripple_libs INTERFACE ed25519::ed25519 diff --git a/cmake/RippledCore.cmake b/cmake/RippledCore.cmake index d4ff75434e..ec56323926 100644 --- a/cmake/RippledCore.cmake +++ b/cmake/RippledCore.cmake @@ -65,7 +65,7 @@ target_link_libraries(xrpl.imports.main xrpl.libpb xxHash::xxhash $<$:antithesis-sdk-cpp> - wasmedge::wasmedge + wamr::wamr ) include(add_module) diff --git a/conanfile.py b/conanfile.py index d49f45903e..a0b1602daa 100644 --- a/conanfile.py +++ b/conanfile.py @@ -33,7 +33,7 @@ class Xrpl(ConanFile): 'soci/4.0.3', 'xxhash/0.8.2', 'zlib/1.3.1', - 'wasmedge/0.14.1', + 'wamr/2.2.0', ] tool_requires = [ diff --git a/external/wamr/conandata.yml b/external/wamr/conandata.yml new file mode 100644 index 0000000000..6c49946eca --- /dev/null +++ b/external/wamr/conandata.yml @@ -0,0 +1,6 @@ +patches: + 2.2.0: + - 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 new file mode 100644 index 0000000000..d54bb6f98f --- /dev/null +++ b/external/wamr/conanfile.py @@ -0,0 +1,90 @@ +from conans import ConanFile, tools +from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout +from conan.tools.files import apply_conandata_patches, export_conandata_patches, get, copy +import os +import json + +required_conan_version = ">=1.55.0" + +class WamrConan(ConanFile): + name = "wamr" + version = "2.2.0" + license = "Apache License v2.0" + url = "https://github.com/bytecodealliance/wasm-micro-runtime.git" + description = "Webassembly micro runtime" + package_type = "library" + settings = "os", "compiler", "build_type", "arch" + options = {"shared": [True, False], "fPIC": [True, False]} + default_options = {"shared": False, "fPIC": True} + generators = "CMakeToolchain", "CMakeDeps" + #requires = [("llvm/20.1.1@")] + + def export_sources(self): + export_conandata_patches(self) + pass + + + #def build_requirements(self): + # self.tool_requires("llvm/20.1.1") + + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + + def layout(self): + cmake_layout(self, src_folder="src") + + + def source(self): + git = tools.Git() + git.clone("https://github.com/bytecodealliance/wasm-micro-runtime.git", "c883fafead005e87ad3122b05409886f507c1cb0",shallow=True) + #get(self, **self.conan_data["sources"][self.version], strip_root=True) + + + def generate(self): + tc = CMakeToolchain(self) + + tc.variables["WAMR_BUILD_INTERP"] = 1 + tc.variables["WAMR_BUILD_FAST_INTERP"] = 1 + tc.variables["WAMR_BUILD_INSTRUCTION_METERING"] = 1 + tc.variables["WAMR_BUILD_AOT"] = 0 + tc.variables["WAMR_BUILD_JIT"] = 0 + tc.variables["WAMR_BUILD_FAST_JIT"] = 0 + tc.variables["WAMR_DISABLE_HW_BOUND_CHECK"] = 1 + tc.variables["WAMR_DISABLE_STACK_HW_BOUND_CHECK"] = 1 + #tc.variables["WAMR_BUILD_FAST_JIT"] = 0 if self.settings.os == "Windows" else 1 + #ll_dep = self.dependencies["llvm"] + #self.output.info(f"-----------package_folder: {type(ll_dep.__dict__)}") + #tc.variables["LLVM_DIR"] = os.path.join(ll_dep.package_folder, "lib", "cmake", "llvm") + tc.generate() + + # This generates "foo-config.cmake" and "bar-config.cmake" in self.generators_folder + deps = CMakeDeps(self) + deps.generate() + + + def build(self): + apply_conandata_patches(self) + cmake = CMake(self) + cmake.verbose = True + cmake.configure() + cmake.build() + #self.run(f'echo {self.source_folder}') + # Explicit way: + # self.run('cmake %s/hello %s' % (self.source_folder, cmake.command_line)) + # self.run("cmake --build . %s" % cmake.build_config) + + + def package(self): + cmake = CMake(self) + cmake.verbose = True + cmake.install() + + + def package_info(self): + 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 new file mode 100644 index 0000000000..7cc3544b68 --- /dev/null +++ b/external/wamr/patches/ripp_metering.patch @@ -0,0 +1,417 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 551991f8..5f48a0b8 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,7 +1,7 @@ + # Copyright (C) 2019 Intel Corporation. All rights reserved. + # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +-cmake_minimum_required (VERSION 3.14) ++cmake_minimum_required (VERSION 3.20) + + 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 +--- 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, + return -1.0; + #endif + } ++ ++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_exec_env.c b/core/iwasm/common/wasm_exec_env.c +index e33fd9f3..d1ff9c41 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); + #endif + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ exec_env->instructions_to_execute = -1; ++ 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..900fb536 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, ++ int64 instructions_to_execute) ++{ ++ exec_env->instructions_to_execute = instructions_to_execute; ++} ++ ++int64 ++wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env) ++{ ++ return exec_env->instructions_to_execute; ++} ++ ++void ++wasm_runtime_set_instruction_schedule(WASMExecEnv *exec_env, ++ int64 const *instructions_schedule) ++{ ++ for(int i = 0; i < 256; ++i) ++ exec_env->instructions_schedule[i] = instructions_schedule[i]; ++} ++ ++#endif ++ + WASMFuncType * + wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function, + uint32 module_type) +@@ -7803,7 +7828,7 @@ wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env) + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + boundary = boundary + page_size * guard_page_count; + #endif +- if ((uint8 *)&boundary < boundary) { ++ if (((uint8 *)&boundary < boundary) && ((uint8 *)&boundary > exec_env->native_stack_boundary)){ + wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), + "native stack overflow"); + return false; +diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h +index 8ac032bf..5ca5d489 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 ++ + /* See wasm_export.h for description */ + WASM_RUNTIME_API_EXTERN void ++wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, ++ int64 instructions_to_execute); ++WASM_RUNTIME_API_EXTERN int64 ++wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env); ++ ++WASM_RUNTIME_API_EXTERN void ++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); + +diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h +index 241a0eec..82e048c0 100644 +--- a/core/iwasm/include/wasm_c_api.h ++++ b/core/iwasm/include/wasm_c_api.h +@@ -701,6 +701,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 *); + ++struct WASMExecEnv; ++typedef struct WASMExecEnv *wasm_exec_env_t; ++ ++WASM_API_EXTERN wasm_exec_env_t wasm_instance_exec_env(const wasm_instance_t*); ++ + /////////////////////////////////////////////////////////////////////////////// + // Convenience + +diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h +index b73a0364..f7e20b24 100644 +--- a/core/iwasm/include/wasm_export.h ++++ b/core/iwasm/include/wasm_export.h +@@ -1821,6 +1821,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, ++ int64_t instruction_count); ++ ++WASM_RUNTIME_API_EXTERN int64_t ++wasm_runtime_get_instruction_count_limit(wasm_exec_env_t exec_env); ++ ++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..1aa1fe1c 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,18 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global) + #endif + } + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++#define CHECK_INSTRUCTION_LIMIT() \ ++ if (instructions_to_execute == 0) { \ ++ wasm_set_exception(module, "instruction limit exceeded"); \ ++ goto got_exception; \ ++ } \ ++ else if (instructions_to_execute > 0) \ ++ instructions_to_execute--; ++#else ++#define CHECK_INSTRUCTION_LIMIT() (void)0 ++#endif ++ + static void + wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, +@@ -1605,6 +1623,13 @@ 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; ++ if(exec_env) ++ instructions_to_execute = exec_env->instructions_to_execute; ++#endif ++ + #if WASM_ENABLE_EXCE_HANDLING != 0 + int32_t exception_tag_index; + #endif +@@ -6859,6 +6884,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; ++#endif ++ + if (!prev_frame->ip) { + /* Called from native. */ + return; +@@ -6899,6 +6929,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; ++#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..a7078fe8 100644 +--- a/core/iwasm/interpreter/wasm_interp_fast.c ++++ b/core/iwasm/interpreter/wasm_interp_fast.c +@@ -105,6 +105,20 @@ typedef float64 CellType_F64; + goto unaligned_atomic; \ + } while (0) + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++#define CHECK_INSTRUCTION_LIMIT() \ ++ if (instructions_to_execute >= 0) \ ++ { \ ++ instructions_to_execute -= instructions_schedule[opc]; \ ++ if (instructions_to_execute < 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) + { +@@ -1439,8 +1453,10 @@ wasm_interp_dump_op_count() + #if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + #define FETCH_OPCODE_AND_DISPATCH() \ + do { \ ++ const uint64 opc = *frame_ip; \ + const void *p_label_addr = *(void **)frame_ip; \ + frame_ip += sizeof(void *); \ ++ CHECK_INSTRUCTION_LIMIT(); \ + goto *p_label_addr; \ + } while (0) + #else +@@ -1450,8 +1466,10 @@ wasm_interp_dump_op_count() + const void *p_label_addr; \ + bh_assert(((uintptr_t)frame_ip & 1) == 0); \ + /* int32 relative offset was emitted in 64-bit target */ \ ++ const uint64 opc = LOAD_U32_WITH_2U16S(frame_ip); \ + 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 +@@ -1460,8 +1478,10 @@ wasm_interp_dump_op_count() + const void *p_label_addr; \ + bh_assert(((uintptr_t)frame_ip & 1) == 0); \ + /* uint32 label address was emitted in 32-bit target */ \ ++ const uint64 opc = LOAD_U32_WITH_2U16S(frame_ip); \ + 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 +@@ -1538,6 +1558,17 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, + uint8 *maddr = NULL; + uint32 local_idx, local_offset, global_idx; + 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; ++ instructions_schedule = exec_env->instructions_schedule; ++ } ++#endif ++ + #if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + #if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 +@@ -7761,6 +7792,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; ++#endif ++ + if (!prev_frame->ip) + /* Called from native. */ + return; +@@ -7789,6 +7825,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; ++#endif + return; + + #if WASM_ENABLE_LABELS_AS_VALUES == 0 +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/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 6b25da6363..75467fc7c8 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -19,6 +19,7 @@ #include +#include #include #include @@ -1921,12 +1922,12 @@ struct Escrow_test : public beast::unit_test::suite { // not enough gas - // This function takes 110 gas + // This function takes 4 gas // In testing, 1 gas costs 1 drop - auto const finishFee = env.current()->fees().base + 108; + auto const finishFee = env.current()->fees().base + 4; env(finish(carol, alice, seq), fee(finishFee), - comp_allowance(108), + comp_allowance(2), ter(tecFAILED_PROCESSING)); } diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp index ce8d836269..a46d71a569 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -21,105 +21,21 @@ #include -#include - -#include +#include namespace ripple { namespace test { /* Host function body definition. */ -WasmEdge_Result -Add(void* Data, - const WasmEdge_CallingFrameContext* CallFrameCxt, - const WasmEdge_Value* In, - WasmEdge_Value* Out) +using Add_proto = int32_t(int32_t, int32_t); +wasm_trap_t* +Add(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results) { - int32_t Val1 = WasmEdge_ValueGetI32(In[0]); - int32_t Val2 = WasmEdge_ValueGetI32(In[1]); + int32_t Val1 = params->data[0].of.i32; + int32_t Val2 = params->data[1].of.i32; // printf("Host function \"Add\": %d + %d\n", Val1, Val2); - Out[0] = WasmEdge_ValueGenI32(Val1 + Val2); - return WasmEdge_Result_Success; -} - -void -invokeAdd() -{ - /* Create the VM context. */ - WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(NULL, NULL); - - // clang-format off - /* The WASM module buffer. */ - uint8_t WASM[] = {/* WASM header */ - 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, - /* Type section */ - 0x01, 0x07, 0x01, - /* function type {i32, i32} -> {i32} */ - 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, - /* Import section */ - 0x02, 0x13, 0x01, - /* module name: "extern" */ - 0x06, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6E, - /* extern name: "func-add" */ - 0x08, 0x66, 0x75, 0x6E, 0x63, 0x2D, 0x61, 0x64, 0x64, - /* import desc: func 0 */ - 0x00, 0x00, - /* Function section */ - 0x03, 0x02, 0x01, 0x00, - /* Export section */ - 0x07, 0x0A, 0x01, - /* export name: "addTwo" */ - 0x06, 0x61, 0x64, 0x64, 0x54, 0x77, 0x6F, - /* export desc: func 0 */ - 0x00, 0x01, - /* Code section */ - 0x0A, 0x0A, 0x01, - /* code body */ - 0x08, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x00, 0x0B}; - // clang-format on - - /* Create the module instance. */ - WasmEdge_String ExportName = WasmEdge_StringCreateByCString("extern"); - WasmEdge_ModuleInstanceContext* HostModCxt = - WasmEdge_ModuleInstanceCreate(ExportName); - WasmEdge_ValType ParamList[2] = { - WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType ReturnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* HostFType = - WasmEdge_FunctionTypeCreate(ParamList, 2, ReturnList, 1); - WasmEdge_FunctionInstanceContext* HostFunc = - WasmEdge_FunctionInstanceCreate(HostFType, Add, NULL, 0); - WasmEdge_FunctionTypeDelete(HostFType); - WasmEdge_String HostFuncName = WasmEdge_StringCreateByCString("func-add"); - WasmEdge_ModuleInstanceAddFunction(HostModCxt, HostFuncName, HostFunc); - WasmEdge_StringDelete(HostFuncName); - - WasmEdge_VMRegisterModuleFromImport(VMCxt, HostModCxt); - - /* The parameters and returns arrays. */ - WasmEdge_Value Params[2] = { - WasmEdge_ValueGenI32(1234), WasmEdge_ValueGenI32(5678)}; - WasmEdge_Value Returns[1]; - /* Function name. */ - WasmEdge_String FuncName = WasmEdge_StringCreateByCString("addTwo"); - /* Run the WASM function from buffer. */ - WasmEdge_Result Res = WasmEdge_VMRunWasmFromBuffer( - VMCxt, WASM, sizeof(WASM), FuncName, Params, 2, Returns, 1); - - if (WasmEdge_ResultOK(Res)) - { - // printf("invokeAdd get the result: %d\n", - // WasmEdge_ValueGetI32(Returns[0])); - } - else - { - printf("Error message: %s\n", WasmEdge_ResultGetMessage(Res)); - } - - /* Resources deallocations. */ - WasmEdge_VMDelete(VMCxt); - WasmEdge_StringDelete(FuncName); - WasmEdge_ModuleInstanceDelete(HostModCxt); + results->data[0] = WASM_I32_VAL(Val1 + Val2); + return nullptr; } struct Wasm_test : public beast::unit_test::suite @@ -128,8 +44,46 @@ struct Wasm_test : public beast::unit_test::suite testWasmtimeLib() { testcase("wasmtime lib test"); - invokeAdd(); - BEAST_EXPECT(true); + // clang-format off + /* The WASM module buffer. */ + wbytes const wasm = {/* WASM header */ + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, + /* Type section */ + 0x01, 0x07, 0x01, + /* function type {i32, i32} -> {i32} */ + 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, + /* Import section */ + 0x02, 0x13, 0x01, + /* module name: "extern" */ + 0x06, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6E, + /* extern name: "func-add" */ + 0x08, 0x66, 0x75, 0x6E, 0x63, 0x2D, 0x61, 0x64, 0x64, + /* import desc: func 0 */ + 0x00, 0x00, + /* Function section */ + 0x03, 0x02, 0x01, 0x00, + /* Export section */ + 0x07, 0x0A, 0x01, + /* export name: "addTwo" */ + 0x06, 0x61, 0x64, 0x64, 0x54, 0x77, 0x6F, + /* export desc: func 0 */ + 0x00, 0x01, + /* Code section */ + 0x0A, 0x0A, 0x01, + /* code body */ + 0x08, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x00, 0x0B}; + // clang-format on + auto& vm = WasmEngine::instance(); + + std::vector imports; + WasmImpFunc( + imports, "func-add", reinterpret_cast(&Add)); + + auto res = vm.run(wasm, "addTwo", imports, wasmParams(1234, 5678)); + + // if (res) printf("invokeAdd get the result: %d\n", res.value()); + + BEAST_EXPECT(res.has_value() && res.value() == 6912); } void @@ -1412,9 +1366,9 @@ struct Wasm_test : public beast::unit_test::suite BadTestHostFunctions nfs(&env); std::string funcName("ready"); auto re = runEscrowWasm(wasm, funcName, &nfs, 100000); - BEAST_EXPECT(re.error()); - std::cout << "bad case (more than MAX_PAGES) result " << re.error() - << std::endl; + if (BEAST_EXPECT(!re)) + std::cout << "bad case (more than MAX_PAGES) result " + << re.error() << std::endl; } { // fail because recursion too deep @@ -2143,9 +2097,9 @@ struct Wasm_test : public beast::unit_test::suite TestHostFunctions nfs(&env); std::string funcName("recursive"); auto re = runEscrowWasm(wasm, funcName, &nfs, 1000'000'000); - BEAST_EXPECT(re.error()); - std::cout << "bad case (deep recursion) result " << re.error() - << std::endl; + if (BEAST_EXPECT(re.error())) + std::cout << "bad case (deep recursion) result " << re.error() + << std::endl; } } diff --git a/src/xrpld/app/misc/WamrVM.cpp b/src/xrpld/app/misc/WamrVM.cpp new file mode 100644 index 0000000000..a92d8e463e --- /dev/null +++ b/src/xrpld/app/misc/WamrVM.cpp @@ -0,0 +1,754 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2020 Ripple Labs Inc. + + 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 + +#include + +namespace ripple { + +////////////////////////////////////////////////////////////////////////////////////////// + +namespace { + +static void +print_wasm_error(const char* message, wasm_trap_t* trap) +{ + fprintf(stderr, "WAMR error: %s\n", message); + wasm_byte_vec_t error_message; + + if (trap) + { + wasm_trap_message(trap, &error_message); + wasm_trap_delete(trap); + fprintf( + stderr, + "WAMR trap: %.*s\n", + (int)error_message.size, + error_message.data); + wasm_byte_vec_delete(&error_message); + } +} + +} // namespace + +InstancePtr +InstanceWrapper::init( + wasm_store_t* s, + wasm_module_t* m, + int32_t maxPages, + wasm_extern_vec_t* expt, + wasm_extern_vec_t const& imports) +{ + wasm_trap_t* trap = nullptr; + InstantiationArgs inst_args{ + 128 * 1024, + 256 * 1024, + static_cast(maxPages > 0 ? maxPages : 0)}; + + InstancePtr mi = InstancePtr( + wasm_instance_new_with_args_ex(s, m, &imports, &trap, &inst_args), + &wasm_instance_delete); + + if (!mi || trap) + { + print_wasm_error("can't create instance", trap); + throw std::runtime_error("WAMR: can't create instance"); + } + wasm_instance_exports(mi.get(), expt); + return mi; +} + +InstanceWrapper::InstanceWrapper() + : exports{0, nullptr, 0, 0, nullptr} + , mod_inst(nullptr, &wasm_instance_delete) +{ +} + +InstanceWrapper::InstanceWrapper(InstanceWrapper&& o) + : exports{0, nullptr, 0, 0, nullptr} + , mod_inst(nullptr, &wasm_instance_delete) +{ + *this = std::move(o); +} + +InstanceWrapper& +InstanceWrapper::operator=(InstanceWrapper&& o) +{ + if (this == &o) + return *this; + + if (exports.size) + wasm_extern_vec_delete(&exports); + exports = o.exports; + o.exports = {0, nullptr, 0, 0, nullptr}; + + mod_inst = std::move(o.mod_inst); + + return *this; +} + +InstanceWrapper::InstanceWrapper( + wasm_store_t* s, + wasm_module_t* m, + int32_t maxPages, + wasm_extern_vec_t const& imports) + : exports WASM_EMPTY_VEC, mod_inst(init(s, m, maxPages, &exports, imports)) +{ +} + +InstanceWrapper::~InstanceWrapper() +{ + if (exports.size) + wasm_extern_vec_delete(&exports); +} + +InstanceWrapper::operator bool() const +{ + return static_cast(mod_inst); +} + +wasm_func_t* +InstanceWrapper::getFunc( + std::string_view funcName, + wasm_exporttype_vec_t const& export_types) const +{ + wasm_func_t* f = nullptr; + + if (!export_types.size) + throw std::runtime_error("WAMR: no export"); + if (export_types.size != exports.size) + throw std::runtime_error("WAMR: invalid export"); + + for (unsigned i = 0; i < export_types.size; ++i) + { + auto const* exp_type(export_types.data[i]); + + wasm_name_t const* name = wasm_exporttype_name(exp_type); + const wasm_externtype_t* exn_type = wasm_exporttype_type(exp_type); + if (wasm_externtype_kind(exn_type) == WASM_EXTERN_FUNC) + { + if (funcName == std::string_view(name->data, name->size - 1)) + { + auto* exn(exports.data[i]); + if (wasm_extern_kind(exn) != WASM_EXTERN_FUNC) + throw std::runtime_error("WAMR: invalid export"); + + f = wasm_extern_as_func(exn); + break; + } + } + } + + if (!f) + throw std::runtime_error( + "WAMR: can't find function " + std::string(funcName)); + + return f; +} + +wmem +InstanceWrapper::getMem() const +{ + wasm_memory_t* mem = nullptr; + for (unsigned i = 0; i < exports.size; ++i) + { + auto* e(exports.data[i]); + if (wasm_extern_kind(e) == WASM_EXTERN_MEMORY) + { + mem = wasm_extern_as_memory(e); + break; + } + } + + if (!mem) + throw std::runtime_error("WAMR: no memory exported"); + + return { + reinterpret_cast(wasm_memory_data(mem)), + wasm_memory_data_size(mem)}; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ModulePtr +ModuleWrapper::init(wasm_store_t* s, wbytes const& wasmBin) +{ + wasm_byte_vec_t const code{ + wasmBin.size(), + (char*)(wasmBin.data()), + wasmBin.size(), + sizeof(std::remove_reference_t::value_type), + nullptr}; + ModulePtr m = ModulePtr(wasm_module_new(s, &code), &wasm_module_delete); + return m; +} + +ModuleWrapper::ModuleWrapper() + : module(nullptr, &wasm_module_delete) + , export_types{0, nullptr, 0, 0, nullptr} +{ +} + +ModuleWrapper::ModuleWrapper(ModuleWrapper&& o) + : module(nullptr, &wasm_module_delete) + , export_types{0, nullptr, 0, 0, nullptr} +{ + *this = std::move(o); +} + +ModuleWrapper& +ModuleWrapper::operator=(ModuleWrapper&& o) +{ + if (this == &o) + return *this; + + module = std::move(o.module); + mod_inst = std::move(o.mod_inst); + if (export_types.size) + wasm_exporttype_vec_delete(&export_types); + export_types = o.export_types; + o.export_types = {0, nullptr, 0, 0, nullptr}; + exec_env = o.exec_env; + o.exec_env = nullptr; + return *this; +} + +ModuleWrapper::ModuleWrapper( + wasm_store_t* s, + wbytes const& wasmBin, + bool instantiate, + int32_t maxPages, + std::vector const& imports) + : module(init(s, wasmBin)), export_types{0, nullptr, 0, 0, nullptr} +{ + if (!module) + throw std::runtime_error("WAMR: can't create module"); + + wasm_module_exports(module.get(), &export_types); + if (instantiate) + { + auto wimports = buildImports(s, imports); + addInstance(s, maxPages, wimports); + } +} + +ModuleWrapper::~ModuleWrapper() +{ + if (export_types.size) + wasm_exporttype_vec_delete(&export_types); +} + +ModuleWrapper::operator bool() const +{ + return mod_inst; +} + +void +ModuleWrapper::makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp) +{ + auto const paramSize = imp.params.size(); + + if (paramSize) + { + wasm_valtype_vec_new(&v, paramSize, nullptr); + v.num_elems = paramSize; + } + else + v = WASM_EMPTY_VEC; + for (unsigned i = 0; i < paramSize; ++i) + { + auto const vt = imp.params[i]; + switch (vt) + { + case WT_I32: + v.data[i] = wasm_valtype_new_i32(); + break; + case WT_I64: + v.data[i] = wasm_valtype_new_i64(); + break; + case WT_F32: + v.data[i] = wasm_valtype_new_f32(); + break; + case WT_F64: + v.data[i] = wasm_valtype_new_f64(); + break; + default: + throw std::runtime_error("Invalid import type"); + } + } +} + +void +ModuleWrapper::makeImpReturn(wasm_valtype_vec_t& v, WasmImportFunc const& imp) +{ + if (imp.result) + { + wasm_valtype_vec_new(&v, 1, nullptr); + v.num_elems = 1; + switch (*imp.result) + { + case WT_I32: + v.data[0] = wasm_valtype_new_i32(); + break; + case WT_I64: + v.data[0] = wasm_valtype_new_i64(); + break; + case WT_F32: + v.data[0] = wasm_valtype_new_f32(); + break; + case WT_F64: + v.data[0] = wasm_valtype_new_f64(); + break; + default: + throw std::runtime_error("Invalid return type"); + } + } + else + v = WASM_EMPTY_VEC; +} + +wasm_extern_vec_t +ModuleWrapper::buildImports( + wasm_store_t* s, + std::vector const& imports) +{ + wasm_importtype_vec_t importTypes = WASM_EMPTY_VEC; + wasm_module_imports(module.get(), &importTypes); + std:: + unique_ptr + itDeleter(&importTypes, &wasm_importtype_vec_delete); + + wasm_extern_vec_t wimports = WASM_EMPTY_VEC; + if (!importTypes.num_elems) + return wimports; + + wasm_extern_vec_new(&wimports, importTypes.size, nullptr); + wimports.num_elems = importTypes.num_elems; + + for (unsigned i = 0; i < importTypes.num_elems; ++i) + { + wasm_importtype_t const* importtype = importTypes.data[i]; + if (wasm_importtype_is_linked(importtype)) + { + // create a placeholder + wimports.data[i] = wasm_extern_new_empty( + s, wasm_externtype_kind(wasm_importtype_type(importtype))); + continue; + } + + // wasm_name_t const* mn = wasm_importtype_module(importtype); + // auto modName = std::string_view(mn->data, mn->num_elems - 1); + wasm_name_t const* fn = wasm_importtype_name(importtype); + auto fieldName = std::string_view(fn->data, fn->num_elems - 1); + + // for multi-module support + // if ((W_ENV != modName) && (W_HOST_LIB != modName)) + // continue; + + for (auto const& imp : imports) + { + if (imp.name != fieldName) + continue; + + wasm_valtype_vec_t params, results; + makeImpReturn(results, imp); + makeImpParams(params, imp); + + using ftype_ptr = std:: + unique_ptr; + ftype_ptr ftype( + wasm_functype_new(¶ms, &results), &wasm_functype_delete); + wasm_func_t* func = wasm_func_new_with_env( + s, + ftype.get(), + reinterpret_cast(imp.wrap), + imp.udata, + nullptr); + + wimports.data[i] = wasm_func_as_extern(func); + break; + } + } + + return wimports; +} + +wasm_func_t* +ModuleWrapper::getFunc(std::string_view funcName) const +{ + return mod_inst.getFunc(funcName, export_types); +} + +wmem +ModuleWrapper::getMem() const +{ + return mod_inst.getMem(); +} + +int +ModuleWrapper::addInstance( + wasm_store_t* s, + int32_t maxPages, + wasm_extern_vec_t const& imports) +{ + mod_inst = {s, module.get(), maxPages, imports}; + exec_env = wasm_instance_exec_env(mod_inst.mod_inst.get()); + + return 0; +} + +// int +// my_module_t::delInstance(int i) +// { +// if (i >= mod_inst.size()) +// return -1; +// if (!mod_inst[i]) +// mod_inst[i] = my_mod_inst_t(); +// return i; +// } + +std::int64_t +ModuleWrapper::setGas(std::int64_t gas) +{ + if (exec_env) + { + wasm_runtime_set_instruction_count_limit(exec_env, gas); + return gas; + } + return 0; +} + +std::int64_t +ModuleWrapper::getGas() +{ + return exec_env ? wasm_runtime_get_instruction_count_limit(exec_env) : 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// void +// WamrEngine::clearModules() +// { +// modules.clear(); +// store.reset(); // to free the memory before creating new store +// store = {wasm_store_new(engine.get()), &wasm_store_delete}; +// } + +WamrEngine::WamrEngine() + : engine(wasm_engine_new(), &wasm_engine_delete) + , store(nullptr, &wasm_store_delete) +{ + wasm_runtime_set_default_running_mode(Mode_Interp); + wasm_runtime_set_log_level(WASM_LOG_LEVEL_FATAL); + // wasm_runtime_set_log_level(WASM_LOG_LEVEL_VERBOSE); +} + +int +WamrEngine::addModule( + wbytes const& wasmCode, + bool instantiate, + std::vector const& imports) +{ + module.reset(); + store.reset(); // to free the memory before creating new store + store = {wasm_store_new(engine.get()), &wasm_store_delete}; + module = std::make_unique( + store.get(), wasmCode, instantiate, defMaxPages, imports); + setGas(defGas); + return module ? 0 : -1; +} + +int +WamrEngine::addInstance() +{ + return module->addInstance(store.get(), defMaxPages); +} + +wasm_func_t* +WamrEngine::getFunc(std::string_view funcName) +{ + return module->getFunc(funcName); +} + +std::vector +WamrEngine::convertParams(std::vector const& params) +{ + std::vector v; + v.reserve(params.size()); + for (auto const& p : params) + { + switch (p.type) + { + case WT_I32: + v.push_back(WASM_I32_VAL(p.of.i32)); + break; + case WT_I64: + v.push_back(WASM_I64_VAL(p.of.i64)); + break; + case WT_F32: + v.push_back(WASM_F32_VAL(p.of.f32)); + break; + case WT_F64: + v.push_back(WASM_F64_VAL(p.of.f64)); + break; + default: + break; + } + } + + return v; +} + +void +WamrEngine::add_param(std::vector& in, int32_t p) +{ + in.emplace_back(); + auto& el(in.back()); + memset(&el, 0, sizeof(el)); + el = WASM_I32_VAL(p); // WASM_I32; +} + +void +WamrEngine::add_param(std::vector& in, int64_t p) +{ + in.emplace_back(); + auto& el(in.back()); + el = WASM_I64_VAL(p); +} + +template +WamrResult +WamrEngine::call(std::string_view func, Types... args) +{ + // Lookup our export function + auto* f = getFunc(func); + return call(f, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call(wasm_func_t* func, Types... args) +{ + std::vector in; + return call(func, in, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call(wasm_func_t* func, std::vector& in) +{ + // wasm_val_t rs[1] = {WASM_I32_VAL(0)}; + WamrResult ret(NR); + // if (NR) { wasm_val_vec_new_uninitialized(&ret, NR); // + // wasm_val_vec_new(&ret, NR, &rs[0]); // ret = WASM_ARRAY_VEC(rs); } + + wasm_val_vec_t const inv = in.empty() + ? wasm_val_vec_t WASM_EMPTY_VEC + : wasm_val_vec_t{ + in.size(), + in.data(), + in.size(), + sizeof(std::remove_reference_t::value_type), + nullptr}; + trap = wasm_func_call(func, &inv, &ret.r); + if (trap) + print_wasm_error("failed to call func", trap); + + // assert(results[0].kind == WASM_I32); + // if (NR) printf("Result P5: %d\n", ret[0].of.i32); + + return ret; +} + +template +WamrResult +WamrEngine::call( + wasm_func_t* func, + std::vector& in, + std::int32_t p, + Types... args) +{ + add_param(in, p); + return call(func, in, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call( + wasm_func_t* func, + + std::vector& in, + std::int64_t p, + Types... args) +{ + add_param(in, p); + return call(func, in, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call( + wasm_func_t* func, + std::vector& in, + uint8_t const* d, + std::size_t sz, + Types... args) +{ + auto res = call<1>(W_ALLOC, static_cast(sz)); + + if (trap || (res.r.data[0].kind != WASM_I32)) + return {}; + auto const ptr = res.r.data[0].of.i32; + if (!ptr) + throw std::runtime_error( + "WAMR: can't allocate memory, " + std::to_string(sz) + " bytes"); + + auto mem = getMem(); + memcpy(mem.p + ptr, d, sz); + + add_param(in, ptr); + add_param(in, static_cast(sz)); + return call(func, in, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call( + wasm_func_t* func, + std::vector& in, + wbytes const& p, + Types... args) +{ + return call(func, in, p.data(), p.size(), std::forward(args)...); +} + +Expected +WamrEngine::run( + wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params) +{ + try + { + return runHlp(wasmCode, funcName, imports, params); + } + catch (std::exception const&) + { + } + catch (...) + { + } + return Unexpected(tecFAILED_PROCESSING); +} + +Expected +WamrEngine::runHlp( + wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params) +{ + // Create and instantiate the module. + if (!wasmCode.empty()) + { + int const m = addModule(wasmCode, true, imports); + if (m < 0) + return Unexpected(tecFAILED_PROCESSING); + } + + if (!module) + return Unexpected(tecFAILED_PROCESSING); + + // Call main + auto* f = getFunc(!funcName.empty() ? funcName : "_start"); + auto p = convertParams(params); + auto res = call<1>(f, p); + if (!res.r.size || trap) + return Unexpected(tecFAILED_PROCESSING); + + assert(res.r.data[0].kind == WASM_I32); + // printf("Result: %d\n", results[0].of.i32); + // return res.r.data[0].of.i32 != 0; + return res.r.data[0].of.i32; +} + +std::int64_t +WamrEngine::initGas(std::int64_t def) +{ + defGas = def; + return def; +} + +std::int32_t +WamrEngine::initMaxPages(std::int32_t def) +{ + defMaxPages = def; + return def; +} + +std::int64_t +WamrEngine::setGas(std::int64_t gas) +{ + if (module) + { + module->setGas(gas); + return gas; + } + return 0; +} + +std::int64_t +WamrEngine::getGas() +{ + return module ? module->getGas() : 0; +} + +wmem +WamrEngine::getMem() const +{ + return module ? module->getMem() : wmem(); +} + +int32_t +WamrEngine::allocate(int32_t sz) +{ + auto res = call<1>(W_ALLOC, static_cast(sz)); + if (trap || (res.r.data[0].kind != WASM_I32)) + return {}; + auto const ptr = res.r.data[0].of.i32; + if (!ptr) + throw std::runtime_error( + "WAMR: can't allocate memory, " + std::to_string(sz) + " bytes"); + return ptr; +} + +wasm_trap_t* +WamrEngine::newTrap(std::string_view txt) +{ + wasm_message_t msg = WASM_EMPTY_VEC; + + if (!txt.empty()) + wasm_name_new(&msg, txt.size(), txt.data()); + + return wasm_trap_new(store.get(), &msg); +} + +} // namespace ripple diff --git a/src/xrpld/app/misc/WamrVM.h b/src/xrpld/app/misc/WamrVM.h new file mode 100644 index 0000000000..113fcf8886 --- /dev/null +++ b/src/xrpld/app/misc/WamrVM.h @@ -0,0 +1,264 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2023 Ripple Labs Inc. + + 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. +*/ +//============================================================================== +#pragma once + +#include + +#include +#include + +namespace ripple { + +// clang-format off + +struct WamrResult +{ + wasm_val_vec_t r; + WamrResult(unsigned N = 0):r{0, nullptr, 0, 0, nullptr} {if (N) wasm_val_vec_new_uninitialized(&r, N);} + ~WamrResult() { if (r.size) wasm_val_vec_delete(&r); } + WamrResult(WamrResult const &) = delete; + WamrResult& operator=(WamrResult const &) = delete; + + WamrResult(WamrResult &&o) {*this = std::move(o);} + WamrResult& operator=(WamrResult &&o){r = o.r; o.r = {0, nullptr, 0, 0, nullptr}; return *this;} + //operator wasm_val_vec_t &() {return r;} +}; + +using ModulePtr = std::unique_ptr; +using InstancePtr = std::unique_ptr; + +// clang-format on + +struct InstanceWrapper +{ + wasm_extern_vec_t exports; + InstancePtr mod_inst; + +private: + static InstancePtr + init( + wasm_store_t* s, + wasm_module_t* m, + int32_t maxPages, + wasm_extern_vec_t* expt, + wasm_extern_vec_t const& imports = WASM_EMPTY_VEC); + +public: + InstanceWrapper(); + + InstanceWrapper(InstanceWrapper&& o); + + InstanceWrapper& + operator=(InstanceWrapper&& o); + + InstanceWrapper( + wasm_store_t* s, + wasm_module_t* m, + int32_t maxPages, + wasm_extern_vec_t const& imports = WASM_EMPTY_VEC); + + ~InstanceWrapper(); + + operator bool() const; + + wasm_func_t* + getFunc( + std::string_view funcName, + wasm_exporttype_vec_t const& export_types) const; + + wmem + getMem() const; +}; + +struct ModuleWrapper +{ + ModulePtr module; + wasm_exec_env_t exec_env = nullptr; + InstanceWrapper mod_inst; + wasm_exporttype_vec_t export_types; + +private: + static ModulePtr + init(wasm_store_t* s, wbytes const& wasmBin); + +public: + ModuleWrapper(); + ModuleWrapper(ModuleWrapper&& o); + ModuleWrapper& + operator=(ModuleWrapper&& o); + ModuleWrapper( + wasm_store_t* s, + wbytes const& wasmBin, + bool instantiate, + int32_t maxPages, + std::vector const& imports = {}); + ~ModuleWrapper(); + + operator bool() const; + + wasm_func_t* + getFunc(std::string_view funcName) const; + wmem + getMem() const; + + int + addInstance( + wasm_store_t* s, + int32_t maxPages, + wasm_extern_vec_t const& imports = WASM_EMPTY_VEC); + + std::int64_t + setGas(std::int64_t gas); + + std::int64_t + getGas(); + +private: + static void + makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp); + static void + makeImpReturn(wasm_valtype_vec_t& v, WasmImportFunc const& imp); + wasm_extern_vec_t + buildImports(wasm_store_t* s, std::vector const& imports); +}; + +class WamrEngine +{ + std::unique_ptr engine; + std::unique_ptr store; + std::unique_ptr module; + wasm_trap_t* trap = nullptr; + std::int64_t defGas = -1; + std::int32_t defMaxPages = -1; + +public: + WamrEngine(); + ~WamrEngine() = default; + + Expected + run(wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params); + + std::int64_t + initGas(std::int64_t def); + + std::int64_t + setGas(std::int64_t gas); + + std::int32_t + initMaxPages(std::int32_t def); + + std::int64_t + getGas(); + + // Host functions helper functionality + wmem + getMem() const; + + int32_t + allocate(int32_t size); + + wasm_trap_t* + newTrap(std::string_view msg); + +private: + Expected + runHlp( + wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params); + + int + addModule( + wbytes const& wasmCode, + bool instantiate, + std::vector const& imports); + void + clearModules(); + int + addInstance(); + int32_t + runFunc(std::string_view const funcName, int32_t p); + + int32_t + makeModule( + wbytes const& wasmCode, + wasm_extern_vec_t const& imports = WASM_EMPTY_VEC); + + wasm_func_t* + getFunc(std::string_view funcName); + + std::vector + convertParams(std::vector const& params); + + void + add_param(std::vector& in, int32_t p); + void + add_param(std::vector& in, int64_t p); + + template + inline WamrResult + call(std::string_view func, Types... args); + + template + inline WamrResult + call(wasm_func_t* func, Types... args); + + template + inline WamrResult + call(wasm_func_t* f, std::vector& in); + + template + inline WamrResult + call( + wasm_func_t* func, + std::vector& in, + std::int32_t p, + Types... args); + + template + inline WamrResult + call( + wasm_func_t* func, + std::vector& in, + std::int64_t p, + Types... args); + + template + inline WamrResult + call( + wasm_func_t* func, + std::vector& in, + uint8_t const* d, + std::size_t sz, + Types... args); + + template + inline WamrResult + call( + wasm_func_t* func, + std::vector& in, + wbytes const& p, + Types... args); +}; + +} // namespace ripple diff --git a/src/xrpld/app/misc/WasmHostFuncImpl.h b/src/xrpld/app/misc/WasmHostFuncImpl.h index aa42f79ff6..e1943244e7 100644 --- a/src/xrpld/app/misc/WasmHostFuncImpl.h +++ b/src/xrpld/app/misc/WasmHostFuncImpl.h @@ -19,14 +19,12 @@ #ifndef RIPPLE_APP_MISC_WASMHOSTFUNCIMPL_H_INLCUDED #define RIPPLE_APP_MISC_WASMHOSTFUNCIMPL_H_INLCUDED +#include +#include + #include #include -#include "xrpl/basics/base_uint.h" -#include "xrpld/app/misc/WasmVM.h" -#include "xrpld/app/tx/detail/ApplyContext.h" -#include - namespace ripple { class WasmHostFunctionsImpl : public HostFunctions { diff --git a/src/xrpld/app/misc/WasmHostFuncWrapper.cpp b/src/xrpld/app/misc/WasmHostFuncWrapper.cpp new file mode 100644 index 0000000000..d893a93889 --- /dev/null +++ b/src/xrpld/app/misc/WasmHostFuncWrapper.cpp @@ -0,0 +1,392 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + 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 +#include + +#include + +namespace ripple { + +wasm_trap_t* +getLedgerSqn_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto* hf = reinterpret_cast(env); + int32_t const sqn = hf->getLedgerSqn(); + results->data[0] = WASM_I32_VAL(sqn); + + return nullptr; +} + +wasm_trap_t* +getParentLedgerTime_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto* hf = reinterpret_cast(env); + int32_t const ltime = hf->getParentLedgerTime(); + results->data[0] = WASM_I32_VAL(ltime); + return nullptr; +} + +static Expected +getParameterData(wasm_val_vec_t const* params, size_t index) +{ + auto& vm = WasmEngine::instance(); + auto fnameOffset = params->data[index].of.i32; + auto fnameLen = params->data[index + 1].of.i32; + auto mem = vm.getMem(); + if (!mem.s) + return Unexpected("No memory exported"); + + if (mem.s <= fnameOffset + fnameLen) + return Unexpected("Memory access failed"); + Bytes fname(mem.p + fnameOffset, mem.p + fnameOffset + fnameLen); + return fname; +} + +static Expected +getFieldName(wasm_val_vec_t const* params, size_t index) +{ + auto const dataRes = getParameterData(params, index); + if (dataRes) + { + return std::string(dataRes->begin(), dataRes->end()); + } + else + { + auto& vm = WasmEngine::instance(); + return Unexpected( + reinterpret_cast(vm.newTrap(dataRes.error()))); + } +} + +static Expected +setData(Bytes const& data) +{ + auto& vm = WasmEngine::instance(); + auto mem = vm.getMem(); + if (!mem.s) + return Unexpected("No memory exported"); + + int32_t const dataLen = static_cast(data.size()); + int32_t const dataPtr = vm.allocate(dataLen); + if (!dataPtr) + return Unexpected("Allocation error"); + memcpy(mem.p + dataPtr, data.data(), dataLen); + + auto retPtr = vm.allocate(8); + if (!retPtr) + return Unexpected("Allocation error"); + int32_t* retData = reinterpret_cast(mem.p + retPtr); + retData[0] = dataPtr; + retData[1] = dataLen; + + return retPtr; +} + +wasm_trap_t* +getTxField_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto fname = getFieldName(params, 0); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + auto fieldData = hf->getTxField(fname.value()); + if (!fieldData) + return reinterpret_cast(vm.newTrap("Field not found")); + + auto pointer = setData(fieldData.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size()); + return nullptr; +} + +wasm_trap_t* +getLedgerEntryField_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + int32_t const type = params->data[0].of.i32; + auto lkData = getParameterData(params, 1); + if (!lkData) + return reinterpret_cast(vm.newTrap()); + + auto fname = getFieldName(params, 3); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + auto fieldData = + hf->getLedgerEntryField(type, lkData.value(), fname.value()); + if (!fieldData) + return reinterpret_cast(vm.newTrap()); + auto pointer = setData(fieldData.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size()); + return nullptr; +} + +wasm_trap_t* +getCurrentLedgerEntryField_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto fname = getFieldName(params, 0); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + auto fieldData = hf->getCurrentLedgerEntryField(fname.value()); + if (!fieldData) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(fieldData.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size()); + return nullptr; +} + +wasm_trap_t* +getNFT_wrap(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + auto account = getFieldName(params, 0); + if (!account) + return reinterpret_cast(vm.newTrap()); + + auto nftId = getFieldName(params, 2); + if (!nftId) + return reinterpret_cast(vm.newTrap()); + + auto nftURI = hf->getNFT(account.value(), nftId.value()); + if (!nftURI) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(nftURI.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +accountKeylet_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto account = getFieldName(params, 0); + if (!account) + return reinterpret_cast(vm.newTrap()); + + auto keylet = hf->accountKeylet(account.value()); + if (!keylet) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(keylet.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +credentialKeylet_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto subject = getFieldName(params, 0); + if (!subject) + return reinterpret_cast(vm.newTrap()); + + auto issuer = getFieldName(params, 2); + if (!issuer) + return reinterpret_cast(vm.newTrap()); + + auto credentialType = getFieldName(params, 4); + if (!credentialType) + return reinterpret_cast(vm.newTrap()); + + auto keylet = hf->credentialKeylet( + subject.value(), issuer.value(), credentialType.value()); + if (!keylet) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(keylet.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +escrowKeylet_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto account = getFieldName(params, 0); + if (!account) + return reinterpret_cast(vm.newTrap()); + + int32_t const sequence = params->data[2].of.i32; + + auto keylet = hf->escrowKeylet(account.value(), sequence); + if (!keylet) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(keylet.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +oracleKeylet_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto account = getFieldName(params, 0); + if (!account) + return reinterpret_cast(vm.newTrap()); + + auto documentId = params->data[2].of.i32; + + auto keylet = hf->escrowKeylet(account.value(), documentId); + if (!keylet) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(keylet.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +updateData_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto fname = getParameterData(params, 0); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + if (!hf->updateData(fname.value())) + return reinterpret_cast(vm.newTrap()); + + return nullptr; +} + +wasm_trap_t* +computeSha512HalfHash_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto fname = getParameterData(params, 0); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + auto hres = hf->computeSha512HalfHash(fname.value()); + Bytes digest{hres.begin(), hres.end()}; + auto pointer = setData(digest); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32(32); + return nullptr; +} + +wasm_trap_t* +print_wrap(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + // auto* hf = reinterpret_cast(env); + + auto f = getParameterData(params, 0); + if (!f) + return reinterpret_cast(vm.newTrap()); + std::string s(f->begin(), f->end()); + if (s.size() < 4096) + std::cout << s << std::endl; + return nullptr; +} + +} // namespace ripple diff --git a/src/xrpld/app/misc/WasmHostFuncWrapper.h b/src/xrpld/app/misc/WasmHostFuncWrapper.h new file mode 100644 index 0000000000..c3f5bb11a9 --- /dev/null +++ b/src/xrpld/app/misc/WasmHostFuncWrapper.h @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + + 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. +*/ +//============================================================================== + +#pragma once + +#include + +namespace ripple { + +using getLedgerSqn_proto = int32_t(); +wasm_trap_t* +getLedgerSqn_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using getParentLedgerTime_proto = int32_t(); +wasm_trap_t* +getParentLedgerTime_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using getTxField_proto = uint32_t*(char const*, int32_t); +wasm_trap_t* +getTxField_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results); + +using getLedgerEntryField_proto = + uint32_t*(int32_t, uint8_t const*, int32_t, char const*, int32_t); +wasm_trap_t* +getLedgerEntryField_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results); + +using getCurrentLedgerEntryField_proto = uint32_t*(char const*, int32_t); +wasm_trap_t* +getCurrentLedgerEntryField_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results); + +using getNFT_proto = uint32_t*(char const*, int32_t, char const*, int32_t); +wasm_trap_t* +getNFT_wrap(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results); + +using accountKeylet_proto = uint32_t*(char const*, int32_t); +wasm_trap_t* +accountKeylet_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results); + +using credentialKeylet_proto = + uint32_t*(char const*, int32_t, char const*, int32_t, char const*, int32_t); +wasm_trap_t* +credentialKeylet_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results); + +using escrowKeylet_proto = uint32_t*(char const*, int32_t, int32_t); +wasm_trap_t* +escrowKeylet_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results); + +using oracleKeylet_proto = uint32_t*(char const*, int32_t, int32_t); +wasm_trap_t* +oracleKeylet_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results); + +using updateData_proto = void(uint8_t const*, int32_t); +wasm_trap_t* +updateData_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results); + +using computeSha512HalfHash_proto = uint32_t*(uint8_t const*, int32_t); +wasm_trap_t* +computeSha512HalfHash_wrap( + void* env, + const wasm_val_vec_t* params, + wasm_val_vec_t* results); + +using print_proto = void(char const*, int32_t); +wasm_trap_t* +print_wrap(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results); + +} // namespace ripple diff --git a/src/xrpld/app/misc/WasmVM.cpp b/src/xrpld/app/misc/WasmVM.cpp index 1764734db3..d786cae7fb 100644 --- a/src/xrpld/app/misc/WasmVM.cpp +++ b/src/xrpld/app/misc/WasmVM.cpp @@ -17,730 +17,118 @@ */ //============================================================================== -#include +#include +#include -#include "xrpl/protocol/AccountID.h" -#include "xrpl/protocol/LedgerFormats.h" +#include +#include + +#include namespace ripple { -WasmEdge_Result -getLedgerSqn( - void* data, - const WasmEdge_CallingFrameContext*, - const WasmEdge_Value*, - WasmEdge_Value* out) -{ - out[0] = WasmEdge_ValueGenI32(((HostFunctions*)data)->getLedgerSqn()); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -getParentLedgerTime( - void* data, - const WasmEdge_CallingFrameContext*, - const WasmEdge_Value*, - WasmEdge_Value* out) -{ - out[0] = - WasmEdge_ValueGenI32(((HostFunctions*)data)->getParentLedgerTime()); - return WasmEdge_Result_Success; -} - -Expected -getParameterData( - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - size_t index) -{ - auto fnameOffset = (uint32_t)WasmEdge_ValueGetI32(in[index]); - auto fnameLen = (uint32_t)WasmEdge_ValueGetI32(in[index + 1]); - Bytes fname(fnameLen, char{0}); - WasmEdge_MemoryInstanceContext* mem = - WasmEdge_CallingFrameGetMemoryInstance(fm, 0); - WasmEdge_Result Res = WasmEdge_MemoryInstanceGetData( - mem, (uint8_t*)(fname.data()), fnameOffset, fnameLen); - if (WasmEdge_ResultOK(Res)) - { - return fname; - } - else - { - return Unexpected(Res); - } -} - -Expected -getFieldName( - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - size_t index) -{ - auto dataRes = getParameterData(fm, in, index); - if (dataRes) - { - return std::string(dataRes.value().begin(), dataRes->end()); - } - else - { - return Unexpected(dataRes.error()); - } -} - -Expected -setData(const WasmEdge_CallingFrameContext* fm, Bytes const& data) -{ - auto alloc = [fm](int32_t dataLen) -> int32_t { - WasmEdge_String allocFunc = WasmEdge_StringCreateByCString("allocate"); - auto mod = WasmEdge_CallingFrameGetModuleInstance(fm); - WasmEdge_FunctionInstanceContext* func = - WasmEdge_ModuleInstanceFindFunction(mod, allocFunc); - WasmEdge_Value allocParams[1] = { - WasmEdge_ValueGenI32(dataLen)}; // 4 for prepend the data size - WasmEdge_Value allocReturns[1]; - auto executor = WasmEdge_CallingFrameGetExecutor(fm); - auto res = WasmEdge_ExecutorInvoke( - executor, func, allocParams, 1, allocReturns, 1); - if (WasmEdge_ResultOK(res)) - { - return WasmEdge_ValueGetI32(allocReturns[0]); - } - else - { - return 0; - } - }; - - auto dataLen = (int32_t)data.size(); - auto dataPtr = alloc(dataLen); - auto retPtr = alloc(8); - if (dataPtr && retPtr) - { - auto mem = WasmEdge_CallingFrameGetMemoryInstance(fm, 0); - auto res = - WasmEdge_MemoryInstanceSetData(mem, data.data(), dataPtr, dataLen); - if (WasmEdge_ResultOK(res)) - { - unsigned char intBuf[8]; // little-endian - for (size_t i = 0; i < 4; ++i) - { - intBuf[i] = (dataPtr >> (i * 8)) & 0xFF; - } - for (size_t i = 0; i < 4; ++i) - { - intBuf[i + 4] = (dataLen >> (i * 8)) & 0xFF; - } - - res = WasmEdge_MemoryInstanceSetData(mem, intBuf, retPtr, 8); - if (WasmEdge_ResultOK(res)) - { - return WasmEdge_ValueGenI32(retPtr); - } - } - } - return Unexpected(WasmEdge_Result_Fail); -} - -WasmEdge_Result -getTxField( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto fname = getFieldName(fm, in, 0); - if (!fname) - return fname.error(); - - auto fieldData = ((HostFunctions*)data)->getTxField(fname.value()); - if (!fieldData) - return WasmEdge_Result_Fail; - - auto pointer = setData(fm, fieldData.value()); - if (!pointer) - return pointer.error(); - - out[0] = pointer.value(); - // out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size()); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -getLedgerEntryField( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto type = WasmEdge_ValueGetI32(in[0]); - auto lkData = getParameterData(fm, in, 1); - if (!lkData) - return lkData.error(); - - auto fname = getFieldName(fm, in, 3); - if (!fname) - return fname.error(); - - auto fieldData = - ((HostFunctions*)data) - ->getLedgerEntryField(type, lkData.value(), fname.value()); - if (!fieldData) - return WasmEdge_Result_Fail; - auto pointer = setData(fm, fieldData.value()); - if (!pointer) - return pointer.error(); - - out[0] = pointer.value(); - // out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size()); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -getCurrentLedgerEntryField( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto fname = getFieldName(fm, in, 0); - if (!fname) - return fname.error(); - - auto fieldData = - ((HostFunctions*)data)->getCurrentLedgerEntryField(fname.value()); - if (!fieldData) - return WasmEdge_Result_Fail; - - auto pointer = setData(fm, fieldData.value()); - if (!pointer) - return pointer.error(); - - out[0] = pointer.value(); - // out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size()); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -getNFT( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto account = getFieldName(fm, in, 0); - if (!account) - return account.error(); - - auto nftId = getFieldName(fm, in, 2); - if (!nftId) - return nftId.error(); - - auto nftURI = - ((HostFunctions*)data)->getNFT(account.value(), nftId.value()); - if (!nftURI) - return WasmEdge_Result_Fail; - - auto pointer = setData(fm, nftURI.value()); - if (!pointer) - return pointer.error(); - - out[0] = pointer.value(); - // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -accountKeylet( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto account = getFieldName(fm, in, 0); - if (!account) - return account.error(); - - auto keylet = ((HostFunctions*)data)->accountKeylet(account.value()); - if (!keylet) - return WasmEdge_Result_Fail; - - auto pointer = setData(fm, keylet.value()); - if (!pointer) - return pointer.error(); - - out[0] = pointer.value(); - // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -credentialKeylet( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto subject = getFieldName(fm, in, 0); - if (!subject) - return subject.error(); - - auto issuer = getFieldName(fm, in, 2); - if (!issuer) - return issuer.error(); - - auto credentialType = getFieldName(fm, in, 4); - if (!credentialType) - return credentialType.error(); - - auto keylet = - ((HostFunctions*)data) - ->credentialKeylet( - subject.value(), issuer.value(), credentialType.value()); - if (!keylet) - return WasmEdge_Result_Fail; - - auto pointer = setData(fm, keylet.value()); - if (!pointer) - return pointer.error(); - - out[0] = pointer.value(); - // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -escrowKeylet( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto account = getFieldName(fm, in, 0); - if (!account) - return account.error(); - - auto sequence = WasmEdge_ValueGetI32(in[2]); - - auto keylet = - ((HostFunctions*)data)->escrowKeylet(account.value(), sequence); - if (!keylet) - return WasmEdge_Result_Fail; - - auto pointer = setData(fm, keylet.value()); - if (!pointer) - return pointer.error(); - - out[0] = pointer.value(); - // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -oracleKeylet( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto account = getFieldName(fm, in, 0); - if (!account) - return account.error(); - - auto documentId = WasmEdge_ValueGetI32(in[2]); - - auto keylet = - ((HostFunctions*)data)->escrowKeylet(account.value(), documentId); - if (!keylet) - return WasmEdge_Result_Fail; - - auto pointer = setData(fm, keylet.value()); - if (!pointer) - return pointer.error(); - - out[0] = pointer.value(); - // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -updateData( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto fname = getParameterData(fm, in, 0); - if (!fname) - return fname.error(); - - if (((HostFunctions*)data)->updateData(fname.value())) - return WasmEdge_Result_Success; - else - return WasmEdge_Result_Fail; -} - -WasmEdge_Result -computeSha512HalfHash( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto fname = getParameterData(fm, in, 0); - if (!fname) - return fname.error(); - - auto hres = ((HostFunctions*)data)->computeSha512HalfHash(fname.value()); - Bytes digest{hres.begin(), hres.end()}; - auto pointer = setData(fm, digest); - if (!pointer) - return pointer.error(); - - out[0] = pointer.value(); - // out[1] = WasmEdge_ValueGenI32(32); - return WasmEdge_Result_Success; -} - -WasmEdge_Result -print( - void* data, - const WasmEdge_CallingFrameContext* fm, - const WasmEdge_Value* in, - WasmEdge_Value* out) -{ - auto f = getParameterData(fm, in, 0); - if (!f) - return f.error(); - std::string s(f.value().begin(), f.value().end()); - std::cout << s << std::endl; - return WasmEdge_Result_Success; -} - Expected runEscrowWasm( - std::vector const& wasmCode, - std::string const& funcName, + Bytes const& wasmCode, + std::string_view funcName, HostFunctions* hfs, uint64_t gasLimit) { - // WasmEdge_LogOff(); - // TODO deletes // create VM and set cost limit - WasmEdge_ConfigureContext* conf = WasmEdge_ConfigureCreate(); - WasmEdge_ConfigureStatisticsSetInstructionCounting(conf, true); - WasmEdge_ConfigureStatisticsSetCostMeasuring(conf, true); - WasmEdge_ConfigureSetMaxMemoryPage(conf, MAX_PAGES); + auto& vm = WasmEngine::instance(); + vm.initGas(gasLimit); + vm.initMaxPages(MAX_PAGES); - WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(conf, NULL); - WasmEdge_StatisticsContext* StatCxt = - WasmEdge_VMGetStatisticsContext(VMCxt); - WasmEdge_StatisticsSetCostLimit(StatCxt, gasLimit); + std::vector imports; - { // register host function - // module - WasmEdge_String libName = WasmEdge_StringCreateByCString("host_lib"); - WasmEdge_ModuleInstanceContext* hostMod = - WasmEdge_ModuleInstanceCreate(libName); - WasmEdge_StringDelete(libName); + WASM_IMPORT_FUNC(imports, getLedgerSqn, hfs) + WASM_IMPORT_FUNC(imports, getParentLedgerTime, hfs) + WASM_IMPORT_FUNC(imports, getTxField, hfs) + WASM_IMPORT_FUNC(imports, getLedgerEntryField, hfs) + WASM_IMPORT_FUNC(imports, getCurrentLedgerEntryField, hfs) + WASM_IMPORT_FUNC(imports, getNFT, hfs) + WASM_IMPORT_FUNC(imports, accountKeylet, hfs) + WASM_IMPORT_FUNC(imports, credentialKeylet, hfs) + WASM_IMPORT_FUNC(imports, escrowKeylet, hfs) + WASM_IMPORT_FUNC(imports, oracleKeylet, hfs) + WASM_IMPORT_FUNC(imports, updateData, hfs) + WASM_IMPORT_FUNC(imports, computeSha512HalfHash, hfs) + WASM_IMPORT_FUNC(imports, print, hfs) - // getLedgerSqn - { - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(NULL, 0, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, getLedgerSqn, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); + std::int64_t const sgas = gasLimit; // vm.getGas(); + auto ret = vm.run(wasmCode, funcName, imports); + if (!ret.has_value()) + return Unexpected(ret.error()); + std::int64_t const egas = vm.getGas(); + std::uint64_t const spent = static_cast(sgas - egas); - WasmEdge_String fName = - WasmEdge_StringCreateByCString("getLedgerSqn"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - // WasmEdge_FunctionInstanceDelete(hostFunc); - } - - // getParentLedgerTime - { - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(NULL, 0, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, getParentLedgerTime, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - - WasmEdge_String fName = - WasmEdge_StringCreateByCString("getParentLedgerTime"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - // WasmEdge_FunctionInstanceDelete(hostFunc); - } - - // getTxField - { - WasmEdge_ValType inputList[2] = { - WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, getTxField, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = - WasmEdge_StringCreateByCString("getTxField"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // getLedgerEntryField - { - WasmEdge_ValType inputList[5] = { - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 5, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, getLedgerEntryField, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = - WasmEdge_StringCreateByCString("getLedgerEntryField"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // getCurrentLedgerEntryField - { - WasmEdge_ValType inputList[2] = { - WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, getCurrentLedgerEntryField, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = - WasmEdge_StringCreateByCString("getCurrentLedgerEntryField"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // getNFT - { - WasmEdge_ValType inputList[4] = { - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate(hostFuncType, getNFT, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = WasmEdge_StringCreateByCString("getNFT"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // updateData - { - WasmEdge_ValType inputList[2] = { - WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 2, NULL, 0); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, updateData, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = - WasmEdge_StringCreateByCString("updateData"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // computeSha512HalfHash - { - WasmEdge_ValType inputList[2] = { - WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, computeSha512HalfHash, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = - WasmEdge_StringCreateByCString("computeSha512HalfHash"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // accountKeylet - { - WasmEdge_ValType inputList[2] = { - WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, accountKeylet, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = - WasmEdge_StringCreateByCString("accountKeylet"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // credentialKeylet - { - WasmEdge_ValType inputList[6] = { - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 6, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, credentialKeylet, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - WasmEdge_String fName = - WasmEdge_StringCreateByCString("credentialKeylet"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // escrowKeylet - { - WasmEdge_ValType inputList[3] = { - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 3, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, escrowKeylet, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = - WasmEdge_StringCreateByCString("escrowKeylet"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // oracleKeylet - { - WasmEdge_ValType inputList[3] = { - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32(), - WasmEdge_ValTypeGenI32()}; - WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 3, returnList, 1); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate( - hostFuncType, oracleKeylet, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = - WasmEdge_StringCreateByCString("oracleKeylet"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - // print - { - WasmEdge_ValType inputList[2] = { - WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()}; - WasmEdge_FunctionTypeContext* hostFuncType = - WasmEdge_FunctionTypeCreate(inputList, 2, NULL, 0); - WasmEdge_FunctionInstanceContext* hostFunc = - WasmEdge_FunctionInstanceCreate(hostFuncType, print, hfs, 100); - // WasmEdge_FunctionTypeDelete(hostFuncType); - // WasmEdge_FunctionInstanceDelete(hostFunc); - - WasmEdge_String fName = WasmEdge_StringCreateByCString("print"); - WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc); - // WasmEdge_StringDelete(fName); - } - WasmEdge_Result regRe = - WasmEdge_VMRegisterModuleFromImport(VMCxt, hostMod); - if (!WasmEdge_ResultOK(regRe)) - { - printf("host func reg error\n"); - return Unexpected(tecFAILED_PROCESSING); - } - } - - WasmEdge_Result loadRes = - WasmEdge_VMLoadWasmFromBuffer(VMCxt, wasmCode.data(), wasmCode.size()); - if (!WasmEdge_ResultOK(loadRes)) - { - printf("load error, %p, %d\n", wasmCode.data(), wasmCode.size()); - return Unexpected(tecFAILED_PROCESSING); - } - WasmEdge_Result validateRes = WasmEdge_VMValidate(VMCxt); - if (!WasmEdge_ResultOK(validateRes)) - { - printf("validate error\n"); - return Unexpected(tecFAILED_PROCESSING); - } - WasmEdge_Result instantiateRes = WasmEdge_VMInstantiate(VMCxt); - if (!WasmEdge_ResultOK(instantiateRes)) - { - printf("instantiate error\n"); - return Unexpected(tecFAILED_PROCESSING); - } - WasmEdge_Value funcReturns[1]; - WasmEdge_String func = WasmEdge_StringCreateByCString(funcName.c_str()); - WasmEdge_Result funcRes = - WasmEdge_VMExecute(VMCxt, func, NULL, 0, funcReturns, 1); - - bool ok = WasmEdge_ResultOK(funcRes); - EscrowResult re; - if (ok) - { - auto sc = WasmEdge_VMGetStatisticsContext(VMCxt); - re.cost = WasmEdge_StatisticsGetTotalCost(sc); - // WasmEdge_StatisticsGetTotalCost, WasmEdge_StatisticsGetInstrCount - auto result = WasmEdge_ValueGetI32(funcReturns[0]); - if (result != 0) - re.result = true; - else - re.result = false; - } - else - { - printf("Error message: %s\n", WasmEdge_ResultGetMessage(funcRes)); - } - - WasmEdge_VMDelete(VMCxt); - WasmEdge_StringDelete(func); - // delete other obj allocated - if (ok) - return re; - else - return Unexpected(tecFAILED_PROCESSING); + return EscrowResult{static_cast(ret.value()), spent}; } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +WasmEngine::WasmEngine() : impl(std::make_unique()) +{ +} + +WasmEngine& +WasmEngine::instance() +{ + static WasmEngine e; + return e; +} + +Expected +WasmEngine::run( + wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params) +{ + return impl->run(wasmCode, funcName, imports, params); +} + +std::int64_t +WasmEngine::initGas(std::int64_t def) +{ + return impl->initGas(def); +} + +std::int32_t +WasmEngine::initMaxPages(std::int32_t def) +{ + return impl->initMaxPages(def); +} + +// gas = 1'000'000'000LL +std::int64_t +WasmEngine::setGas(std::int64_t gas) +{ + return impl->setGas(gas); +} + +std::int64_t +WasmEngine::getGas() +{ + return impl->getGas(); +} + +wmem +WasmEngine::getMem() const +{ + return impl->getMem(); +} + +int32_t +WasmEngine::allocate(int32_t size) +{ + return impl->allocate(size); +} + +void* +WasmEngine::newTrap(std::string_view msg) +{ + return impl->newTrap(msg); +} + } // namespace ripple diff --git a/src/xrpld/app/misc/WasmVM.h b/src/xrpld/app/misc/WasmVM.h index b0839dc76d..b383bba37d 100644 --- a/src/xrpld/app/misc/WasmVM.h +++ b/src/xrpld/app/misc/WasmVM.h @@ -16,16 +16,43 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== -#ifndef RIPPLE_APP_MISC_WASMVM_H_INLCUDED -#define RIPPLE_APP_MISC_WASMVM_H_INLCUDED +#pragma once #include +#include +#include #include -#include "xrpl/basics/base_uint.h" -#include +#include +#include +#include +#include + +// #include + +#include + +namespace bft = boost::function_types; namespace ripple { + +static const std::string_view W_ENV = "env"; +static const std::string_view W_HOST_LIB = "host_lib"; +static const std::string_view W_MEM = "memory"; +static const std::string_view W_STORE = "store"; +static const std::string_view W_LOAD = "load"; +static const std::string_view W_SIZE = "size"; +static const std::string_view W_ALLOC = "allocate"; +static const std::string_view W_DEALLOC = "deallocate"; +static const std::string_view W_PROC_EXIT = "proc_exit"; + +using wbytes = std::vector; +struct wmem +{ + std::uint8_t* p = nullptr; + std::size_t s = 0; +}; + const uint32_t MAX_PAGES = 128; // 8MB = 64KB*128 typedef std::vector Bytes; @@ -122,12 +149,220 @@ struct HostFunctions virtual ~HostFunctions() = default; }; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +enum WasmTypes { WT_I32, WT_I64, WT_F32, WT_F64 }; + +struct WasmImportFunc +{ + std::string name; + std::optional result; + std::vector params; + void* udata = nullptr; + // wasm_func_callback_with_env_t + void* wrap = nullptr; +}; + +#define WASM_IMPORT_FUNC(v, f, ...) \ + WasmImpFunc( \ + v, #f, reinterpret_cast(&f##_wrap), ##__VA_ARGS__); + +template +void +WasmImpArgs(WasmImportFunc& e) +{ + if constexpr (N < C) + { + using at = typename boost::mpl::at_c::type; + if constexpr (std::is_pointer_v) + e.params.push_back(WT_I32); + else if constexpr (std::is_same_v) + e.params.push_back(WT_I32); + else if constexpr (std::is_same_v) + e.params.push_back(WT_I64); + else if constexpr (std::is_same_v) + e.params.push_back(WT_F32); + else if constexpr (std::is_same_v) + e.params.push_back(WT_F64); + else + static_assert(std::is_pointer_v, "Unsupported argument type"); + + return WasmImpArgs(e); + } + return; +} + +template +void +WasmImpRet(WasmImportFunc& e) +{ + if constexpr (std::is_pointer_v) + e.result = WT_I32; + else if constexpr (std::is_same_v) + e.result = WT_I32; + else if constexpr (std::is_same_v) + e.result = WT_I64; + else if constexpr (std::is_same_v) + e.result = WT_F32; + else if constexpr (std::is_same_v) + e.result = WT_F64; + else if constexpr (std::is_void_v) + e.result.reset(); +#if (defined(__GNUC__) && (__GNUC__ >= 14)) || \ + ((defined(__clang_major__)) && (__clang_major__ >= 18)) + else + static_assert(false, "Unsupported return type"); +#endif +} + +template +void +WasmImpFuncHelper(WasmImportFunc& e) +{ + using rt = typename bft::result_type::type; + using pt = typename bft::parameter_types::type; + // typename boost::mpl::at_c::type + + WasmImpRet(e); + WasmImpArgs<0, bft::function_arity::value, pt>(e); + // WasmImpWrap(e, std::forward(f)); +} + +template +void +WasmImpFunc( + std::vector& v, + std::string_view imp_name, + void* f_wrap, + void* data = nullptr) +{ + WasmImportFunc e; + e.name = imp_name; + e.udata = data; + e.wrap = f_wrap; + WasmImpFuncHelper(e); + v.push_back(std::move(e)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct WasmParam +{ + WasmTypes type = WT_I32; + union + { + std::int32_t i32; + std::int64_t i64 = 0; + float f32; + double f64; + } of; +}; + +template +inline std::vector +wasmParams(Types... args) +{ + std::vector v; + v.reserve(sizeof...(args)); + return wasmParams(v, std::forward(args)...); +} + +template +inline std::vector +wasmParams(std::vector& v, std::int32_t p, Types... args) +{ + v.push_back({.type = WT_I32, .of = {.i32 = p}}); + return wasmParams(v, std::forward(args)...); +} + +template +inline std::vector +wasmParams(std::vector& v, std::int64_t p, Types... args) +{ + v.push_back({.type = WT_I64, .of = {.i64 = p}}); + return wasmParams(v, std::forward(args)...); +} + +template +inline std::vector +wasmParams(std::vector& v, float p, Types... args) +{ + v.push_back({.type = WT_F32, .of = {.f32 = p}}); + return wasmParams(v, std::forward(args)...); +} + +template +inline std::vector +wasmParams(std::vector& v, double p, Types... args) +{ + v.push_back({.type = WT_F64, .of = {.f64 = p}}); + return wasmParams(v, std::forward(args)...); +} + +inline std::vector +wasmParams(std::vector& v) +{ + return v; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class WamrEngine; +class WasmEngine +{ + std::unique_ptr const impl; + + WasmEngine(); + + WasmEngine(WasmEngine const&) = delete; + WasmEngine(WasmEngine&&) = delete; + WasmEngine& + operator=(WasmEngine const&) = delete; + WasmEngine& + operator=(WasmEngine&&) = delete; + +public: + ~WasmEngine() = default; + + static WasmEngine& + instance(); + + Expected + run(wbytes const& wasmCode, + std::string_view funcName = {}, + std::vector const& imports = {}, + std::vector const& params = {}); + + std::int64_t + initGas(std::int64_t def = 1'000'000'000'000LL); + + std::int32_t + initMaxPages(std::int32_t def); + + std::int64_t + setGas(std::int64_t gas); + + std::int64_t + getGas(); + + // for host functions usage + wmem + getMem() const; + + int32_t + allocate(int32_t size); + + void* + newTrap(std::string_view msg = {}); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Expected runEscrowWasm( - std::vector const& wasmCode, - std::string const& funcName, + Bytes const& wasmCode, + std::string_view funcName, HostFunctions* hfs, uint64_t gasLimit); } // namespace ripple -#endif // RIPPLE_APP_MISC_WASMVM_H_INLCUDED