Merge branch 'ripple/smart-escrow' into develop2

This commit is contained in:
Mayukha Vadari
2025-05-01 18:44:06 -04:00
committed by GitHub
21 changed files with 2463 additions and 838 deletions

View File

@@ -19,6 +19,7 @@
#include <test/jtx.h>
#include <xrpld/app/misc/WasmVM.h>
#include <xrpld/app/tx/applySteps.h>
#include <xrpld/ledger/Dir.h>
@@ -1948,12 +1949,12 @@ struct Escrow_test : public beast::unit_test::suite
{
// not enough gas
// This function takes 110 gas
// This function takes 4 gas
// In testing, 1 gas costs 1 drop
auto const finishFee = env.current()->fees().base + 108;
auto const finishFee = env.current()->fees().base + 4;
env(finish(carol, alice, seq),
fee(finishFee),
comp_allowance(108),
comp_allowance(2),
ter(tecFAILED_PROCESSING));
}

View File

@@ -21,105 +21,21 @@
#include <xrpld/app/misc/WasmVM.h>
#include <wasmedge/wasmedge.h>
#include <iterator>
#include <iwasm/wasm_c_api.h>
namespace ripple {
namespace test {
/* Host function body definition. */
WasmEdge_Result
Add(void* Data,
const WasmEdge_CallingFrameContext* CallFrameCxt,
const WasmEdge_Value* In,
WasmEdge_Value* Out)
using Add_proto = int32_t(int32_t, int32_t);
wasm_trap_t*
Add(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results)
{
int32_t Val1 = WasmEdge_ValueGetI32(In[0]);
int32_t Val2 = WasmEdge_ValueGetI32(In[1]);
int32_t Val1 = params->data[0].of.i32;
int32_t Val2 = params->data[1].of.i32;
// printf("Host function \"Add\": %d + %d\n", Val1, Val2);
Out[0] = WasmEdge_ValueGenI32(Val1 + Val2);
return WasmEdge_Result_Success;
}
void
invokeAdd()
{
/* Create the VM context. */
WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(NULL, NULL);
// clang-format off
/* The WASM module buffer. */
uint8_t WASM[] = {/* WASM header */
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00,
/* Type section */
0x01, 0x07, 0x01,
/* function type {i32, i32} -> {i32} */
0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F,
/* Import section */
0x02, 0x13, 0x01,
/* module name: "extern" */
0x06, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6E,
/* extern name: "func-add" */
0x08, 0x66, 0x75, 0x6E, 0x63, 0x2D, 0x61, 0x64, 0x64,
/* import desc: func 0 */
0x00, 0x00,
/* Function section */
0x03, 0x02, 0x01, 0x00,
/* Export section */
0x07, 0x0A, 0x01,
/* export name: "addTwo" */
0x06, 0x61, 0x64, 0x64, 0x54, 0x77, 0x6F,
/* export desc: func 0 */
0x00, 0x01,
/* Code section */
0x0A, 0x0A, 0x01,
/* code body */
0x08, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x00, 0x0B};
// clang-format on
/* Create the module instance. */
WasmEdge_String ExportName = WasmEdge_StringCreateByCString("extern");
WasmEdge_ModuleInstanceContext* HostModCxt =
WasmEdge_ModuleInstanceCreate(ExportName);
WasmEdge_ValType ParamList[2] = {
WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()};
WasmEdge_ValType ReturnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* HostFType =
WasmEdge_FunctionTypeCreate(ParamList, 2, ReturnList, 1);
WasmEdge_FunctionInstanceContext* HostFunc =
WasmEdge_FunctionInstanceCreate(HostFType, Add, NULL, 0);
WasmEdge_FunctionTypeDelete(HostFType);
WasmEdge_String HostFuncName = WasmEdge_StringCreateByCString("func-add");
WasmEdge_ModuleInstanceAddFunction(HostModCxt, HostFuncName, HostFunc);
WasmEdge_StringDelete(HostFuncName);
WasmEdge_VMRegisterModuleFromImport(VMCxt, HostModCxt);
/* The parameters and returns arrays. */
WasmEdge_Value Params[2] = {
WasmEdge_ValueGenI32(1234), WasmEdge_ValueGenI32(5678)};
WasmEdge_Value Returns[1];
/* Function name. */
WasmEdge_String FuncName = WasmEdge_StringCreateByCString("addTwo");
/* Run the WASM function from buffer. */
WasmEdge_Result Res = WasmEdge_VMRunWasmFromBuffer(
VMCxt, WASM, sizeof(WASM), FuncName, Params, 2, Returns, 1);
if (WasmEdge_ResultOK(Res))
{
// printf("invokeAdd get the result: %d\n",
// WasmEdge_ValueGetI32(Returns[0]));
}
else
{
printf("Error message: %s\n", WasmEdge_ResultGetMessage(Res));
}
/* Resources deallocations. */
WasmEdge_VMDelete(VMCxt);
WasmEdge_StringDelete(FuncName);
WasmEdge_ModuleInstanceDelete(HostModCxt);
results->data[0] = WASM_I32_VAL(Val1 + Val2);
return nullptr;
}
struct Wasm_test : public beast::unit_test::suite
@@ -128,8 +44,46 @@ struct Wasm_test : public beast::unit_test::suite
testWasmtimeLib()
{
testcase("wasmtime lib test");
invokeAdd();
BEAST_EXPECT(true);
// clang-format off
/* The WASM module buffer. */
wbytes const wasm = {/* WASM header */
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00,
/* Type section */
0x01, 0x07, 0x01,
/* function type {i32, i32} -> {i32} */
0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F,
/* Import section */
0x02, 0x13, 0x01,
/* module name: "extern" */
0x06, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6E,
/* extern name: "func-add" */
0x08, 0x66, 0x75, 0x6E, 0x63, 0x2D, 0x61, 0x64, 0x64,
/* import desc: func 0 */
0x00, 0x00,
/* Function section */
0x03, 0x02, 0x01, 0x00,
/* Export section */
0x07, 0x0A, 0x01,
/* export name: "addTwo" */
0x06, 0x61, 0x64, 0x64, 0x54, 0x77, 0x6F,
/* export desc: func 0 */
0x00, 0x01,
/* Code section */
0x0A, 0x0A, 0x01,
/* code body */
0x08, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x00, 0x0B};
// clang-format on
auto& vm = WasmEngine::instance();
std::vector<WasmImportFunc> imports;
WasmImpFunc<Add_proto>(
imports, "func-add", reinterpret_cast<void*>(&Add));
auto res = vm.run(wasm, "addTwo", imports, wasmParams(1234, 5678));
// if (res) printf("invokeAdd get the result: %d\n", res.value());
BEAST_EXPECT(res.has_value() && res.value() == 6912);
}
void
@@ -1412,9 +1366,9 @@ struct Wasm_test : public beast::unit_test::suite
BadTestHostFunctions nfs(&env);
std::string funcName("ready");
auto re = runEscrowWasm(wasm, funcName, &nfs, 100000);
BEAST_EXPECT(re.error());
std::cout << "bad case (more than MAX_PAGES) result " << re.error()
<< std::endl;
if (BEAST_EXPECT(!re))
std::cout << "bad case (more than MAX_PAGES) result "
<< re.error() << std::endl;
}
{ // fail because recursion too deep
@@ -2143,9 +2097,9 @@ struct Wasm_test : public beast::unit_test::suite
TestHostFunctions nfs(&env);
std::string funcName("recursive");
auto re = runEscrowWasm(wasm, funcName, &nfs, 1000'000'000);
BEAST_EXPECT(re.error());
std::cout << "bad case (deep recursion) result " << re.error()
<< std::endl;
if (BEAST_EXPECT(re.error()))
std::cout << "bad case (deep recursion) result " << re.error()
<< std::endl;
}
}

View File

@@ -38,9 +38,9 @@ setupConfigForUnitTests(Config& cfg)
cfg.FEES.reference_fee = UNIT_TEST_REFERENCE_FEE;
cfg.FEES.account_reserve = XRP(200).value().xrp().drops();
cfg.FEES.owner_reserve = XRP(50).value().xrp().drops();
cfg.FEES.extension_compute_limit = 4294967295;
cfg.FEES.extension_size_limit = 4294967295;
cfg.FEES.gas_price = 1000000;
cfg.FEES.extension_compute_limit = 1'000'000;
cfg.FEES.extension_size_limit = 100'000;
cfg.FEES.gas_price = 1'000'000; // 1 drop = 1,000,000 micro-drops
// The Beta API (currently v2) is always available to tests
cfg.BETA_RPC_API = true;

