Switch to WAMR (#5416)

* Switch to WAMR
This commit is contained in:
Olek
2025-05-01 18:02:06 -04:00
committed by GitHub
parent f03b5883bd
commit bb9bc764bc
18 changed files with 2454 additions and 829 deletions

View File

@@ -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: |

View File

@@ -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: |

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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
View 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
View 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"

View 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:

View File

@@ -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));
}

View File

@@ -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;
}
}

View 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(&params, &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
View 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

View File

@@ -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
{

View 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

View 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

View File

@@ -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

View File

@@ -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