mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 10:35:50 +00:00
1
.github/actions/dependencies/action.yml
vendored
1
.github/actions/dependencies/action.yml
vendored
@@ -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: |
|
||||
|
||||
1
.github/workflows/nix.yml
vendored
1
.github/workflows/nix.yml
vendored
@@ -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: |
|
||||
|
||||
11
BUILD.md
11
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -65,7 +65,7 @@ target_link_libraries(xrpl.imports.main
|
||||
xrpl.libpb
|
||||
xxHash::xxhash
|
||||
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>
|
||||
wasmedge::wasmedge
|
||||
wamr::wamr
|
||||
)
|
||||
|
||||
include(add_module)
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
6
external/wamr/conandata.yml
vendored
Normal file
6
external/wamr/conandata.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
patches:
|
||||
2.2.0:
|
||||
- patch_description: add metering to iwasm interpreter
|
||||
patch_file: patches/ripp_metering.patch
|
||||
patch_type: conan
|
||||
|
||||
90
external/wamr/conanfile.py
vendored
Normal file
90
external/wamr/conanfile.py
vendored
Normal file
@@ -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"
|
||||
|
||||
417
external/wamr/patches/ripp_metering.patch
vendored
Normal file
417
external/wamr/patches/ripp_metering.patch
vendored
Normal file
@@ -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:
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpld/app/misc/WasmVM.h>
|
||||
#include <xrpld/app/tx/applySteps.h>
|
||||
#include <xrpld/ledger/Dir.h>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,105 +21,21 @@
|
||||
|
||||
#include <xrpld/app/misc/WasmVM.h>
|
||||
|
||||
#include <wasmedge/wasmedge.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <iwasm/wasm_c_api.h>
|
||||
|
||||
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<WasmImportFunc> imports;
|
||||
WasmImpFunc<Add_proto>(
|
||||
imports, "func-add", reinterpret_cast<void*>(&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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
754
src/xrpld/app/misc/WamrVM.cpp
Normal file
754
src/xrpld/app/misc/WamrVM.cpp
Normal file
@@ -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 <xrpld/app/misc/WamrVM.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
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<uint32_t>(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<bool>(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<std::uint8_t*>(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<decltype(wasmBin)>::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<WasmImportFunc> 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<WasmImportFunc> const& imports)
|
||||
{
|
||||
wasm_importtype_vec_t importTypes = WASM_EMPTY_VEC;
|
||||
wasm_module_imports(module.get(), &importTypes);
|
||||
std::
|
||||
unique_ptr<wasm_importtype_vec_t, decltype(&wasm_importtype_vec_delete)>
|
||||
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<wasm_functype_t, decltype(&wasm_functype_delete)>;
|
||||
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<wasm_func_callback_with_env_t>(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<WasmImportFunc> 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<ModuleWrapper>(
|
||||
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<wasm_val_t>
|
||||
WamrEngine::convertParams(std::vector<WasmParam> const& params)
|
||||
{
|
||||
std::vector<wasm_val_t> 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<wasm_val_t>& 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<wasm_val_t>& in, int64_t p)
|
||||
{
|
||||
in.emplace_back();
|
||||
auto& el(in.back());
|
||||
el = WASM_I64_VAL(p);
|
||||
}
|
||||
|
||||
template <int NR, class... Types>
|
||||
WamrResult
|
||||
WamrEngine::call(std::string_view func, Types... args)
|
||||
{
|
||||
// Lookup our export function
|
||||
auto* f = getFunc(func);
|
||||
return call<NR>(f, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <int NR, class... Types>
|
||||
WamrResult
|
||||
WamrEngine::call(wasm_func_t* func, Types... args)
|
||||
{
|
||||
std::vector<wasm_val_t> in;
|
||||
return call<NR>(func, in, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <int NR, class... Types>
|
||||
WamrResult
|
||||
WamrEngine::call(wasm_func_t* func, std::vector<wasm_val_t>& 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<decltype(in)>::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 <int NR, class... Types>
|
||||
WamrResult
|
||||
WamrEngine::call(
|
||||
wasm_func_t* func,
|
||||
std::vector<wasm_val_t>& in,
|
||||
std::int32_t p,
|
||||
Types... args)
|
||||
{
|
||||
add_param(in, p);
|
||||
return call<NR>(func, in, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <int NR, class... Types>
|
||||
WamrResult
|
||||
WamrEngine::call(
|
||||
wasm_func_t* func,
|
||||
|
||||
std::vector<wasm_val_t>& in,
|
||||
std::int64_t p,
|
||||
Types... args)
|
||||
{
|
||||
add_param(in, p);
|
||||
return call<NR>(func, in, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <int NR, class... Types>
|
||||
WamrResult
|
||||
WamrEngine::call(
|
||||
wasm_func_t* func,
|
||||
std::vector<wasm_val_t>& in,
|
||||
uint8_t const* d,
|
||||
std::size_t sz,
|
||||
Types... args)
|
||||
{
|
||||
auto res = call<1>(W_ALLOC, static_cast<int32_t>(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<int32_t>(sz));
|
||||
return call<NR>(func, in, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <int NR, class... Types>
|
||||
WamrResult
|
||||
WamrEngine::call(
|
||||
wasm_func_t* func,
|
||||
std::vector<wasm_val_t>& in,
|
||||
wbytes const& p,
|
||||
Types... args)
|
||||
{
|
||||
return call<NR>(func, in, p.data(), p.size(), std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
Expected<int32_t, TER>
|
||||
WamrEngine::run(
|
||||
wbytes const& wasmCode,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmImportFunc> const& imports,
|
||||
std::vector<WasmParam> const& params)
|
||||
{
|
||||
try
|
||||
{
|
||||
return runHlp(wasmCode, funcName, imports, params);
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
return Unexpected<TER>(tecFAILED_PROCESSING);
|
||||
}
|
||||
|
||||
Expected<int32_t, TER>
|
||||
WamrEngine::runHlp(
|
||||
wbytes const& wasmCode,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmImportFunc> const& imports,
|
||||
std::vector<WasmParam> const& params)
|
||||
{
|
||||
// Create and instantiate the module.
|
||||
if (!wasmCode.empty())
|
||||
{
|
||||
int const m = addModule(wasmCode, true, imports);
|
||||
if (m < 0)
|
||||
return Unexpected<TER>(tecFAILED_PROCESSING);
|
||||
}
|
||||
|
||||
if (!module)
|
||||
return Unexpected<TER>(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<TER>(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<int32_t>(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
|
||||
264
src/xrpld/app/misc/WamrVM.h
Normal file
264
src/xrpld/app/misc/WamrVM.h
Normal file
@@ -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 <xrpld/app/misc/WasmVM.h>
|
||||
|
||||
#include <iwasm/wasm_c_api.h>
|
||||
#include <iwasm/wasm_export.h>
|
||||
|
||||
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<wasm_module_t, decltype(&wasm_module_delete)>;
|
||||
using InstancePtr = std::unique_ptr<wasm_instance_t, decltype(&wasm_instance_delete)>;
|
||||
|
||||
// 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<WasmImportFunc> 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<WasmImportFunc> const& imports);
|
||||
};
|
||||
|
||||
class WamrEngine
|
||||
{
|
||||
std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)> engine;
|
||||
std::unique_ptr<wasm_store_t, decltype(&wasm_store_delete)> store;
|
||||
std::unique_ptr<ModuleWrapper> module;
|
||||
wasm_trap_t* trap = nullptr;
|
||||
std::int64_t defGas = -1;
|
||||
std::int32_t defMaxPages = -1;
|
||||
|
||||
public:
|
||||
WamrEngine();
|
||||
~WamrEngine() = default;
|
||||
|
||||
Expected<int32_t, TER>
|
||||
run(wbytes const& wasmCode,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmImportFunc> const& imports,
|
||||
std::vector<WasmParam> 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<int32_t, TER>
|
||||
runHlp(
|
||||
wbytes const& wasmCode,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmImportFunc> const& imports,
|
||||
std::vector<WasmParam> const& params);
|
||||
|
||||
int
|
||||
addModule(
|
||||
wbytes const& wasmCode,
|
||||
bool instantiate,
|
||||
std::vector<WasmImportFunc> 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<wasm_val_t>
|
||||
convertParams(std::vector<WasmParam> const& params);
|
||||
|
||||
void
|
||||
add_param(std::vector<wasm_val_t>& in, int32_t p);
|
||||
void
|
||||
add_param(std::vector<wasm_val_t>& in, int64_t p);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WamrResult
|
||||
call(std::string_view func, Types... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WamrResult
|
||||
call(wasm_func_t* func, Types... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WamrResult
|
||||
call(wasm_func_t* f, std::vector<wasm_val_t>& in);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WamrResult
|
||||
call(
|
||||
wasm_func_t* func,
|
||||
std::vector<wasm_val_t>& in,
|
||||
std::int32_t p,
|
||||
Types... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WamrResult
|
||||
call(
|
||||
wasm_func_t* func,
|
||||
std::vector<wasm_val_t>& in,
|
||||
std::int64_t p,
|
||||
Types... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WamrResult
|
||||
call(
|
||||
wasm_func_t* func,
|
||||
std::vector<wasm_val_t>& in,
|
||||
uint8_t const* d,
|
||||
std::size_t sz,
|
||||
Types... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WamrResult
|
||||
call(
|
||||
wasm_func_t* func,
|
||||
std::vector<wasm_val_t>& in,
|
||||
wbytes const& p,
|
||||
Types... args);
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
@@ -19,14 +19,12 @@
|
||||
#ifndef RIPPLE_APP_MISC_WASMHOSTFUNCIMPL_H_INLCUDED
|
||||
#define RIPPLE_APP_MISC_WASMHOSTFUNCIMPL_H_INLCUDED
|
||||
|
||||
#include <xrpld/app/misc/WasmVM.h>
|
||||
#include <xrpld/app/tx/detail/ApplyContext.h>
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include "xrpl/basics/base_uint.h"
|
||||
#include "xrpld/app/misc/WasmVM.h"
|
||||
#include "xrpld/app/tx/detail/ApplyContext.h"
|
||||
#include <wasmedge/wasmedge.h>
|
||||
|
||||
namespace ripple {
|
||||
class WasmHostFunctionsImpl : public HostFunctions
|
||||
{
|
||||
|
||||
392
src/xrpld/app/misc/WasmHostFuncWrapper.cpp
Normal file
392
src/xrpld/app/misc/WasmHostFuncWrapper.cpp
Normal file
@@ -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 <xrpld/app/misc/WasmHostFuncWrapper.h>
|
||||
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
||||
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
wasm_trap_t*
|
||||
getLedgerSqn_wrap(
|
||||
void* env,
|
||||
wasm_val_vec_t const* params,
|
||||
wasm_val_vec_t* results)
|
||||
{
|
||||
auto* hf = reinterpret_cast<HostFunctions*>(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<HostFunctions*>(env);
|
||||
int32_t const ltime = hf->getParentLedgerTime();
|
||||
results->data[0] = WASM_I32_VAL(ltime);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Expected<Bytes, std::string>
|
||||
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<std::string>("No memory exported");
|
||||
|
||||
if (mem.s <= fnameOffset + fnameLen)
|
||||
return Unexpected<std::string>("Memory access failed");
|
||||
Bytes fname(mem.p + fnameOffset, mem.p + fnameOffset + fnameLen);
|
||||
return fname;
|
||||
}
|
||||
|
||||
static Expected<std::string, wasm_trap_t*>
|
||||
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<wasm_trap_t*>(
|
||||
reinterpret_cast<wasm_trap_t*>(vm.newTrap(dataRes.error())));
|
||||
}
|
||||
}
|
||||
|
||||
static Expected<int32_t, std::string>
|
||||
setData(Bytes const& data)
|
||||
{
|
||||
auto& vm = WasmEngine::instance();
|
||||
auto mem = vm.getMem();
|
||||
if (!mem.s)
|
||||
return Unexpected<std::string>("No memory exported");
|
||||
|
||||
int32_t const dataLen = static_cast<int32_t>(data.size());
|
||||
int32_t const dataPtr = vm.allocate(dataLen);
|
||||
if (!dataPtr)
|
||||
return Unexpected<std::string>("Allocation error");
|
||||
memcpy(mem.p + dataPtr, data.data(), dataLen);
|
||||
|
||||
auto retPtr = vm.allocate(8);
|
||||
if (!retPtr)
|
||||
return Unexpected<std::string>("Allocation error");
|
||||
int32_t* retData = reinterpret_cast<int32_t*>(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<HostFunctions*>(env);
|
||||
|
||||
auto fname = getFieldName(params, 0);
|
||||
if (!fname)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto fieldData = hf->getTxField(fname.value());
|
||||
if (!fieldData)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap("Field not found"));
|
||||
|
||||
auto pointer = setData(fieldData.value());
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
|
||||
int32_t const type = params->data[0].of.i32;
|
||||
auto lkData = getParameterData(params, 1);
|
||||
if (!lkData)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto fname = getFieldName(params, 3);
|
||||
if (!fname)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto fieldData =
|
||||
hf->getLedgerEntryField(type, lkData.value(), fname.value());
|
||||
if (!fieldData)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
auto pointer = setData(fieldData.value());
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
|
||||
auto fname = getFieldName(params, 0);
|
||||
if (!fname)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto fieldData = hf->getCurrentLedgerEntryField(fname.value());
|
||||
if (!fieldData)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto pointer = setData(fieldData.value());
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
auto account = getFieldName(params, 0);
|
||||
if (!account)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto nftId = getFieldName(params, 2);
|
||||
if (!nftId)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto nftURI = hf->getNFT(account.value(), nftId.value());
|
||||
if (!nftURI)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto pointer = setData(nftURI.value());
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
|
||||
auto account = getFieldName(params, 0);
|
||||
if (!account)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto keylet = hf->accountKeylet(account.value());
|
||||
if (!keylet)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto pointer = setData(keylet.value());
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
|
||||
auto subject = getFieldName(params, 0);
|
||||
if (!subject)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto issuer = getFieldName(params, 2);
|
||||
if (!issuer)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto credentialType = getFieldName(params, 4);
|
||||
if (!credentialType)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto keylet = hf->credentialKeylet(
|
||||
subject.value(), issuer.value(), credentialType.value());
|
||||
if (!keylet)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto pointer = setData(keylet.value());
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
|
||||
auto account = getFieldName(params, 0);
|
||||
if (!account)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
int32_t const sequence = params->data[2].of.i32;
|
||||
|
||||
auto keylet = hf->escrowKeylet(account.value(), sequence);
|
||||
if (!keylet)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto pointer = setData(keylet.value());
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
|
||||
auto account = getFieldName(params, 0);
|
||||
if (!account)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto documentId = params->data[2].of.i32;
|
||||
|
||||
auto keylet = hf->escrowKeylet(account.value(), documentId);
|
||||
if (!keylet)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto pointer = setData(keylet.value());
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
|
||||
auto fname = getParameterData(params, 0);
|
||||
if (!fname)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
if (!hf->updateData(fname.value()))
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
|
||||
auto fname = getParameterData(params, 0);
|
||||
if (!fname)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
auto hres = hf->computeSha512HalfHash(fname.value());
|
||||
Bytes digest{hres.begin(), hres.end()};
|
||||
auto pointer = setData(digest);
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(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<HostFunctions*>(env);
|
||||
|
||||
auto f = getParameterData(params, 0);
|
||||
if (!f)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
std::string s(f->begin(), f->end());
|
||||
if (s.size() < 4096)
|
||||
std::cout << s << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
113
src/xrpld/app/misc/WasmHostFuncWrapper.h
Normal file
113
src/xrpld/app/misc/WasmHostFuncWrapper.h
Normal file
@@ -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 <xrpld/app/misc/WamrVM.h>
|
||||
|
||||
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
|
||||
@@ -17,730 +17,118 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/app/misc/WasmVM.h>
|
||||
#include <xrpld/app/misc/WamrVM.h>
|
||||
#include <xrpld/app/misc/WasmHostFuncWrapper.h>
|
||||
|
||||
#include "xrpl/protocol/AccountID.h"
|
||||
#include "xrpl/protocol/LedgerFormats.h"
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
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<Bytes, WasmEdge_Result>
|
||||
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<WasmEdge_Result>(Res);
|
||||
}
|
||||
}
|
||||
|
||||
Expected<std::string, WasmEdge_Result>
|
||||
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<WasmEdge_Result>(dataRes.error());
|
||||
}
|
||||
}
|
||||
|
||||
Expected<WasmEdge_Value, WasmEdge_Result>
|
||||
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>(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<EscrowResult, TER>
|
||||
runEscrowWasm(
|
||||
std::vector<uint8_t> 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<WasmImportFunc> 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<TER>(ret.error());
|
||||
std::int64_t const egas = vm.getGas();
|
||||
std::uint64_t const spent = static_cast<std::uint64_t>(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<TER>(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<TER>(tecFAILED_PROCESSING);
|
||||
}
|
||||
WasmEdge_Result validateRes = WasmEdge_VMValidate(VMCxt);
|
||||
if (!WasmEdge_ResultOK(validateRes))
|
||||
{
|
||||
printf("validate error\n");
|
||||
return Unexpected<TER>(tecFAILED_PROCESSING);
|
||||
}
|
||||
WasmEdge_Result instantiateRes = WasmEdge_VMInstantiate(VMCxt);
|
||||
if (!WasmEdge_ResultOK(instantiateRes))
|
||||
{
|
||||
printf("instantiate error\n");
|
||||
return Unexpected<TER>(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<TER>(tecFAILED_PROCESSING);
|
||||
return EscrowResult{static_cast<bool>(ret.value()), spent};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
WasmEngine::WasmEngine() : impl(std::make_unique<WamrEngine>())
|
||||
{
|
||||
}
|
||||
|
||||
WasmEngine&
|
||||
WasmEngine::instance()
|
||||
{
|
||||
static WasmEngine e;
|
||||
return e;
|
||||
}
|
||||
|
||||
Expected<int32_t, TER>
|
||||
WasmEngine::run(
|
||||
wbytes const& wasmCode,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmImportFunc> const& imports,
|
||||
std::vector<WasmParam> 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
|
||||
|
||||
@@ -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 <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include "xrpl/basics/base_uint.h"
|
||||
#include <wasmedge/wasmedge.h>
|
||||
#include <boost/function_types/function_arity.hpp>
|
||||
#include <boost/function_types/parameter_types.hpp>
|
||||
#include <boost/function_types/result_type.hpp>
|
||||
#include <boost/mpl/vector.hpp>
|
||||
|
||||
// #include <iwasm/wasm_c_api.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
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<std::uint8_t>;
|
||||
struct wmem
|
||||
{
|
||||
std::uint8_t* p = nullptr;
|
||||
std::size_t s = 0;
|
||||
};
|
||||
|
||||
const uint32_t MAX_PAGES = 128; // 8MB = 64KB*128
|
||||
|
||||
typedef std::vector<uint8_t> 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<WasmTypes> result;
|
||||
std::vector<WasmTypes> params;
|
||||
void* udata = nullptr;
|
||||
// wasm_func_callback_with_env_t
|
||||
void* wrap = nullptr;
|
||||
};
|
||||
|
||||
#define WASM_IMPORT_FUNC(v, f, ...) \
|
||||
WasmImpFunc<f##_proto>( \
|
||||
v, #f, reinterpret_cast<void*>(&f##_wrap), ##__VA_ARGS__);
|
||||
|
||||
template <int N, int C, typename mpl>
|
||||
void
|
||||
WasmImpArgs(WasmImportFunc& e)
|
||||
{
|
||||
if constexpr (N < C)
|
||||
{
|
||||
using at = typename boost::mpl::at_c<mpl, N>::type;
|
||||
if constexpr (std::is_pointer_v<at>)
|
||||
e.params.push_back(WT_I32);
|
||||
else if constexpr (std::is_same_v<at, std::int32_t>)
|
||||
e.params.push_back(WT_I32);
|
||||
else if constexpr (std::is_same_v<at, std::int64_t>)
|
||||
e.params.push_back(WT_I64);
|
||||
else if constexpr (std::is_same_v<at, float>)
|
||||
e.params.push_back(WT_F32);
|
||||
else if constexpr (std::is_same_v<at, double>)
|
||||
e.params.push_back(WT_F64);
|
||||
else
|
||||
static_assert(std::is_pointer_v<at>, "Unsupported argument type");
|
||||
|
||||
return WasmImpArgs<N + 1, C, mpl>(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename rt>
|
||||
void
|
||||
WasmImpRet(WasmImportFunc& e)
|
||||
{
|
||||
if constexpr (std::is_pointer_v<rt>)
|
||||
e.result = WT_I32;
|
||||
else if constexpr (std::is_same_v<rt, std::int32_t>)
|
||||
e.result = WT_I32;
|
||||
else if constexpr (std::is_same_v<rt, std::int64_t>)
|
||||
e.result = WT_I64;
|
||||
else if constexpr (std::is_same_v<rt, float>)
|
||||
e.result = WT_F32;
|
||||
else if constexpr (std::is_same_v<rt, double>)
|
||||
e.result = WT_F64;
|
||||
else if constexpr (std::is_void_v<rt>)
|
||||
e.result.reset();
|
||||
#if (defined(__GNUC__) && (__GNUC__ >= 14)) || \
|
||||
((defined(__clang_major__)) && (__clang_major__ >= 18))
|
||||
else
|
||||
static_assert(false, "Unsupported return type");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void
|
||||
WasmImpFuncHelper(WasmImportFunc& e)
|
||||
{
|
||||
using rt = typename bft::result_type<F>::type;
|
||||
using pt = typename bft::parameter_types<F>::type;
|
||||
// typename boost::mpl::at_c<mpl, N>::type
|
||||
|
||||
WasmImpRet<rt>(e);
|
||||
WasmImpArgs<0, bft::function_arity<F>::value, pt>(e);
|
||||
// WasmImpWrap(e, std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void
|
||||
WasmImpFunc(
|
||||
std::vector<WasmImportFunc>& 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<F>(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 <class... Types>
|
||||
inline std::vector<WasmParam>
|
||||
wasmParams(Types... args)
|
||||
{
|
||||
std::vector<WasmParam> v;
|
||||
v.reserve(sizeof...(args));
|
||||
return wasmParams(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline std::vector<WasmParam>
|
||||
wasmParams(std::vector<WasmParam>& v, std::int32_t p, Types... args)
|
||||
{
|
||||
v.push_back({.type = WT_I32, .of = {.i32 = p}});
|
||||
return wasmParams(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline std::vector<WasmParam>
|
||||
wasmParams(std::vector<WasmParam>& v, std::int64_t p, Types... args)
|
||||
{
|
||||
v.push_back({.type = WT_I64, .of = {.i64 = p}});
|
||||
return wasmParams(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline std::vector<WasmParam>
|
||||
wasmParams(std::vector<WasmParam>& v, float p, Types... args)
|
||||
{
|
||||
v.push_back({.type = WT_F32, .of = {.f32 = p}});
|
||||
return wasmParams(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline std::vector<WasmParam>
|
||||
wasmParams(std::vector<WasmParam>& v, double p, Types... args)
|
||||
{
|
||||
v.push_back({.type = WT_F64, .of = {.f64 = p}});
|
||||
return wasmParams(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
inline std::vector<WasmParam>
|
||||
wasmParams(std::vector<WasmParam>& v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class WamrEngine;
|
||||
class WasmEngine
|
||||
{
|
||||
std::unique_ptr<WamrEngine> 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<int32_t, TER>
|
||||
run(wbytes const& wasmCode,
|
||||
std::string_view funcName = {},
|
||||
std::vector<WasmImportFunc> const& imports = {},
|
||||
std::vector<WasmParam> 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<EscrowResult, TER>
|
||||
runEscrowWasm(
|
||||
std::vector<uint8_t> 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
|
||||
|
||||
Reference in New Issue
Block a user