View File

@@ -0,0 +1,754 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpld/app/misc/WamrVM.h>
#include <memory>
namespace ripple {
//////////////////////////////////////////////////////////////////////////////////////////
namespace {
static void
print_wasm_error(const char* message, wasm_trap_t* trap)
{
fprintf(stderr, "WAMR error: %s\n", message);
wasm_byte_vec_t error_message;
if (trap)
{
wasm_trap_message(trap, &error_message);
wasm_trap_delete(trap);
fprintf(
stderr,
"WAMR trap: %.*s\n",
(int)error_message.size,
error_message.data);
wasm_byte_vec_delete(&error_message);
}
}
} // namespace
InstancePtr
InstanceWrapper::init(
wasm_store_t* s,
wasm_module_t* m,
int32_t maxPages,
wasm_extern_vec_t* expt,
wasm_extern_vec_t const& imports)
{
wasm_trap_t* trap = nullptr;
InstantiationArgs inst_args{
128 * 1024,
256 * 1024,
static_cast<uint32_t>(maxPages > 0 ? maxPages : 0)};
InstancePtr mi = InstancePtr(
wasm_instance_new_with_args_ex(s, m, &imports, &trap, &inst_args),
&wasm_instance_delete);
if (!mi || trap)
{
print_wasm_error("can't create instance", trap);
throw std::runtime_error("WAMR: can't create instance");
}
wasm_instance_exports(mi.get(), expt);
return mi;
}
InstanceWrapper::InstanceWrapper()
: exports{0, nullptr, 0, 0, nullptr}
, mod_inst(nullptr, &wasm_instance_delete)
{
}
InstanceWrapper::InstanceWrapper(InstanceWrapper&& o)
: exports{0, nullptr, 0, 0, nullptr}
, mod_inst(nullptr, &wasm_instance_delete)
{
*this = std::move(o);
}
InstanceWrapper&
InstanceWrapper::operator=(InstanceWrapper&& o)
{
if (this == &o)
return *this;
if (exports.size)
wasm_extern_vec_delete(&exports);
exports = o.exports;
o.exports = {0, nullptr, 0, 0, nullptr};
mod_inst = std::move(o.mod_inst);
return *this;
}
InstanceWrapper::InstanceWrapper(
wasm_store_t* s,
wasm_module_t* m,
int32_t maxPages,
wasm_extern_vec_t const& imports)
: exports WASM_EMPTY_VEC, mod_inst(init(s, m, maxPages, &exports, imports))
{
}
InstanceWrapper::~InstanceWrapper()
{
if (exports.size)
wasm_extern_vec_delete(&exports);
}
InstanceWrapper::operator bool() const
{
return static_cast<bool>(mod_inst);
}
wasm_func_t*
InstanceWrapper::getFunc(
std::string_view funcName,
wasm_exporttype_vec_t const& export_types) const
{
wasm_func_t* f = nullptr;
if (!export_types.size)
throw std::runtime_error("WAMR: no export");
if (export_types.size != exports.size)
throw std::runtime_error("WAMR: invalid export");
for (unsigned i = 0; i < export_types.size; ++i)
{
auto const* exp_type(export_types.data[i]);
wasm_name_t const* name = wasm_exporttype_name(exp_type);
const wasm_externtype_t* exn_type = wasm_exporttype_type(exp_type);
if (wasm_externtype_kind(exn_type) == WASM_EXTERN_FUNC)
{
if (funcName == std::string_view(name->data, name->size - 1))
{
auto* exn(exports.data[i]);
if (wasm_extern_kind(exn) != WASM_EXTERN_FUNC)
throw std::runtime_error("WAMR: invalid export");
f = wasm_extern_as_func(exn);
break;
}
}
}
if (!f)
throw std::runtime_error(
"WAMR: can't find function " + std::string(funcName));
return f;
}
wmem
InstanceWrapper::getMem() const
{
wasm_memory_t* mem = nullptr;
for (unsigned i = 0; i < exports.size; ++i)
{
auto* e(exports.data[i]);
if (wasm_extern_kind(e) == WASM_EXTERN_MEMORY)
{
mem = wasm_extern_as_memory(e);
break;
}
}
if (!mem)
throw std::runtime_error("WAMR: no memory exported");
return {
reinterpret_cast<std::uint8_t*>(wasm_memory_data(mem)),
wasm_memory_data_size(mem)};
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModulePtr
ModuleWrapper::init(wasm_store_t* s, wbytes const& wasmBin)
{
wasm_byte_vec_t const code{
wasmBin.size(),
(char*)(wasmBin.data()),
wasmBin.size(),
sizeof(std::remove_reference_t<decltype(wasmBin)>::value_type),
nullptr};
ModulePtr m = ModulePtr(wasm_module_new(s, &code), &wasm_module_delete);
return m;
}
ModuleWrapper::ModuleWrapper()
: module(nullptr, &wasm_module_delete)
, export_types{0, nullptr, 0, 0, nullptr}
{
}
ModuleWrapper::ModuleWrapper(ModuleWrapper&& o)
: module(nullptr, &wasm_module_delete)
, export_types{0, nullptr, 0, 0, nullptr}
{
*this = std::move(o);
}
ModuleWrapper&
ModuleWrapper::operator=(ModuleWrapper&& o)
{
if (this == &o)
return *this;
module = std::move(o.module);
mod_inst = std::move(o.mod_inst);
if (export_types.size)
wasm_exporttype_vec_delete(&export_types);
export_types = o.export_types;
o.export_types = {0, nullptr, 0, 0, nullptr};
exec_env = o.exec_env;
o.exec_env = nullptr;
return *this;
}
ModuleWrapper::ModuleWrapper(
wasm_store_t* s,
wbytes const& wasmBin,
bool instantiate,
int32_t maxPages,
std::vector<WasmImportFunc> const& imports)
: module(init(s, wasmBin)), export_types{0, nullptr, 0, 0, nullptr}
{
if (!module)
throw std::runtime_error("WAMR: can't create module");
wasm_module_exports(module.get(), &export_types);
if (instantiate)
{
auto wimports = buildImports(s, imports);
addInstance(s, maxPages, wimports);
}
}
ModuleWrapper::~ModuleWrapper()
{
if (export_types.size)
wasm_exporttype_vec_delete(&export_types);
}
ModuleWrapper::operator bool() const
{
return mod_inst;
}
void
ModuleWrapper::makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp)
{
auto const paramSize = imp.params.size();
if (paramSize)
{
wasm_valtype_vec_new(&v, paramSize, nullptr);
v.num_elems = paramSize;
}
else
v = WASM_EMPTY_VEC;
for (unsigned i = 0; i < paramSize; ++i)
{
auto const vt = imp.params[i];
switch (vt)
{
case WT_I32:
v.data[i] = wasm_valtype_new_i32();
break;
case WT_I64:
v.data[i] = wasm_valtype_new_i64();
break;
case WT_F32:
v.data[i] = wasm_valtype_new_f32();
break;
case WT_F64:
v.data[i] = wasm_valtype_new_f64();
break;
default:
throw std::runtime_error("Invalid import type");
}
}
}
void
ModuleWrapper::makeImpReturn(wasm_valtype_vec_t& v, WasmImportFunc const& imp)
{
if (imp.result)
{
wasm_valtype_vec_new(&v, 1, nullptr);
v.num_elems = 1;
switch (*imp.result)
{
case WT_I32:
v.data[0] = wasm_valtype_new_i32();
break;
case WT_I64:
v.data[0] = wasm_valtype_new_i64();
break;
case WT_F32:
v.data[0] = wasm_valtype_new_f32();
break;
case WT_F64:
v.data[0] = wasm_valtype_new_f64();
break;
default:
throw std::runtime_error("Invalid return type");
}
}
else
v = WASM_EMPTY_VEC;
}
wasm_extern_vec_t
ModuleWrapper::buildImports(
wasm_store_t* s,
std::vector<WasmImportFunc> const& imports)
{
wasm_importtype_vec_t importTypes = WASM_EMPTY_VEC;
wasm_module_imports(module.get(), &importTypes);
std::
unique_ptr<wasm_importtype_vec_t, decltype(&wasm_importtype_vec_delete)>
itDeleter(&importTypes, &wasm_importtype_vec_delete);
wasm_extern_vec_t wimports = WASM_EMPTY_VEC;
if (!importTypes.num_elems)
return wimports;
wasm_extern_vec_new(&wimports, importTypes.size, nullptr);
wimports.num_elems = importTypes.num_elems;
for (unsigned i = 0; i < importTypes.num_elems; ++i)
{
wasm_importtype_t const* importtype = importTypes.data[i];
if (wasm_importtype_is_linked(importtype))
{
// create a placeholder
wimports.data[i] = wasm_extern_new_empty(
s, wasm_externtype_kind(wasm_importtype_type(importtype)));
continue;
}
// wasm_name_t const* mn = wasm_importtype_module(importtype);
// auto modName = std::string_view(mn->data, mn->num_elems - 1);
wasm_name_t const* fn = wasm_importtype_name(importtype);
auto fieldName = std::string_view(fn->data, fn->num_elems - 1);
// for multi-module support
// if ((W_ENV != modName) && (W_HOST_LIB != modName))
// continue;
for (auto const& imp : imports)
{
if (imp.name != fieldName)
continue;
wasm_valtype_vec_t params, results;
makeImpReturn(results, imp);
makeImpParams(params, imp);
using ftype_ptr = std::
unique_ptr<wasm_functype_t, decltype(&wasm_functype_delete)>;
ftype_ptr ftype(
wasm_functype_new(&params, &results), &wasm_functype_delete);
wasm_func_t* func = wasm_func_new_with_env(
s,
ftype.get(),
reinterpret_cast<wasm_func_callback_with_env_t>(imp.wrap),
imp.udata,
nullptr);
wimports.data[i] = wasm_func_as_extern(func);
break;
}
}
return wimports;
}
wasm_func_t*
ModuleWrapper::getFunc(std::string_view funcName) const
{
return mod_inst.getFunc(funcName, export_types);
}
wmem
ModuleWrapper::getMem() const
{
return mod_inst.getMem();
}
int
ModuleWrapper::addInstance(
wasm_store_t* s,
int32_t maxPages,
wasm_extern_vec_t const& imports)
{
mod_inst = {s, module.get(), maxPages, imports};
exec_env = wasm_instance_exec_env(mod_inst.mod_inst.get());
return 0;
}
// int
// my_module_t::delInstance(int i)
// {
// if (i >= mod_inst.size())
// return -1;
// if (!mod_inst[i])
// mod_inst[i] = my_mod_inst_t();
// return i;
// }
std::int64_t
ModuleWrapper::setGas(std::int64_t gas)
{
if (exec_env)
{
wasm_runtime_set_instruction_count_limit(exec_env, gas);
return gas;
}
return 0;
}
std::int64_t
ModuleWrapper::getGas()
{
return exec_env ? wasm_runtime_get_instruction_count_limit(exec_env) : 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// void
// WamrEngine::clearModules()
// {
// modules.clear();
// store.reset(); // to free the memory before creating new store
// store = {wasm_store_new(engine.get()), &wasm_store_delete};
// }
WamrEngine::WamrEngine()
: engine(wasm_engine_new(), &wasm_engine_delete)
, store(nullptr, &wasm_store_delete)
{
wasm_runtime_set_default_running_mode(Mode_Interp);
wasm_runtime_set_log_level(WASM_LOG_LEVEL_FATAL);
// wasm_runtime_set_log_level(WASM_LOG_LEVEL_VERBOSE);
}
int
WamrEngine::addModule(
wbytes const& wasmCode,
bool instantiate,
std::vector<WasmImportFunc> const& imports)
{
module.reset();
store.reset(); // to free the memory before creating new store
store = {wasm_store_new(engine.get()), &wasm_store_delete};
module = std::make_unique<ModuleWrapper>(
store.get(), wasmCode, instantiate, defMaxPages, imports);
setGas(defGas);
return module ? 0 : -1;
}
int
WamrEngine::addInstance()
{
return module->addInstance(store.get(), defMaxPages);
}
wasm_func_t*
WamrEngine::getFunc(std::string_view funcName)
{
return module->getFunc(funcName);
}
std::vector<wasm_val_t>
WamrEngine::convertParams(std::vector<WasmParam> const& params)
{
std::vector<wasm_val_t> v;
v.reserve(params.size());
for (auto const& p : params)
{
switch (p.type)
{
case WT_I32:
v.push_back(WASM_I32_VAL(p.of.i32));
break;
case WT_I64:
v.push_back(WASM_I64_VAL(p.of.i64));
break;
case WT_F32:
v.push_back(WASM_F32_VAL(p.of.f32));
break;
case WT_F64:
v.push_back(WASM_F64_VAL(p.of.f64));
break;
default:
break;
}
}
return v;
}
void
WamrEngine::add_param(std::vector<wasm_val_t>& in, int32_t p)
{
in.emplace_back();
auto& el(in.back());
memset(&el, 0, sizeof(el));
el = WASM_I32_VAL(p); // WASM_I32;
}
void
WamrEngine::add_param(std::vector<wasm_val_t>& in, int64_t p)
{
in.emplace_back();
auto& el(in.back());
el = WASM_I64_VAL(p);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(std::string_view func, Types... args)
{
// Lookup our export function
auto* f = getFunc(func);
return call<NR>(f, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(wasm_func_t* func, Types... args)
{
std::vector<wasm_val_t> in;
return call<NR>(func, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(wasm_func_t* func, std::vector<wasm_val_t>& in)
{
// wasm_val_t rs[1] = {WASM_I32_VAL(0)};
WamrResult ret(NR);
// if (NR) { wasm_val_vec_new_uninitialized(&ret, NR); //
// wasm_val_vec_new(&ret, NR, &rs[0]); // ret = WASM_ARRAY_VEC(rs); }
wasm_val_vec_t const inv = in.empty()
? wasm_val_vec_t WASM_EMPTY_VEC
: wasm_val_vec_t{
in.size(),
in.data(),
in.size(),
sizeof(std::remove_reference_t<decltype(in)>::value_type),
nullptr};
trap = wasm_func_call(func, &inv, &ret.r);
if (trap)
print_wasm_error("failed to call func", trap);
// assert(results[0].kind == WASM_I32);
// if (NR) printf("Result P5: %d\n", ret[0].of.i32);
return ret;
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(
wasm_func_t* func,
std::vector<wasm_val_t>& in,
std::int32_t p,
Types... args)
{
add_param(in, p);
return call<NR>(func, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(
wasm_func_t* func,
std::vector<wasm_val_t>& in,
std::int64_t p,
Types... args)
{
add_param(in, p);
return call<NR>(func, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(
wasm_func_t* func,
std::vector<wasm_val_t>& in,
uint8_t const* d,
std::size_t sz,
Types... args)
{
auto res = call<1>(W_ALLOC, static_cast<int32_t>(sz));
if (trap || (res.r.data[0].kind != WASM_I32))
return {};
auto const ptr = res.r.data[0].of.i32;
if (!ptr)
throw std::runtime_error(
"WAMR: can't allocate memory, " + std::to_string(sz) + " bytes");
auto mem = getMem();
memcpy(mem.p + ptr, d, sz);
add_param(in, ptr);
add_param(in, static_cast<int32_t>(sz));
return call<NR>(func, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(
wasm_func_t* func,
std::vector<wasm_val_t>& in,
wbytes const& p,
Types... args)
{
return call<NR>(func, in, p.data(), p.size(), std::forward<Types>(args)...);
}
Expected<int32_t, TER>
WamrEngine::run(
wbytes const& wasmCode,
std::string_view funcName,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params)
{
try
{
return runHlp(wasmCode, funcName, imports, params);
}
catch (std::exception const&)
{
}
catch (...)
{
}
return Unexpected<TER>(tecFAILED_PROCESSING);
}
Expected<int32_t, TER>
WamrEngine::runHlp(
wbytes const& wasmCode,
std::string_view funcName,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params)
{
// Create and instantiate the module.
if (!wasmCode.empty())
{
int const m = addModule(wasmCode, true, imports);
if (m < 0)
return Unexpected<TER>(tecFAILED_PROCESSING);
}
if (!module)
return Unexpected<TER>(tecFAILED_PROCESSING);
// Call main
auto* f = getFunc(!funcName.empty() ? funcName : "_start");
auto p = convertParams(params);
auto res = call<1>(f, p);
if (!res.r.size || trap)
return Unexpected<TER>(tecFAILED_PROCESSING);
assert(res.r.data[0].kind == WASM_I32);
// printf("Result: %d\n", results[0].of.i32);
// return res.r.data[0].of.i32 != 0;
return res.r.data[0].of.i32;
}
std::int64_t
WamrEngine::initGas(std::int64_t def)
{
defGas = def;
return def;
}
std::int32_t
WamrEngine::initMaxPages(std::int32_t def)
{
defMaxPages = def;
return def;
}
std::int64_t
WamrEngine::setGas(std::int64_t gas)
{
if (module)
{
module->setGas(gas);
return gas;
}
return 0;
}
std::int64_t
WamrEngine::getGas()
{
return module ? module->getGas() : 0;
}
wmem
WamrEngine::getMem() const
{
return module ? module->getMem() : wmem();
}
int32_t
WamrEngine::allocate(int32_t sz)
{
auto res = call<1>(W_ALLOC, static_cast<int32_t>(sz));
if (trap || (res.r.data[0].kind != WASM_I32))
return {};
auto const ptr = res.r.data[0].of.i32;
if (!ptr)
throw std::runtime_error(
"WAMR: can't allocate memory, " + std::to_string(sz) + " bytes");
return ptr;
}
wasm_trap_t*
WamrEngine::newTrap(std::string_view txt)
{
wasm_message_t msg = WASM_EMPTY_VEC;
if (!txt.empty())
wasm_name_new(&msg, txt.size(), txt.data());
return wasm_trap_new(store.get(), &msg);
}
} // namespace ripple

264
src/xrpld/app/misc/WamrVM.h Normal file
View File

@@ -0,0 +1,264 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#pragma once
#include <xrpld/app/misc/WasmVM.h>
#include <iwasm/wasm_c_api.h>
#include <iwasm/wasm_export.h>
namespace ripple {
// clang-format off
struct WamrResult
{
wasm_val_vec_t r;
WamrResult(unsigned N = 0):r{0, nullptr, 0, 0, nullptr} {if (N) wasm_val_vec_new_uninitialized(&r, N);}
~WamrResult() { if (r.size) wasm_val_vec_delete(&r); }
WamrResult(WamrResult const &) = delete;
WamrResult& operator=(WamrResult const &) = delete;
WamrResult(WamrResult &&o) {*this = std::move(o);}
WamrResult& operator=(WamrResult &&o){r = o.r; o.r = {0, nullptr, 0, 0, nullptr}; return *this;}
//operator wasm_val_vec_t &() {return r;}
};
using ModulePtr = std::unique_ptr<wasm_module_t, decltype(&wasm_module_delete)>;
using InstancePtr = std::unique_ptr<wasm_instance_t, decltype(&wasm_instance_delete)>;
// clang-format on
struct InstanceWrapper
{
wasm_extern_vec_t exports;
InstancePtr mod_inst;
private:
static InstancePtr
init(
wasm_store_t* s,
wasm_module_t* m,
int32_t maxPages,
wasm_extern_vec_t* expt,
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
public:
InstanceWrapper();
InstanceWrapper(InstanceWrapper&& o);
InstanceWrapper&
operator=(InstanceWrapper&& o);
InstanceWrapper(
wasm_store_t* s,
wasm_module_t* m,
int32_t maxPages,
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
~InstanceWrapper();
operator bool() const;
wasm_func_t*
getFunc(
std::string_view funcName,
wasm_exporttype_vec_t const& export_types) const;
wmem
getMem() const;
};
struct ModuleWrapper
{
ModulePtr module;
wasm_exec_env_t exec_env = nullptr;
InstanceWrapper mod_inst;
wasm_exporttype_vec_t export_types;
private:
static ModulePtr
init(wasm_store_t* s, wbytes const& wasmBin);
public:
ModuleWrapper();
ModuleWrapper(ModuleWrapper&& o);
ModuleWrapper&
operator=(ModuleWrapper&& o);
ModuleWrapper(
wasm_store_t* s,
wbytes const& wasmBin,
bool instantiate,
int32_t maxPages,
std::vector<WasmImportFunc> const& imports = {});
~ModuleWrapper();
operator bool() const;
wasm_func_t*
getFunc(std::string_view funcName) const;
wmem
getMem() const;
int
addInstance(
wasm_store_t* s,
int32_t maxPages,
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
std::int64_t
setGas(std::int64_t gas);
std::int64_t
getGas();
private:
static void
makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp);
static void
makeImpReturn(wasm_valtype_vec_t& v, WasmImportFunc const& imp);
wasm_extern_vec_t
buildImports(wasm_store_t* s, std::vector<WasmImportFunc> const& imports);
};
class WamrEngine
{
std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)> engine;
std::unique_ptr<wasm_store_t, decltype(&wasm_store_delete)> store;
std::unique_ptr<ModuleWrapper> module;
wasm_trap_t* trap = nullptr;
std::int64_t defGas = -1;
std::int32_t defMaxPages = -1;
public:
WamrEngine();
~WamrEngine() = default;
Expected<int32_t, TER>
run(wbytes const& wasmCode,
std::string_view funcName,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params);
std::int64_t
initGas(std::int64_t def);
std::int64_t
setGas(std::int64_t gas);
std::int32_t
initMaxPages(std::int32_t def);
std::int64_t
getGas();
// Host functions helper functionality
wmem
getMem() const;
int32_t
allocate(int32_t size);
wasm_trap_t*
newTrap(std::string_view msg);
private:
Expected<int32_t, TER>
runHlp(
wbytes const& wasmCode,
std::string_view funcName,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params);
int
addModule(
wbytes const& wasmCode,
bool instantiate,
std::vector<WasmImportFunc> const& imports);
void
clearModules();
int
addInstance();
int32_t
runFunc(std::string_view const funcName, int32_t p);
int32_t
makeModule(
wbytes const& wasmCode,
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
wasm_func_t*
getFunc(std::string_view funcName);
std::vector<wasm_val_t>
convertParams(std::vector<WasmParam> const& params);
void
add_param(std::vector<wasm_val_t>& in, int32_t p);
void
add_param(std::vector<wasm_val_t>& in, int64_t p);
template <int NR, class... Types>
inline WamrResult
call(std::string_view func, Types... args);
template <int NR, class... Types>
inline WamrResult
call(wasm_func_t* func, Types... args);
template <int NR, class... Types>
inline WamrResult
call(wasm_func_t* f, std::vector<wasm_val_t>& in);
template <int NR, class... Types>
inline WamrResult
call(
wasm_func_t* func,
std::vector<wasm_val_t>& in,
std::int32_t p,
Types... args);
template <int NR, class... Types>
inline WamrResult
call(
wasm_func_t* func,
std::vector<wasm_val_t>& in,
std::int64_t p,
Types... args);
template <int NR, class... Types>
inline WamrResult
call(
wasm_func_t* func,
std::vector<wasm_val_t>& in,
uint8_t const* d,
std::size_t sz,
Types... args);
template <int NR, class... Types>
inline WamrResult
call(
wasm_func_t* func,
std::vector<wasm_val_t>& in,
wbytes const& p,
Types... args);
};
} // namespace ripple

View File

@@ -19,14 +19,12 @@
#ifndef RIPPLE_APP_MISC_WASMHOSTFUNCIMPL_H_INLCUDED
#define RIPPLE_APP_MISC_WASMHOSTFUNCIMPL_H_INLCUDED
#include <xrpld/app/misc/WasmVM.h>
#include <xrpld/app/tx/detail/ApplyContext.h>
#include <xrpl/basics/Expected.h>
#include <xrpl/protocol/TER.h>
#include "xrpl/basics/base_uint.h"
#include "xrpld/app/misc/WasmVM.h"
#include "xrpld/app/tx/detail/ApplyContext.h"
#include <wasmedge/wasmedge.h>
namespace ripple {
class WasmHostFunctionsImpl : public HostFunctions
{

View File

@@ -0,0 +1,392 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpld/app/misc/WasmHostFuncWrapper.h>
#include <xrpld/app/tx/detail/NFTokenUtils.h>
#include <xrpl/protocol/digest.h>
namespace ripple {
wasm_trap_t*
getLedgerSqn_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
int32_t const sqn = hf->getLedgerSqn();
results->data[0] = WASM_I32_VAL(sqn);
return nullptr;
}
wasm_trap_t*
getParentLedgerTime_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
int32_t const ltime = hf->getParentLedgerTime();
results->data[0] = WASM_I32_VAL(ltime);
return nullptr;
}
static Expected<Bytes, std::string>
getParameterData(wasm_val_vec_t const* params, size_t index)
{
auto& vm = WasmEngine::instance();
auto fnameOffset = params->data[index].of.i32;
auto fnameLen = params->data[index + 1].of.i32;
auto mem = vm.getMem();
if (!mem.s)
return Unexpected<std::string>("No memory exported");
if (mem.s <= fnameOffset + fnameLen)
return Unexpected<std::string>("Memory access failed");
Bytes fname(mem.p + fnameOffset, mem.p + fnameOffset + fnameLen);
return fname;
}
static Expected<std::string, wasm_trap_t*>
getFieldName(wasm_val_vec_t const* params, size_t index)
{
auto const dataRes = getParameterData(params, index);
if (dataRes)
{
return std::string(dataRes->begin(), dataRes->end());
}
else
{
auto& vm = WasmEngine::instance();
return Unexpected<wasm_trap_t*>(
reinterpret_cast<wasm_trap_t*>(vm.newTrap(dataRes.error())));
}
}
static Expected<int32_t, std::string>
setData(Bytes const& data)
{
auto& vm = WasmEngine::instance();
auto mem = vm.getMem();
if (!mem.s)
return Unexpected<std::string>("No memory exported");
int32_t const dataLen = static_cast<int32_t>(data.size());
int32_t const dataPtr = vm.allocate(dataLen);
if (!dataPtr)
return Unexpected<std::string>("Allocation error");
memcpy(mem.p + dataPtr, data.data(), dataLen);
auto retPtr = vm.allocate(8);
if (!retPtr)
return Unexpected<std::string>("Allocation error");
int32_t* retData = reinterpret_cast<int32_t*>(mem.p + retPtr);
retData[0] = dataPtr;
retData[1] = dataLen;
return retPtr;
}
wasm_trap_t*
getTxField_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto fname = getFieldName(params, 0);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto fieldData = hf->getTxField(fname.value());
if (!fieldData)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap("Field not found"));
auto pointer = setData(fieldData.value());
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size());
return nullptr;
}
wasm_trap_t*
getLedgerEntryField_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
int32_t const type = params->data[0].of.i32;
auto lkData = getParameterData(params, 1);
if (!lkData)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto fname = getFieldName(params, 3);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto fieldData =
hf->getLedgerEntryField(type, lkData.value(), fname.value());
if (!fieldData)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto pointer = setData(fieldData.value());
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size());
return nullptr;
}
wasm_trap_t*
getCurrentLedgerEntryField_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto fname = getFieldName(params, 0);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto fieldData = hf->getCurrentLedgerEntryField(fname.value());
if (!fieldData)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto pointer = setData(fieldData.value());
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size());
return nullptr;
}
wasm_trap_t*
getNFT_wrap(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto account = getFieldName(params, 0);
if (!account)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto nftId = getFieldName(params, 2);
if (!nftId)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto nftURI = hf->getNFT(account.value(), nftId.value());
if (!nftURI)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto pointer = setData(nftURI.value());
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return nullptr;
}
wasm_trap_t*
accountKeylet_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto account = getFieldName(params, 0);
if (!account)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto keylet = hf->accountKeylet(account.value());
if (!keylet)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto pointer = setData(keylet.value());
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return nullptr;
}
wasm_trap_t*
credentialKeylet_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto subject = getFieldName(params, 0);
if (!subject)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto issuer = getFieldName(params, 2);
if (!issuer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto credentialType = getFieldName(params, 4);
if (!credentialType)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto keylet = hf->credentialKeylet(
subject.value(), issuer.value(), credentialType.value());
if (!keylet)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto pointer = setData(keylet.value());
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return nullptr;
}
wasm_trap_t*
escrowKeylet_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto account = getFieldName(params, 0);
if (!account)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
int32_t const sequence = params->data[2].of.i32;
auto keylet = hf->escrowKeylet(account.value(), sequence);
if (!keylet)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto pointer = setData(keylet.value());
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return nullptr;
}
wasm_trap_t*
oracleKeylet_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto account = getFieldName(params, 0);
if (!account)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto documentId = params->data[2].of.i32;
auto keylet = hf->escrowKeylet(account.value(), documentId);
if (!keylet)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto pointer = setData(keylet.value());
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return nullptr;
}
wasm_trap_t*
updateData_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto fname = getParameterData(params, 0);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
if (!hf->updateData(fname.value()))
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
return nullptr;
}
wasm_trap_t*
computeSha512HalfHash_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto fname = getParameterData(params, 0);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto hres = hf->computeSha512HalfHash(fname.value());
Bytes digest{hres.begin(), hres.end()};
auto pointer = setData(digest);
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32(32);
return nullptr;
}
wasm_trap_t*
print_wrap(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
// auto* hf = reinterpret_cast<HostFunctions*>(env);
auto f = getParameterData(params, 0);
if (!f)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
std::string s(f->begin(), f->end());
if (s.size() < 4096)
std::cout << s << std::endl;
return nullptr;
}
} // namespace ripple

View File

@@ -0,0 +1,113 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#pragma once
#include <xrpld/app/misc/WamrVM.h>
namespace ripple {
using getLedgerSqn_proto = int32_t();
wasm_trap_t*
getLedgerSqn_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results);
using getParentLedgerTime_proto = int32_t();
wasm_trap_t*
getParentLedgerTime_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results);
using getTxField_proto = uint32_t*(char const*, int32_t);
wasm_trap_t*
getTxField_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results);
using getLedgerEntryField_proto =
uint32_t*(int32_t, uint8_t const*, int32_t, char const*, int32_t);
wasm_trap_t*
getLedgerEntryField_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results);
using getCurrentLedgerEntryField_proto = uint32_t*(char const*, int32_t);
wasm_trap_t*
getCurrentLedgerEntryField_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results);
using getNFT_proto = uint32_t*(char const*, int32_t, char const*, int32_t);
wasm_trap_t*
getNFT_wrap(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results);
using accountKeylet_proto = uint32_t*(char const*, int32_t);
wasm_trap_t*
accountKeylet_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results);
using credentialKeylet_proto =
uint32_t*(char const*, int32_t, char const*, int32_t, char const*, int32_t);
wasm_trap_t*
credentialKeylet_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results);
using escrowKeylet_proto = uint32_t*(char const*, int32_t, int32_t);
wasm_trap_t*
escrowKeylet_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results);
using oracleKeylet_proto = uint32_t*(char const*, int32_t, int32_t);
wasm_trap_t*
oracleKeylet_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results);
using updateData_proto = void(uint8_t const*, int32_t);
wasm_trap_t*
updateData_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results);
using computeSha512HalfHash_proto = uint32_t*(uint8_t const*, int32_t);
wasm_trap_t*
computeSha512HalfHash_wrap(
void* env,
const wasm_val_vec_t* params,
wasm_val_vec_t* results);
using print_proto = void(char const*, int32_t);
wasm_trap_t*
print_wrap(void* env, const wasm_val_vec_t* params, wasm_val_vec_t* results);
} // namespace ripple

