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
|
||||
conan export --version 1.1.10 recipes/snappy/all
|
||||
conan export --version 4.0.3 recipes/soci/all
|
||||
conan export --version 0.42.1 external/wasmi
|
||||
rm -rf .git
|
||||
```
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ endif()
|
||||
|
||||
find_package(nudb REQUIRED)
|
||||
find_package(date REQUIRED)
|
||||
find_package(wasmi REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
|
||||
target_link_libraries(xrpl_libs INTERFACE
|
||||
|
||||
@@ -63,6 +63,7 @@ target_link_libraries(xrpl.imports.main
|
||||
Xrpl::opts
|
||||
Xrpl::syslibs
|
||||
secp256k1::secp256k1
|
||||
wasmi::wasmi
|
||||
xrpl.libpb
|
||||
xxHash::xxhash
|
||||
$<$<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
|
||||
import re
|
||||
|
||||
|
||||
class Xrpl(ConanFile):
|
||||
name = 'xrpl'
|
||||
|
||||
@@ -29,6 +30,7 @@ class Xrpl(ConanFile):
|
||||
'nudb/2.0.9',
|
||||
'openssl/3.5.4',
|
||||
'soci/4.0.3',
|
||||
'wasmi/0.42.1',
|
||||
'zlib/1.3.1',
|
||||
]
|
||||
|
||||
@@ -133,6 +135,7 @@ class Xrpl(ConanFile):
|
||||
self.folders.generators = 'build/generators'
|
||||
|
||||
generators = 'CMakeDeps'
|
||||
|
||||
def generate(self):
|
||||
tc = CMakeToolchain(self)
|
||||
tc.variables['tests'] = self.options.tests
|
||||
@@ -190,6 +193,7 @@ class Xrpl(ConanFile):
|
||||
'protobuf::libprotobuf',
|
||||
'soci::soci',
|
||||
'sqlite3::sqlite',
|
||||
'wasmi::wasmi',
|
||||
'xxhash::xxhash',
|
||||
'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
|
||||
power(Number const& f, unsigned n);
|
||||
|
||||
// logarithm with base 10
|
||||
Number
|
||||
lg(Number const& value);
|
||||
|
||||
// Returns f^(1/d)
|
||||
// Uses Newton–Raphson iterations until the result stops changing
|
||||
// to find the root of the polynomial g(x) = x^d - f
|
||||
|
||||
@@ -39,6 +39,13 @@ private:
|
||||
normalize();
|
||||
|
||||
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;
|
||||
explicit IOUAmount(Number const& other);
|
||||
IOUAmount(beast::Zero);
|
||||
|
||||
@@ -116,6 +116,13 @@ std::uint8_t constexpr vaultMaximumIOUScale = 18;
|
||||
* another vault; counted from 0 */
|
||||
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. */
|
||||
using LedgerIndex = std::uint32_t;
|
||||
|
||||
|
||||
@@ -122,6 +122,8 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temARRAY_TOO_LARGE,
|
||||
temBAD_TRANSFER_FEE,
|
||||
temINVALID_INNER_BATCH,
|
||||
|
||||
temBAD_WASM,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -166,6 +168,8 @@ enum TEFcodes : TERUnderlyingType {
|
||||
tefNO_TICKET,
|
||||
tefNFTOKEN_IS_NOT_TRANSFERABLE,
|
||||
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
|
||||
// reclaimed after those networks reset.
|
||||
tecNO_DELEGATE_PERMISSION = 198,
|
||||
tecWASM_REJECTED = 199,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -604,6 +604,48 @@ power(Number const& f, unsigned n)
|
||||
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)
|
||||
// Uses Newton–Raphson iterations until the result stops changing
|
||||
// to find the non-negative root of the polynomial g(x) = x^d - f
|
||||
|
||||
@@ -39,13 +39,6 @@ setSTNumberSwitchover(bool 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::minPositiveAmount()
|
||||
{
|
||||
@@ -293,7 +286,8 @@ mulRatio(
|
||||
{
|
||||
if (!result)
|
||||
{
|
||||
return IOUAmount(-minMantissa, minExponent);
|
||||
return IOUAmount(
|
||||
-IOUAmount::minMantissa, IOUAmount::minExponent);
|
||||
}
|
||||
// This subtraction cannot underflow because `result` is not zero
|
||||
return IOUAmount(result.mantissa() - 1, result.exponent());
|
||||
|
||||
@@ -108,6 +108,7 @@ transResults()
|
||||
MAKE_ERROR(tecLIMIT_EXCEEDED, "Limit exceeded."),
|
||||
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(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(tefBAD_ADD_AUTH, "Not authorized to add account."),
|
||||
@@ -131,6 +132,8 @@ transResults()
|
||||
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
|
||||
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(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(telBAD_DOMAIN, "Domain too long."),
|
||||
@@ -200,6 +203,7 @@ transResults()
|
||||
MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."),
|
||||
MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."),
|
||||
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(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