mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 03:26:01 +00:00
Wasmi engine
This commit is contained in:
1
BUILD.md
1
BUILD.md
@@ -144,6 +144,7 @@ git fetch origin master
|
|||||||
git checkout master
|
git checkout master
|
||||||
conan export --version 1.1.10 recipes/snappy/all
|
conan export --version 1.1.10 recipes/snappy/all
|
||||||
conan export --version 4.0.3 recipes/soci/all
|
conan export --version 4.0.3 recipes/soci/all
|
||||||
|
conan export --version 0.42.1 external/wasmi
|
||||||
rm -rf .git
|
rm -rf .git
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ endif()
|
|||||||
|
|
||||||
find_package(nudb REQUIRED)
|
find_package(nudb REQUIRED)
|
||||||
find_package(date REQUIRED)
|
find_package(date REQUIRED)
|
||||||
|
find_package(wasmi REQUIRED)
|
||||||
find_package(xxHash REQUIRED)
|
find_package(xxHash REQUIRED)
|
||||||
|
|
||||||
target_link_libraries(xrpl_libs INTERFACE
|
target_link_libraries(xrpl_libs INTERFACE
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ target_link_libraries(xrpl.imports.main
|
|||||||
Xrpl::opts
|
Xrpl::opts
|
||||||
Xrpl::syslibs
|
Xrpl::syslibs
|
||||||
secp256k1::secp256k1
|
secp256k1::secp256k1
|
||||||
|
wasmi::wasmi
|
||||||
xrpl.libpb
|
xrpl.libpb
|
||||||
xxHash::xxhash
|
xxHash::xxhash
|
||||||
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>
|
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>
|
||||||
|
|||||||
59
conan.lock
59
conan.lock
@@ -1,59 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.5",
|
|
||||||
"requires": [
|
|
||||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
|
|
||||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1756234289.683",
|
|
||||||
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1756234266.869",
|
|
||||||
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1756234262.318",
|
|
||||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246",
|
|
||||||
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1759820024.194",
|
|
||||||
"re2/20230301#dfd6e2bf050eb90ddd8729cfb4c844a4%1756234257.976",
|
|
||||||
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
|
|
||||||
"openssl/3.5.4#a1d5835cc6ed5c5b8f3cd5b9b5d24205%1760106486.594",
|
|
||||||
"nudb/2.0.9#c62cfd501e57055a7e0d8ee3d5e5427d%1756234237.107",
|
|
||||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1756234228.999",
|
|
||||||
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1756223727.64",
|
|
||||||
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1756230911.03",
|
|
||||||
"libarchive/3.8.1#5cf685686322e906cb42706ab7e099a8%1756234256.696",
|
|
||||||
"jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244",
|
|
||||||
"grpc/1.50.1#02291451d1e17200293a409410d1c4e1%1756234248.958",
|
|
||||||
"doctest/2.4.12#eb9fb352fb2fdfc8abb17ec270945165%1749889324.069",
|
|
||||||
"date/3.0.4#f74bbba5a08fa388256688743136cb6f%1756234217.493",
|
|
||||||
"c-ares/1.34.5#b78b91e7cfb1f11ce777a285bbf169c6%1756234217.915",
|
|
||||||
"bzip2/1.0.8#00b4a4658791c1f06914e087f0e792f5%1756234261.716",
|
|
||||||
"boost/1.88.0#8852c0b72ce8271fb8ff7c53456d4983%1756223752.326",
|
|
||||||
"abseil/20230802.1#f0f91485b111dc9837a68972cb19ca7b%1756234220.907"
|
|
||||||
],
|
|
||||||
"build_requires": [
|
|
||||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
|
|
||||||
"strawberryperl/5.32.1.1#707032463aa0620fa17ec0d887f5fe41%1756234281.733",
|
|
||||||
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
|
|
||||||
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1756234232.901",
|
|
||||||
"msys2/cci.latest#5b73b10144f73cc5bfe0572ed9be39e1%1751977009.857",
|
|
||||||
"m4/1.4.19#f119296e5c4772b3bb7ab060ae8f417b%1760707875.678",
|
|
||||||
"cmake/3.31.8#dde3bde00bb843687e55aea5afa0e220%1756234232.89",
|
|
||||||
"b2/5.3.3#107c15377719889654eb9a162a673975%1756234226.28",
|
|
||||||
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
|
|
||||||
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86"
|
|
||||||
],
|
|
||||||
"python_requires": [],
|
|
||||||
"overrides": {
|
|
||||||
"protobuf/3.21.12": [
|
|
||||||
null,
|
|
||||||
"protobuf/3.21.12"
|
|
||||||
],
|
|
||||||
"lz4/1.9.4": [
|
|
||||||
"lz4/1.10.0"
|
|
||||||
],
|
|
||||||
"boost/1.83.0": [
|
|
||||||
"boost/1.88.0"
|
|
||||||
],
|
|
||||||
"sqlite3/[>=3.44 <4]": [
|
|
||||||
"sqlite3/3.49.1"
|
|
||||||
],
|
|
||||||
"lz4/[>=1.9.4 <2]": [
|
|
||||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"config_requires": []
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ from conan import ConanFile, __version__ as conan_version
|
|||||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
class Xrpl(ConanFile):
|
class Xrpl(ConanFile):
|
||||||
name = 'xrpl'
|
name = 'xrpl'
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ class Xrpl(ConanFile):
|
|||||||
'nudb/2.0.9',
|
'nudb/2.0.9',
|
||||||
'openssl/3.5.4',
|
'openssl/3.5.4',
|
||||||
'soci/4.0.3',
|
'soci/4.0.3',
|
||||||
|
'wasmi/0.42.1',
|
||||||
'zlib/1.3.1',
|
'zlib/1.3.1',
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -133,6 +135,7 @@ class Xrpl(ConanFile):
|
|||||||
self.folders.generators = 'build/generators'
|
self.folders.generators = 'build/generators'
|
||||||
|
|
||||||
generators = 'CMakeDeps'
|
generators = 'CMakeDeps'
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
tc = CMakeToolchain(self)
|
tc = CMakeToolchain(self)
|
||||||
tc.variables['tests'] = self.options.tests
|
tc.variables['tests'] = self.options.tests
|
||||||
@@ -190,6 +193,7 @@ class Xrpl(ConanFile):
|
|||||||
'protobuf::libprotobuf',
|
'protobuf::libprotobuf',
|
||||||
'soci::soci',
|
'soci::soci',
|
||||||
'sqlite3::sqlite',
|
'sqlite3::sqlite',
|
||||||
|
'wasmi::wasmi',
|
||||||
'xxhash::xxhash',
|
'xxhash::xxhash',
|
||||||
'zlib::zlib',
|
'zlib::zlib',
|
||||||
]
|
]
|
||||||
|
|||||||
5
external/wasmi/conandata.yml
vendored
Normal file
5
external/wasmi/conandata.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
patches:
|
||||||
|
0.42.1:
|
||||||
|
- patch_description: install only static lib (to prevent conflict with shared)
|
||||||
|
patch_file: patches/wasmi.patch
|
||||||
|
patch_type: conan
|
||||||
90
external/wasmi/conanfile.py
vendored
Normal file
90
external/wasmi/conanfile.py
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
from conan 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,
|
||||||
|
)
|
||||||
|
from conan.tools.scm import Git
|
||||||
|
|
||||||
|
import os
|
||||||
|
# import json
|
||||||
|
|
||||||
|
required_conan_version = ">=1.55.0"
|
||||||
|
|
||||||
|
class WasmiConan(ConanFile):
|
||||||
|
name = "wasmi"
|
||||||
|
license = "Apache License v2.0"
|
||||||
|
url = "https://github.com/wasmi-labs/wasmi.git"
|
||||||
|
description = "WebAssembly (Wasm) interpreter"
|
||||||
|
package_type = "library"
|
||||||
|
settings = "os", "compiler", "build_type", "arch"
|
||||||
|
options = {"shared": [True, False]}
|
||||||
|
default_options = {"shared": False}
|
||||||
|
# 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
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def layout(self):
|
||||||
|
cmake_layout(self, src_folder="src")
|
||||||
|
|
||||||
|
|
||||||
|
def source(self):
|
||||||
|
git = Git(self)
|
||||||
|
git.fetch_commit(
|
||||||
|
url="https://github.com/wasmi-labs/wasmi.git",
|
||||||
|
commit="f628a7a86c9715f2c306f6ef9aea1cc2bdca5fa7",
|
||||||
|
)
|
||||||
|
#get(self, **self.conan_data["sources"][self.version], strip_root=True)
|
||||||
|
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
tc = CMakeToolchain(self)
|
||||||
|
|
||||||
|
tc.variables["CMAKE_CXX_STANDARD"] = 20
|
||||||
|
tc.variables["BUILD_SHARED_LIBS"] = 0
|
||||||
|
|
||||||
|
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(build_script_folder=os.path.join(self.source_folder, "crates", "c_api"))
|
||||||
|
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 = ["wasmi"]
|
||||||
|
self.cpp_info.names["cmake_find_package"] = "wasmi"
|
||||||
|
self.cpp_info.names["cmake_find_package_multi"] = "wasmi"
|
||||||
|
|
||||||
15
external/wasmi/patches/static_only.patch
vendored
Normal file
15
external/wasmi/patches/static_only.patch
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
diff --git a/crates/c_api/CMakeLists.txt b/crates/c_api/CMakeLists.txt
|
||||||
|
index b15c787..97dd294 100644
|
||||||
|
--- a/crates/c_api/CMakeLists.txt
|
||||||
|
+++ b/crates/c_api/CMakeLists.txt
|
||||||
|
@@ -43,6 +43,10 @@ endif()
|
||||||
|
list(TRANSFORM WASMI_SHARED_FILES PREPEND ${WASMI_TARGET_DIR}/)
|
||||||
|
list(TRANSFORM WASMI_STATIC_FILES PREPEND ${WASMI_TARGET_DIR}/)
|
||||||
|
|
||||||
|
+if(NOT BUILD_SHARED_LIBS)
|
||||||
|
+ set(WASMI_SHARED_FILES )
|
||||||
|
+endif()
|
||||||
|
+
|
||||||
|
# Instructions on how to build and install the Wasmi Rust crate.
|
||||||
|
find_program(WASMI_CARGO_BINARY cargo REQUIRED)
|
||||||
|
include(ExternalProject)
|
||||||
109
external/wasmi/patches/wasmi.patch
vendored
Normal file
109
external/wasmi/patches/wasmi.patch
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
diff --git a/crates/c_api/CMakeLists.txt b/crates/c_api/CMakeLists.txt
|
||||||
|
index b15c787a..0d0a04b7 100644
|
||||||
|
--- a/crates/c_api/CMakeLists.txt
|
||||||
|
+++ b/crates/c_api/CMakeLists.txt
|
||||||
|
@@ -43,6 +43,11 @@ endif()
|
||||||
|
list(TRANSFORM WASMI_SHARED_FILES PREPEND ${WASMI_TARGET_DIR}/)
|
||||||
|
list(TRANSFORM WASMI_STATIC_FILES PREPEND ${WASMI_TARGET_DIR}/)
|
||||||
|
|
||||||
|
+if(NOT BUILD_SHARED_LIBS)
|
||||||
|
+ set(WASMI_SHARED_FILES )
|
||||||
|
+endif()
|
||||||
|
+
|
||||||
|
+
|
||||||
|
# Instructions on how to build and install the Wasmi Rust crate.
|
||||||
|
find_program(WASMI_CARGO_BINARY cargo REQUIRED)
|
||||||
|
include(ExternalProject)
|
||||||
|
diff --git a/crates/c_api/include/wasm.h b/crates/c_api/include/wasm.h
|
||||||
|
index 5ee617ff..0199192d 100644
|
||||||
|
--- a/crates/c_api/include/wasm.h
|
||||||
|
+++ b/crates/c_api/include/wasm.h
|
||||||
|
@@ -146,6 +146,13 @@ WASM_DECLARE_OWN(store)
|
||||||
|
|
||||||
|
WASM_API_EXTERN own wasm_store_t* wasm_store_new(wasm_engine_t*);
|
||||||
|
|
||||||
|
+// Store fuel functions (forward declarations)
|
||||||
|
+struct wasmi_error;
|
||||||
|
+
|
||||||
|
+WASM_API_EXTERN struct wasmi_error* wasm_store_get_fuel(const wasm_store_t*, uint64_t* fuel);
|
||||||
|
+WASM_API_EXTERN struct wasmi_error* wasm_store_set_fuel(wasm_store_t*, uint64_t fuel);
|
||||||
|
+//WASM_API_EXTERN void *wasm_store_get_data(const wasm_store_t*);
|
||||||
|
+//WASM_API_EXTERN void wasm_store_set_data(wasm_store_t*, void *data);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Type Representations
|
||||||
|
diff --git a/crates/c_api/include/wasmi.h b/crates/c_api/include/wasmi.h
|
||||||
|
index 2caffa37..0c0584ec 100644
|
||||||
|
--- a/crates/c_api/include/wasmi.h
|
||||||
|
+++ b/crates/c_api/include/wasmi.h
|
||||||
|
@@ -10,7 +10,7 @@
|
||||||
|
/**
|
||||||
|
* \brief Wasmi version string.
|
||||||
|
*/
|
||||||
|
-#define WASMI_VERSION "0.35.0"
|
||||||
|
+#define WASMI_VERSION "0.42.1"
|
||||||
|
/**
|
||||||
|
* \brief Wasmi major version number.
|
||||||
|
*/
|
||||||
|
@@ -18,10 +18,10 @@
|
||||||
|
/**
|
||||||
|
* \brief Wasmi minor version number.
|
||||||
|
*/
|
||||||
|
-#define WASMI_VERSION_MINOR 35
|
||||||
|
+#define WASMI_VERSION_MINOR 42
|
||||||
|
/**
|
||||||
|
* \brief Wasmi patch version number.
|
||||||
|
*/
|
||||||
|
-#define WASMI_VERSION_PATCH 0
|
||||||
|
+#define WASMI_VERSION_PATCH 1
|
||||||
|
|
||||||
|
#endif // WASMI_H
|
||||||
|
diff --git a/crates/c_api/src/store.rs b/crates/c_api/src/store.rs
|
||||||
|
index 56d4898f..543dbff8 100644
|
||||||
|
--- a/crates/c_api/src/store.rs
|
||||||
|
+++ b/crates/c_api/src/store.rs
|
||||||
|
@@ -175,3 +175,44 @@ pub extern "C" fn wasmi_context_set_fuel(
|
||||||
|
) -> Option<Box<wasmi_error_t>> {
|
||||||
|
crate::handle_result(store.set_fuel(fuel), |()| {})
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
+////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
+////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
+////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
+
|
||||||
|
+/// Returns the current fuel of the wasm store context in `fuel`.
|
||||||
|
+///
|
||||||
|
+/// Wraps [`Store::get_fuel`].
|
||||||
|
+///
|
||||||
|
+/// # Errors
|
||||||
|
+///
|
||||||
|
+/// If [`Store::get_fuel`] errors.
|
||||||
|
+#[no_mangle]
|
||||||
|
+pub extern "C" fn wasm_store_get_fuel(
|
||||||
|
+ store: &wasm_store_t,
|
||||||
|
+ fuel: &mut u64,
|
||||||
|
+) -> Option<Box<wasmi_error_t>> {
|
||||||
|
+ let context = unsafe { store.inner.context() };
|
||||||
|
+ crate::handle_result(context.get_fuel(), |amt| {
|
||||||
|
+ *fuel = amt;
|
||||||
|
+ })
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Sets the current fuel of the wasm store context to `fuel`.
|
||||||
|
+///
|
||||||
|
+/// Wraps [`Store::set_fuel`].
|
||||||
|
+///
|
||||||
|
+/// # Errors
|
||||||
|
+///
|
||||||
|
+/// If [`Store::set_fuel`] errors.
|
||||||
|
+#[no_mangle]
|
||||||
|
+pub extern "C" fn wasm_store_set_fuel(
|
||||||
|
+ store: &mut wasm_store_t,
|
||||||
|
+ fuel: u64,
|
||||||
|
+) -> Option<Box<wasmi_error_t>> {
|
||||||
|
+
|
||||||
|
+ let mut context = unsafe { store.inner.context_mut() };
|
||||||
|
+ crate::handle_result(context.set_fuel(fuel), |()| {})
|
||||||
|
+}
|
||||||
|
+
|
||||||
@@ -341,6 +341,10 @@ abs(Number x) noexcept
|
|||||||
Number
|
Number
|
||||||
power(Number const& f, unsigned n);
|
power(Number const& f, unsigned n);
|
||||||
|
|
||||||
|
// logarithm with base 10
|
||||||
|
Number
|
||||||
|
lg(Number const& value);
|
||||||
|
|
||||||
// Returns f^(1/d)
|
// Returns f^(1/d)
|
||||||
// Uses Newton–Raphson iterations until the result stops changing
|
// Uses Newton–Raphson iterations until the result stops changing
|
||||||
// to find the root of the polynomial g(x) = x^d - f
|
// to find the root of the polynomial g(x) = x^d - f
|
||||||
|
|||||||
@@ -39,6 +39,13 @@ private:
|
|||||||
normalize();
|
normalize();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/* The range for the mantissa when normalized */
|
||||||
|
static std::int64_t constexpr minMantissa = 1000000000000000ull;
|
||||||
|
static std::int64_t constexpr maxMantissa = 9999999999999999ull;
|
||||||
|
/* The range for the exponent when normalized */
|
||||||
|
static int constexpr minExponent = -96;
|
||||||
|
static int constexpr maxExponent = 80;
|
||||||
|
|
||||||
IOUAmount() = default;
|
IOUAmount() = default;
|
||||||
explicit IOUAmount(Number const& other);
|
explicit IOUAmount(Number const& other);
|
||||||
IOUAmount(beast::Zero);
|
IOUAmount(beast::Zero);
|
||||||
|
|||||||
@@ -116,6 +116,13 @@ std::uint8_t constexpr vaultMaximumIOUScale = 18;
|
|||||||
* another vault; counted from 0 */
|
* another vault; counted from 0 */
|
||||||
std::uint8_t constexpr maxAssetCheckDepth = 5;
|
std::uint8_t constexpr maxAssetCheckDepth = 5;
|
||||||
|
|
||||||
|
/** The maximum length of a Data field in Escrow object that can be updated by
|
||||||
|
* Wasm code */
|
||||||
|
std::size_t constexpr maxWasmDataLength = 4 * 1024;
|
||||||
|
|
||||||
|
/** The maximum length of a parameters passed from Wasm code*/
|
||||||
|
std::size_t constexpr maxWasmParamLength = 1024;
|
||||||
|
|
||||||
/** A ledger index. */
|
/** A ledger index. */
|
||||||
using LedgerIndex = std::uint32_t;
|
using LedgerIndex = std::uint32_t;
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,8 @@ enum TEMcodes : TERUnderlyingType {
|
|||||||
temARRAY_TOO_LARGE,
|
temARRAY_TOO_LARGE,
|
||||||
temBAD_TRANSFER_FEE,
|
temBAD_TRANSFER_FEE,
|
||||||
temINVALID_INNER_BATCH,
|
temINVALID_INNER_BATCH,
|
||||||
|
|
||||||
|
temBAD_WASM,
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -166,6 +168,8 @@ enum TEFcodes : TERUnderlyingType {
|
|||||||
tefNO_TICKET,
|
tefNO_TICKET,
|
||||||
tefNFTOKEN_IS_NOT_TRANSFERABLE,
|
tefNFTOKEN_IS_NOT_TRANSFERABLE,
|
||||||
tefINVALID_LEDGER_FIX_TYPE,
|
tefINVALID_LEDGER_FIX_TYPE,
|
||||||
|
tefNO_WASM,
|
||||||
|
tefWASM_FIELD_NOT_INCLUDED,
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -347,6 +351,7 @@ enum TECcodes : TERUnderlyingType {
|
|||||||
// backward compatibility with historical data on non-prod networks, can be
|
// backward compatibility with historical data on non-prod networks, can be
|
||||||
// reclaimed after those networks reset.
|
// reclaimed after those networks reset.
|
||||||
tecNO_DELEGATE_PERMISSION = 198,
|
tecNO_DELEGATE_PERMISSION = 198,
|
||||||
|
tecWASM_REJECTED = 199,
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -604,6 +604,48 @@ power(Number const& f, unsigned n)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Continued fraction approximation of ln(x)
|
||||||
|
static Number
|
||||||
|
ln(Number const& x, unsigned iterations = 50)
|
||||||
|
{
|
||||||
|
if (x <= 0)
|
||||||
|
throw std::runtime_error("Not positive value");
|
||||||
|
|
||||||
|
Number const z = (x - 1) / (x + 1);
|
||||||
|
Number const zz = z * z;
|
||||||
|
Number denom = Number(1, -10);
|
||||||
|
|
||||||
|
// Construct the fraction from the bottom up
|
||||||
|
for (int i = iterations; i > 0; --i)
|
||||||
|
{
|
||||||
|
Number k(2 * i - 1);
|
||||||
|
denom = k - (i * i * zz / denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const r = 2 * z / denom;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Number
|
||||||
|
lg(Number const& x)
|
||||||
|
{
|
||||||
|
static Number const ln10 = ln(Number(10));
|
||||||
|
|
||||||
|
if (x <= Number(10))
|
||||||
|
{
|
||||||
|
auto const r = ln(x) / ln10;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ln(x) = ln(normX * 10^norm) = ln(normX) + norm * ln(10)
|
||||||
|
int diffExp = 15 + x.exponent();
|
||||||
|
Number const normalX = x / Number(1, diffExp); // (1 <= normalX < 10)
|
||||||
|
auto const lnX = ln(normalX) + diffExp * ln10;
|
||||||
|
|
||||||
|
auto const r = lnX / ln10;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns f^(1/d)
|
// Returns f^(1/d)
|
||||||
// Uses Newton–Raphson iterations until the result stops changing
|
// Uses Newton–Raphson iterations until the result stops changing
|
||||||
// to find the non-negative root of the polynomial g(x) = x^d - f
|
// to find the non-negative root of the polynomial g(x) = x^d - f
|
||||||
|
|||||||
@@ -39,13 +39,6 @@ setSTNumberSwitchover(bool v)
|
|||||||
*getStaticSTNumberSwitchover() = v;
|
*getStaticSTNumberSwitchover() = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The range for the mantissa when normalized */
|
|
||||||
static std::int64_t constexpr minMantissa = 1000000000000000ull;
|
|
||||||
static std::int64_t constexpr maxMantissa = 9999999999999999ull;
|
|
||||||
/* The range for the exponent when normalized */
|
|
||||||
static int constexpr minExponent = -96;
|
|
||||||
static int constexpr maxExponent = 80;
|
|
||||||
|
|
||||||
IOUAmount
|
IOUAmount
|
||||||
IOUAmount::minPositiveAmount()
|
IOUAmount::minPositiveAmount()
|
||||||
{
|
{
|
||||||
@@ -293,7 +286,8 @@ mulRatio(
|
|||||||
{
|
{
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
return IOUAmount(-minMantissa, minExponent);
|
return IOUAmount(
|
||||||
|
-IOUAmount::minMantissa, IOUAmount::minExponent);
|
||||||
}
|
}
|
||||||
// This subtraction cannot underflow because `result` is not zero
|
// This subtraction cannot underflow because `result` is not zero
|
||||||
return IOUAmount(result.mantissa() - 1, result.exponent());
|
return IOUAmount(result.mantissa() - 1, result.exponent());
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ transResults()
|
|||||||
MAKE_ERROR(tecLIMIT_EXCEEDED, "Limit exceeded."),
|
MAKE_ERROR(tecLIMIT_EXCEEDED, "Limit exceeded."),
|
||||||
MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."),
|
MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."),
|
||||||
MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."),
|
MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."),
|
||||||
|
MAKE_ERROR(tecWASM_REJECTED, "The custom WASM code that was run rejected your transaction."),
|
||||||
|
|
||||||
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
|
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
|
||||||
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
|
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
|
||||||
@@ -131,6 +132,8 @@ transResults()
|
|||||||
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
|
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
|
||||||
MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."),
|
MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."),
|
||||||
MAKE_ERROR(tefINVALID_LEDGER_FIX_TYPE, "The LedgerFixType field has an invalid value."),
|
MAKE_ERROR(tefINVALID_LEDGER_FIX_TYPE, "The LedgerFixType field has an invalid value."),
|
||||||
|
MAKE_ERROR(tefNO_WASM, "There is no WASM code to run, but a WASM-specific field was included."),
|
||||||
|
MAKE_ERROR(tefWASM_FIELD_NOT_INCLUDED, "WASM code requires a field to be included that was not included."),
|
||||||
|
|
||||||
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
|
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
|
||||||
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),
|
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),
|
||||||
@@ -200,6 +203,7 @@ transResults()
|
|||||||
MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."),
|
MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."),
|
||||||
MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."),
|
MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."),
|
||||||
MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."),
|
MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."),
|
||||||
|
MAKE_ERROR(temBAD_WASM, "Malformed: Provided WASM code is invalid."),
|
||||||
|
|
||||||
MAKE_ERROR(terRETRY, "Retry transaction."),
|
MAKE_ERROR(terRETRY, "Retry transaction."),
|
||||||
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),
|
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),
|
||||||
|
|||||||
2986
src/test/app/HostFuncImpl_test.cpp
Normal file
2986
src/test/app/HostFuncImpl_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1361
src/test/app/TestHostFunctions.h
Normal file
1361
src/test/app/TestHostFunctions.h
Normal file
File diff suppressed because it is too large
Load Diff
709
src/test/app/Wasm_test.cpp
Normal file
709
src/test/app/Wasm_test.cpp
Normal file
@@ -0,0 +1,709 @@
|
|||||||
|
#ifdef _DEBUG
|
||||||
|
// #define DEBUG_OUTPUT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <test/app/TestHostFunctions.h>
|
||||||
|
|
||||||
|
#include <xrpld/app/wasm/HostFuncWrapper.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
bool
|
||||||
|
testGetDataIncrement();
|
||||||
|
|
||||||
|
using Add_proto = int32_t(int32_t, int32_t);
|
||||||
|
static wasm_trap_t*
|
||||||
|
Add(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
results->data[0] = WASM_I32_VAL(Val1 + Val2);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Wasm_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
testGetDataHelperFunctions()
|
||||||
|
{
|
||||||
|
testcase("getData helper functions");
|
||||||
|
BEAST_EXPECT(testGetDataIncrement());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testWasmLib()
|
||||||
|
{
|
||||||
|
testcase("wasmtime lib test");
|
||||||
|
// clang-format off
|
||||||
|
/* The WASM module buffer. */
|
||||||
|
Bytes 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 re = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports);
|
||||||
|
|
||||||
|
// if (res) printf("invokeAdd get the result: %d\n", res.value());
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 6'912, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 3, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testBadWasm()
|
||||||
|
{
|
||||||
|
testcase("bad wasm test");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
|
||||||
|
Env env{*this};
|
||||||
|
HostFunctions hfs;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto wasmHex = "00000000";
|
||||||
|
auto wasmStr = boost::algorithm::unhex(std::string(wasmHex));
|
||||||
|
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
std::string funcName("mock_escrow");
|
||||||
|
|
||||||
|
auto re = runEscrowWasm(wasm, funcName, {}, &hfs, 15, env.journal);
|
||||||
|
BEAST_EXPECT(!re);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto wasmHex = "00112233445566778899AA";
|
||||||
|
auto wasmStr = boost::algorithm::unhex(std::string(wasmHex));
|
||||||
|
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
std::string funcName("mock_escrow");
|
||||||
|
|
||||||
|
auto const re =
|
||||||
|
preflightEscrowWasm(wasm, funcName, {}, &hfs, env.journal);
|
||||||
|
BEAST_EXPECT(!isTesSuccess(re));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// FinishFunction wrong function name
|
||||||
|
// pub fn bad() -> bool {
|
||||||
|
// unsafe { host_lib::getLedgerSqn() >= 5 }
|
||||||
|
// }
|
||||||
|
auto const badWasmHex =
|
||||||
|
"0061736d010000000105016000017f02190108686f73745f6c69620c6765"
|
||||||
|
"744c656467657253716e00000302010005030100100611027f00418080c0"
|
||||||
|
"000b7f00418080c0000b072b04066d656d6f727902000362616400010a5f"
|
||||||
|
"5f646174615f656e6403000b5f5f686561705f6261736503010a09010700"
|
||||||
|
"100041044a0b004d0970726f64756365727302086c616e67756167650104"
|
||||||
|
"52757374000c70726f6365737365642d6279010572757374631d312e3835"
|
||||||
|
"2e31202834656231363132353020323032352d30332d31352900490f7461"
|
||||||
|
"726765745f6665617475726573042b0f6d757461626c652d676c6f62616c"
|
||||||
|
"732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a"
|
||||||
|
"6d756c746976616c7565";
|
||||||
|
auto wasmStr = boost::algorithm::unhex(std::string(badWasmHex));
|
||||||
|
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
auto const re = preflightEscrowWasm(
|
||||||
|
wasm, ESCROW_FUNCTION_NAME, {}, &hfs, env.journal);
|
||||||
|
BEAST_EXPECT(!isTesSuccess(re));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testWasmLedgerSqn()
|
||||||
|
{
|
||||||
|
testcase("Wasm get ledger sequence");
|
||||||
|
|
||||||
|
auto wasmStr = boost::algorithm::unhex(ledgerSqnWasmHex);
|
||||||
|
Bytes wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
|
||||||
|
Env env{*this};
|
||||||
|
TestLedgerDataProvider hf(&env);
|
||||||
|
|
||||||
|
std::vector<WasmImportFunc> imports;
|
||||||
|
WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hf, 33);
|
||||||
|
auto& engine = WasmEngine::instance();
|
||||||
|
|
||||||
|
auto re = engine.run(
|
||||||
|
wasm,
|
||||||
|
ESCROW_FUNCTION_NAME,
|
||||||
|
{},
|
||||||
|
imports,
|
||||||
|
&hf,
|
||||||
|
1'000'000,
|
||||||
|
env.journal);
|
||||||
|
|
||||||
|
// code takes 11 gas + 1 getLedgerSqn call
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 0, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 5, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// empty module - run the same instance
|
||||||
|
re = engine.run(
|
||||||
|
{}, ESCROW_FUNCTION_NAME, {}, imports, &hf, 1'000'000, env.journal);
|
||||||
|
|
||||||
|
// code takes 22 gas + 2 getLedgerSqn calls
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 5, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 10, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testWasmFib()
|
||||||
|
{
|
||||||
|
testcase("Wasm fibo");
|
||||||
|
|
||||||
|
auto const ws = boost::algorithm::unhex(fibWasmHex);
|
||||||
|
Bytes const wasm(ws.begin(), ws.end());
|
||||||
|
auto& engine = WasmEngine::instance();
|
||||||
|
|
||||||
|
auto const re = engine.run(wasm, "fib", wasmParams(10));
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 55, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 696, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testWasmSha()
|
||||||
|
{
|
||||||
|
testcase("Wasm sha");
|
||||||
|
|
||||||
|
auto const ws = boost::algorithm::unhex(sha512PureWasmHex);
|
||||||
|
Bytes const wasm(ws.begin(), ws.end());
|
||||||
|
auto& engine = WasmEngine::instance();
|
||||||
|
|
||||||
|
auto const re =
|
||||||
|
engine.run(wasm, "sha512_process", wasmParams(sha512PureWasmHex));
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 34'432, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 145'573, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testWasmB58()
|
||||||
|
{
|
||||||
|
testcase("Wasm base58");
|
||||||
|
auto const ws = boost::algorithm::unhex(b58WasmHex);
|
||||||
|
Bytes const wasm(ws.begin(), ws.end());
|
||||||
|
auto& engine = WasmEngine::instance();
|
||||||
|
|
||||||
|
Bytes outb;
|
||||||
|
outb.resize(1024);
|
||||||
|
|
||||||
|
auto const minsz = std::min(
|
||||||
|
static_cast<std::uint32_t>(512),
|
||||||
|
static_cast<std::uint32_t>(b58WasmHex.size()));
|
||||||
|
auto const s = std::string_view(b58WasmHex.c_str(), minsz);
|
||||||
|
|
||||||
|
auto const re = engine.run(wasm, "b58enco", wasmParams(outb, s));
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 700, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 2'701'528, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testWasmSP1Verifier()
|
||||||
|
{
|
||||||
|
testcase("Wasm sp1 zkproof verifier");
|
||||||
|
auto const ws = boost::algorithm::unhex(sp1WasmHex);
|
||||||
|
Bytes const wasm(ws.begin(), ws.end());
|
||||||
|
auto& engine = WasmEngine::instance();
|
||||||
|
|
||||||
|
auto const re = engine.run(wasm, "sp1_groth16_verifier");
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 1, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
re->cost == 4'191'711'969ll, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testWasmBG16Verifier()
|
||||||
|
{
|
||||||
|
testcase("Wasm BG16 zkproof verifier");
|
||||||
|
auto const ws = boost::algorithm::unhex(zkProofWasmHex);
|
||||||
|
Bytes const wasm(ws.begin(), ws.end());
|
||||||
|
auto& engine = WasmEngine::instance();
|
||||||
|
|
||||||
|
auto const re = engine.run(wasm, "bellman_groth16_test");
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 1, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 270'282'552, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testHFCost()
|
||||||
|
{
|
||||||
|
testcase("wasm test host functions cost");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
|
||||||
|
Env env(*this);
|
||||||
|
{
|
||||||
|
std::string const wasmHex = allHostFunctionsWasmHex;
|
||||||
|
std::string const wasmStr = boost::algorithm::unhex(wasmHex);
|
||||||
|
std::vector<uint8_t> const wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
auto& engine = WasmEngine::instance();
|
||||||
|
|
||||||
|
TestHostFunctions hfs(env, 0);
|
||||||
|
std::vector<WasmImportFunc> imp = createWasmImport(&hfs);
|
||||||
|
for (auto& i : imp)
|
||||||
|
i.gas = 0;
|
||||||
|
|
||||||
|
auto re = engine.run(
|
||||||
|
wasm,
|
||||||
|
ESCROW_FUNCTION_NAME,
|
||||||
|
{},
|
||||||
|
imp,
|
||||||
|
&hfs,
|
||||||
|
1'000'000,
|
||||||
|
env.journal);
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 1, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 842, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::string const wasmHex = allHostFunctionsWasmHex;
|
||||||
|
std::string const wasmStr = boost::algorithm::unhex(wasmHex);
|
||||||
|
std::vector<uint8_t> const wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
auto& engine = WasmEngine::instance();
|
||||||
|
|
||||||
|
TestHostFunctions hfs(env, 0);
|
||||||
|
std::vector<WasmImportFunc> const imp = createWasmImport(&hfs);
|
||||||
|
|
||||||
|
auto re = engine.run(
|
||||||
|
wasm,
|
||||||
|
ESCROW_FUNCTION_NAME,
|
||||||
|
{},
|
||||||
|
imp,
|
||||||
|
&hfs,
|
||||||
|
1'000'000,
|
||||||
|
env.journal);
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 1, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 842, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testEscrowWasmDN()
|
||||||
|
{
|
||||||
|
testcase("escrow wasm devnet test");
|
||||||
|
|
||||||
|
std::string const wasmStr =
|
||||||
|
boost::algorithm::unhex(allHostFunctionsWasmHex);
|
||||||
|
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env{*this};
|
||||||
|
{
|
||||||
|
TestHostFunctions nfs(env, 0);
|
||||||
|
auto re =
|
||||||
|
runEscrowWasm(wasm, ESCROW_FUNCTION_NAME, {}, &nfs, 100'000);
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 1, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 842, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // fail because trying to access nonexistent field
|
||||||
|
struct BadTestHostFunctions : public TestHostFunctions
|
||||||
|
{
|
||||||
|
explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getTxField(SField const& fname) override
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BadTestHostFunctions nfs(env);
|
||||||
|
auto re =
|
||||||
|
runEscrowWasm(wasm, ESCROW_FUNCTION_NAME, {}, &nfs, 100'000);
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == -201, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 262, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // fail because trying to allocate more than MAX_PAGES memory
|
||||||
|
struct BadTestHostFunctions : public TestHostFunctions
|
||||||
|
{
|
||||||
|
explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getTxField(SField const& fname) override
|
||||||
|
{
|
||||||
|
return Bytes((128 + 1) * 64 * 1024, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BadTestHostFunctions nfs(env);
|
||||||
|
auto re =
|
||||||
|
runEscrowWasm(wasm, ESCROW_FUNCTION_NAME, {}, &nfs, 100'000);
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == -201, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 262, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // fail because recursion too deep
|
||||||
|
|
||||||
|
auto const wasmStr = boost::algorithm::unhex(deepRecursionHex);
|
||||||
|
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
TestHostFunctionsSink nfs(env);
|
||||||
|
std::string funcName("recursive");
|
||||||
|
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 1'000'000'000);
|
||||||
|
BEAST_EXPECT(!re && re.error());
|
||||||
|
// std::cout << "bad case (deep recursion) result " << re.error()
|
||||||
|
// << std::endl;
|
||||||
|
|
||||||
|
auto const& sink = nfs.getSink();
|
||||||
|
auto countSubstr = [](std::string const& str,
|
||||||
|
std::string const& substr) {
|
||||||
|
std::size_t pos = 0;
|
||||||
|
int occurrences = 0;
|
||||||
|
while ((pos = str.find(substr, pos)) != std::string::npos)
|
||||||
|
{
|
||||||
|
occurrences++;
|
||||||
|
pos += substr.length();
|
||||||
|
}
|
||||||
|
return occurrences;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto const s = sink.messages().str();
|
||||||
|
BEAST_EXPECT(
|
||||||
|
countSubstr(s, "WASMI Error: failure to call func") == 1);
|
||||||
|
BEAST_EXPECT(countSubstr(s, "exception: <recursive> failure") > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto wasmStr = boost::algorithm::unhex(ledgerSqnWasmHex);
|
||||||
|
Bytes wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
TestLedgerDataProvider ledgerDataProvider(&env);
|
||||||
|
|
||||||
|
std::vector<WasmImportFunc> imports;
|
||||||
|
WASM_IMPORT_FUNC2(
|
||||||
|
imports, getLedgerSqn, "get_ledger_sqn2", &ledgerDataProvider);
|
||||||
|
|
||||||
|
auto& engine = WasmEngine::instance();
|
||||||
|
|
||||||
|
auto re = engine.run(
|
||||||
|
wasm,
|
||||||
|
ESCROW_FUNCTION_NAME,
|
||||||
|
{},
|
||||||
|
imports,
|
||||||
|
nullptr,
|
||||||
|
1'000'000,
|
||||||
|
env.journal);
|
||||||
|
|
||||||
|
// expected import not provided
|
||||||
|
BEAST_EXPECT(!re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testFloat()
|
||||||
|
{
|
||||||
|
testcase("float point");
|
||||||
|
|
||||||
|
std::string const funcName("finish");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
|
||||||
|
Env env(*this);
|
||||||
|
{
|
||||||
|
std::string const wasmHex = floatTestsWasmHex;
|
||||||
|
std::string const wasmStr = boost::algorithm::unhex(wasmHex);
|
||||||
|
std::vector<uint8_t> const wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
TestHostFunctions hf(env, 0);
|
||||||
|
auto re = runEscrowWasm(wasm, funcName, {}, &hf, 100'000);
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 1, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 326, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::string const wasmHex = float0Hex;
|
||||||
|
std::string const wasmStr = boost::algorithm::unhex(wasmHex);
|
||||||
|
std::vector<uint8_t> const wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
TestHostFunctions hf(env, 0);
|
||||||
|
auto re = runEscrowWasm(wasm, funcName, {}, &hf, 100'000);
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECTS(re->result == 1, std::to_string(re->result));
|
||||||
|
BEAST_EXPECTS(re->cost == 34, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
perfTest()
|
||||||
|
{
|
||||||
|
testcase("Perf test host functions");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// std::string const funcName("test");
|
||||||
|
auto const& wasmHex = hfPerfTest;
|
||||||
|
std::string const wasmStr = boost::algorithm::unhex(wasmHex);
|
||||||
|
std::vector<uint8_t> const wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
|
||||||
|
// std::string const credType = "abcde";
|
||||||
|
// std::string const credType2 = "fghijk";
|
||||||
|
// std::string const credType3 = "0123456";
|
||||||
|
// char const uri[] = "uri";
|
||||||
|
|
||||||
|
Account const alan{"alan"};
|
||||||
|
Account const bob{"bob"};
|
||||||
|
Account const issuer{"issuer"};
|
||||||
|
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
// Env env(*this, envconfig(), {}, nullptr,
|
||||||
|
// beast::severities::kTrace);
|
||||||
|
env.fund(XRP(5000), alan, bob, issuer);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// // create escrow
|
||||||
|
// auto const seq = env.seq(alan);
|
||||||
|
// auto const k = keylet::escrow(alan, seq);
|
||||||
|
// // auto const allowance = 3'600;
|
||||||
|
// auto escrowCreate = escrow::create(alan, bob, XRP(1000));
|
||||||
|
// XRPAmount txnFees = env.current()->fees().base + 1000;
|
||||||
|
// env(escrowCreate,
|
||||||
|
// escrow::finish_function(wasmHex),
|
||||||
|
// escrow::finish_time(env.now() + 11s),
|
||||||
|
// escrow::cancel_time(env.now() + 100s),
|
||||||
|
// escrow::data("1000000000"), // 1000 XRP in drops
|
||||||
|
// memodata("memo1234567"),
|
||||||
|
// memodata("2memo1234567"),
|
||||||
|
// fee(txnFees));
|
||||||
|
|
||||||
|
// // create depositPreauth
|
||||||
|
// auto const k = keylet::depositPreauth(
|
||||||
|
// bob,
|
||||||
|
// {{issuer.id(), makeSlice(credType)},
|
||||||
|
// {issuer.id(), makeSlice(credType2)},
|
||||||
|
// {issuer.id(), makeSlice(credType3)}});
|
||||||
|
// env(deposit::authCredentials(
|
||||||
|
// bob,
|
||||||
|
// {{issuer, credType},
|
||||||
|
// {issuer, credType2},
|
||||||
|
// {issuer, credType3}}));
|
||||||
|
|
||||||
|
// create nft
|
||||||
|
[[maybe_unused]] uint256 const nft0{
|
||||||
|
token::getNextID(env, alan, 0u)};
|
||||||
|
env(token::mint(alan, 0u));
|
||||||
|
auto const k = keylet::nftoffer(alan, 0);
|
||||||
|
[[maybe_unused]] uint256 const nft1{
|
||||||
|
token::getNextID(env, alan, 0u)};
|
||||||
|
|
||||||
|
env(token::mint(alan, 0u),
|
||||||
|
token::uri(
|
||||||
|
"https://github.com/XRPLF/XRPL-Standards/discussions/"
|
||||||
|
"279?id=github.com/XRPLF/XRPL-Standards/discussions/"
|
||||||
|
"279&ut=github.com/XRPLF/XRPL-Standards/discussions/"
|
||||||
|
"279&sid=github.com/XRPLF/XRPL-Standards/discussions/"
|
||||||
|
"279&aot=github.com/XRPLF/XRPL-Standards/disc"));
|
||||||
|
[[maybe_unused]] uint256 const nft2{
|
||||||
|
token::getNextID(env, alan, 0u)};
|
||||||
|
env(token::mint(alan, 0u));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
PerfHostFunctions nfs(env, k, env.tx());
|
||||||
|
|
||||||
|
auto re = runEscrowWasm(wasm, ESCROW_FUNCTION_NAME, {}, &nfs);
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(re->result);
|
||||||
|
std::cout << "Res: " << re->result << " cost: " << re->cost
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// env(escrow::finish(alan, alan, seq),
|
||||||
|
// escrow::comp_allowance(allowance),
|
||||||
|
// fee(txnFees),
|
||||||
|
// ter(tesSUCCESS));
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testCodecovWasm()
|
||||||
|
{
|
||||||
|
testcase("Codecov wasm test");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
|
||||||
|
Env env{*this};
|
||||||
|
|
||||||
|
auto const wasmStr = boost::algorithm::unhex(codecovTestsWasmHex);
|
||||||
|
Bytes const wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
TestHostFunctions hfs(env, 0);
|
||||||
|
|
||||||
|
auto const allowance = 1'814;
|
||||||
|
auto re = runEscrowWasm(
|
||||||
|
wasm, ESCROW_FUNCTION_NAME, {}, &hfs, allowance, env.journal);
|
||||||
|
|
||||||
|
if (BEAST_EXPECT(re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(re->result);
|
||||||
|
BEAST_EXPECTS(re->cost == allowance, std::to_string(re->cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testDisabledFloat()
|
||||||
|
{
|
||||||
|
testcase("disabled float");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env{*this};
|
||||||
|
|
||||||
|
auto const wasmStr = boost::algorithm::unhex(disabledFloatHex);
|
||||||
|
Bytes wasm(wasmStr.begin(), wasmStr.end());
|
||||||
|
std::string const funcName("finish");
|
||||||
|
TestHostFunctions hfs(env, 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
// f32 set constant, opcode disabled exception
|
||||||
|
auto const re =
|
||||||
|
runEscrowWasm(wasm, funcName, {}, &hfs, 1'000'000, env.journal);
|
||||||
|
if (BEAST_EXPECT(!re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(re.error() == tecFAILED_PROCESSING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// f32 add, can't create module exception
|
||||||
|
wasm[0x117] = 0x92;
|
||||||
|
auto const re =
|
||||||
|
runEscrowWasm(wasm, funcName, {}, &hfs, 1'000'000, env.journal);
|
||||||
|
if (BEAST_EXPECT(!re.has_value()))
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(re.error() == tecFAILED_PROCESSING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
using namespace test::jtx;
|
||||||
|
|
||||||
|
testGetDataHelperFunctions();
|
||||||
|
testWasmLib();
|
||||||
|
testBadWasm();
|
||||||
|
testWasmLedgerSqn();
|
||||||
|
|
||||||
|
testWasmFib();
|
||||||
|
testWasmSha();
|
||||||
|
testWasmB58();
|
||||||
|
|
||||||
|
// running too long
|
||||||
|
// testWasmSP1Verifier();
|
||||||
|
testWasmBG16Verifier();
|
||||||
|
|
||||||
|
testHFCost();
|
||||||
|
|
||||||
|
testEscrowWasmDN();
|
||||||
|
testFloat();
|
||||||
|
|
||||||
|
testCodecovWasm();
|
||||||
|
// testDisabledFloat();
|
||||||
|
|
||||||
|
// perfTest();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(Wasm, app, ripple);
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace ripple
|
||||||
10296
src/test/app/wasm_fixtures/fixtures.cpp
Normal file
10296
src/test/app/wasm_fixtures/fixtures.cpp
Normal file
File diff suppressed because it is too large
Load Diff
35
src/test/app/wasm_fixtures/fixtures.h
Normal file
35
src/test/app/wasm_fixtures/fixtures.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// TODO: consider moving these to separate files (and figure out the build)
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern std::string const ledgerSqnWasmHex;
|
||||||
|
|
||||||
|
extern std::string const allHostFunctionsWasmHex;
|
||||||
|
|
||||||
|
extern std::string const deepRecursionHex;
|
||||||
|
|
||||||
|
extern std::string const fibWasmHex;
|
||||||
|
|
||||||
|
extern std::string const b58WasmHex;
|
||||||
|
|
||||||
|
extern std::string const sha512PureWasmHex;
|
||||||
|
|
||||||
|
extern std::string const zkProofWasmHex;
|
||||||
|
|
||||||
|
extern std::string const sp1WasmHex;
|
||||||
|
|
||||||
|
extern std::string const hfPerfTest;
|
||||||
|
|
||||||
|
extern std::string const allKeyletsWasmHex;
|
||||||
|
|
||||||
|
extern std::string const codecovTestsWasmHex;
|
||||||
|
|
||||||
|
extern std::string const floatTestsWasmHex;
|
||||||
|
|
||||||
|
extern std::string const float0Hex;
|
||||||
|
|
||||||
|
extern std::string const disabledFloatHex;
|
||||||
|
|
||||||
|
extern std::string const updateDataWasmHex;
|
||||||
495
src/xrpld/app/wasm/HostFunc.h
Normal file
495
src/xrpld/app/wasm/HostFunc.h
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpld/app/wasm/ParamsHelper.h>
|
||||||
|
|
||||||
|
#include <xrpl/basics/Expected.h>
|
||||||
|
#include <xrpl/basics/Slice.h>
|
||||||
|
#include <xrpl/beast/utility/Journal.h>
|
||||||
|
#include <xrpl/protocol/AccountID.h>
|
||||||
|
#include <xrpl/protocol/Asset.h>
|
||||||
|
#include <xrpl/protocol/Keylet.h>
|
||||||
|
#include <xrpl/protocol/TER.h>
|
||||||
|
#include <xrpl/protocol/UintTypes.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
enum class HostFunctionError : int32_t {
|
||||||
|
INTERNAL = -1,
|
||||||
|
FIELD_NOT_FOUND = -2,
|
||||||
|
BUFFER_TOO_SMALL = -3,
|
||||||
|
NO_ARRAY = -4,
|
||||||
|
NOT_LEAF_FIELD = -5,
|
||||||
|
LOCATOR_MALFORMED = -6,
|
||||||
|
SLOT_OUT_RANGE = -7,
|
||||||
|
SLOTS_FULL = -8,
|
||||||
|
EMPTY_SLOT = -9,
|
||||||
|
LEDGER_OBJ_NOT_FOUND = -10,
|
||||||
|
DECODING = -11,
|
||||||
|
DATA_FIELD_TOO_LARGE = -12,
|
||||||
|
POINTER_OUT_OF_BOUNDS = -13,
|
||||||
|
NO_MEM_EXPORTED = -14,
|
||||||
|
INVALID_PARAMS = -15,
|
||||||
|
INVALID_ACCOUNT = -16,
|
||||||
|
INVALID_FIELD = -17,
|
||||||
|
INDEX_OUT_OF_BOUNDS = -18,
|
||||||
|
FLOAT_INPUT_MALFORMED = -19,
|
||||||
|
FLOAT_COMPUTATION_ERROR = -20,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline int32_t
|
||||||
|
HfErrorToInt(HostFunctionError e)
|
||||||
|
{
|
||||||
|
return static_cast<int32_t>(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
floatToString(Slice const& data);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatFromIntImpl(int64_t x, int32_t mode);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatFromUintImpl(uint64_t x, int32_t mode);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode);
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
floatCompareImpl(Slice const& x, Slice const& y);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatAddImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatDivideImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatRootImpl(Slice const& x, int32_t n, int32_t mode);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatPowerImpl(Slice const& x, int32_t n, int32_t mode);
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatLogImpl(Slice const& x, int32_t mode);
|
||||||
|
|
||||||
|
struct HostFunctions
|
||||||
|
{
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
virtual void
|
||||||
|
setRT(void const*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void const*
|
||||||
|
getRT() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual beast::Journal
|
||||||
|
getJournal()
|
||||||
|
{
|
||||||
|
return beast::Journal{beast::Journal::getNullSink()};
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<std::int32_t, HostFunctionError>
|
||||||
|
getLedgerSqn()
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<std::int32_t, HostFunctionError>
|
||||||
|
getParentLedgerTime()
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Hash, HostFunctionError>
|
||||||
|
getParentLedgerHash()
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
getBaseFee()
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
isAmendmentEnabled(uint256 const& amendmentId)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
isAmendmentEnabled(std::string_view const& amendmentName)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
getTxField(SField const& fname)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
getCurrentLedgerObjField(SField const& fname)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
getLedgerObjField(int32_t cacheIdx, SField const& fname)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
getTxNestedField(Slice const& locator)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
getCurrentLedgerObjNestedField(Slice const& locator)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
getTxArrayLen(SField const& fname)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
getCurrentLedgerObjArrayLen(SField const& fname)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
getTxNestedArrayLen(Slice const& locator)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
getCurrentLedgerObjNestedArrayLen(Slice const& locator)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
updateData(Slice const& data)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
checkSignature(
|
||||||
|
Slice const& message,
|
||||||
|
Slice const& signature,
|
||||||
|
Slice const& pubkey)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Hash, HostFunctionError>
|
||||||
|
computeSha512HalfHash(Slice const& data)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
accountKeylet(AccountID const& account)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
ammKeylet(Asset const& issue1, Asset const& issue2)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
checkKeylet(AccountID const& account, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
credentialKeylet(
|
||||||
|
AccountID const& subject,
|
||||||
|
AccountID const& issuer,
|
||||||
|
Slice const& credentialType)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
didKeylet(AccountID const& account)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
delegateKeylet(AccountID const& account, AccountID const& authorize)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
depositPreauthKeylet(AccountID const& account, AccountID const& authorize)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
escrowKeylet(AccountID const& account, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
lineKeylet(
|
||||||
|
AccountID const& account1,
|
||||||
|
AccountID const& account2,
|
||||||
|
Currency const& currency)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
mptokenKeylet(MPTID const& mptid, AccountID const& holder)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
nftOfferKeylet(AccountID const& account, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
offerKeylet(AccountID const& account, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
oracleKeylet(AccountID const& account, std::uint32_t docId)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
paychanKeylet(
|
||||||
|
AccountID const& account,
|
||||||
|
AccountID const& destination,
|
||||||
|
std::uint32_t seq)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
permissionedDomainKeylet(AccountID const& account, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
signersKeylet(AccountID const& account)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
ticketKeylet(AccountID const& account, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
vaultKeylet(AccountID const& account, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
getNFT(AccountID const& account, uint256 const& nftId)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
getNFTIssuer(uint256 const& nftId)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<std::uint32_t, HostFunctionError>
|
||||||
|
getNFTTaxon(uint256 const& nftId)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
getNFTFlags(uint256 const& nftId)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
getNFTTransferFee(uint256 const& nftId)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<std::uint32_t, HostFunctionError>
|
||||||
|
getNFTSerial(uint256 const& nftId)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
trace(std::string_view const& msg, Slice const& data, bool asHex)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
traceNum(std::string_view const& msg, int64_t data)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
traceAccount(std::string_view const& msg, AccountID const& account)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
traceFloat(std::string_view const& msg, Slice const& data)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
traceAmount(std::string_view const& msg, STAmount const& amount)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatFromInt(int64_t x, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatFromUint(uint64_t x, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatSet(int64_t mantissa, int32_t exponent, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<int32_t, HostFunctionError>
|
||||||
|
floatCompare(Slice const& x, Slice const& y)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatAdd(Slice const& x, Slice const& y, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatSubtract(Slice const& x, Slice const& y, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatMultiply(Slice const& x, Slice const& y, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatDivide(Slice const& x, Slice const& y, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatRoot(Slice const& x, int32_t n, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatPower(Slice const& x, int32_t n, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Expected<Bytes, HostFunctionError>
|
||||||
|
floatLog(Slice const& x, int32_t mode)
|
||||||
|
{
|
||||||
|
return Unexpected(HostFunctionError::INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~HostFunctions() = default;
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
280
src/xrpld/app/wasm/HostFuncImpl.h
Normal file
280
src/xrpld/app/wasm/HostFuncImpl.h
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpld/app/tx/detail/ApplyContext.h>
|
||||||
|
#include <xrpld/app/wasm/HostFunc.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
class WasmHostFunctionsImpl : public HostFunctions
|
||||||
|
{
|
||||||
|
ApplyContext& ctx;
|
||||||
|
Keylet leKey;
|
||||||
|
std::shared_ptr<SLE const> currentLedgerObj = nullptr;
|
||||||
|
bool isLedgerObjCached = false;
|
||||||
|
|
||||||
|
static int constexpr MAX_CACHE = 256;
|
||||||
|
std::array<std::shared_ptr<SLE const>, MAX_CACHE> cache;
|
||||||
|
std::optional<Bytes> data_;
|
||||||
|
|
||||||
|
void const* rt_ = nullptr;
|
||||||
|
|
||||||
|
Expected<std::shared_ptr<SLE const>, HostFunctionError>
|
||||||
|
getCurrentLedgerObj()
|
||||||
|
{
|
||||||
|
if (!isLedgerObjCached)
|
||||||
|
{
|
||||||
|
isLedgerObjCached = true;
|
||||||
|
currentLedgerObj = ctx.view().read(leKey);
|
||||||
|
}
|
||||||
|
if (currentLedgerObj)
|
||||||
|
return currentLedgerObj;
|
||||||
|
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
normalizeCacheIndex(int32_t cacheIdx)
|
||||||
|
{
|
||||||
|
--cacheIdx;
|
||||||
|
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||||
|
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||||
|
if (!cache[cacheIdx])
|
||||||
|
return Unexpected(HostFunctionError::EMPTY_SLOT);
|
||||||
|
return cacheIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
WasmHostFunctionsImpl(ApplyContext& ctx, Keylet const& leKey)
|
||||||
|
: ctx(ctx), leKey(leKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
setRT(void const* rt) override
|
||||||
|
{
|
||||||
|
rt_ = rt;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void const*
|
||||||
|
getRT() const override
|
||||||
|
{
|
||||||
|
return rt_;
|
||||||
|
}
|
||||||
|
|
||||||
|
beast::Journal
|
||||||
|
getJournal() override
|
||||||
|
{
|
||||||
|
return ctx.journal;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Bytes> const&
|
||||||
|
getData() const
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<std::int32_t, HostFunctionError>
|
||||||
|
getLedgerSqn() override;
|
||||||
|
|
||||||
|
Expected<std::int32_t, HostFunctionError>
|
||||||
|
getParentLedgerTime() override;
|
||||||
|
|
||||||
|
Expected<Hash, HostFunctionError>
|
||||||
|
getParentLedgerHash() override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
getBaseFee() override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
isAmendmentEnabled(uint256 const& amendmentId) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
isAmendmentEnabled(std::string_view const& amendmentName) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getTxField(SField const& fname) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getCurrentLedgerObjField(SField const& fname) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getLedgerObjField(int32_t cacheIdx, SField const& fname) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getTxNestedField(Slice const& locator) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getCurrentLedgerObjNestedField(Slice const& locator) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
getTxArrayLen(SField const& fname) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
getCurrentLedgerObjArrayLen(SField const& fname) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
getTxNestedArrayLen(Slice const& locator) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
updateData(Slice const& data) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
checkSignature(
|
||||||
|
Slice const& message,
|
||||||
|
Slice const& signature,
|
||||||
|
Slice const& pubkey) override;
|
||||||
|
|
||||||
|
Expected<Hash, HostFunctionError>
|
||||||
|
computeSha512HalfHash(Slice const& data) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
accountKeylet(AccountID const& account) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
ammKeylet(Asset const& issue1, Asset const& issue2) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
checkKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
credentialKeylet(
|
||||||
|
AccountID const& subject,
|
||||||
|
AccountID const& issuer,
|
||||||
|
Slice const& credentialType) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
didKeylet(AccountID const& account) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
delegateKeylet(AccountID const& account, AccountID const& authorize)
|
||||||
|
override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
depositPreauthKeylet(AccountID const& account, AccountID const& authorize)
|
||||||
|
override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
escrowKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
lineKeylet(
|
||||||
|
AccountID const& account1,
|
||||||
|
AccountID const& account2,
|
||||||
|
Currency const& currency) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
mptokenKeylet(MPTID const& mptid, AccountID const& holder) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
nftOfferKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
offerKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
oracleKeylet(AccountID const& account, std::uint32_t docId) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
paychanKeylet(
|
||||||
|
AccountID const& account,
|
||||||
|
AccountID const& destination,
|
||||||
|
std::uint32_t seq) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
permissionedDomainKeylet(AccountID const& account, std::uint32_t seq)
|
||||||
|
override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
signersKeylet(AccountID const& account) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
ticketKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
vaultKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getNFT(AccountID const& account, uint256 const& nftId) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
getNFTIssuer(uint256 const& nftId) override;
|
||||||
|
|
||||||
|
Expected<std::uint32_t, HostFunctionError>
|
||||||
|
getNFTTaxon(uint256 const& nftId) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
getNFTFlags(uint256 const& nftId) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
getNFTTransferFee(uint256 const& nftId) override;
|
||||||
|
|
||||||
|
Expected<std::uint32_t, HostFunctionError>
|
||||||
|
getNFTSerial(uint256 const& nftId) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
trace(std::string_view const& msg, Slice const& data, bool asHex) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
traceNum(std::string_view const& msg, int64_t data) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
traceAccount(std::string_view const& msg, AccountID const& account)
|
||||||
|
override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
traceFloat(std::string_view const& msg, Slice const& data) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
traceAmount(std::string_view const& msg, STAmount const& amount) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatFromInt(int64_t x, int32_t mode) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatFromUint(uint64_t x, int32_t mode) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatSet(int64_t mantissa, int32_t exponent, int32_t mode) override;
|
||||||
|
|
||||||
|
Expected<int32_t, HostFunctionError>
|
||||||
|
floatCompare(Slice const& x, Slice const& y) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatAdd(Slice const& x, Slice const& y, int32_t mode) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatSubtract(Slice const& x, Slice const& y, int32_t mode) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatMultiply(Slice const& x, Slice const& y, int32_t mode) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatDivide(Slice const& x, Slice const& y, int32_t mode) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatRoot(Slice const& x, int32_t n, int32_t mode) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatPower(Slice const& x, int32_t n, int32_t mode) override;
|
||||||
|
|
||||||
|
Expected<Bytes, HostFunctionError>
|
||||||
|
floatLog(Slice const& x, int32_t mode) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
534
src/xrpld/app/wasm/HostFuncWrapper.h
Normal file
534
src/xrpld/app/wasm/HostFuncWrapper.h
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpld/app/wasm/WasmiVM.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 getParentLedgerHash_proto = int32_t(uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getParentLedgerHash_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getBaseFee_proto = int32_t();
|
||||||
|
wasm_trap_t*
|
||||||
|
getBaseFee_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using isAmendmentEnabled_proto = int32_t(uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
isAmendmentEnabled_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using cacheLedgerObj_proto = int32_t(uint8_t const*, int32_t, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
cacheLedgerObj_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getTxField_proto = int32_t(int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getTxField_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getCurrentLedgerObjField_proto = int32_t(int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getCurrentLedgerObjField_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getLedgerObjField_proto = int32_t(int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getLedgerObjField_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getTxNestedField_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getTxNestedField_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getCurrentLedgerObjNestedField_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getCurrentLedgerObjNestedField_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getLedgerObjNestedField_proto =
|
||||||
|
int32_t(int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getLedgerObjNestedField_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getTxArrayLen_proto = int32_t(int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getTxArrayLen_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getCurrentLedgerObjArrayLen_proto = int32_t(int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getCurrentLedgerObjArrayLen_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getLedgerObjArrayLen_proto = int32_t(int32_t, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getLedgerObjArrayLen_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getTxNestedArrayLen_proto = int32_t(uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getTxNestedArrayLen_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getCurrentLedgerObjNestedArrayLen_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getCurrentLedgerObjNestedArrayLen_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getLedgerObjNestedArrayLen_proto =
|
||||||
|
int32_t(int32_t, uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getLedgerObjNestedArrayLen_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using updateData_proto = int32_t(uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
updateData_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using checkSignature_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
checkSignature_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using computeSha512HalfHash_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
computeSha512HalfHash_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using accountKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
accountKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using ammKeylet_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
ammKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using checkKeylet_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
checkKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using credentialKeylet_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
credentialKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using delegateKeylet_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
delegateKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using depositPreauthKeylet_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
depositPreauthKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using didKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
didKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using escrowKeylet_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
escrowKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using lineKeylet_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
lineKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using mptIssuanceKeylet_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
mptIssuanceKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using mptokenKeylet_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
mptokenKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using nftOfferKeylet_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
nftOfferKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using offerKeylet_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
offerKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using oracleKeylet_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
oracleKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using paychanKeylet_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
paychanKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using permissionedDomainKeylet_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
permissionedDomainKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using signersKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
signersKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using ticketKeylet_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
ticketKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using vaultKeylet_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
vaultKeylet_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getNFT_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getNFT_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getNFTIssuer_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getNFTIssuer_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getNFTTaxon_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getNFTTaxon_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getNFTFlags_proto = int32_t(uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getNFTFlags_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getNFTTransferFee_proto = int32_t(uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getNFTTransferFee_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using getNFTSerial_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
getNFTSerial_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using trace_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
trace_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using traceNum_proto = int32_t(uint8_t const*, int32_t, int64_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
traceNum_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using traceAccount_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
traceAccount_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using traceFloat_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
traceFloat_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using traceAmount_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
traceAmount_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatFromInt_proto = int32_t(int64_t, uint8_t*, int32_t, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatFromInt_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatFromUint_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatFromUint_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatSet_proto = int32_t(int32_t, int64_t, uint8_t*, int32_t, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatSet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatCompare_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatCompare_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatAdd_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatAdd_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatSubtract_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatSubtract_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatMultiply_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatMultiply_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatDivide_proto = int32_t(
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t const*,
|
||||||
|
int32_t,
|
||||||
|
uint8_t*,
|
||||||
|
int32_t,
|
||||||
|
int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatDivide_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatRoot_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatRoot_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatPower_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatPower_wrap(
|
||||||
|
void* env,
|
||||||
|
wasm_val_vec_t const* params,
|
||||||
|
wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
using floatLog_proto =
|
||||||
|
int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||||
|
wasm_trap_t*
|
||||||
|
floatLog_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
248
src/xrpld/app/wasm/ParamsHelper.h
Normal file
248
src/xrpld/app/wasm/ParamsHelper.h
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpl/basics/base_uint.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 <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace bft = boost::function_types;
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
using Bytes = std::vector<std::uint8_t>;
|
||||||
|
using Hash = ripple::uint256;
|
||||||
|
|
||||||
|
struct wmem
|
||||||
|
{
|
||||||
|
std::uint8_t* p = nullptr;
|
||||||
|
std::size_t s = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct WasmResult
|
||||||
|
{
|
||||||
|
T result;
|
||||||
|
int64_t cost;
|
||||||
|
};
|
||||||
|
typedef WasmResult<int32_t> EscrowResult;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
enum WasmTypes { WT_I32, WT_I64, WT_U8V };
|
||||||
|
|
||||||
|
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;
|
||||||
|
uint32_t gas = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WASM_IMPORT_FUNC(v, f, ...) \
|
||||||
|
WasmImpFunc<f##_proto>( \
|
||||||
|
v, #f, reinterpret_cast<void*>(&f##_wrap), ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define WASM_IMPORT_FUNC2(v, f, n, ...) \
|
||||||
|
WasmImpFunc<f##_proto>( \
|
||||||
|
v, n, 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
|
||||||
|
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_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,
|
||||||
|
uint32_t gas = 0)
|
||||||
|
{
|
||||||
|
WasmImportFunc e;
|
||||||
|
e.name = imp_name;
|
||||||
|
e.udata = data;
|
||||||
|
e.wrap = f_wrap;
|
||||||
|
e.gas = gas;
|
||||||
|
WasmImpFuncHelper<F>(e);
|
||||||
|
v.push_back(std::move(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct WasmParamVec
|
||||||
|
{
|
||||||
|
std::uint8_t const* d = nullptr;
|
||||||
|
std::int32_t sz = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WasmParam
|
||||||
|
{
|
||||||
|
WasmTypes type = WT_I32;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
std::int32_t i32;
|
||||||
|
std::int64_t i64 = 0;
|
||||||
|
float f32;
|
||||||
|
double f64;
|
||||||
|
WasmParamVec u8v;
|
||||||
|
} of;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class... Types>
|
||||||
|
inline void
|
||||||
|
wasmParamsHlp(std::vector<WasmParam>& v, std::int32_t p, Types&&... args)
|
||||||
|
{
|
||||||
|
v.push_back({.type = WT_I32, .of = {.i32 = p}});
|
||||||
|
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Types>
|
||||||
|
inline void
|
||||||
|
wasmParamsHlp(std::vector<WasmParam>& v, std::int64_t p, Types&&... args)
|
||||||
|
{
|
||||||
|
v.push_back({.type = WT_I64, .of = {.i64 = p}});
|
||||||
|
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are not supporting float/double for now
|
||||||
|
// Leaving this code here so that it is easier to add later if needed
|
||||||
|
// template <class... Types>
|
||||||
|
// inline void
|
||||||
|
// wasmParamsHlp(std::vector<WasmParam>& v, float p, Types&&... args)
|
||||||
|
// {
|
||||||
|
// v.push_back({.type = WT_F32, .of = {.f32 = p}});
|
||||||
|
// wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// template <class... Types>
|
||||||
|
// inline void
|
||||||
|
// wasmParamsHlp(std::vector<WasmParam>& v, double p, Types&&... args)
|
||||||
|
// {
|
||||||
|
// v.push_back({.type = WT_F64, .of = {.f64 = p}});
|
||||||
|
// wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||||
|
// }
|
||||||
|
|
||||||
|
template <class... Types>
|
||||||
|
inline void
|
||||||
|
wasmParamsHlp(
|
||||||
|
std::vector<WasmParam>& v,
|
||||||
|
std::uint8_t const* dt,
|
||||||
|
std::int32_t sz,
|
||||||
|
Types&&... args)
|
||||||
|
{
|
||||||
|
v.push_back({.type = WT_U8V, .of = {.u8v = {.d = dt, .sz = sz}}});
|
||||||
|
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Types>
|
||||||
|
inline void
|
||||||
|
wasmParamsHlp(std::vector<WasmParam>& v, Bytes const& p, Types&&... args)
|
||||||
|
{
|
||||||
|
wasmParamsHlp(
|
||||||
|
v,
|
||||||
|
p.data(),
|
||||||
|
static_cast<std::int32_t>(p.size()),
|
||||||
|
std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Types>
|
||||||
|
inline void
|
||||||
|
wasmParamsHlp(
|
||||||
|
std::vector<WasmParam>& v,
|
||||||
|
std::string_view const& p,
|
||||||
|
Types&&... args)
|
||||||
|
{
|
||||||
|
wasmParamsHlp(
|
||||||
|
v,
|
||||||
|
reinterpret_cast<std::uint8_t const*>(p.data()),
|
||||||
|
static_cast<std::int32_t>(p.size()),
|
||||||
|
std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Types>
|
||||||
|
inline void
|
||||||
|
wasmParamsHlp(std::vector<WasmParam>& v, std::string const& p, Types&&... args)
|
||||||
|
{
|
||||||
|
wasmParamsHlp(
|
||||||
|
v,
|
||||||
|
reinterpret_cast<std::uint8_t const*>(p.c_str()),
|
||||||
|
static_cast<std::int32_t>(p.size()),
|
||||||
|
std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
wasmParamsHlp(std::vector<WasmParam>& v)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Types>
|
||||||
|
inline std::vector<WasmParam>
|
||||||
|
wasmParams(Types&&... args)
|
||||||
|
{
|
||||||
|
std::vector<WasmParam> v;
|
||||||
|
v.reserve(sizeof...(args));
|
||||||
|
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
89
src/xrpld/app/wasm/WasmVM.h
Normal file
89
src/xrpld/app/wasm/WasmVM.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpld/app/wasm/HostFunc.h>
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
static std::string_view const W_ENV = "env";
|
||||||
|
static std::string_view const W_HOST_LIB = "host_lib";
|
||||||
|
static std::string_view const W_MEM = "memory";
|
||||||
|
static std::string_view const W_STORE = "store";
|
||||||
|
static std::string_view const W_LOAD = "load";
|
||||||
|
static std::string_view const W_SIZE = "size";
|
||||||
|
static std::string_view const W_ALLOC = "allocate";
|
||||||
|
static std::string_view const W_DEALLOC = "deallocate";
|
||||||
|
static std::string_view const W_PROC_EXIT = "proc_exit";
|
||||||
|
|
||||||
|
static std::string_view const ESCROW_FUNCTION_NAME = "finish";
|
||||||
|
|
||||||
|
class WasmiEngine;
|
||||||
|
|
||||||
|
class WasmEngine
|
||||||
|
{
|
||||||
|
std::unique_ptr<WasmiEngine> 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<WasmResult<int32_t>, TER>
|
||||||
|
run(Bytes const& wasmCode,
|
||||||
|
std::string_view funcName = {},
|
||||||
|
std::vector<WasmParam> const& params = {},
|
||||||
|
std::vector<WasmImportFunc> const& imports = {},
|
||||||
|
HostFunctions* hfs = nullptr,
|
||||||
|
int64_t gasLimit = -1,
|
||||||
|
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
check(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params = {},
|
||||||
|
std::vector<WasmImportFunc> const& imports = {},
|
||||||
|
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
|
||||||
|
|
||||||
|
// Host functions helper functionality
|
||||||
|
void*
|
||||||
|
newTrap(std::string_view msg = {});
|
||||||
|
|
||||||
|
beast::Journal
|
||||||
|
getJournal() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
std::vector<WasmImportFunc>
|
||||||
|
createWasmImport(HostFunctions* hfs);
|
||||||
|
|
||||||
|
Expected<EscrowResult, TER>
|
||||||
|
runEscrowWasm(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName = ESCROW_FUNCTION_NAME,
|
||||||
|
std::vector<WasmParam> const& params = {},
|
||||||
|
HostFunctions* hfs = nullptr,
|
||||||
|
int64_t gasLimit = -1,
|
||||||
|
beast::Journal j = beast::Journal(beast::Journal::getNullSink()));
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
preflightEscrowWasm(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName = ESCROW_FUNCTION_NAME,
|
||||||
|
std::vector<WasmParam> const& params = {},
|
||||||
|
HostFunctions* hfs = nullptr,
|
||||||
|
beast::Journal j = beast::Journal(beast::Journal::getNullSink()));
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
298
src/xrpld/app/wasm/WasmiVM.h
Normal file
298
src/xrpld/app/wasm/WasmiVM.h
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpld/app/wasm/WasmVM.h>
|
||||||
|
|
||||||
|
#include <wasm.h>
|
||||||
|
#include <wasmi.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
struct WasmiResult
|
||||||
|
{
|
||||||
|
wasm_val_vec_t r;
|
||||||
|
bool f; // failure flag
|
||||||
|
|
||||||
|
WasmiResult(unsigned N = 0) : r{0, nullptr}, f(false)
|
||||||
|
{
|
||||||
|
if (N)
|
||||||
|
wasm_val_vec_new_uninitialized(&r, N);
|
||||||
|
}
|
||||||
|
|
||||||
|
~WasmiResult()
|
||||||
|
{
|
||||||
|
if (r.size)
|
||||||
|
wasm_val_vec_delete(&r);
|
||||||
|
}
|
||||||
|
|
||||||
|
WasmiResult(WasmiResult const&) = delete;
|
||||||
|
WasmiResult&
|
||||||
|
operator=(WasmiResult const&) = delete;
|
||||||
|
|
||||||
|
WasmiResult(WasmiResult&& o)
|
||||||
|
{
|
||||||
|
*this = std::move(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
WasmiResult&
|
||||||
|
operator=(WasmiResult&& o)
|
||||||
|
{
|
||||||
|
r = o.r;
|
||||||
|
o.r = {0, nullptr};
|
||||||
|
f = o.f;
|
||||||
|
o.f = false;
|
||||||
|
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)>;
|
||||||
|
|
||||||
|
using FuncInfo = std::pair<wasm_func_t const*, wasm_functype_t const*>;
|
||||||
|
|
||||||
|
struct InstanceWrapper
|
||||||
|
{
|
||||||
|
wasm_extern_vec_t exports_;
|
||||||
|
InstancePtr instance_;
|
||||||
|
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
|
||||||
|
|
||||||
|
private:
|
||||||
|
static InstancePtr
|
||||||
|
init(
|
||||||
|
wasm_store_t* s,
|
||||||
|
wasm_module_t* m,
|
||||||
|
wasm_extern_vec_t* expt,
|
||||||
|
wasm_extern_vec_t const& imports,
|
||||||
|
beast::Journal j);
|
||||||
|
|
||||||
|
public:
|
||||||
|
InstanceWrapper();
|
||||||
|
|
||||||
|
InstanceWrapper(InstanceWrapper&& o);
|
||||||
|
|
||||||
|
InstanceWrapper&
|
||||||
|
operator=(InstanceWrapper&& o);
|
||||||
|
|
||||||
|
InstanceWrapper(
|
||||||
|
wasm_store_t* s,
|
||||||
|
wasm_module_t* m,
|
||||||
|
wasm_extern_vec_t const& imports,
|
||||||
|
beast::Journal j);
|
||||||
|
|
||||||
|
~InstanceWrapper();
|
||||||
|
|
||||||
|
operator bool() const;
|
||||||
|
|
||||||
|
FuncInfo
|
||||||
|
getFunc(
|
||||||
|
std::string_view funcName,
|
||||||
|
wasm_exporttype_vec_t const& export_types) const;
|
||||||
|
|
||||||
|
wmem
|
||||||
|
getMem() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModuleWrapper
|
||||||
|
{
|
||||||
|
wasm_store_t* store_ = nullptr;
|
||||||
|
ModulePtr module_;
|
||||||
|
InstanceWrapper instanceWrap_;
|
||||||
|
wasm_exporttype_vec_t exportTypes_;
|
||||||
|
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ModulePtr
|
||||||
|
init(wasm_store_t* s, Bytes const& wasmBin, beast::Journal j);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ModuleWrapper();
|
||||||
|
ModuleWrapper(ModuleWrapper&& o);
|
||||||
|
ModuleWrapper&
|
||||||
|
operator=(ModuleWrapper&& o);
|
||||||
|
ModuleWrapper(
|
||||||
|
wasm_store_t* s,
|
||||||
|
Bytes const& wasmBin,
|
||||||
|
bool instantiate,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
beast::Journal j);
|
||||||
|
~ModuleWrapper();
|
||||||
|
|
||||||
|
operator bool() const;
|
||||||
|
|
||||||
|
FuncInfo
|
||||||
|
getFunc(std::string_view funcName) const;
|
||||||
|
wmem
|
||||||
|
getMem() const;
|
||||||
|
|
||||||
|
InstanceWrapper const&
|
||||||
|
getInstance(int i = 0) const;
|
||||||
|
|
||||||
|
int
|
||||||
|
addInstance(
|
||||||
|
wasm_store_t* s,
|
||||||
|
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
|
||||||
|
|
||||||
|
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 WasmiEngine
|
||||||
|
{
|
||||||
|
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> moduleWrap_;
|
||||||
|
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
|
||||||
|
|
||||||
|
std::mutex m_; // 1 instance mutex
|
||||||
|
|
||||||
|
public:
|
||||||
|
WasmiEngine();
|
||||||
|
~WasmiEngine() = default;
|
||||||
|
|
||||||
|
static std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)>
|
||||||
|
init();
|
||||||
|
|
||||||
|
Expected<WasmResult<int32_t>, TER>
|
||||||
|
run(Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
HostFunctions* hfs,
|
||||||
|
int64_t gas,
|
||||||
|
beast::Journal j);
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
check(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
beast::Journal j);
|
||||||
|
|
||||||
|
std::int64_t
|
||||||
|
getGas();
|
||||||
|
|
||||||
|
// Host functions helper functionality
|
||||||
|
wasm_trap_t*
|
||||||
|
newTrap(std::string_view msg);
|
||||||
|
|
||||||
|
beast::Journal
|
||||||
|
getJournal() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
InstanceWrapper const&
|
||||||
|
getRT(int m = 0, int i = 0);
|
||||||
|
|
||||||
|
wmem
|
||||||
|
getMem() const;
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
allocate(int32_t size);
|
||||||
|
|
||||||
|
Expected<WasmResult<int32_t>, TER>
|
||||||
|
runHlp(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
HostFunctions* hfs,
|
||||||
|
int64_t gas);
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
checkHlp(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports);
|
||||||
|
|
||||||
|
int
|
||||||
|
addModule(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
bool instantiate,
|
||||||
|
int64_t gas,
|
||||||
|
std::vector<WasmImportFunc> const& imports);
|
||||||
|
void
|
||||||
|
clearModules();
|
||||||
|
|
||||||
|
// int addInstance();
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
runFunc(std::string_view const funcName, int32_t p);
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
makeModule(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
|
||||||
|
|
||||||
|
FuncInfo
|
||||||
|
getFunc(std::string_view funcName);
|
||||||
|
|
||||||
|
std::vector<wasm_val_t>
|
||||||
|
convertParams(std::vector<WasmParam> const& params);
|
||||||
|
|
||||||
|
static int
|
||||||
|
compareParamTypes(
|
||||||
|
wasm_valtype_vec_t const* ftp,
|
||||||
|
std::vector<wasm_val_t> const& p);
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_param(std::vector<wasm_val_t>& in, int32_t p);
|
||||||
|
static void
|
||||||
|
add_param(std::vector<wasm_val_t>& in, int64_t p);
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
inline WasmiResult
|
||||||
|
call(std::string_view func, Types&&... args);
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
inline WasmiResult
|
||||||
|
call(FuncInfo const& f, Types&&... args);
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
inline WasmiResult
|
||||||
|
call(FuncInfo const& f, std::vector<wasm_val_t>& in);
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
inline WasmiResult
|
||||||
|
call(
|
||||||
|
FuncInfo const& f,
|
||||||
|
std::vector<wasm_val_t>& in,
|
||||||
|
std::int32_t p,
|
||||||
|
Types&&... args);
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
inline WasmiResult
|
||||||
|
call(
|
||||||
|
FuncInfo const& f,
|
||||||
|
std::vector<wasm_val_t>& in,
|
||||||
|
std::int64_t p,
|
||||||
|
Types&&... args);
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
inline WasmiResult
|
||||||
|
call(
|
||||||
|
FuncInfo const& f,
|
||||||
|
std::vector<wasm_val_t>& in,
|
||||||
|
uint8_t const* d,
|
||||||
|
std::size_t sz,
|
||||||
|
Types&&... args);
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
inline WasmiResult
|
||||||
|
call(
|
||||||
|
FuncInfo const& f,
|
||||||
|
std::vector<wasm_val_t>& in,
|
||||||
|
Bytes const& p,
|
||||||
|
Types&&... args);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
1303
src/xrpld/app/wasm/detail/HostFuncImpl.cpp
Normal file
1303
src/xrpld/app/wasm/detail/HostFuncImpl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2024
src/xrpld/app/wasm/detail/HostFuncWrapper.cpp
Normal file
2024
src/xrpld/app/wasm/detail/HostFuncWrapper.cpp
Normal file
File diff suppressed because it is too large
Load Diff
217
src/xrpld/app/wasm/detail/WasmVM.cpp
Normal file
217
src/xrpld/app/wasm/detail/WasmVM.cpp
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
#ifdef _DEBUG
|
||||||
|
// #define DEBUG_OUTPUT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <xrpld/app/wasm/HostFunc.h>
|
||||||
|
#include <xrpld/app/wasm/HostFuncWrapper.h>
|
||||||
|
#include <xrpld/app/wasm/WasmiVM.h>
|
||||||
|
|
||||||
|
#include <xrpl/basics/Log.h>
|
||||||
|
#include <xrpl/protocol/AccountID.h>
|
||||||
|
#include <xrpl/protocol/Feature.h>
|
||||||
|
#include <xrpl/protocol/LedgerFormats.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
static void
|
||||||
|
setCommonHostFunctions(HostFunctions* hfs, std::vector<WasmImportFunc>& i)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
WASM_IMPORT_FUNC2(i, getLedgerSqn, "get_ledger_sqn", hfs, 60);
|
||||||
|
WASM_IMPORT_FUNC2(i, getParentLedgerTime, "get_parent_ledger_time", hfs, 60);
|
||||||
|
WASM_IMPORT_FUNC2(i, getParentLedgerHash, "get_parent_ledger_hash", hfs, 60);
|
||||||
|
WASM_IMPORT_FUNC2(i, getBaseFee, "get_base_fee", hfs, 60);
|
||||||
|
WASM_IMPORT_FUNC2(i, isAmendmentEnabled, "amendment_enabled", hfs, 100);
|
||||||
|
|
||||||
|
WASM_IMPORT_FUNC2(i, cacheLedgerObj, "cache_ledger_obj", hfs, 5'000);
|
||||||
|
WASM_IMPORT_FUNC2(i, getTxField, "get_tx_field", hfs, 70);
|
||||||
|
WASM_IMPORT_FUNC2(i, getCurrentLedgerObjField, "get_current_ledger_obj_field", hfs, 70);
|
||||||
|
WASM_IMPORT_FUNC2(i, getLedgerObjField, "get_ledger_obj_field", hfs, 70);
|
||||||
|
WASM_IMPORT_FUNC2(i, getTxNestedField, "get_tx_nested_field", hfs, 110);
|
||||||
|
WASM_IMPORT_FUNC2(i, getCurrentLedgerObjNestedField, "get_current_ledger_obj_nested_field", hfs, 110);
|
||||||
|
WASM_IMPORT_FUNC2(i, getLedgerObjNestedField, "get_ledger_obj_nested_field", hfs, 110);
|
||||||
|
WASM_IMPORT_FUNC2(i, getTxArrayLen, "get_tx_array_len", hfs, 40);
|
||||||
|
WASM_IMPORT_FUNC2(i, getCurrentLedgerObjArrayLen, "get_current_ledger_obj_array_len", hfs, 40);
|
||||||
|
WASM_IMPORT_FUNC2(i, getLedgerObjArrayLen, "get_ledger_obj_array_len", hfs, 40);
|
||||||
|
WASM_IMPORT_FUNC2(i, getTxNestedArrayLen, "get_tx_nested_array_len", hfs, 70);
|
||||||
|
WASM_IMPORT_FUNC2(i, getCurrentLedgerObjNestedArrayLen, "get_current_ledger_obj_nested_array_len", hfs, 70);
|
||||||
|
WASM_IMPORT_FUNC2(i, getLedgerObjNestedArrayLen, "get_ledger_obj_nested_array_len", hfs, 70);
|
||||||
|
|
||||||
|
WASM_IMPORT_FUNC2(i, checkSignature, "check_sig", hfs, 300);
|
||||||
|
WASM_IMPORT_FUNC2(i, computeSha512HalfHash, "compute_sha512_half", hfs, 2000);
|
||||||
|
|
||||||
|
WASM_IMPORT_FUNC2(i, accountKeylet, "account_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, ammKeylet, "amm_keylet", hfs, 450);
|
||||||
|
WASM_IMPORT_FUNC2(i, checkKeylet, "check_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, credentialKeylet, "credential_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, delegateKeylet, "delegate_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, depositPreauthKeylet, "deposit_preauth_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, didKeylet, "did_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, escrowKeylet, "escrow_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, lineKeylet, "line_keylet", hfs, 400);
|
||||||
|
WASM_IMPORT_FUNC2(i, mptIssuanceKeylet, "mpt_issuance_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, mptokenKeylet, "mptoken_keylet", hfs, 500);
|
||||||
|
WASM_IMPORT_FUNC2(i, nftOfferKeylet, "nft_offer_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, offerKeylet, "offer_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, oracleKeylet, "oracle_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, paychanKeylet, "paychan_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, permissionedDomainKeylet, "permissioned_domain_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, signersKeylet, "signers_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, ticketKeylet, "ticket_keylet", hfs, 350);
|
||||||
|
WASM_IMPORT_FUNC2(i, vaultKeylet, "vault_keylet", hfs, 350);
|
||||||
|
|
||||||
|
WASM_IMPORT_FUNC2(i, getNFT, "get_nft", hfs, 1000);
|
||||||
|
WASM_IMPORT_FUNC2(i, getNFTIssuer, "get_nft_issuer", hfs, 70);
|
||||||
|
WASM_IMPORT_FUNC2(i, getNFTTaxon, "get_nft_taxon", hfs, 60);
|
||||||
|
WASM_IMPORT_FUNC2(i, getNFTFlags, "get_nft_flags", hfs, 60);
|
||||||
|
WASM_IMPORT_FUNC2(i, getNFTTransferFee, "get_nft_transfer_fee", hfs, 60);
|
||||||
|
WASM_IMPORT_FUNC2(i, getNFTSerial, "get_nft_serial", hfs, 60);
|
||||||
|
|
||||||
|
WASM_IMPORT_FUNC (i, trace, hfs, 500);
|
||||||
|
WASM_IMPORT_FUNC2(i, traceNum, "trace_num", hfs, 500);
|
||||||
|
WASM_IMPORT_FUNC2(i, traceAccount, "trace_account", hfs, 500);
|
||||||
|
WASM_IMPORT_FUNC2(i, traceFloat, "trace_opaque_float", hfs, 500);
|
||||||
|
WASM_IMPORT_FUNC2(i, traceAmount, "trace_amount", hfs, 500);
|
||||||
|
|
||||||
|
WASM_IMPORT_FUNC2(i, floatFromInt, "float_from_int", hfs, 100);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatFromUint, "float_from_uint", hfs, 130);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatSet, "float_set", hfs, 100);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatCompare, "float_compare", hfs, 80);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatAdd, "float_add", hfs, 160);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatSubtract, "float_subtract", hfs, 160);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatMultiply, "float_multiply", hfs, 300);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatDivide, "float_divide", hfs, 300);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatRoot, "float_root", hfs, 5'500);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatPower, "float_pow", hfs, 5'500);
|
||||||
|
WASM_IMPORT_FUNC2(i, floatLog, "float_log", hfs, 12'000);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WasmImportFunc>
|
||||||
|
createWasmImport(HostFunctions* hfs)
|
||||||
|
{
|
||||||
|
std::vector<WasmImportFunc> i;
|
||||||
|
|
||||||
|
if (hfs)
|
||||||
|
{
|
||||||
|
setCommonHostFunctions(hfs, i);
|
||||||
|
WASM_IMPORT_FUNC2(i, updateData, "update_data", hfs, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<EscrowResult, TER>
|
||||||
|
runEscrowWasm(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
HostFunctions* hfs,
|
||||||
|
int64_t gasLimit,
|
||||||
|
beast::Journal j)
|
||||||
|
{
|
||||||
|
// create VM and set cost limit
|
||||||
|
auto& vm = WasmEngine::instance();
|
||||||
|
// vm.initMaxPages(MAX_PAGES);
|
||||||
|
|
||||||
|
auto const ret = vm.run(
|
||||||
|
wasmCode,
|
||||||
|
funcName,
|
||||||
|
params,
|
||||||
|
createWasmImport(hfs),
|
||||||
|
hfs,
|
||||||
|
gasLimit,
|
||||||
|
hfs ? hfs->getJournal() : j);
|
||||||
|
|
||||||
|
// std::cout << "runEscrowWasm, mod size: " << wasmCode.size()
|
||||||
|
// << ", gasLimit: " << gasLimit << ", funcName: " << funcName;
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_OUTPUT
|
||||||
|
std::cout << ", error: " << ret.error() << std::endl;
|
||||||
|
#endif
|
||||||
|
return Unexpected<TER>(ret.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_OUTPUT
|
||||||
|
std::cout << ", ret: " << ret->result << ", gas spent: " << ret->cost
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
return EscrowResult{ret->result, ret->cost};
|
||||||
|
}
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
preflightEscrowWasm(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
HostFunctions* hfs,
|
||||||
|
beast::Journal j)
|
||||||
|
{
|
||||||
|
// create VM and set cost limit
|
||||||
|
auto& vm = WasmEngine::instance();
|
||||||
|
// vm.initMaxPages(MAX_PAGES);
|
||||||
|
|
||||||
|
auto const ret = vm.check(
|
||||||
|
wasmCode,
|
||||||
|
funcName,
|
||||||
|
params,
|
||||||
|
createWasmImport(hfs),
|
||||||
|
hfs ? hfs->getJournal() : j);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
WasmEngine::WasmEngine() : impl(std::make_unique<WasmiEngine>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WasmEngine&
|
||||||
|
WasmEngine::instance()
|
||||||
|
{
|
||||||
|
static WasmEngine e;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<WasmResult<int32_t>, TER>
|
||||||
|
WasmEngine::run(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
HostFunctions* hfs,
|
||||||
|
int64_t gasLimit,
|
||||||
|
beast::Journal j)
|
||||||
|
{
|
||||||
|
return impl->run(wasmCode, funcName, params, imports, hfs, gasLimit, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
WasmEngine::check(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
beast::Journal j)
|
||||||
|
{
|
||||||
|
return impl->check(wasmCode, funcName, params, imports, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
WasmEngine::newTrap(std::string_view msg)
|
||||||
|
{
|
||||||
|
return impl->newTrap(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
beast::Journal
|
||||||
|
WasmEngine::getJournal() const
|
||||||
|
{
|
||||||
|
return impl->getJournal();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
934
src/xrpld/app/wasm/detail/WasmiVM.cpp
Normal file
934
src/xrpld/app/wasm/detail/WasmiVM.cpp
Normal file
@@ -0,0 +1,934 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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/wasm/WasmiVM.h>
|
||||||
|
|
||||||
|
#include <xrpl/basics/Log.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// #define DEBUG_OUTPUT 1
|
||||||
|
#endif
|
||||||
|
// #define SHOW_CALL_TIME 1
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void
|
||||||
|
print_wasm_error(std::string_view msg, wasm_trap_t* trap, beast::Journal jlog)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_OUTPUT
|
||||||
|
auto& j = std::cerr;
|
||||||
|
#else
|
||||||
|
auto j = jlog.warn();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
wasm_byte_vec_t error_message WASM_EMPTY_VEC;
|
||||||
|
|
||||||
|
if (trap)
|
||||||
|
wasm_trap_message(trap, &error_message);
|
||||||
|
|
||||||
|
if (error_message.size)
|
||||||
|
{
|
||||||
|
j << "WASMI Error: " << msg << ", "
|
||||||
|
<< std::string_view(error_message.data, error_message.size - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
j << "WASMI Error: " << msg;
|
||||||
|
|
||||||
|
if (error_message.size)
|
||||||
|
wasm_byte_vec_delete(&error_message);
|
||||||
|
|
||||||
|
if (trap)
|
||||||
|
wasm_trap_delete(trap);
|
||||||
|
|
||||||
|
#ifdef DEBUG_OUTPUT
|
||||||
|
j << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
InstancePtr
|
||||||
|
InstanceWrapper::init(
|
||||||
|
wasm_store_t* s,
|
||||||
|
wasm_module_t* m,
|
||||||
|
wasm_extern_vec_t* expt,
|
||||||
|
wasm_extern_vec_t const& imports,
|
||||||
|
beast::Journal j)
|
||||||
|
{
|
||||||
|
wasm_trap_t* trap = nullptr;
|
||||||
|
InstancePtr mi = InstancePtr(
|
||||||
|
wasm_instance_new(s, m, &imports, &trap), &wasm_instance_delete);
|
||||||
|
|
||||||
|
if (!mi || trap)
|
||||||
|
{
|
||||||
|
print_wasm_error("can't create instance", trap, j);
|
||||||
|
throw std::runtime_error("can't create instance");
|
||||||
|
}
|
||||||
|
wasm_instance_exports(mi.get(), expt);
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceWrapper::InstanceWrapper()
|
||||||
|
: exports_{0, nullptr}, instance_(nullptr, &wasm_instance_delete)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceWrapper::InstanceWrapper(InstanceWrapper&& o)
|
||||||
|
: exports_{0, nullptr}, instance_(nullptr, &wasm_instance_delete)
|
||||||
|
{
|
||||||
|
*this = std::move(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceWrapper::InstanceWrapper(
|
||||||
|
wasm_store_t* s,
|
||||||
|
wasm_module_t* m,
|
||||||
|
wasm_extern_vec_t const& imports,
|
||||||
|
beast::Journal j)
|
||||||
|
: exports_ WASM_EMPTY_VEC
|
||||||
|
, instance_(init(s, m, &exports_, imports, j))
|
||||||
|
, j_(j)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceWrapper::~InstanceWrapper()
|
||||||
|
{
|
||||||
|
if (exports_.size)
|
||||||
|
wasm_extern_vec_delete(&exports_);
|
||||||
|
}
|
||||||
|
|
||||||
|
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};
|
||||||
|
|
||||||
|
instance_ = std::move(o.instance_);
|
||||||
|
|
||||||
|
j_ = o.j_;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceWrapper::
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(instance_);
|
||||||
|
}
|
||||||
|
|
||||||
|
FuncInfo
|
||||||
|
InstanceWrapper::getFunc(
|
||||||
|
std::string_view funcName,
|
||||||
|
wasm_exporttype_vec_t const& export_types) const
|
||||||
|
{
|
||||||
|
wasm_func_t* f = nullptr;
|
||||||
|
wasm_functype_t* ft = nullptr;
|
||||||
|
|
||||||
|
if (!instance_)
|
||||||
|
throw std::runtime_error("no instance");
|
||||||
|
|
||||||
|
if (!export_types.size)
|
||||||
|
throw std::runtime_error("no export");
|
||||||
|
if (export_types.size != exports_.size)
|
||||||
|
throw std::runtime_error("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);
|
||||||
|
wasm_externtype_t const* 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))
|
||||||
|
{
|
||||||
|
auto* exn(exports_.data[i]);
|
||||||
|
if (wasm_extern_kind(exn) != WASM_EXTERN_FUNC)
|
||||||
|
throw std::runtime_error("invalid export");
|
||||||
|
|
||||||
|
ft = wasm_externtype_as_functype(
|
||||||
|
const_cast<wasm_externtype_t*>(exn_type));
|
||||||
|
f = wasm_extern_as_func(exn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f || !ft)
|
||||||
|
throw std::runtime_error(
|
||||||
|
"can't find function <" + std::string(funcName) + ">");
|
||||||
|
|
||||||
|
return {f, ft};
|
||||||
|
}
|
||||||
|
|
||||||
|
wmem
|
||||||
|
InstanceWrapper::getMem() const
|
||||||
|
{
|
||||||
|
if (!instance_)
|
||||||
|
throw std::runtime_error("no instance");
|
||||||
|
|
||||||
|
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("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, Bytes const& wasmBin, beast::Journal j)
|
||||||
|
{
|
||||||
|
wasm_byte_vec_t const code{wasmBin.size(), (char*)(wasmBin.data())};
|
||||||
|
ModulePtr m = ModulePtr(wasm_module_new(s, &code), &wasm_module_delete);
|
||||||
|
if (!m)
|
||||||
|
throw std::runtime_error("can't create module");
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleWrapper::ModuleWrapper()
|
||||||
|
: module_(nullptr, &wasm_module_delete), exportTypes_{0, nullptr}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleWrapper::ModuleWrapper(ModuleWrapper&& o)
|
||||||
|
: module_(nullptr, &wasm_module_delete), exportTypes_{0, nullptr}
|
||||||
|
{
|
||||||
|
*this = std::move(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleWrapper::ModuleWrapper(
|
||||||
|
wasm_store_t* s,
|
||||||
|
Bytes const& wasmBin,
|
||||||
|
bool instantiate,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
beast::Journal j)
|
||||||
|
: store_(s), module_(init(s, wasmBin, j)), exportTypes_{0, nullptr}, j_(j)
|
||||||
|
{
|
||||||
|
wasm_module_exports(module_.get(), &exportTypes_);
|
||||||
|
if (instantiate)
|
||||||
|
{
|
||||||
|
auto wimports = buildImports(s, imports);
|
||||||
|
addInstance(s, wimports);
|
||||||
|
wasm_extern_vec_delete(&wimports);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleWrapper::~ModuleWrapper()
|
||||||
|
{
|
||||||
|
if (exportTypes_.size)
|
||||||
|
wasm_exporttype_vec_delete(&exportTypes_);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleWrapper&
|
||||||
|
ModuleWrapper::operator=(ModuleWrapper&& o)
|
||||||
|
{
|
||||||
|
if (this == &o)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
store_ = o.store_;
|
||||||
|
o.store_ = nullptr;
|
||||||
|
module_ = std::move(o.module_);
|
||||||
|
instanceWrap_ = std::move(o.instanceWrap_);
|
||||||
|
if (exportTypes_.size)
|
||||||
|
wasm_exporttype_vec_delete(&exportTypes_);
|
||||||
|
exportTypes_ = o.exportTypes_;
|
||||||
|
o.exportTypes_ = {0, nullptr};
|
||||||
|
j_ = o.j_;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleWrapper::
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return instanceWrap_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ModuleWrapper::makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp)
|
||||||
|
{
|
||||||
|
auto const paramSize = imp.params.size();
|
||||||
|
|
||||||
|
if (paramSize)
|
||||||
|
{
|
||||||
|
wasm_valtype_vec_new_uninitialized(&v, 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;
|
||||||
|
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_uninitialized(&v, 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;
|
||||||
|
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.size)
|
||||||
|
return wimports;
|
||||||
|
|
||||||
|
wasm_extern_vec_new_uninitialized(&wimports, importTypes.size);
|
||||||
|
|
||||||
|
unsigned impCnt = 0;
|
||||||
|
for (unsigned i = 0; i < importTypes.size; ++i)
|
||||||
|
{
|
||||||
|
wasm_importtype_t const* importtype = importTypes.data[i];
|
||||||
|
|
||||||
|
// wasm_name_t const* mn = wasm_importtype_module(importtype);
|
||||||
|
// auto modName = std::string_view(mn->data, mn->num_elems);
|
||||||
|
wasm_name_t const* fn = wasm_importtype_name(importtype);
|
||||||
|
auto fieldName = std::string_view(fn->data, fn->size);
|
||||||
|
|
||||||
|
wasm_externkind_t const itype =
|
||||||
|
wasm_externtype_kind(wasm_importtype_type(importtype));
|
||||||
|
if ((itype) != WASM_EXTERN_FUNC)
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Invalid import type " + std::to_string(itype));
|
||||||
|
|
||||||
|
// for multi-module support
|
||||||
|
// if ((W_ENV != modName) && (W_HOST_LIB != modName))
|
||||||
|
// continue;
|
||||||
|
|
||||||
|
bool impSet = false;
|
||||||
|
for (auto const& imp : imports)
|
||||||
|
{
|
||||||
|
if (imp.name != fieldName)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
wasm_valtype_vec_t params = WASM_EMPTY_VEC,
|
||||||
|
results = WASM_EMPTY_VEC;
|
||||||
|
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);
|
||||||
|
if (!func)
|
||||||
|
{
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
throw std::runtime_error(
|
||||||
|
"can't create import function " + imp.name);
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (imp.gas && !wasm_func_set_gas(func, imp.gas))
|
||||||
|
// {
|
||||||
|
// // LCOV_EXCL_START
|
||||||
|
// throw std::runtime_error(
|
||||||
|
// "can't set gas for import function " + imp.name);
|
||||||
|
// // LCOV_EXCL_STOP
|
||||||
|
// }
|
||||||
|
|
||||||
|
wimports.data[i] = wasm_func_as_extern(func);
|
||||||
|
++impCnt;
|
||||||
|
impSet = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!impSet)
|
||||||
|
{
|
||||||
|
print_wasm_error(
|
||||||
|
std::string("Import not found: ") + fieldName.data(),
|
||||||
|
nullptr,
|
||||||
|
j_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impCnt != importTypes.size)
|
||||||
|
{
|
||||||
|
print_wasm_error(
|
||||||
|
std::string("Imports not finished: ") + std::to_string(impCnt) +
|
||||||
|
"/" + std::to_string(importTypes.size),
|
||||||
|
nullptr,
|
||||||
|
j_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wimports;
|
||||||
|
}
|
||||||
|
|
||||||
|
FuncInfo
|
||||||
|
ModuleWrapper::getFunc(std::string_view funcName) const
|
||||||
|
{
|
||||||
|
return instanceWrap_.getFunc(funcName, exportTypes_);
|
||||||
|
}
|
||||||
|
|
||||||
|
wmem
|
||||||
|
ModuleWrapper::getMem() const
|
||||||
|
{
|
||||||
|
return instanceWrap_.getMem();
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceWrapper const&
|
||||||
|
ModuleWrapper::getInstance(int) const
|
||||||
|
{
|
||||||
|
return instanceWrap_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ModuleWrapper::addInstance(wasm_store_t* s, wasm_extern_vec_t const& imports)
|
||||||
|
{
|
||||||
|
instanceWrap_ = {s, module_.get(), imports, j_};
|
||||||
|
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::getGas()
|
||||||
|
{
|
||||||
|
if (!store_)
|
||||||
|
return 0;
|
||||||
|
std::uint64_t gas = 0;
|
||||||
|
wasm_store_get_fuel(store_, &gas);
|
||||||
|
return static_cast<std::int64_t>(gas);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// void
|
||||||
|
// WasmiEngine::clearModules()
|
||||||
|
// {
|
||||||
|
// modules.clear();
|
||||||
|
// store.reset(); // to free the memory before creating new store
|
||||||
|
// store = {wasm_store_new(engine.get()), &wasm_store_delete};
|
||||||
|
// }
|
||||||
|
|
||||||
|
std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)>
|
||||||
|
WasmiEngine::init()
|
||||||
|
{
|
||||||
|
wasm_config_t* config = wasm_config_new();
|
||||||
|
if (!config)
|
||||||
|
return std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)>{
|
||||||
|
nullptr, &wasm_engine_delete};
|
||||||
|
wasmi_config_consume_fuel_set(config, true);
|
||||||
|
|
||||||
|
return std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)>(
|
||||||
|
wasm_engine_new_with_config(config), &wasm_engine_delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
WasmiEngine::WasmiEngine()
|
||||||
|
: engine_(init()), store_(nullptr, &wasm_store_delete)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
WasmiEngine::addModule(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
bool instantiate,
|
||||||
|
int64_t gas,
|
||||||
|
std::vector<WasmImportFunc> const& imports)
|
||||||
|
{
|
||||||
|
moduleWrap_.reset();
|
||||||
|
store_.reset(); // to free the memory before creating new store
|
||||||
|
store_ = {wasm_store_new(engine_.get()), &wasm_store_delete};
|
||||||
|
|
||||||
|
if (gas < 0)
|
||||||
|
gas = std::numeric_limits<decltype(gas)>::max();
|
||||||
|
wasmi_error_t* err =
|
||||||
|
wasm_store_set_fuel(store_.get(), static_cast<std::uint64_t>(gas));
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
print_wasm_error("Error setting gas", nullptr, j_);
|
||||||
|
throw std::runtime_error("can't set gas");
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleWrap_ = std::make_unique<ModuleWrapper>(
|
||||||
|
store_.get(), wasmCode, instantiate, imports, j_);
|
||||||
|
|
||||||
|
if (!moduleWrap_)
|
||||||
|
throw std::runtime_error("can't create module wrapper");
|
||||||
|
|
||||||
|
return moduleWrap_ ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// int
|
||||||
|
// WasmiEngine::addInstance()
|
||||||
|
// {
|
||||||
|
// return module->addInstance(store.get());
|
||||||
|
// }
|
||||||
|
|
||||||
|
FuncInfo
|
||||||
|
WasmiEngine::getFunc(std::string_view funcName)
|
||||||
|
{
|
||||||
|
return moduleWrap_->getFunc(funcName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<wasm_val_t>
|
||||||
|
WasmiEngine::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_U8V: {
|
||||||
|
auto const sz = p.of.u8v.sz;
|
||||||
|
auto const ptr = allocate(sz);
|
||||||
|
auto mem = getMem();
|
||||||
|
memcpy(mem.p + ptr, p.of.u8v.d, sz);
|
||||||
|
|
||||||
|
v.push_back(WASM_I32_VAL(ptr));
|
||||||
|
v.push_back(WASM_I32_VAL(sz));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"unknown parameter type: " + std::to_string(p.type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
WasmiEngine::compareParamTypes(
|
||||||
|
wasm_valtype_vec_t const* ftp,
|
||||||
|
std::vector<wasm_val_t> const& p)
|
||||||
|
{
|
||||||
|
if (ftp->size != p.size())
|
||||||
|
return std::min(ftp->size, p.size());
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < ftp->size; ++i)
|
||||||
|
{
|
||||||
|
auto const t1 = wasm_valtype_kind(ftp->data[i]);
|
||||||
|
auto const t2 = p[i].kind;
|
||||||
|
if (t1 != t2)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WasmiEngine::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
|
||||||
|
WasmiEngine::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>
|
||||||
|
WasmiResult
|
||||||
|
WasmiEngine::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>
|
||||||
|
WasmiResult
|
||||||
|
WasmiEngine::call(FuncInfo const& f, Types&&... args)
|
||||||
|
{
|
||||||
|
std::vector<wasm_val_t> in;
|
||||||
|
return call<NR>(f, in, std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SHOW_CALL_TIME
|
||||||
|
static inline uint64_t
|
||||||
|
usecs()
|
||||||
|
{
|
||||||
|
uint64_t x =
|
||||||
|
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||||
|
.count();
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
WasmiResult
|
||||||
|
WasmiEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in)
|
||||||
|
{
|
||||||
|
// wasm_val_t rs[1] = {WASM_I32_VAL(0)};
|
||||||
|
WasmiResult 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()};
|
||||||
|
|
||||||
|
#ifdef SHOW_CALL_TIME
|
||||||
|
auto const start = usecs();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
wasm_trap_t* trap = wasm_func_call(f.first, &inv, &ret.r);
|
||||||
|
|
||||||
|
#ifdef SHOW_CALL_TIME
|
||||||
|
auto const finish = usecs();
|
||||||
|
auto const delta_ms = (finish - start) / 1000;
|
||||||
|
std::cout << "wasm_func_call: " << delta_ms << "ms" << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (trap)
|
||||||
|
{
|
||||||
|
ret.f = true;
|
||||||
|
print_wasm_error("failure to call func", trap, j_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert(results[0].kind == WASM_I32);
|
||||||
|
// if (NR) printf("Result P5: %d\n", ret[0].of.i32);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
WasmiResult
|
||||||
|
WasmiEngine::call(
|
||||||
|
FuncInfo const& f,
|
||||||
|
std::vector<wasm_val_t>& in,
|
||||||
|
std::int32_t p,
|
||||||
|
Types&&... args)
|
||||||
|
{
|
||||||
|
add_param(in, p);
|
||||||
|
return call<NR>(f, in, std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
WasmiResult
|
||||||
|
WasmiEngine::call(
|
||||||
|
FuncInfo const& f,
|
||||||
|
std::vector<wasm_val_t>& in,
|
||||||
|
std::int64_t p,
|
||||||
|
Types&&... args)
|
||||||
|
{
|
||||||
|
add_param(in, p);
|
||||||
|
return call<NR>(f, in, std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
WasmiResult
|
||||||
|
WasmiEngine::call(
|
||||||
|
FuncInfo const& f,
|
||||||
|
std::vector<wasm_val_t>& in,
|
||||||
|
uint8_t const* d,
|
||||||
|
std::size_t sz,
|
||||||
|
Types&&... args)
|
||||||
|
{
|
||||||
|
auto const ptr = allocate(sz);
|
||||||
|
auto mem = getMem();
|
||||||
|
memcpy(mem.p + ptr, d, sz);
|
||||||
|
|
||||||
|
add_param(in, ptr);
|
||||||
|
add_param(in, static_cast<int32_t>(sz));
|
||||||
|
return call<NR>(f, in, std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int NR, class... Types>
|
||||||
|
WasmiResult
|
||||||
|
WasmiEngine::call(
|
||||||
|
FuncInfo const& f,
|
||||||
|
std::vector<wasm_val_t>& in,
|
||||||
|
Bytes const& p,
|
||||||
|
Types&&... args)
|
||||||
|
{
|
||||||
|
return call<NR>(f, in, p.data(), p.size(), std::forward<Types>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<WasmResult<int32_t>, TER>
|
||||||
|
WasmiEngine::run(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
HostFunctions* hfs,
|
||||||
|
int64_t gas,
|
||||||
|
beast::Journal j)
|
||||||
|
{
|
||||||
|
j_ = j;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return runHlp(wasmCode, funcName, params, imports, hfs, gas);
|
||||||
|
}
|
||||||
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
print_wasm_error(std::string("exception: ") + e.what(), nullptr, j_);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
print_wasm_error(std::string("exception: unknown"), nullptr, j_);
|
||||||
|
}
|
||||||
|
return Unexpected<TER>(tecFAILED_PROCESSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<WasmResult<int32_t>, TER>
|
||||||
|
WasmiEngine::runHlp(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
HostFunctions* hfs,
|
||||||
|
int64_t gas)
|
||||||
|
{
|
||||||
|
// currently only 1 module support, possible parallel UT run
|
||||||
|
std::lock_guard<decltype(m_)> lg(m_);
|
||||||
|
|
||||||
|
// Create and instantiate the module.
|
||||||
|
if (!wasmCode.empty())
|
||||||
|
{
|
||||||
|
[[maybe_unused]] int const m = addModule(wasmCode, true, gas, imports);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!moduleWrap_ || !moduleWrap_->instanceWrap_)
|
||||||
|
throw std::runtime_error("no instance");
|
||||||
|
|
||||||
|
if (hfs)
|
||||||
|
hfs->setRT(&getRT());
|
||||||
|
|
||||||
|
// Call main
|
||||||
|
auto const f = getFunc(!funcName.empty() ? funcName : "_start");
|
||||||
|
auto const* ftp = wasm_functype_params(f.second);
|
||||||
|
|
||||||
|
// not const because passed directly to VM function (which accept non
|
||||||
|
// const)
|
||||||
|
auto p = convertParams(params);
|
||||||
|
|
||||||
|
if (int const comp = compareParamTypes(ftp, p); comp >= 0)
|
||||||
|
throw std::runtime_error(
|
||||||
|
"invalid parameter type #" + std::to_string(comp));
|
||||||
|
|
||||||
|
auto const res = call<1>(f, p);
|
||||||
|
|
||||||
|
if (res.f)
|
||||||
|
throw std::runtime_error("<" + std::string(funcName) + "> failure");
|
||||||
|
else if (!res.r.size)
|
||||||
|
throw std::runtime_error(
|
||||||
|
"<" + std::string(funcName) + "> return nothing");
|
||||||
|
|
||||||
|
assert(res.r.data[0].kind == WASM_I32);
|
||||||
|
if (gas == -1)
|
||||||
|
gas = std::numeric_limits<decltype(gas)>::max();
|
||||||
|
WasmResult<int32_t> const ret{
|
||||||
|
res.r.data[0].of.i32, gas - moduleWrap_->getGas()};
|
||||||
|
|
||||||
|
// #ifdef DEBUG_OUTPUT
|
||||||
|
// auto& j = std::cerr;
|
||||||
|
// #else
|
||||||
|
// auto j = j_.debug();
|
||||||
|
// #endif
|
||||||
|
// j << "WASMI Res: " << ret.result << " cost: " << ret.cost << std::endl;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
WasmiEngine::check(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports,
|
||||||
|
beast::Journal j)
|
||||||
|
{
|
||||||
|
j_ = j;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return checkHlp(wasmCode, funcName, params, imports);
|
||||||
|
}
|
||||||
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
print_wasm_error(std::string("exception: ") + e.what(), nullptr, j_);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
print_wasm_error(std::string("exception: unknown"), nullptr, j_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return temBAD_WASM;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
WasmiEngine::checkHlp(
|
||||||
|
Bytes const& wasmCode,
|
||||||
|
std::string_view funcName,
|
||||||
|
std::vector<WasmParam> const& params,
|
||||||
|
std::vector<WasmImportFunc> const& imports)
|
||||||
|
{
|
||||||
|
// currently only 1 module support, possible parallel UT run
|
||||||
|
std::lock_guard<decltype(m_)> lg(m_);
|
||||||
|
|
||||||
|
// Create and instantiate the module.
|
||||||
|
if (wasmCode.empty())
|
||||||
|
throw std::runtime_error("empty nodule");
|
||||||
|
|
||||||
|
int const m = addModule(wasmCode, true, -1, imports);
|
||||||
|
if ((m < 0) || !moduleWrap_ || !moduleWrap_->instanceWrap_)
|
||||||
|
throw std::runtime_error("no instance");
|
||||||
|
|
||||||
|
// Looking for a func and compare parameter types
|
||||||
|
auto const f = getFunc(!funcName.empty() ? funcName : "_start");
|
||||||
|
auto const* ftp = wasm_functype_params(f.second);
|
||||||
|
auto const p = convertParams(params);
|
||||||
|
|
||||||
|
if (int const comp = compareParamTypes(ftp, p); comp >= 0)
|
||||||
|
throw std::runtime_error(
|
||||||
|
"invalid parameter type #" + std::to_string(comp));
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::int64_t
|
||||||
|
WasmiEngine::getGas()
|
||||||
|
{
|
||||||
|
return moduleWrap_ ? moduleWrap_->getGas() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
wmem
|
||||||
|
WasmiEngine::getMem() const
|
||||||
|
{
|
||||||
|
return moduleWrap_ ? moduleWrap_->getMem() : wmem();
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceWrapper const&
|
||||||
|
WasmiEngine::getRT(int m, int i)
|
||||||
|
{
|
||||||
|
if (!moduleWrap_)
|
||||||
|
throw std::runtime_error("no module");
|
||||||
|
return moduleWrap_->getInstance(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
WasmiEngine::allocate(int32_t sz)
|
||||||
|
{
|
||||||
|
auto res = call<1>(W_ALLOC, static_cast<int32_t>(sz));
|
||||||
|
|
||||||
|
if (res.f || !res.r.size || (res.r.data[0].kind != WASM_I32) ||
|
||||||
|
!res.r.data[0].of.i32)
|
||||||
|
throw std::runtime_error(
|
||||||
|
"can't allocate memory, " + std::to_string(sz) + " bytes");
|
||||||
|
return res.r.data[0].of.i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_trap_t*
|
||||||
|
WasmiEngine::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);
|
||||||
|
}
|
||||||
|
|
||||||
|
beast::Journal
|
||||||
|
WasmiEngine::getJournal() const
|
||||||
|
{
|
||||||
|
return j_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
Reference in New Issue
Block a user