View File

@@ -17,730 +17,118 @@
*/
//==============================================================================
#include <xrpld/app/misc/WasmVM.h>
#include <xrpld/app/misc/WamrVM.h>
#include <xrpld/app/misc/WasmHostFuncWrapper.h>
#include "xrpl/protocol/AccountID.h"
#include "xrpl/protocol/LedgerFormats.h"
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <memory>
namespace ripple {
WasmEdge_Result
getLedgerSqn(
void* data,
const WasmEdge_CallingFrameContext*,
const WasmEdge_Value*,
WasmEdge_Value* out)
{
out[0] = WasmEdge_ValueGenI32(((HostFunctions*)data)->getLedgerSqn());
return WasmEdge_Result_Success;
}
WasmEdge_Result
getParentLedgerTime(
void* data,
const WasmEdge_CallingFrameContext*,
const WasmEdge_Value*,
WasmEdge_Value* out)
{
out[0] =
WasmEdge_ValueGenI32(((HostFunctions*)data)->getParentLedgerTime());
return WasmEdge_Result_Success;
}
Expected<Bytes, WasmEdge_Result>
getParameterData(
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
size_t index)
{
auto fnameOffset = (uint32_t)WasmEdge_ValueGetI32(in[index]);
auto fnameLen = (uint32_t)WasmEdge_ValueGetI32(in[index + 1]);
Bytes fname(fnameLen, char{0});
WasmEdge_MemoryInstanceContext* mem =
WasmEdge_CallingFrameGetMemoryInstance(fm, 0);
WasmEdge_Result Res = WasmEdge_MemoryInstanceGetData(
mem, (uint8_t*)(fname.data()), fnameOffset, fnameLen);
if (WasmEdge_ResultOK(Res))
{
return fname;
}
else
{
return Unexpected<WasmEdge_Result>(Res);
}
}
Expected<std::string, WasmEdge_Result>
getFieldName(
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
size_t index)
{
auto dataRes = getParameterData(fm, in, index);
if (dataRes)
{
return std::string(dataRes.value().begin(), dataRes->end());
}
else
{
return Unexpected<WasmEdge_Result>(dataRes.error());
}
}
Expected<WasmEdge_Value, WasmEdge_Result>
setData(const WasmEdge_CallingFrameContext* fm, Bytes const& data)
{
auto alloc = [fm](int32_t dataLen) -> int32_t {
WasmEdge_String allocFunc = WasmEdge_StringCreateByCString("allocate");
auto mod = WasmEdge_CallingFrameGetModuleInstance(fm);
WasmEdge_FunctionInstanceContext* func =
WasmEdge_ModuleInstanceFindFunction(mod, allocFunc);
WasmEdge_Value allocParams[1] = {
WasmEdge_ValueGenI32(dataLen)}; // 4 for prepend the data size
WasmEdge_Value allocReturns[1];
auto executor = WasmEdge_CallingFrameGetExecutor(fm);
auto res = WasmEdge_ExecutorInvoke(
executor, func, allocParams, 1, allocReturns, 1);
if (WasmEdge_ResultOK(res))
{
return WasmEdge_ValueGetI32(allocReturns[0]);
}
else
{
return 0;
}
};
auto dataLen = (int32_t)data.size();
auto dataPtr = alloc(dataLen);
auto retPtr = alloc(8);
if (dataPtr && retPtr)
{
auto mem = WasmEdge_CallingFrameGetMemoryInstance(fm, 0);
auto res =
WasmEdge_MemoryInstanceSetData(mem, data.data(), dataPtr, dataLen);
if (WasmEdge_ResultOK(res))
{
unsigned char intBuf[8]; // little-endian
for (size_t i = 0; i < 4; ++i)
{
intBuf[i] = (dataPtr >> (i * 8)) & 0xFF;
}
for (size_t i = 0; i < 4; ++i)
{
intBuf[i + 4] = (dataLen >> (i * 8)) & 0xFF;
}
res = WasmEdge_MemoryInstanceSetData(mem, intBuf, retPtr, 8);
if (WasmEdge_ResultOK(res))
{
return WasmEdge_ValueGenI32(retPtr);
}
}
}
return Unexpected<WasmEdge_Result>(WasmEdge_Result_Fail);
}
WasmEdge_Result
getTxField(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto fname = getFieldName(fm, in, 0);
if (!fname)
return fname.error();
auto fieldData = ((HostFunctions*)data)->getTxField(fname.value());
if (!fieldData)
return WasmEdge_Result_Fail;
auto pointer = setData(fm, fieldData.value());
if (!pointer)
return pointer.error();
out[0] = pointer.value();
// out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size());
return WasmEdge_Result_Success;
}
WasmEdge_Result
getLedgerEntryField(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto type = WasmEdge_ValueGetI32(in[0]);
auto lkData = getParameterData(fm, in, 1);
if (!lkData)
return lkData.error();
auto fname = getFieldName(fm, in, 3);
if (!fname)
return fname.error();
auto fieldData =
((HostFunctions*)data)
->getLedgerEntryField(type, lkData.value(), fname.value());
if (!fieldData)
return WasmEdge_Result_Fail;
auto pointer = setData(fm, fieldData.value());
if (!pointer)
return pointer.error();
out[0] = pointer.value();
// out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size());
return WasmEdge_Result_Success;
}
WasmEdge_Result
getCurrentLedgerEntryField(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto fname = getFieldName(fm, in, 0);
if (!fname)
return fname.error();
auto fieldData =
((HostFunctions*)data)->getCurrentLedgerEntryField(fname.value());
if (!fieldData)
return WasmEdge_Result_Fail;
auto pointer = setData(fm, fieldData.value());
if (!pointer)
return pointer.error();
out[0] = pointer.value();
// out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size());
return WasmEdge_Result_Success;
}
WasmEdge_Result
getNFT(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto account = getFieldName(fm, in, 0);
if (!account)
return account.error();
auto nftId = getFieldName(fm, in, 2);
if (!nftId)
return nftId.error();
auto nftURI =
((HostFunctions*)data)->getNFT(account.value(), nftId.value());
if (!nftURI)
return WasmEdge_Result_Fail;
auto pointer = setData(fm, nftURI.value());
if (!pointer)
return pointer.error();
out[0] = pointer.value();
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return WasmEdge_Result_Success;
}
WasmEdge_Result
accountKeylet(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto account = getFieldName(fm, in, 0);
if (!account)
return account.error();
auto keylet = ((HostFunctions*)data)->accountKeylet(account.value());
if (!keylet)
return WasmEdge_Result_Fail;
auto pointer = setData(fm, keylet.value());
if (!pointer)
return pointer.error();
out[0] = pointer.value();
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return WasmEdge_Result_Success;
}
WasmEdge_Result
credentialKeylet(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto subject = getFieldName(fm, in, 0);
if (!subject)
return subject.error();
auto issuer = getFieldName(fm, in, 2);
if (!issuer)
return issuer.error();
auto credentialType = getFieldName(fm, in, 4);
if (!credentialType)
return credentialType.error();
auto keylet =
((HostFunctions*)data)
->credentialKeylet(
subject.value(), issuer.value(), credentialType.value());
if (!keylet)
return WasmEdge_Result_Fail;
auto pointer = setData(fm, keylet.value());
if (!pointer)
return pointer.error();
out[0] = pointer.value();
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return WasmEdge_Result_Success;
}
WasmEdge_Result
escrowKeylet(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto account = getFieldName(fm, in, 0);
if (!account)
return account.error();
auto sequence = WasmEdge_ValueGetI32(in[2]);
auto keylet =
((HostFunctions*)data)->escrowKeylet(account.value(), sequence);
if (!keylet)
return WasmEdge_Result_Fail;
auto pointer = setData(fm, keylet.value());
if (!pointer)
return pointer.error();
out[0] = pointer.value();
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return WasmEdge_Result_Success;
}
WasmEdge_Result
oracleKeylet(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto account = getFieldName(fm, in, 0);
if (!account)
return account.error();
auto documentId = WasmEdge_ValueGetI32(in[2]);
auto keylet =
((HostFunctions*)data)->escrowKeylet(account.value(), documentId);
if (!keylet)
return WasmEdge_Result_Fail;
auto pointer = setData(fm, keylet.value());
if (!pointer)
return pointer.error();
out[0] = pointer.value();
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return WasmEdge_Result_Success;
}
WasmEdge_Result
updateData(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto fname = getParameterData(fm, in, 0);
if (!fname)
return fname.error();
if (((HostFunctions*)data)->updateData(fname.value()))
return WasmEdge_Result_Success;
else
return WasmEdge_Result_Fail;
}
WasmEdge_Result
computeSha512HalfHash(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto fname = getParameterData(fm, in, 0);
if (!fname)
return fname.error();
auto hres = ((HostFunctions*)data)->computeSha512HalfHash(fname.value());
Bytes digest{hres.begin(), hres.end()};
auto pointer = setData(fm, digest);
if (!pointer)
return pointer.error();
out[0] = pointer.value();
// out[1] = WasmEdge_ValueGenI32(32);
return WasmEdge_Result_Success;
}
WasmEdge_Result
print(
void* data,
const WasmEdge_CallingFrameContext* fm,
const WasmEdge_Value* in,
WasmEdge_Value* out)
{
auto f = getParameterData(fm, in, 0);
if (!f)
return f.error();
std::string s(f.value().begin(), f.value().end());
std::cout << s << std::endl;
return WasmEdge_Result_Success;
}
Expected<EscrowResult, TER>
runEscrowWasm(
std::vector<uint8_t> const& wasmCode,
std::string const& funcName,
Bytes const& wasmCode,
std::string_view funcName,
HostFunctions* hfs,
uint64_t gasLimit)
{
// WasmEdge_LogOff();
// TODO deletes
// create VM and set cost limit
WasmEdge_ConfigureContext* conf = WasmEdge_ConfigureCreate();
WasmEdge_ConfigureStatisticsSetInstructionCounting(conf, true);
WasmEdge_ConfigureStatisticsSetCostMeasuring(conf, true);
WasmEdge_ConfigureSetMaxMemoryPage(conf, MAX_PAGES);
auto& vm = WasmEngine::instance();
vm.initGas(gasLimit);
vm.initMaxPages(MAX_PAGES);
WasmEdge_VMContext* VMCxt = WasmEdge_VMCreate(conf, NULL);
WasmEdge_StatisticsContext* StatCxt =
WasmEdge_VMGetStatisticsContext(VMCxt);
WasmEdge_StatisticsSetCostLimit(StatCxt, gasLimit);
std::vector<WasmImportFunc> imports;
{ // register host function
// module
WasmEdge_String libName = WasmEdge_StringCreateByCString("host_lib");
WasmEdge_ModuleInstanceContext* hostMod =
WasmEdge_ModuleInstanceCreate(libName);
WasmEdge_StringDelete(libName);
WASM_IMPORT_FUNC(imports, getLedgerSqn, hfs)
WASM_IMPORT_FUNC(imports, getParentLedgerTime, hfs)
WASM_IMPORT_FUNC(imports, getTxField, hfs)
WASM_IMPORT_FUNC(imports, getLedgerEntryField, hfs)
WASM_IMPORT_FUNC(imports, getCurrentLedgerEntryField, hfs)
WASM_IMPORT_FUNC(imports, getNFT, hfs)
WASM_IMPORT_FUNC(imports, accountKeylet, hfs)
WASM_IMPORT_FUNC(imports, credentialKeylet, hfs)
WASM_IMPORT_FUNC(imports, escrowKeylet, hfs)
WASM_IMPORT_FUNC(imports, oracleKeylet, hfs)
WASM_IMPORT_FUNC(imports, updateData, hfs)
WASM_IMPORT_FUNC(imports, computeSha512HalfHash, hfs)
WASM_IMPORT_FUNC(imports, print, hfs)
// getLedgerSqn
{
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(NULL, 0, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, getLedgerSqn, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
std::int64_t const sgas = gasLimit; // vm.getGas();
auto ret = vm.run(wasmCode, funcName, imports);
if (!ret.has_value())
return Unexpected<TER>(ret.error());
std::int64_t const egas = vm.getGas();
std::uint64_t const spent = static_cast<std::uint64_t>(sgas - egas);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("getLedgerSqn");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
// WasmEdge_FunctionInstanceDelete(hostFunc);
}
// getParentLedgerTime
{
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(NULL, 0, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, getParentLedgerTime, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("getParentLedgerTime");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
// WasmEdge_FunctionInstanceDelete(hostFunc);
}
// getTxField
{
WasmEdge_ValType inputList[2] = {
WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()};
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, getTxField, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("getTxField");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// getLedgerEntryField
{
WasmEdge_ValType inputList[5] = {
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32()};
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 5, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, getLedgerEntryField, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("getLedgerEntryField");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// getCurrentLedgerEntryField
{
WasmEdge_ValType inputList[2] = {
WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()};
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, getCurrentLedgerEntryField, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("getCurrentLedgerEntryField");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// getNFT
{
WasmEdge_ValType inputList[4] = {
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32()};
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(hostFuncType, getNFT, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName = WasmEdge_StringCreateByCString("getNFT");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// updateData
{
WasmEdge_ValType inputList[2] = {
WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 2, NULL, 0);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, updateData, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("updateData");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// computeSha512HalfHash
{
WasmEdge_ValType inputList[2] = {
WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()};
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, computeSha512HalfHash, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("computeSha512HalfHash");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// accountKeylet
{
WasmEdge_ValType inputList[2] = {
WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()};
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 2, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, accountKeylet, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("accountKeylet");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// credentialKeylet
{
WasmEdge_ValType inputList[6] = {
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32()};
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 6, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, credentialKeylet, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("credentialKeylet");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// escrowKeylet
{
WasmEdge_ValType inputList[3] = {
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32()};
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 3, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, escrowKeylet, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("escrowKeylet");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// oracleKeylet
{
WasmEdge_ValType inputList[3] = {
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32(),
WasmEdge_ValTypeGenI32()};
WasmEdge_ValType returnList[1] = {WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 3, returnList, 1);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(
hostFuncType, oracleKeylet, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName =
WasmEdge_StringCreateByCString("oracleKeylet");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
// print
{
WasmEdge_ValType inputList[2] = {
WasmEdge_ValTypeGenI32(), WasmEdge_ValTypeGenI32()};
WasmEdge_FunctionTypeContext* hostFuncType =
WasmEdge_FunctionTypeCreate(inputList, 2, NULL, 0);
WasmEdge_FunctionInstanceContext* hostFunc =
WasmEdge_FunctionInstanceCreate(hostFuncType, print, hfs, 100);
// WasmEdge_FunctionTypeDelete(hostFuncType);
// WasmEdge_FunctionInstanceDelete(hostFunc);
WasmEdge_String fName = WasmEdge_StringCreateByCString("print");
WasmEdge_ModuleInstanceAddFunction(hostMod, fName, hostFunc);
// WasmEdge_StringDelete(fName);
}
WasmEdge_Result regRe =
WasmEdge_VMRegisterModuleFromImport(VMCxt, hostMod);
if (!WasmEdge_ResultOK(regRe))
{
printf("host func reg error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
}
WasmEdge_Result loadRes =
WasmEdge_VMLoadWasmFromBuffer(VMCxt, wasmCode.data(), wasmCode.size());
if (!WasmEdge_ResultOK(loadRes))
{
printf("load error, %p, %d\n", wasmCode.data(), wasmCode.size());
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result validateRes = WasmEdge_VMValidate(VMCxt);
if (!WasmEdge_ResultOK(validateRes))
{
printf("validate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Result instantiateRes = WasmEdge_VMInstantiate(VMCxt);
if (!WasmEdge_ResultOK(instantiateRes))
{
printf("instantiate error\n");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
WasmEdge_Value funcReturns[1];
WasmEdge_String func = WasmEdge_StringCreateByCString(funcName.c_str());
WasmEdge_Result funcRes =
WasmEdge_VMExecute(VMCxt, func, NULL, 0, funcReturns, 1);
bool ok = WasmEdge_ResultOK(funcRes);
EscrowResult re;
if (ok)
{
auto sc = WasmEdge_VMGetStatisticsContext(VMCxt);
re.cost = WasmEdge_StatisticsGetTotalCost(sc);
// WasmEdge_StatisticsGetTotalCost, WasmEdge_StatisticsGetInstrCount
auto result = WasmEdge_ValueGetI32(funcReturns[0]);
if (result != 0)
re.result = true;
else
re.result = false;
}
else
{
printf("Error message: %s\n", WasmEdge_ResultGetMessage(funcRes));
}
WasmEdge_VMDelete(VMCxt);
WasmEdge_StringDelete(func);
// delete other obj allocated
if (ok)
return re;
else
return Unexpected<TER>(tecFAILED_PROCESSING);
return EscrowResult{static_cast<bool>(ret.value()), spent};
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
WasmEngine::WasmEngine() : impl(std::make_unique<WamrEngine>())
{
}
WasmEngine&
WasmEngine::instance()
{
static WasmEngine e;
return e;
}
Expected<int32_t, TER>
WasmEngine::run(
wbytes const& wasmCode,
std::string_view funcName,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params)
{
return impl->run(wasmCode, funcName, imports, params);
}
std::int64_t
WasmEngine::initGas(std::int64_t def)
{
return impl->initGas(def);
}
std::int32_t
WasmEngine::initMaxPages(std::int32_t def)
{
return impl->initMaxPages(def);
}
// gas = 1'000'000'000LL
std::int64_t
WasmEngine::setGas(std::int64_t gas)
{
return impl->setGas(gas);
}
std::int64_t
WasmEngine::getGas()
{
return impl->getGas();
}
wmem
WasmEngine::getMem() const
{
return impl->getMem();
}
int32_t
WasmEngine::allocate(int32_t size)
{
return impl->allocate(size);
}
void*
WasmEngine::newTrap(std::string_view msg)
{
return impl->newTrap(msg);
}
} // namespace ripple

View File

@@ -16,16 +16,43 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_APP_MISC_WASMVM_H_INLCUDED
#define RIPPLE_APP_MISC_WASMVM_H_INLCUDED
#pragma once
#include <xrpl/basics/Expected.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/TER.h>
#include "xrpl/basics/base_uint.h"
#include <wasmedge/wasmedge.h>
#include <boost/function_types/function_arity.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/mpl/vector.hpp>
// #include <iwasm/wasm_c_api.h>
#include <string_view>
namespace bft = boost::function_types;
namespace ripple {
static const std::string_view W_ENV = "env";
static const std::string_view W_HOST_LIB = "host_lib";
static const std::string_view W_MEM = "memory";
static const std::string_view W_STORE = "store";
static const std::string_view W_LOAD = "load";
static const std::string_view W_SIZE = "size";
static const std::string_view W_ALLOC = "allocate";
static const std::string_view W_DEALLOC = "deallocate";
static const std::string_view W_PROC_EXIT = "proc_exit";
using wbytes = std::vector<std::uint8_t>;
struct wmem
{
std::uint8_t* p = nullptr;
std::size_t s = 0;
};
const uint32_t MAX_PAGES = 128; // 8MB = 64KB*128
typedef std::vector<uint8_t> Bytes;
@@ -122,12 +149,220 @@ struct HostFunctions
virtual ~HostFunctions() = default;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum WasmTypes { WT_I32, WT_I64, WT_F32, WT_F64 };
struct WasmImportFunc
{
std::string name;
std::optional<WasmTypes> result;
std::vector<WasmTypes> params;
void* udata = nullptr;
// wasm_func_callback_with_env_t
void* wrap = nullptr;
};
#define WASM_IMPORT_FUNC(v, f, ...) \
WasmImpFunc<f##_proto>( \
v, #f, reinterpret_cast<void*>(&f##_wrap), ##__VA_ARGS__);
template <int N, int C, typename mpl>
void
WasmImpArgs(WasmImportFunc& e)
{
if constexpr (N < C)
{
using at = typename boost::mpl::at_c<mpl, N>::type;
if constexpr (std::is_pointer_v<at>)
e.params.push_back(WT_I32);
else if constexpr (std::is_same_v<at, std::int32_t>)
e.params.push_back(WT_I32);
else if constexpr (std::is_same_v<at, std::int64_t>)
e.params.push_back(WT_I64);
else if constexpr (std::is_same_v<at, float>)
e.params.push_back(WT_F32);
else if constexpr (std::is_same_v<at, double>)
e.params.push_back(WT_F64);
else
static_assert(std::is_pointer_v<at>, "Unsupported argument type");
return WasmImpArgs<N + 1, C, mpl>(e);
}
return;
}
template <typename rt>
void
WasmImpRet(WasmImportFunc& e)
{
if constexpr (std::is_pointer_v<rt>)
e.result = WT_I32;
else if constexpr (std::is_same_v<rt, std::int32_t>)
e.result = WT_I32;
else if constexpr (std::is_same_v<rt, std::int64_t>)
e.result = WT_I64;
else if constexpr (std::is_same_v<rt, float>)
e.result = WT_F32;
else if constexpr (std::is_same_v<rt, double>)
e.result = WT_F64;
else if constexpr (std::is_void_v<rt>)
e.result.reset();
#if (defined(__GNUC__) && (__GNUC__ >= 14)) || \
((defined(__clang_major__)) && (__clang_major__ >= 18))
else
static_assert(false, "Unsupported return type");
#endif
}
template <typename F>
void
WasmImpFuncHelper(WasmImportFunc& e)
{
using rt = typename bft::result_type<F>::type;
using pt = typename bft::parameter_types<F>::type;
// typename boost::mpl::at_c<mpl, N>::type
WasmImpRet<rt>(e);
WasmImpArgs<0, bft::function_arity<F>::value, pt>(e);
// WasmImpWrap(e, std::forward<F>(f));
}
template <typename F>
void
WasmImpFunc(
std::vector<WasmImportFunc>& v,
std::string_view imp_name,
void* f_wrap,
void* data = nullptr)
{
WasmImportFunc e;
e.name = imp_name;
e.udata = data;
e.wrap = f_wrap;
WasmImpFuncHelper<F>(e);
v.push_back(std::move(e));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct WasmParam
{
WasmTypes type = WT_I32;
union
{
std::int32_t i32;
std::int64_t i64 = 0;
float f32;
double f64;
} of;
};
template <class... Types>
inline std::vector<WasmParam>
wasmParams(Types... args)
{
std::vector<WasmParam> v;
v.reserve(sizeof...(args));
return wasmParams(v, std::forward<Types>(args)...);
}
template <class... Types>
inline std::vector<WasmParam>
wasmParams(std::vector<WasmParam>& v, std::int32_t p, Types... args)
{
v.push_back({.type = WT_I32, .of = {.i32 = p}});
return wasmParams(v, std::forward<Types>(args)...);
}
template <class... Types>
inline std::vector<WasmParam>
wasmParams(std::vector<WasmParam>& v, std::int64_t p, Types... args)
{
v.push_back({.type = WT_I64, .of = {.i64 = p}});
return wasmParams(v, std::forward<Types>(args)...);
}
template <class... Types>
inline std::vector<WasmParam>
wasmParams(std::vector<WasmParam>& v, float p, Types... args)
{
v.push_back({.type = WT_F32, .of = {.f32 = p}});
return wasmParams(v, std::forward<Types>(args)...);
}
template <class... Types>
inline std::vector<WasmParam>
wasmParams(std::vector<WasmParam>& v, double p, Types... args)
{
v.push_back({.type = WT_F64, .of = {.f64 = p}});
return wasmParams(v, std::forward<Types>(args)...);
}
inline std::vector<WasmParam>
wasmParams(std::vector<WasmParam>& v)
{
return v;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class WamrEngine;
class WasmEngine
{
std::unique_ptr<WamrEngine> const impl;
WasmEngine();
WasmEngine(WasmEngine const&) = delete;
WasmEngine(WasmEngine&&) = delete;
WasmEngine&
operator=(WasmEngine const&) = delete;
WasmEngine&
operator=(WasmEngine&&) = delete;
public:
~WasmEngine() = default;
static WasmEngine&
instance();
Expected<int32_t, TER>
run(wbytes const& wasmCode,
std::string_view funcName = {},
std::vector<WasmImportFunc> const& imports = {},
std::vector<WasmParam> const& params = {});
std::int64_t
initGas(std::int64_t def = 1'000'000'000'000LL);
std::int32_t
initMaxPages(std::int32_t def);
std::int64_t
setGas(std::int64_t gas);
std::int64_t
getGas();
// for host functions usage
wmem
getMem() const;
int32_t
allocate(int32_t size);
void*
newTrap(std::string_view msg = {});
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expected<EscrowResult, TER>
runEscrowWasm(
std::vector<uint8_t> const& wasmCode,
std::string const& funcName,
Bytes const& wasmCode,
std::string_view funcName,
HostFunctions* hfs,
uint64_t gasLimit);
} // namespace ripple
#endif // RIPPLE_APP_MISC_WASMVM_H_INLCUDED

View File

@@ -74,13 +74,13 @@ struct FeeSetup
XRPAmount owner_reserve{2 * DROPS_PER_XRP};
/** The compute limit for Feature Extensions. */
std::uint32_t extension_compute_limit{4294967295};
std::uint32_t extension_compute_limit{1'000'000};
/** The WASM size limit for Feature Extensions. */
std::uint32_t extension_size_limit{4294967295};
std::uint32_t extension_size_limit{100'000};
/** The price of 1 WASM gas, in micro-drops. */
std::uint32_t gas_price{1000000};
std::uint32_t gas_price{1'000'000};
/* (Remember to update the example cfg files when changing any of these
* values.) */