More hostfunctions (#5451)

* Bug fixes:
- Fix bugs found during schedule table tests
- Add more tests
- Add parameters passing for runEscrowWasm function

* Add new host-functions
 fix wamr logging
 add runtime passing through HF
 fix runEscrowWasm interface

* Improve logs

* Fix logging bug

* Set 4k limit for update_data HF

* allHF wasm module fixes
This commit is contained in:
Olek
2025-05-30 19:01:27 -04:00
committed by GitHub
parent 1f8aece8cd
commit 6a6fed5dce
16 changed files with 13722 additions and 1944 deletions

View File

@@ -126,6 +126,10 @@ std::uint8_t constexpr vaultStrategyFirstComeFirstServe = 1;
* 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;
/** A ledger index. */
using LedgerIndex = std::uint32_t;

View File

@@ -2196,6 +2196,7 @@ struct Escrow_test : public beast::unit_test::suite
using namespace jtx;
using namespace std::chrono;
// TODO: create wasm module for all host functions
static auto wasmHex = allHostFunctionsHex;
// let sender = get_tx_account_id();
// let owner = get_current_escrow_account_id();
@@ -2277,7 +2278,7 @@ struct Escrow_test : public beast::unit_test::suite
auto const txMeta = env.meta();
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == 2412);
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == 487);
env.close();
BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0);
@@ -2303,6 +2304,8 @@ struct Escrow_test : public beast::unit_test::suite
testCreateFinishFunctionPreflight();
testFinishWasmFailures();
testFinishFunction();
// TODO: Update module with new host functions
testAllHostFunctions();
}
};

View File

@@ -20,18 +20,20 @@
#include <test/app/wasm_fixtures/fixtures.h>
#include <test/jtx.h>
#include <xrpld/app/misc/WasmHostFunc.h>
#include <xrpld/app/misc/WasmVM.h>
#include <iwasm/wasm_c_api.h>
#include <filesystem>
#ifdef _DEBUG
// #define DEBUG_OUTPUT 1
#endif
namespace ripple {
namespace test {
/* Host function body definition. */
using Add_proto = int32_t(int32_t, int32_t);
wasm_trap_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;
@@ -41,15 +43,497 @@ Add(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
return nullptr;
}
struct TestLedgerDataProvider
{
jtx::Env* env;
public:
TestLedgerDataProvider(jtx::Env* env_) : env(env_)
{
}
int32_t
get_ledger_sqn()
{
return (int32_t)env->current()->seq();
}
};
using getLedgerSqn_proto = std::int32_t();
static wasm_trap_t*
getLedgerSqn_wrap(void* env, wasm_val_vec_t const*, wasm_val_vec_t* results)
{
auto sqn = reinterpret_cast<TestLedgerDataProvider*>(env)->get_ledger_sqn();
results->data[0] = WASM_I32_VAL(sqn);
results->num_elems = 1;
return nullptr;
}
struct TestHostFunctionsOld : public HostFunctions
{
test::jtx::Env* env_;
Bytes accountID_;
Bytes data_;
int clock_drift_ = 0;
test::StreamSink sink_;
beast::Journal jlog_;
void const* rt_ = nullptr;
public:
explicit TestHostFunctionsOld(test::jtx::Env* env, int cd = 0)
: env_(env)
, clock_drift_(cd)
, sink_(beast::severities::kDebug)
, jlog_(sink_)
{
std::string s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
accountID_ = Bytes{s.begin(), s.end()};
std::string t = "10000";
data_ = Bytes{t.begin(), t.end()};
}
virtual void
setRT(void const* rt) override
{
rt_ = rt;
}
virtual void const*
getRT() const override
{
return rt_;
}
test::StreamSink&
getSink()
{
return sink_;
}
beast::Journal
getJournal() override
{
return jlog_;
}
int32_t
getLedgerSqn() override
{
return (int32_t)env_->current()->seq();
}
int32_t
getParentLedgerTime() override
{
return env_->current()->parentCloseTime().time_since_epoch().count() +
clock_drift_;
}
Expected<Bytes, int32_t>
getTxField(SField const& fname) override
{
return accountID_;
}
Expected<Bytes, int32_t>
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
{
return data_;
}
Expected<Bytes, int32_t>
getCurrentLedgerObjField(SField const& fname) override
{
if (fname.getName() == "Destination" || fname.getName() == "Account")
return accountID_;
else if (fname.getName() == "Data")
return data_;
else if (fname.getName() == "FinishAfter")
{
auto t =
env_->current()->parentCloseTime().time_since_epoch().count();
std::string s = std::to_string(t);
return Bytes{s.begin(), s.end()};
}
return Unexpected(-1);
}
};
struct TestHostFunctions : public HostFunctions
{
test::jtx::Env& env_;
AccountID accountID_;
Bytes data_;
int clock_drift_ = 0;
void const* rt_ = nullptr;
public:
explicit TestHostFunctions(test::jtx::Env& env, int cd = 0)
: env_(env), clock_drift_(cd)
{
auto opt = parseBase58<AccountID>("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
if (opt)
accountID_ = *opt;
std::string t = "10000";
data_ = Bytes{t.begin(), t.end()};
}
virtual void
setRT(void const* rt) override
{
rt_ = rt;
}
virtual void const*
getRT() const override
{
return rt_;
}
beast::Journal
getJournal() override
{
return env_.journal;
}
int32_t
getLedgerSqn() override
{
return static_cast<int32_t>(env_.current()->seq());
}
int32_t
getParentLedgerTime() override
{
return env_.current()->parentCloseTime().time_since_epoch().count() +
clock_drift_;
}
virtual int32_t
cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx) override
{
return 1;
}
Expected<Bytes, int32_t>
getTxField(SField const& fname) override
{
if (fname == sfAccount)
return Bytes(accountID_.begin(), accountID_.end());
else if (fname == sfFee)
{
int64_t x = 235;
uint8_t const* p = reinterpret_cast<uint8_t const*>(&x);
return Bytes{p, p + sizeof(x)};
}
else if (fname == sfSequence)
{
int32_t x = getLedgerSqn();
uint8_t const* p = reinterpret_cast<uint8_t const*>(&x);
return Bytes{p, p + sizeof(x)};
}
return Bytes();
}
Expected<Bytes, int32_t>
getTxNestedField(Slice const& locator) override
{
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41,
0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34,
0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee,
0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
return Bytes(&a[0], &a[sizeof(a)]);
}
Expected<Bytes, int32_t>
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
{
// auto const& sn = fname.getName();
if (fname == sfBalance)
{
int64_t x = 10'000;
uint8_t const* p = reinterpret_cast<uint8_t const*>(&x);
return Bytes{p, p + sizeof(x)};
}
return data_;
}
Expected<Bytes, int32_t>
getCurrentLedgerObjField(SField const& fname) override
{
auto const& sn = fname.getName();
if (sn == "Destination" || sn == "Account")
return Bytes(accountID_.begin(), accountID_.end());
else if (sn == "Data")
return data_;
else if (sn == "FinishAfter")
{
auto t =
env_.current()->parentCloseTime().time_since_epoch().count();
std::string s = std::to_string(t);
return Bytes{s.begin(), s.end()};
}
return Unexpected(-1);
}
int32_t
getTxArrayLen(SField const& fname) override
{
return 32;
}
int32_t
getTxNestedArrayLen(Slice const& locator) override
{
return 32;
}
int32_t
updateData(Bytes const& data) override
{
return 0;
}
Expected<Bytes, int32_t>
accountKeylet(AccountID const& account) override
{
if (!account)
return Unexpected(HF_ERR_INVALID_ACCOUNT);
auto const keylet = keylet::account(account);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
int32_t
trace(std::string const& msg, Bytes const& data, bool asHex) override
{
#ifdef DEBUG_OUTPUT
auto& j = std::cerr;
#else
auto j = getJournal().trace();
#endif
j << msg;
if (!asHex)
j << std::string_view(
reinterpret_cast<char const*>(data.data()), data.size());
else
{
auto const hex =
boost::algorithm::hex(std::string(data.begin(), data.end()));
j << hex;
}
#ifdef DEBUG_OUTPUT
j << std::endl;
#endif
return msg.size() + data.size() * (asHex ? 2 : 1);
}
int32_t
traceNum(std::string const& msg, int64_t data) override
{
#ifdef DEBUG_OUTPUT
auto& j = std::cerr;
#else
auto j = getJournal().trace();
#endif
j << msg << data;
#ifdef DEBUG_OUTPUT
j << std::endl;
#endif
return msg.size() + sizeof(data);
}
};
struct Wasm_test : public beast::unit_test::suite
{
void
testWasmtimeLib()
testWasmFib()
{
testcase("Wasm fibo");
auto const ws = boost::algorithm::unhex(fib32Hex);
Bytes const wasm(ws.begin(), ws.end());
auto& engine = WasmEngine::instance();
auto const r = engine.run(wasm, "fib", wasmParams(10));
BEAST_EXPECT(r.has_value() && (r->result == 55));
}
void
testWasmSha()
{
testcase("Wasm sha");
auto const ws = boost::algorithm::unhex(sha512PureHex);
Bytes const wasm(ws.begin(), ws.end());
auto& engine = WasmEngine::instance();
auto const r =
engine.run(wasm, "sha512_process", wasmParams(sha512PureHex));
BEAST_EXPECT(r.has_value() && (r->result == 34432));
}
void
testWasmB58()
{
testcase("Wasm base58");
auto const ws = boost::algorithm::unhex(b58Hex);
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>(b58Hex.size()));
auto const s = std::string_view(b58Hex.c_str(), minsz);
auto const r = engine.run(wasm, "b58enco", wasmParams(outb, s));
BEAST_EXPECT(r.has_value() && r->result);
}
void
testWasmSP1Verifier()
{
testcase("Wasm sp1 zkproof verifier");
auto const ws = boost::algorithm::unhex(sp1_wasm);
Bytes const wasm(ws.begin(), ws.end());
auto& engine = WasmEngine::instance();
auto const r = engine.run(wasm, "sp1_groth16_verifier");
BEAST_EXPECT(r.has_value() && r->result);
}
void
testWasmBG16Verifier()
{
testcase("Wasm BG16 zkproof verifier");
auto const ws = boost::algorithm::unhex(zkProofHex);
Bytes const wasm(ws.begin(), ws.end());
auto& engine = WasmEngine::instance();
auto const r = engine.run(wasm, "bellman_groth16_test");
BEAST_EXPECT(r.has_value() && r->result);
}
void
testWasmLedgerSqn()
{
testcase("Wasm get ledger sequence");
auto wasmStr = boost::algorithm::unhex(ledgerSqnHex);
Bytes wasm(wasmStr.begin(), wasmStr.end());
using namespace test::jtx;
Env env{*this};
TestLedgerDataProvider ledgerDataProvider(&env);
std::string const funcName("finish");
std::vector<WasmImportFunc> imports;
WASM_IMPORT_FUNC(imports, getLedgerSqn, &ledgerDataProvider);
auto& engine = WasmEngine::instance();
auto r = engine.run(
wasm, funcName, {}, imports, nullptr, 1'000'000, env.journal);
if (BEAST_EXPECT(r.has_value()))
BEAST_EXPECT(!r->result);
env.close();
env.close();
env.close();
env.close();
// empty module - run the same module
r = engine.run(
{}, funcName, {}, imports, nullptr, 1'000'000, env.journal);
if (BEAST_EXPECT(r.has_value()))
BEAST_EXPECT(r->result);
}
void
testWasmCheckJson()
{
testcase("Wasm check json");
using namespace test::jtx;
Env env{*this};
auto const wasmStr = boost::algorithm::unhex(checkJsonHex);
Bytes const wasm(wasmStr.begin(), wasmStr.end());
std::string const funcName("check_accountID");
{
std::string str = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
Bytes data(str.begin(), str.end());
auto re = runEscrowWasm(
wasm, funcName, wasmParams(data), nullptr, -1, env.journal);
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(re.value().result);
}
{
std::string str = "rHb9CJAWyB4rj91VRWn96DkukG4bwdty00";
Bytes data(str.begin(), str.end());
auto re = runEscrowWasm(
wasm, funcName, wasmParams(data), nullptr, -1, env.journal);
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(!re.value().result);
}
}
void
testWasmCompareJson()
{
testcase("Wasm compare json");
using namespace test::jtx;
Env env{*this};
auto wasmStr = boost::algorithm::unhex(compareJsonHex);
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
std::string funcName("compare_accountID");
std::vector<uint8_t> const tx_data(tx_js.begin(), tx_js.end());
std::vector<uint8_t> const lo_data(lo_js.begin(), lo_js.end());
auto re = runEscrowWasm(
wasm,
funcName,
wasmParams(tx_data, lo_data),
nullptr,
-1,
env.journal);
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(re.value().result);
std::vector<uint8_t> const lo_data2(lo_js2.begin(), lo_js2.end());
re = runEscrowWasm(
wasm,
funcName,
wasmParams(tx_data, lo_data2),
nullptr,
-1,
env.journal);
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(!re.value().result);
}
void
testWasmLib()
{
testcase("wasmtime lib test");
// clang-format off
/* The WASM module buffer. */
wbytes const wasm = {/* WASM header */
Bytes const wasm = {/* WASM header */
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00,
/* Type section */
0x01, 0x07, 0x01,
@@ -82,11 +566,11 @@ struct Wasm_test : public beast::unit_test::suite
WasmImpFunc<Add_proto>(
imports, "func-add", reinterpret_cast<void*>(&Add));
auto res = vm.run(wasm, "addTwo", imports, wasmParams(1234, 5678));
auto res = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports);
// if (res) printf("invokeAdd get the result: %d\n", res.value());
BEAST_EXPECT(res.has_value() && res.value() == 6912);
BEAST_EXPECT(res.has_value() && res->result == 6912);
}
void
@@ -94,12 +578,15 @@ struct Wasm_test : public beast::unit_test::suite
{
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);
auto re = runEscrowWasm(wasm, funcName, {}, &hfs, 15, env.journal);
BEAST_EXPECT(re.error());
}
@@ -107,9 +594,9 @@ struct Wasm_test : public beast::unit_test::suite
testEscrowWasmDN1()
{
testcase("escrow wasm devnet 1 test");
auto wasmHex = allHostFunctionsHex;
std::string const wasmHex = allHostFunctionsHex;
auto wasmStr = boost::algorithm::unhex(std::string(wasmHex));
std::string const wasmStr = boost::algorithm::unhex(wasmHex);
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
// let sender = get_tx_account_id();
@@ -125,98 +612,11 @@ struct Wasm_test : public beast::unit_test::suite
// pl_time >= e_time
using namespace test::jtx;
struct TestHostFunctions : public HostFunctions
{
Env* env_;
Bytes accountID_;
Bytes data_;
int clock_drift_ = 0;
test::StreamSink sink_;
beast::Journal jlog_;
public:
explicit TestHostFunctions(Env* env, int cd = 0)
: env_(env)
, clock_drift_(cd)
, sink_(beast::severities::kTrace)
, jlog_(sink_)
{
std::string s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
accountID_ = Bytes{s.begin(), s.end()};
std::string t = "10000";
data_ = Bytes{t.begin(), t.end()};
}
test::StreamSink&
getSink()
{
return sink_;
}
beast::Journal
getJournal() override
{
return jlog_;
}
int32_t
getLedgerSqn() override
{
return (int32_t)env_->current()->seq();
}
int32_t
getParentLedgerTime() override
{
return env_->current()
->parentCloseTime()
.time_since_epoch()
.count() +
clock_drift_;
}
std::optional<Bytes>
getTxField(std::string const& fname) override
{
return accountID_;
}
std::optional<Bytes>
getLedgerEntryField(
int32_t type,
Bytes const& kdata,
std::string const& fname) override
{
return data_;
}
std::optional<Bytes>
getCurrentLedgerEntryField(std::string const& fname) override
{
if (fname == "Destination" || fname == "Account")
return accountID_;
else if (fname == "Data")
return data_;
else if (fname == "FinishAfter")
{
auto t = env_->current()
->parentCloseTime()
.time_since_epoch()
.count();
std::string s = std::to_string(t);
return Bytes{s.begin(), s.end()};
}
return std::nullopt;
}
};
Env env{*this};
{
TestHostFunctions nfs(&env, 0);
TestHostFunctions nfs(env, 0);
std::string funcName("finish");
auto re = runEscrowWasm(wasm, funcName, &nfs, 100000);
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re.value().result);
@@ -231,9 +631,9 @@ struct Wasm_test : public beast::unit_test::suite
env.close();
{ // fail because current time < escrow_finish_after time
TestHostFunctions nfs(&env, -1);
TestHostFunctions nfs(env, -1);
std::string funcName("finish");
auto re = runEscrowWasm(wasm, funcName, &nfs, 100000);
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(!re.value().result);
@@ -248,18 +648,18 @@ struct Wasm_test : public beast::unit_test::suite
{ // fail because trying to access nonexistent field
struct BadTestHostFunctions : public TestHostFunctions
{
explicit BadTestHostFunctions(Env* env) : TestHostFunctions(env)
explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env)
{
}
std::optional<Bytes>
getTxField(std::string const& fname) override
Expected<Bytes, int32_t>
getTxField(SField const& fname) override
{
return std::nullopt;
return Unexpected(-1);
}
};
BadTestHostFunctions nfs(&env);
BadTestHostFunctions nfs(env);
std::string funcName("finish");
auto re = runEscrowWasm(wasm, funcName, &nfs, 100000);
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
BEAST_EXPECT(re.error());
// std::cout << "bad case (access nonexistent field) result "
// << re.error() << std::endl;
@@ -268,18 +668,18 @@ struct Wasm_test : public beast::unit_test::suite
{ // fail because trying to allocate more than MAX_PAGES memory
struct BadTestHostFunctions : public TestHostFunctions
{
explicit BadTestHostFunctions(Env* env) : TestHostFunctions(env)
explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env)
{
}
std::optional<Bytes>
getTxField(std::string const& fname) override
Expected<Bytes, int32_t>
getTxField(SField const& fname) override
{
return Bytes((MAX_PAGES + 1) * 64 * 1024, 1);
}
};
BadTestHostFunctions nfs(&env);
BadTestHostFunctions nfs(env);
std::string funcName("finish");
auto re = runEscrowWasm(wasm, funcName, &nfs, 100000);
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
BEAST_EXPECT(!re);
// std::cout << "bad case (more than MAX_PAGES) result "
// << re.error() << std::endl;
@@ -290,9 +690,9 @@ struct Wasm_test : public beast::unit_test::suite
auto wasmStr = boost::algorithm::unhex(std::string(wasmHex));
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
TestHostFunctions nfs(&env);
TestHostFunctionsOld nfs(&env);
std::string funcName("recursive");
auto re = runEscrowWasm(wasm, funcName, &nfs, 1000'000'000);
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 1000'000'000);
BEAST_EXPECT(re.error());
// std::cout << "bad case (deep recursion) result " << re.error()
// << std::endl;
@@ -317,7 +717,56 @@ struct Wasm_test : public beast::unit_test::suite
BEAST_EXPECT(
countSubstr(
sink.messages().str(),
"WAMR trap: Exception: wasm operand stack overflow") == 1);
"WAMR Exception: wasm operand stack overflow") == 1);
}
}
void
testEscrowWasmDN2()
{
testcase("wasm devnet 3 test");
std::string const funcName("finish");
using namespace test::jtx;
Env env(*this);
{
std::string const wasmHex = xrplStdExampleHex;
std::string const wasmStr = boost::algorithm::unhex(wasmHex);
std::vector<uint8_t> const wasm(wasmStr.begin(), wasmStr.end());
TestHostFunctions nfs(env, 0);
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000);
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result);
// std::cout << "good case result " << re.value().result
// << " cost: " << re.value().cost << std::endl;
}
}
env.close();
env.close();
env.close();
env.close();
env.close();
{
std::string const wasmHex = hostFunctions2Hex;
std::string const wasmStr = boost::algorithm::unhex(wasmHex);
std::vector<uint8_t> const wasm(wasmStr.begin(), wasmStr.end());
TestHostFunctions nfs(env, 0);
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000);
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result);
// std::cout << "good case result " << re.value().result
// << " cost: " << re.value().cost << std::endl;
}
}
}
@@ -325,9 +774,24 @@ struct Wasm_test : public beast::unit_test::suite
run() override
{
using namespace test::jtx;
testWasmtimeLib();
testWasmLib();
testBadWasm();
testEscrowWasmDN1();
testWasmCheckJson();
testWasmCompareJson();
testWasmLedgerSqn();
testWasmFib();
testWasmSha();
testWasmB58();
// runing too long
// testWasmSP1Verifier();
// testWasmBG16Verifier();
// TODO: needs fix for new host functions interface
// testEscrowWasmDN1();
testEscrowWasmDN2();
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -28,3 +28,29 @@ extern std::string const ledgerSqnHex;
extern std::string const allHostFunctionsHex;
extern std::string const deepRecursionHex;
extern std::string const tx_js;
extern std::string const lo_js;
extern std::string const lo_js2;
extern std::string const fib32Hex;
extern std::string const fib64Hex;
extern std::string const b58Hex;
extern std::string const sha512PureHex;
extern std::string const checkJsonHex;
extern std::string const compareJsonHex;
extern std::string const zkProofHex;
extern std::string const sp1_wasm;
extern std::string const xrplStdExampleHex;
extern std::string const hostFunctions2Hex;

View File

@@ -23,9 +23,12 @@
#include <memory>
namespace ripple {
#ifdef _DEBUG
// #define DEBUG_OUTPUT 1
// #define DEBUG_OUTPUT_WAMR 1
#endif
//////////////////////////////////////////////////////////////////////////////////////////
namespace ripple {
namespace {
@@ -45,7 +48,7 @@ getLogLevel(beast::severities::Severity severity)
case kError:
return WASM_LOG_LEVEL_ERROR;
default:
UNREACHABLE("invalid severity");
UNREACHABLE("WAMR invalid severity");
[[fallthrough]];
case kFatal:
case kNone:
@@ -70,7 +73,7 @@ getLogLevel(uint32_t severity)
case WASM_LOG_LEVEL_ERROR:
return kError;
default:
UNREACHABLE("invalid severity");
UNREACHABLE("WAMR invalid reverse severity");
[[fallthrough]];
case WASM_LOG_LEVEL_FATAL:
break;
@@ -88,18 +91,18 @@ wamr_log_to_rippled(
char const* fmt,
...)
{
beast::Journal j = debugLog();
beast::Journal j = WasmEngine::instance().getJournal();
// Format the variadic args
char const* safeFile = file ? file : "<null>";
std::ostringstream oss;
oss << "WAMR LOG (" << safeFile << ":" << line << "): ";
oss << "WAMR (" << safeFile << ":" << line << "): ";
va_list args;
va_start(args, fmt);
char formatted[1024];
char formatted[4096];
vsnprintf(formatted, sizeof(formatted), fmt, args);
formatted[sizeof(formatted) - 1] = '\0';
@@ -108,22 +111,47 @@ wamr_log_to_rippled(
oss << formatted;
j.stream(getLogLevel(logLevel)) << oss.str();
#ifdef DEBUG_OUTPUT_WAMR
std::cerr << oss.str() << std::endl;
#endif
//
}
static void
print_wasm_error(char const* message, wasm_trap_t* trap, beast::Journal j)
void
print_wasm_error(std::string_view msg, wasm_trap_t* trap, beast::Journal jlog)
{
j.debug() << "WAMR error: " << message;
#ifdef DEBUG_OUTPUT
auto& j = std::cerr;
#else
auto j = jlog.error();
#endif
j << "WAMR Error: " << msg;
if (trap)
{
wasm_byte_vec_t error_message;
wasm_trap_message(trap, &error_message);
if (error_message.num_elems)
{
j <<
#ifdef DEBUG_OUTPUT
"\nWAMR "
#else
"WAMR "
#endif
<< error_message.data;
}
if (error_message.size)
wasm_byte_vec_delete(&error_message);
wasm_trap_delete(trap);
j.debug() << "WAMR trap: " << error_message.data;
wasm_byte_vec_delete(&error_message);
}
#ifdef DEBUG_OUTPUT
j << std::endl;
#endif
}
} // namespace
@@ -134,7 +162,8 @@ InstanceWrapper::init(
wasm_module_t* m,
int32_t maxPages,
wasm_extern_vec_t* expt,
wasm_extern_vec_t const& imports)
wasm_extern_vec_t const& imports,
beast::Journal j)
{
wasm_trap_t* trap = nullptr;
InstantiationArgs inst_args{
@@ -148,11 +177,8 @@ InstanceWrapper::init(
if (!mi || trap)
{
print_wasm_error(
"can't create instance",
trap,
beast::Journal(beast::Journal::getNullSink()));
throw std::runtime_error("WAMR: can't create instance");
print_wasm_error("can't create instance", trap, j);
throw std::runtime_error("WAMR can't create instance");
}
wasm_instance_exports(mi.get(), expt);
return mi;
@@ -171,6 +197,27 @@ InstanceWrapper::InstanceWrapper(InstanceWrapper&& o)
*this = std::move(o);
}
InstanceWrapper::InstanceWrapper(
wasm_store_t* s,
wasm_module_t* m,
int32_t maxPages,
int64_t gas,
wasm_extern_vec_t const& imports,
beast::Journal j)
: exports WASM_EMPTY_VEC
, mod_inst(init(s, m, maxPages, &exports, imports, j))
, exec_env(wasm_instance_exec_env(mod_inst.get()))
, j_(j)
{
wasm_runtime_set_instruction_count_limit(exec_env, gas);
}
InstanceWrapper::~InstanceWrapper()
{
if (exports.size)
wasm_extern_vec_delete(&exports);
}
InstanceWrapper&
InstanceWrapper::operator=(InstanceWrapper&& o)
{
@@ -183,43 +230,36 @@ InstanceWrapper::operator=(InstanceWrapper&& o)
o.exports = {0, nullptr, 0, 0, nullptr};
mod_inst = std::move(o.mod_inst);
exec_env = o.exec_env;
o.exec_env = nullptr;
j_ = o.j_;
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*
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 (!export_types.size)
throw std::runtime_error("WAMR: no export");
if (export_types.size != exports.size)
throw std::runtime_error("WAMR: invalid export");
if (!mod_inst)
throw std::runtime_error("WAMR no module instance");
for (unsigned i = 0; i < export_types.size; ++i)
if (!export_types.num_elems)
throw std::runtime_error("WAMR no export");
if (export_types.num_elems != exports.num_elems)
throw std::runtime_error("WAMR invalid export");
for (unsigned i = 0; i < export_types.num_elems; ++i)
{
auto const* exp_type(export_types.data[i]);
@@ -231,26 +271,31 @@ InstanceWrapper::getFunc(
{
auto* exn(exports.data[i]);
if (wasm_extern_kind(exn) != WASM_EXTERN_FUNC)
throw std::runtime_error("WAMR: invalid export");
throw std::runtime_error("WAMR invalid export");
ft = wasm_externtype_as_functype(
const_cast<wasm_externtype_t*>(exn_type));
f = wasm_extern_as_func(exn);
break;
}
}
}
if (!f)
if (!f || !ft)
throw std::runtime_error(
"WAMR: can't find function " + std::string(funcName));
"WAMR can't find function <" + std::string(funcName) + ">");
return f;
return {f, ft};
}
wmem
InstanceWrapper::getMem() const
{
if (!mod_inst)
throw std::runtime_error("WAMR no module instance");
wasm_memory_t* mem = nullptr;
for (unsigned i = 0; i < exports.size; ++i)
for (unsigned i = 0; i < exports.num_elems; ++i)
{
auto* e(exports.data[i]);
if (wasm_extern_kind(e) == WASM_EXTERN_MEMORY)
@@ -261,17 +306,23 @@ InstanceWrapper::getMem() const
}
if (!mem)
throw std::runtime_error("WAMR: no memory exported");
throw std::runtime_error("WAMR no memory exported");
return {
reinterpret_cast<std::uint8_t*>(wasm_memory_data(mem)),
wasm_memory_data_size(mem)};
}
std::int64_t
InstanceWrapper::getGas() const
{
return exec_env ? wasm_runtime_get_instruction_count_limit(exec_env) : 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModulePtr
ModuleWrapper::init(wasm_store_t* s, wbytes const& wasmBin)
ModuleWrapper::init(wasm_store_t* s, Bytes const& wasmBin, beast::Journal j)
{
wasm_byte_vec_t const code{
wasmBin.size(),
@@ -280,6 +331,11 @@ ModuleWrapper::init(wasm_store_t* s, wbytes const& wasmBin)
sizeof(std::remove_reference_t<decltype(wasmBin)>::value_type),
nullptr};
ModulePtr m = ModulePtr(wasm_module_new(s, &code), &wasm_module_delete);
if (!m)
{
print_wasm_error("can't create module", nullptr, j);
throw std::runtime_error("WAMR can't create module");
}
return m;
}
@@ -296,6 +352,32 @@ ModuleWrapper::ModuleWrapper(ModuleWrapper&& o)
*this = std::move(o);
}
ModuleWrapper::ModuleWrapper(
wasm_store_t* s,
Bytes const& wasmBin,
bool instantiate,
int32_t maxPages,
int64_t gas,
std::vector<WasmImportFunc> const& imports,
beast::Journal j)
: module(init(s, wasmBin, j))
, export_types{0, nullptr, 0, 0, nullptr}
, j_(j)
{
wasm_module_exports(module.get(), &export_types);
if (instantiate)
{
auto wimports = buildImports(s, imports);
addInstance(s, maxPages, gas, wimports);
}
}
ModuleWrapper::~ModuleWrapper()
{
if (export_types.size)
wasm_exporttype_vec_delete(&export_types);
}
ModuleWrapper&
ModuleWrapper::operator=(ModuleWrapper&& o)
{
@@ -308,36 +390,11 @@ ModuleWrapper::operator=(ModuleWrapper&& o)
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;
j_ = o.j_;
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;
@@ -373,7 +430,7 @@ ModuleWrapper::makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp)
v.data[i] = wasm_valtype_new_f64();
break;
default:
throw std::runtime_error("Invalid import type");
throw std::runtime_error("WAMR Invalid import type");
}
}
}
@@ -400,7 +457,7 @@ ModuleWrapper::makeImpReturn(wasm_valtype_vec_t& v, WasmImportFunc const& imp)
v.data[0] = wasm_valtype_new_f64();
break;
default:
throw std::runtime_error("Invalid return type");
throw std::runtime_error("WAMR Invalid return type");
}
}
else
@@ -425,6 +482,7 @@ ModuleWrapper::buildImports(
wasm_extern_vec_new(&wimports, importTypes.size, nullptr);
wimports.num_elems = importTypes.num_elems;
unsigned impCnt = 0;
for (unsigned i = 0; i < importTypes.num_elems; ++i)
{
wasm_importtype_t const* importtype = importTypes.data[i];
@@ -433,6 +491,7 @@ ModuleWrapper::buildImports(
// create a placeholder
wimports.data[i] = wasm_extern_new_empty(
s, wasm_externtype_kind(wasm_importtype_type(importtype)));
++impCnt;
continue;
}
@@ -445,6 +504,7 @@ ModuleWrapper::buildImports(
// if ((W_ENV != modName) && (W_HOST_LIB != modName))
// continue;
bool impSet = false;
for (auto const& imp : imports)
{
if (imp.name != fieldName)
@@ -466,14 +526,35 @@ ModuleWrapper::buildImports(
nullptr);
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.num_elems)
{
print_wasm_error(
std::string("Imports not finished: ") +
std::to_string(wimports.num_elems) + "/" +
std::to_string(importTypes.num_elems),
nullptr,
j_);
}
return wimports;
}
wasm_func_t*
FuncInfo
ModuleWrapper::getFunc(std::string_view funcName) const
{
return mod_inst.getFunc(funcName, export_types);
@@ -485,15 +566,20 @@ ModuleWrapper::getMem() const
return mod_inst.getMem();
}
InstanceWrapper const&
ModuleWrapper::getInstance(int) const
{
return mod_inst;
}
int
ModuleWrapper::addInstance(
wasm_store_t* s,
int32_t maxPages,
int64_t gas,
wasm_extern_vec_t const& imports)
{
mod_inst = {s, module.get(), maxPages, imports};
exec_env = wasm_instance_exec_env(mod_inst.mod_inst.get());
mod_inst = {s, module.get(), maxPages, gas, imports, j_};
return 0;
}
@@ -507,21 +593,10 @@ ModuleWrapper::addInstance(
// 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;
return mod_inst.getGas();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -545,26 +620,33 @@ WamrEngine::WamrEngine()
int
WamrEngine::addModule(
wbytes const& wasmCode,
Bytes const& wasmCode,
bool instantiate,
int64_t gas,
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);
store.get(), wasmCode, instantiate, defMaxPages, gas, imports, j_);
if (!module)
{
print_wasm_error("can't create module wrapper", nullptr, j_);
throw std::runtime_error("WAMR can't create module wrapper");
}
return module ? 0 : -1;
}
int
WamrEngine::addInstance()
{
return module->addInstance(store.get(), defMaxPages);
}
// int
// WamrEngine::addInstance()
// {
// return module->addInstance(store.get(), defMaxPages);
// }
wasm_func_t*
FuncInfo
WamrEngine::getFunc(std::string_view funcName)
{
return module->getFunc(funcName);
@@ -591,6 +673,16 @@ WamrEngine::convertParams(std::vector<WasmParam> const& params)
case WT_F64:
v.push_back(WASM_F64_VAL(p.of.f64));
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:
break;
}
@@ -618,30 +710,37 @@ WamrEngine::add_param(std::vector<wasm_val_t>& in, int64_t p)
template <int NR, class... Types>
WamrResult
WamrEngine::call(std::string_view func, Types... args)
WamrEngine::call(std::string_view func, Types&&... args)
{
// Lookup our export function
auto* f = getFunc(func);
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)
WamrEngine::call(FuncInfo const& f, Types&&... args)
{
std::vector<wasm_val_t> in;
return call<NR>(func, in, std::forward<Types>(args)...);
return call<NR>(f, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(wasm_func_t* func, std::vector<wasm_val_t>& in)
WamrEngine::call(FuncInfo const& f, 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); }
auto const* ftp = wasm_functype_params(f.second);
if (ftp->num_elems != in.size())
{
print_wasm_error("invalid num of params to call func", nullptr, j_);
return ret;
}
wasm_val_vec_t const inv = in.empty()
? wasm_val_vec_t WASM_EMPTY_VEC
: wasm_val_vec_t{
@@ -650,9 +749,12 @@ WamrEngine::call(wasm_func_t* func, std::vector<wasm_val_t>& in)
in.size(),
sizeof(std::remove_reference_t<decltype(in)>::value_type),
nullptr};
trap = wasm_func_call(func, &inv, &ret.r);
wasm_trap_t* trap = wasm_func_call(f.first, &inv, &ret.r);
if (trap)
{
ret.f = true;
print_wasm_error("failed to call func", trap, j_);
}
// assert(results[0].kind == WASM_I32);
// if (NR) printf("Result P5: %d\n", ret[0].of.i32);
@@ -663,124 +765,136 @@ WamrEngine::call(wasm_func_t* func, std::vector<wasm_val_t>& in)
template <int NR, class... Types>
WamrResult
WamrEngine::call(
wasm_func_t* func,
FuncInfo const& f,
std::vector<wasm_val_t>& in,
std::int32_t p,
Types... args)
Types&&... args)
{
add_param(in, p);
return call<NR>(func, in, std::forward<Types>(args)...);
return call<NR>(f, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(
wasm_func_t* func,
FuncInfo const& f,
std::vector<wasm_val_t>& in,
std::int64_t p,
Types... args)
Types&&... args)
{
add_param(in, p);
return call<NR>(func, in, std::forward<Types>(args)...);
return call<NR>(f, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(
wasm_func_t* func,
FuncInfo const& f,
std::vector<wasm_val_t>& in,
uint8_t const* d,
std::size_t sz,
Types... args)
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 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>(func, in, std::forward<Types>(args)...);
return call<NR>(f, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WamrResult
WamrEngine::call(
wasm_func_t* func,
FuncInfo const& f,
std::vector<wasm_val_t>& in,
wbytes const& p,
Types... args)
Bytes const& p,
Types&&... args)
{
return call<NR>(func, in, p.data(), p.size(), std::forward<Types>(args)...);
return call<NR>(f, in, p.data(), p.size(), std::forward<Types>(args)...);
}
Expected<int32_t, TER>
Expected<WasmResult<int32_t>, TER>
WamrEngine::run(
wbytes const& wasmCode,
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports,
HostFunctions* hfs,
int64_t gas,
beast::Journal j)
{
j_ = j;
wasm_runtime_set_log_level(
std::min(getLogLevel(j_.sink().threshold()), WASM_LOG_LEVEL_ERROR));
try
{
wasm_runtime_set_log_level(getLogLevel(j.sink().threshold()));
j_ = j;
return runHlp(wasmCode, funcName, imports, params);
return runHlp(wasmCode, funcName, params, imports, hfs, gas);
}
catch (std::exception const&)
catch (std::exception const& e)
{
print_wasm_error(std::string("exception: ") + e.what(), nullptr, j_);
}
catch (...)
{
print_wasm_error(std::string("unknown exception"), nullptr, j_);
}
return Unexpected<TER>(tecFAILED_PROCESSING);
}
Expected<int32_t, TER>
Expected<WasmResult<int32_t>, TER>
WamrEngine::runHlp(
wbytes const& wasmCode,
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params)
HostFunctions* hfs,
int64_t gas)
{
// #ifdef DEBUG_OUTPUT
// auto& j = std::cerr;
// #else
// auto j = j_.debug();
// #endif
// Create and instantiate the module.
if (!wasmCode.empty())
{
int const m = addModule(wasmCode, true, imports);
if (m < 0)
return Unexpected<TER>(tecFAILED_PROCESSING);
[[maybe_unused]] int const m = addModule(wasmCode, true, gas, imports);
}
if (!module)
if (!module || !module->mod_inst)
{
print_wasm_error("no instance to run", nullptr, j_);
return Unexpected<TER>(tecFAILED_PROCESSING);
}
if (hfs)
hfs->setRT(&getRT());
// Call main
auto* f = getFunc(!funcName.empty() ? funcName : "_start");
auto f = getFunc(!funcName.empty() ? funcName : "_start");
auto p = convertParams(params);
auto res = call<1>(f, p);
if (!res.r.size || trap)
if (res.f)
{
return Unexpected<TER>(tecFAILED_PROCESSING);
}
else if (!res.r.num_elems)
{
print_wasm_error(
"<" + std::string(funcName) + "> return nothing", nullptr, j_);
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;
WasmResult<int32_t> const ret{res.r.data[0].of.i32, gas - module->getGas()};
// j << "WAMR Res: " << ret.result << " cost: " << ret.cost << std::endl;
return ret;
}
std::int32_t
@@ -790,17 +904,6 @@ WamrEngine::initMaxPages(std::int32_t 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()
{
@@ -813,17 +916,24 @@ WamrEngine::getMem() const
return module ? module->getMem() : wmem();
}
InstanceWrapper const&
WamrEngine::getRT(int m, int i)
{
if (!module)
throw std::runtime_error("WAMR no module");
return module->getInstance(i);
}
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)
if (res.f || !res.r.num_elems || (res.r.data[0].kind != WASM_I32) ||
!res.r.data[0].of.i32)
throw std::runtime_error(
"WAMR: can't allocate memory, " + std::to_string(sz) + " bytes");
return ptr;
"WAMR can't allocate memory, " + std::to_string(sz) + " bytes");
return res.r.data[0].of.i32;
}
wasm_trap_t*
@@ -837,4 +947,10 @@ WamrEngine::newTrap(std::string_view txt)
return wasm_trap_new(store.get(), &msg);
}
beast::Journal
WamrEngine::getJournal() const
{
return j_;
}
} // namespace ripple

View File

@@ -20,37 +20,57 @@
#include <xrpld/app/misc/WasmVM.h>
#include <xrpl/beast/utility/Journal.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;
bool f;
WamrResult(unsigned N = 0) : r{0, nullptr, 0, 0, nullptr}, f(false)
{
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;}
WamrResult(WamrResult&& o)
{
*this = std::move(o);
}
WamrResult&
operator=(WamrResult&& o)
{
r = o.r;
o.r = {0, nullptr, 0, 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 InstancePtr =
std::unique_ptr<wasm_instance_t, decltype(&wasm_instance_delete)>;
// clang-format on
using FuncInfo = std::pair<wasm_func_t const*, wasm_functype_t const*>;
struct InstanceWrapper
{
wasm_extern_vec_t exports;
InstancePtr mod_inst;
wasm_exec_env_t exec_env = nullptr;
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
private:
static InstancePtr
@@ -59,7 +79,8 @@ private:
wasm_module_t* m,
int32_t maxPages,
wasm_extern_vec_t* expt,
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
wasm_extern_vec_t const& imports,
beast::Journal j);
public:
InstanceWrapper();
@@ -73,31 +94,36 @@ public:
wasm_store_t* s,
wasm_module_t* m,
int32_t maxPages,
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
int64_t gas,
wasm_extern_vec_t const& imports,
beast::Journal j);
~InstanceWrapper();
operator bool() const;
wasm_func_t*
FuncInfo
getFunc(
std::string_view funcName,
wasm_exporttype_vec_t const& export_types) const;
wmem
getMem() const;
std::int64_t
getGas() const;
};
struct ModuleWrapper
{
ModulePtr module;
wasm_exec_env_t exec_env = nullptr;
InstanceWrapper mod_inst;
wasm_exporttype_vec_t export_types;
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
private:
static ModulePtr
init(wasm_store_t* s, wbytes const& wasmBin);
init(wasm_store_t* s, Bytes const& wasmBin, beast::Journal j);
public:
ModuleWrapper();
@@ -106,28 +132,31 @@ public:
operator=(ModuleWrapper&& o);
ModuleWrapper(
wasm_store_t* s,
wbytes const& wasmBin,
Bytes const& wasmBin,
bool instantiate,
int32_t maxPages,
std::vector<WasmImportFunc> const& imports = {});
int64_t gas,
std::vector<WasmImportFunc> const& imports,
beast::Journal j);
~ModuleWrapper();
operator bool() const;
wasm_func_t*
FuncInfo
getFunc(std::string_view funcName) const;
wmem
getMem() const;
InstanceWrapper const&
getInstance(int i = 0) const;
int
addInstance(
wasm_store_t* s,
int32_t maxPages,
int64_t gas,
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
std::int64_t
setGas(std::int64_t gas);
std::int64_t
getGas();
@@ -145,8 +174,6 @@ 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;
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
@@ -154,19 +181,15 @@ public:
WamrEngine();
~WamrEngine() = default;
Expected<int32_t, TER>
run(wbytes const& wasmCode,
Expected<WasmResult<int32_t>, TER>
run(Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports,
HostFunctions* hfs,
int64_t gas,
beast::Journal j);
std::int64_t
initGas(std::int64_t def);
std::int64_t
setGas(std::int64_t gas);
std::int32_t
initMaxPages(std::int32_t def);
@@ -174,41 +197,51 @@ public:
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);
wasm_trap_t*
newTrap(std::string_view msg);
private:
Expected<int32_t, TER>
Expected<WasmResult<int32_t>, TER>
runHlp(
wbytes const& wasmCode,
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params);
HostFunctions* hfs,
int64_t gas);
int
addModule(
wbytes const& wasmCode,
Bytes const& wasmCode,
bool instantiate,
int64_t gas,
std::vector<WasmImportFunc> const& imports);
void
clearModules();
int
addInstance();
// int addInstance();
int32_t
runFunc(std::string_view const funcName, int32_t p);
int32_t
makeModule(
wbytes const& wasmCode,
Bytes const& wasmCode,
wasm_extern_vec_t const& imports = WASM_EMPTY_VEC);
wasm_func_t*
FuncInfo
getFunc(std::string_view funcName);
std::vector<wasm_val_t>
@@ -221,48 +254,48 @@ private:
template <int NR, class... Types>
inline WamrResult
call(std::string_view func, Types... args);
call(std::string_view func, Types&&... args);
template <int NR, class... Types>
inline WamrResult
call(wasm_func_t* func, Types... args);
call(FuncInfo const& f, Types&&... args);
template <int NR, class... Types>
inline WamrResult
call(wasm_func_t* f, std::vector<wasm_val_t>& in);
call(FuncInfo const& f, std::vector<wasm_val_t>& in);
template <int NR, class... Types>
inline WamrResult
call(
wasm_func_t* func,
FuncInfo const& f,
std::vector<wasm_val_t>& in,
std::int32_t p,
Types... args);
Types&&... args);
template <int NR, class... Types>
inline WamrResult
call(
wasm_func_t* func,
FuncInfo const& f,
std::vector<wasm_val_t>& in,
std::int64_t p,
Types... args);
Types&&... args);
template <int NR, class... Types>
inline WamrResult
call(
wasm_func_t* func,
FuncInfo const& f,
std::vector<wasm_val_t>& in,
uint8_t const* d,
std::size_t sz,
Types... args);
Types&&... args);
template <int NR, class... Types>
inline WamrResult
call(
wasm_func_t* func,
FuncInfo const& f,
std::vector<wasm_val_t>& in,
wbytes const& p,
Types... args);
Bytes const& p,
Types&&... args);
};
} // namespace ripple

View File

@@ -0,0 +1,224 @@
//------------------------------------------------------------------------------
/*
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/WasmParamsHelper.h>
#include <xrpl/basics/Expected.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/TER.h>
namespace ripple {
int32_t const HF_ERR_INTERNAL = -1;
int32_t const HF_ERR_FIELD_NOT_FOUND = -2;
int32_t const HF_ERR_BUFFER_TOO_SMALL = -3;
int32_t const HF_ERR_NO_ARRAY = -4;
int32_t const HF_ERR_NOT_LEAF_FIELD = -5;
int32_t const HF_ERR_LOCATOR_MALFORMED = -6;
int32_t const HF_ERR_SLOT_OUT_RANGE = -7;
int32_t const HF_ERR_SLOTS_FULL = -8;
int32_t const HF_ERR_INVALID_SLOT = -9;
int32_t const HF_ERR_LEDGER_OBJ_NOT_FOUND = -10;
int32_t const HF_ERR_DECODING = -11;
int32_t const HF_ERR_DATA_FIELD_TOO_LARGE = -12;
int32_t const HF_ERR_OUT_OF_BOUNDS = -13;
int32_t const HF_ERR_NO_MEM_EXPORTED = -14;
int32_t const HF_ERR_INVALID_PARAMS = -15;
int32_t const HF_ERR_INVALID_ACCOUNT = -16;
struct HostFunctions
{
virtual void
setRT(void const*)
{
}
virtual void const*
getRT() const
{
return nullptr;
}
virtual beast::Journal
getJournal()
{
return beast::Journal{beast::Journal::getNullSink()};
}
virtual int32_t
getLedgerSqn()
{
return 1;
}
virtual int32_t
getParentLedgerTime()
{
return 1;
}
virtual Hash
getParentLedgerHash()
{
return {};
}
virtual int32_t
cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx)
{
return HF_ERR_INTERNAL;
}
virtual Expected<Bytes, int32_t>
getTxField(SField const& fname)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual Expected<Bytes, int32_t>
getCurrentLedgerObjField(SField const& fname)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual Expected<Bytes, int32_t>
getLedgerObjField(int32_t cacheIdx, SField const& fname)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual Expected<Bytes, int32_t>
getTxNestedField(Slice const& locator)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual Expected<Bytes, int32_t>
getCurrentLedgerObjNestedField(Slice const& locator)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual Expected<Bytes, int32_t>
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual int32_t
getTxArrayLen(SField const& fname)
{
return HF_ERR_INTERNAL;
}
virtual int32_t
getCurrentLedgerObjArrayLen(SField const& fname)
{
return HF_ERR_INTERNAL;
}
virtual int32_t
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname)
{
return HF_ERR_INTERNAL;
}
virtual int32_t
getTxNestedArrayLen(Slice const& locator)
{
return HF_ERR_INTERNAL;
}
virtual int32_t
getCurrentLedgerObjNestedArrayLen(Slice const& locator)
{
return HF_ERR_INTERNAL;
}
virtual int32_t
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator)
{
return HF_ERR_INTERNAL;
}
virtual int32_t
updateData(Bytes const& data)
{
return HF_ERR_INTERNAL;
}
virtual Hash
computeSha512HalfHash(Bytes const& data)
{
return Hash{};
}
virtual Expected<Bytes, int32_t>
accountKeylet(AccountID const& account)
{
return Bytes{};
}
virtual Expected<Bytes, int32_t>
credentialKeylet(
AccountID const& subject,
AccountID const& issuer,
Bytes const& credentialType)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual Expected<Bytes, int32_t>
escrowKeylet(AccountID const& account, std::uint32_t seq)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual Expected<Bytes, int32_t>
oracleKeylet(AccountID const& account, std::uint32_t docId)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual Expected<Bytes, int32_t>
getNFT(AccountID const& account, uint256 const& nftId)
{
return Unexpected(HF_ERR_INTERNAL);
}
virtual int32_t
trace(std::string const& msg, Bytes const& data, bool asHex)
{
return HF_ERR_INTERNAL;
}
virtual int32_t
traceNum(std::string const& msg, int64_t data)
{
return HF_ERR_INTERNAL;
}
virtual ~HostFunctions() = default;
};
} // namespace ripple

View File

@@ -20,8 +20,14 @@
#include <xrpld/app/misc/WasmHostFuncImpl.h>
#include <xrpld/app/tx/detail/NFTokenUtils.h>
#include <xrpl/protocol/STBitString.h>
#include <xrpl/protocol/digest.h>
#ifdef _DEBUG
// #define DEBUG_OUTPUT 1
// #define DEBUG_OUTPUT_WAMR 1
#endif
namespace ripple {
int32_t
@@ -36,198 +42,455 @@ WasmHostFunctionsImpl::getParentLedgerTime()
return ctx.view().parentCloseTime().time_since_epoch().count(); // TODO try
}
// TODO remove json code after deciding encoding scheme
std::optional<Bytes>
WasmHostFunctionsImpl::getTxField(std::string const& fname)
Hash
WasmHostFunctionsImpl::getParentLedgerHash()
{
auto js = ctx.tx.getJson(JsonOptions::none);
if (js.isMember(fname))
{
auto s = js.get(fname, Json::Value::null).asString();
return Bytes{s.begin(), s.end()};
}
else
return std::nullopt;
return ctx.view().info().parentHash;
}
std::optional<Bytes>
WasmHostFunctionsImpl::getLedgerEntryField(
int32_t type,
Bytes const& kdata,
std::string const& fname)
int32_t
WasmHostFunctionsImpl::cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx)
{
auto kl = [&]() -> std::optional<ripple::Keylet> {
if (type == ltACCOUNT_ROOT)
if (cacheIdx < 0 || cacheIdx > MAX_CACHE)
return HF_ERR_SLOT_OUT_RANGE;
if (!cacheIdx)
{
for (cacheIdx = 0; cacheIdx < MAX_CACHE; ++cacheIdx)
if (!cache[cacheIdx])
break;
}
else
--cacheIdx;
if (cacheIdx >= MAX_CACHE)
return HF_ERR_SLOTS_FULL;
cache[cacheIdx] = ctx.view().read(keylet);
return cache[cacheIdx] ? cacheIdx + 1 : HF_ERR_LEDGER_OBJ_NOT_FOUND;
}
Bytes
getAnyFieldData(STBase const& obj)
{
// auto const& fname = obj.getFName();
if (STI_ACCOUNT == obj.getSType())
{
auto const& super(static_cast<STAccount const&>(obj));
auto const& data = super.value();
return {data.begin(), data.end()};
}
else if (STI_AMOUNT == obj.getSType())
{
auto const& super(static_cast<STAmount const&>(obj));
int64_t const data = super.xrp().drops();
auto const* b = reinterpret_cast<uint8_t const*>(&data);
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
return {b, e};
}
else if (STI_VL == obj.getSType())
{
auto const& super(static_cast<STBlob const&>(obj));
auto const& data = super.value();
return {data.begin(), data.end()};
}
else if (STI_UINT256 == obj.getSType())
{
auto const& super(static_cast<STBitString<256> const&>(obj));
auto const& data = super.value();
return {data.begin(), data.end()};
}
else if (STI_UINT32 == obj.getSType())
{
auto const& super(static_cast<STInteger<std::uint32_t> const&>(obj));
std::uint32_t const data = super.value();
auto const* b = reinterpret_cast<uint8_t const*>(&data);
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
return {b, e};
}
Serializer msg;
obj.add(msg);
return msg.getData();
}
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::getTxField(SField const& fname)
{
auto const* field = ctx.tx.peekAtPField(fname);
if (!field)
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
return getAnyFieldData(*field);
}
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::getCurrentLedgerObjField(SField const& fname)
{
auto const sle = ctx.view().read(leKey);
if (!sle)
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
auto const* field = sle->peekAtPField(fname);
if (!field)
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
return getAnyFieldData(*field);
}
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::getLedgerObjField(int32_t cacheIdx, SField const& fname)
{
--cacheIdx;
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
return Unexpected(HF_ERR_SLOT_OUT_RANGE);
if (!cache[cacheIdx])
return Unexpected(HF_ERR_INVALID_SLOT);
auto const* field = cache[cacheIdx]->peekAtPField(fname);
if (!field)
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
return getAnyFieldData(*field);
}
static Expected<STBase const*, int32_t>
locateField(STObject const* obj, Slice const& loc)
{
if (loc.size() % 4)
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
int32_t const* l = reinterpret_cast<int32_t const*>(loc.data());
int32_t const sz = loc.size() / 4;
STBase const* field = nullptr;
auto const& m = SField::getKnownCodeToField();
for (int i = 0; i < sz; ++i)
{
int32_t const c = l[i];
if (!field)
{
std::string s(kdata.begin(), kdata.end());
auto const account = parseBase58<AccountID>(s);
if (account)
{
return keylet::account(account.value());
}
auto const it = m.find(c);
if (it == m.end())
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
auto const& fname(*it->second);
field = obj->peekAtPField(fname);
if (!field)
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
}
return std::nullopt;
}();
else if (STI_ARRAY == field->getSType())
{
auto const* arr = static_cast<STArray const*>(field);
if (c >= arr->size())
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
field = &(arr->operator[](c));
}
else if (STI_OBJECT == field->getSType())
{
auto const* o = static_cast<STObject const*>(field);
if (!kl || !ctx.view().exists(kl.value()))
return std::nullopt;
auto const it = m.find(c);
if (it == m.end())
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
auto const& fname(*it->second);
auto js = ctx.view().read(kl.value())->getJson(JsonOptions::none);
if (js.isMember(fname))
{
auto s = js.get(fname, Json::Value::null).asString();
return Bytes{s.begin(), s.end()};
field = o->peekAtPField(fname);
if (!field)
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
}
}
else
return std::nullopt;
if (!field || (STI_OBJECT == field->getSType()) ||
(STI_ARRAY == field->getSType()))
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
return field;
}
std::optional<Bytes>
WasmHostFunctionsImpl::getCurrentLedgerEntryField(std::string const& fname)
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::getTxNestedField(Slice const& locator)
{
if (!ctx.view().exists(leKey))
return std::nullopt;
auto const r = locateField(&ctx.tx, locator);
if (!r.has_value())
return Unexpected(r.error());
auto js = ctx.view().read(leKey)->getJson(JsonOptions::none);
if (js.isMember(fname))
{
auto s = js.get(fname, Json::Value::null).asString();
return Bytes{s.begin(), s.end()};
}
else
return std::nullopt;
auto const* field = r.value();
return getAnyFieldData(*field);
}
std::optional<Bytes>
WasmHostFunctionsImpl::getNFT(
std::string const& account,
std::string const& nftId)
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(Slice const& locator)
{
auto const accountId = parseBase58<AccountID>(account);
if (!accountId || accountId->isZero())
{
return std::nullopt;
}
auto const sle = ctx.view().read(leKey);
if (!sle)
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
uint256 nftHash;
if (!nftHash.parseHex(nftId))
{
return std::nullopt;
}
auto const r = locateField(sle.get(), locator);
if (!r.has_value())
return Unexpected(r.error());
auto jv = nft::findToken(ctx.view(), accountId.value(), nftHash);
if (!jv)
{
return std::nullopt;
}
Slice const s = (*jv)[sfURI];
return Bytes{s.begin(), s.end()};
auto const* field = r.value();
return getAnyFieldData(*field);
}
bool
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::getLedgerObjNestedField(
int32_t cacheIdx,
Slice const& locator)
{
--cacheIdx;
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
return Unexpected(HF_ERR_SLOT_OUT_RANGE);
if (!cache[cacheIdx])
return Unexpected(HF_ERR_INVALID_SLOT);
auto const r = locateField(cache[cacheIdx].get(), locator);
if (!r.has_value())
return Unexpected(r.error());
auto const* field = r.value();
return getAnyFieldData(*field);
}
int32_t
WasmHostFunctionsImpl::getTxArrayLen(SField const& fname)
{
if (fname.fieldType != STI_ARRAY)
return HF_ERR_NO_ARRAY;
auto const* field = ctx.tx.peekAtPField(fname);
if (!field)
return HF_ERR_FIELD_NOT_FOUND;
if (field->getSType() != STI_ARRAY)
return HF_ERR_NO_ARRAY;
int32_t const sz = static_cast<STArray const*>(field)->size();
return sz;
}
int32_t
WasmHostFunctionsImpl::getCurrentLedgerObjArrayLen(SField const& fname)
{
if (fname.fieldType != STI_ARRAY)
return HF_ERR_NO_ARRAY;
auto const sle = ctx.view().read(leKey);
if (!sle)
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
auto const* field = sle->peekAtPField(fname);
if (!field)
return HF_ERR_FIELD_NOT_FOUND;
if (field->getSType() != STI_ARRAY)
return HF_ERR_NO_ARRAY;
int32_t const sz = static_cast<STArray const*>(field)->size();
return sz;
}
int32_t
WasmHostFunctionsImpl::getLedgerObjArrayLen(
int32_t cacheIdx,
SField const& fname)
{
if (fname.fieldType != STI_ARRAY)
return HF_ERR_NO_ARRAY;
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
return HF_ERR_SLOT_OUT_RANGE;
if (!cache[cacheIdx])
return HF_ERR_INVALID_SLOT;
auto const* field = cache[cacheIdx]->peekAtPField(fname);
if (!field)
return HF_ERR_FIELD_NOT_FOUND;
if (field->getSType() != STI_ARRAY)
return HF_ERR_NO_ARRAY;
int32_t const sz = static_cast<STArray const*>(field)->size();
return sz;
}
int32_t
WasmHostFunctionsImpl::getTxNestedArrayLen(Slice const& locator)
{
auto const r = locateField(&ctx.tx, locator);
if (!r.has_value())
return r.error();
auto const* field = r.value();
if (field->getSType() != STI_ARRAY)
return HF_ERR_NO_ARRAY;
int32_t const sz = static_cast<STArray const*>(field)->size();
return sz;
}
int32_t
WasmHostFunctionsImpl::getCurrentLedgerObjNestedArrayLen(Slice const& locator)
{
auto const sle = ctx.view().read(leKey);
if (!sle)
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
auto const r = locateField(sle.get(), locator);
if (!r.has_value())
return r.error();
auto const* field = r.value();
if (field->getSType() != STI_ARRAY)
return HF_ERR_NO_ARRAY;
int32_t const sz = static_cast<STArray const*>(field)->size();
return sz;
}
int32_t
WasmHostFunctionsImpl::getLedgerObjNestedArrayLen(
int32_t cacheIdx,
Slice const& locator)
{
--cacheIdx;
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
return HF_ERR_SLOT_OUT_RANGE;
if (!cache[cacheIdx])
return HF_ERR_INVALID_SLOT;
auto const r = locateField(cache[cacheIdx].get(), locator);
if (!r.has_value())
return r.error();
auto const* field = r.value();
if (field->getSType() != STI_ARRAY)
return HF_ERR_NO_ARRAY;
int32_t const sz = static_cast<STArray const*>(field)->size();
return sz;
}
int32_t
WasmHostFunctionsImpl::updateData(Bytes const& data)
{
if (!ctx.view().exists(leKey))
return false;
auto sle = ctx.view().peek(leKey);
if (!sle)
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
sle->setFieldVL(sfData, data);
ctx.view().update(sle);
return true;
return 0;
}
Hash
WasmHostFunctionsImpl::computeSha512HalfHash(Bytes const& data)
{
auto const hash = sha512Half(data);
return uint256::fromVoid(hash.data());
return hash;
}
std::optional<Bytes>
WasmHostFunctionsImpl::accountKeylet(std::string const& account)
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::accountKeylet(AccountID const& account)
{
auto const accountId = parseBase58<AccountID>(account);
if (!accountId || accountId->isZero())
{
return std::nullopt;
}
auto keylet = keylet::account(*accountId).key;
if (!keylet)
{
return std::nullopt;
}
return Bytes{keylet.begin(), keylet.end()};
if (!account)
return Unexpected(HF_ERR_INVALID_ACCOUNT);
auto const keylet = keylet::account(account);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
std::optional<Bytes>
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::credentialKeylet(
std::string const& subject,
std::string const& issuer,
std::string const& credentialType)
AccountID const& subject,
AccountID const& issuer,
Bytes const& credentialType)
{
auto const subjectId = parseBase58<AccountID>(subject);
if (!subjectId || subjectId->isZero())
{
return std::nullopt;
}
if (!subject || !issuer || credentialType.empty() ||
credentialType.size() > maxCredentialTypeLength)
return Unexpected(HF_ERR_INVALID_PARAMS);
auto const issuerId = parseBase58<AccountID>(issuer);
if (!issuerId || issuerId->isZero())
{
return std::nullopt;
}
auto const keylet =
keylet::credential(subject, issuer, makeSlice(credentialType));
auto keylet =
keylet::credential(*subjectId, *issuerId, makeSlice(credentialType))
.key;
if (!keylet)
{
return std::nullopt;
}
return Bytes{keylet.begin(), keylet.end()};
return Bytes{keylet.key.begin(), keylet.key.end()};
}
std::optional<Bytes>
WasmHostFunctionsImpl::escrowKeylet(
std::string const& account,
std::uint32_t const& seq)
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::escrowKeylet(AccountID const& account, std::uint32_t seq)
{
auto const accountId = parseBase58<AccountID>(account);
if (!accountId || accountId->isZero())
{
return std::nullopt;
}
auto keylet = keylet::escrow(*accountId, seq).key;
if (!keylet)
{
return std::nullopt;
}
return Bytes{keylet.begin(), keylet.end()};
if (!account)
return Unexpected(HF_ERR_INVALID_ACCOUNT);
auto const keylet = keylet::escrow(account, seq);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
std::optional<Bytes>
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::oracleKeylet(
std::string const& account,
std::uint32_t const& documentId)
AccountID const& account,
std::uint32_t documentId)
{
auto const accountId = parseBase58<AccountID>(account);
if (!accountId || accountId->isZero())
{
return std::nullopt;
}
auto keylet = keylet::oracle(*accountId, documentId).key;
if (!keylet)
{
return std::nullopt;
}
return Bytes{keylet.begin(), keylet.end()};
if (!account || account.isZero())
return Unexpected(HF_ERR_INVALID_ACCOUNT);
auto const keylet = keylet::oracle(account, documentId);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
Expected<Bytes, int32_t>
WasmHostFunctionsImpl::getNFT(AccountID const& account, uint256 const& nftId)
{
if (!account || !nftId)
return Unexpected(HF_ERR_INVALID_PARAMS);
auto obj = nft::findToken(ctx.view(), account, nftId);
if (!obj)
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
Slice const s = obj->at(sfURI);
return Bytes(s.begin(), s.end());
}
int32_t
WasmHostFunctionsImpl::trace(
std::string const& msg,
Bytes const& data,
bool asHex)
{
#ifdef DEBUG_OUTPUT
auto j = ctx.journal.error();
#else
auto j = ctx.journal.trace();
#endif
j << msg;
if (!asHex)
j << std::string_view(
reinterpret_cast<char const*>(data.data()), data.size());
else
{
auto const hex =
boost::algorithm::hex(std::string(data.begin(), data.end()));
j << hex;
}
return msg.size() + data.size() * (asHex ? 2 : 1);
}
int32_t
WasmHostFunctionsImpl::traceNum(std::string const& msg, int64_t data)
{
#ifdef DEBUG_OUTPUT
auto j = ctx.journal.error();
#else
auto j = ctx.journal.trace();
#endif
j << msg << data;
return msg.size() + sizeof(data);
}
} // namespace ripple

View File

@@ -19,22 +19,38 @@
#pragma once
#include <xrpld/app/misc/WasmVM.h>
#include <xrpld/app/misc/WasmHostFunc.h>
#include <xrpld/app/tx/detail/ApplyContext.h>
#include <xrpl/basics/Expected.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/TER.h>
namespace ripple {
class WasmHostFunctionsImpl : public HostFunctions
{
ApplyContext& ctx;
Keylet leKey;
static int constexpr MAX_CACHE = 256;
std::array<std::shared_ptr<SLE const>, MAX_CACHE> cache;
void const* rt_ = nullptr;
public:
WasmHostFunctionsImpl(ApplyContext& ctx, Keylet leKey)
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
{
@@ -47,46 +63,77 @@ public:
int32_t
getParentLedgerTime() override;
std::optional<Bytes>
getTxField(std::string const& fname) override;
Hash
getParentLedgerHash() override;
std::optional<Bytes>
getLedgerEntryField(
int32_t type,
Bytes const& kdata,
std::string const& fname) override;
int32_t
cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx) override;
std::optional<Bytes>
getCurrentLedgerEntryField(std::string const& fname) override;
Expected<Bytes, int32_t>
getTxField(SField const& fname) override;
std::optional<Bytes>
getNFT(std::string const& account, std::string const& nftId) override;
Expected<Bytes, int32_t>
getCurrentLedgerObjField(SField const& fname) override;
bool
Expected<Bytes, int32_t>
getLedgerObjField(int32_t cacheIdx, SField const& fname) override;
Expected<Bytes, int32_t>
getTxNestedField(Slice const& locator) override;
Expected<Bytes, int32_t>
getCurrentLedgerObjNestedField(Slice const& locator) override;
Expected<Bytes, int32_t>
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override;
int32_t
getTxArrayLen(SField const& fname) override;
int32_t
getCurrentLedgerObjArrayLen(SField const& fname) override;
int32_t
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override;
int32_t
getTxNestedArrayLen(Slice const& locator) override;
int32_t
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override;
int32_t
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override;
int32_t
updateData(Bytes const& data) override;
Hash
computeSha512HalfHash(Bytes const& data) override;
std::optional<Bytes>
accountKeylet(std::string const& account) override;
Expected<Bytes, int32_t>
accountKeylet(AccountID const& account) override;
std::optional<Bytes>
Expected<Bytes, int32_t>
credentialKeylet(
std::string const& subject,
std::string const& issuer,
std::string const& credentialType) override;
AccountID const& subject,
AccountID const& issuer,
Bytes const& credentialType) override;
std::optional<Bytes>
escrowKeylet(std::string const& account, std::uint32_t const& seq) override;
Expected<Bytes, int32_t>
escrowKeylet(AccountID const& account, std::uint32_t seq) override;
std::optional<Bytes>
oracleKeylet(std::string const& account, std::uint32_t const& documentId)
override;
Expected<Bytes, int32_t>
oracleKeylet(AccountID const& account, std::uint32_t documentId) override;
private:
ApplyContext& ctx;
Keylet leKey;
Expected<Bytes, int32_t>
getNFT(AccountID const& account, uint256 const& nftId) override;
int32_t
trace(std::string const& msg, Bytes const& data, bool asHex) override;
int32_t
traceNum(std::string const& msg, int64_t data) override;
};
} // namespace ripple

View File

@@ -17,6 +17,8 @@
*/
//==============================================================================
#include <xrpld/app/misc/WamrVM.h>
#include <xrpld/app/misc/WasmHostFunc.h>
#include <xrpld/app/misc/WasmHostFuncWrapper.h>
#include <xrpld/app/tx/detail/NFTokenUtils.h>
@@ -24,6 +26,83 @@
namespace ripple {
static int32_t
setData(
InstanceWrapper const* rt,
int32_t dst,
int32_t dsz,
uint8_t const* src,
int32_t ssz)
{
auto mem = rt ? rt->getMem() : wmem();
if (!mem.s)
return HF_ERR_NO_MEM_EXPORTED;
if (dst + dsz > mem.s)
return HF_ERR_OUT_OF_BOUNDS;
if (ssz > dsz)
return HF_ERR_BUFFER_TOO_SMALL;
memcpy(mem.p + dst, src, ssz);
return ssz;
}
static Expected<Bytes, int32_t>
getData(InstanceWrapper const* rt, int32_t src, int32_t ssz)
{
auto mem = rt ? rt->getMem() : wmem();
if (!mem.s)
return Unexpected(HF_ERR_NO_MEM_EXPORTED);
if (src + ssz > mem.s)
return Unexpected(HF_ERR_OUT_OF_BOUNDS);
Bytes data(mem.p + src, mem.p + src + ssz);
return data;
}
static Expected<AccountID, int32_t>
getDataAccount(InstanceWrapper const* rt, int32_t ptr, int32_t sz)
{
auto const r = getData(rt, ptr, sz);
if (r->size() < AccountID::bytes)
return Unexpected(HF_ERR_INVALID_PARAMS);
return AccountID::fromVoid(r->data());
}
static Expected<std::string, int32_t>
getDataString(InstanceWrapper const* rt, int32_t src, int32_t ssz)
{
auto mem = rt ? rt->getMem() : wmem();
if (!mem.s)
return Unexpected(HF_ERR_NO_MEM_EXPORTED);
if (src + ssz > mem.s)
return Unexpected(HF_ERR_OUT_OF_BOUNDS);
std::string data(mem.p + src, mem.p + src + ssz);
return data;
}
#define RET(x) \
results->data[0] = WASM_I32_VAL(x); \
results->num_elems = 1; \
return nullptr;
wasm_trap_t*
getLedgerSqnOld_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
// auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
int32_t const sqn = hf->getLedgerSqn();
RET(sqn);
}
wasm_trap_t*
getLedgerSqn_wrap(
void* env,
@@ -31,10 +110,15 @@ getLedgerSqn_wrap(
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
int32_t const sqn = hf->getLedgerSqn();
results->data[0] = WASM_I32_VAL(sqn);
return nullptr;
RET(setData(
rt,
params->data[0].of.i32,
params->data[1].of.i32,
reinterpret_cast<uint8_t const*>(&sqn),
static_cast<int32_t>(sizeof(sqn))));
}
wasm_trap_t*
@@ -44,65 +128,57 @@ getParentLedgerTime_wrap(
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
int32_t const ltime = hf->getParentLedgerTime();
results->data[0] = WASM_I32_VAL(ltime);
return nullptr;
RET(setData(
rt,
params->data[0].of.i32,
params->data[1].of.i32,
reinterpret_cast<uint8_t const*>(&ltime),
static_cast<int32_t>(sizeof(ltime))));
}
static Expected<Bytes, std::string>
getParameterData(wasm_val_vec_t const* params, size_t index)
wasm_trap_t*
getParentLedgerHash_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
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;
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
Hash const hash = hf->getParentLedgerHash();
RET(setData(
rt,
params->data[0].of.i32,
params->data[1].of.i32,
hash.data(),
static_cast<int32_t>(hash.size())));
}
static Expected<std::string, wasm_trap_t*>
getFieldName(wasm_val_vec_t const* params, size_t index)
wasm_trap_t*
cacheLedgerObj_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto const dataRes = getParameterData(params, index);
if (dataRes)
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!r)
{
return std::string(dataRes->begin(), dataRes->end());
RET(r.error());
}
else
if (r->size() < uint256::bytes)
{
auto& vm = WasmEngine::instance();
return Unexpected<wasm_trap_t*>(
reinterpret_cast<wasm_trap_t*>(vm.newTrap(dataRes.error())));
RET(HF_ERR_INVALID_PARAMS);
}
}
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;
uint256 const key(uint256::fromVoid(r->data()));
int32_t const idx =
hf->cacheLedgerObj(keylet::unchecked(key), params->data[2].of.i32);
RET(idx);
}
wasm_trap_t*
@@ -111,224 +187,300 @@ getTxField_wrap(
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto fname = getFieldName(params, 0);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto const& m = SField::getKnownCodeToField();
auto const it = m.find(params->data[0].of.i32);
if (it == m.end())
{
RET(HF_ERR_FIELD_NOT_FOUND);
}
auto const& fname(*it->second);
auto fieldData = hf->getTxField(fname.value());
auto fieldData = hf->getTxField(fname);
if (!fieldData)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap("Field not found"));
{
RET(fieldData.error());
}
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;
RET(setData(
rt,
params->data[1].of.i32,
params->data[2].of.i32,
fieldData->data(),
fieldData->size()));
}
wasm_trap_t*
getLedgerEntryField_wrap(
getCurrentLedgerObjField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
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 const& m = SField::getKnownCodeToField();
auto const it = m.find(params->data[0].of.i32);
if (it == m.end())
{
RET(HF_ERR_FIELD_NOT_FOUND);
}
auto const& fname(*it->second);
auto fname = getFieldName(params, 3);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto fieldData =
hf->getLedgerEntryField(type, lkData.value(), fname.value());
auto fieldData = hf->getCurrentLedgerObjField(fname);
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());
{
RET(fieldData.error());
}
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size());
return nullptr;
RET(setData(
rt,
params->data[1].of.i32,
params->data[2].of.i32,
fieldData->data(),
fieldData->size()));
}
wasm_trap_t*
getCurrentLedgerEntryField_wrap(
getLedgerObjField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto fname = getFieldName(params, 0);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto const& m = SField::getKnownCodeToField();
auto const it = m.find(params->data[1].of.i32);
if (it == m.end())
{
RET(HF_ERR_FIELD_NOT_FOUND);
}
auto const& fname(*it->second);
auto fieldData = hf->getCurrentLedgerEntryField(fname.value());
auto fieldData = hf->getLedgerObjField(params->data[0].of.i32, fname);
if (!fieldData)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
{
RET(fieldData.error());
}
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;
RET(setData(
rt,
params->data[2].of.i32,
params->data[3].of.i32,
fieldData->data(),
fieldData->size()));
}
wasm_trap_t*
getNFT_wrap(void* env, wasm_val_vec_t const* 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(
getTxNestedField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto account = getFieldName(params, 0);
if (!account)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!r)
{
RET(r.error());
}
auto keylet = hf->accountKeylet(account.value());
if (!keylet)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto fieldData = hf->getTxNestedField(makeSlice(r.value()));
if (!fieldData)
{
RET(fieldData.error());
}
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;
RET(setData(
rt,
params->data[2].of.i32,
params->data[3].of.i32,
fieldData->data(),
fieldData->size()));
}
wasm_trap_t*
credentialKeylet_wrap(
getCurrentLedgerObjNestedField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto subject = getFieldName(params, 0);
if (!subject)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!r)
{
RET(r.error());
}
auto issuer = getFieldName(params, 2);
if (!issuer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto fieldData = hf->getCurrentLedgerObjNestedField(makeSlice(r.value()));
if (!fieldData)
{
RET(fieldData.error());
}
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;
RET(setData(
rt,
params->data[2].of.i32,
params->data[3].of.i32,
fieldData->data(),
fieldData->size()));
}
wasm_trap_t*
escrowKeylet_wrap(
getLedgerObjNestedField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto account = getFieldName(params, 0);
if (!account)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto const r = getData(rt, params->data[1].of.i32, params->data[2].of.i32);
if (!r)
{
RET(r.error());
}
int32_t const sequence = params->data[2].of.i32;
auto fieldData = hf->getLedgerObjNestedField(
params->data[0].of.i32, makeSlice(r.value()));
if (!fieldData)
{
RET(fieldData.error());
}
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;
RET(setData(
rt,
params->data[3].of.i32,
params->data[4].of.i32,
fieldData->data(),
fieldData->size()));
}
wasm_trap_t*
oracleKeylet_wrap(
getTxArrayLen_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
// auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto account = getFieldName(params, 0);
if (!account)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto const& m = SField::getKnownCodeToField();
auto const it = m.find(params->data[0].of.i32);
if (it == m.end())
{
RET(HF_ERR_FIELD_NOT_FOUND);
}
auto const& fname(*it->second);
auto documentId = params->data[2].of.i32;
int32_t sz = hf->getTxArrayLen(fname);
RET(sz);
}
auto keylet = hf->escrowKeylet(account.value(), documentId);
if (!keylet)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
wasm_trap_t*
getCurrentLedgerObjArrayLen_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
// auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto pointer = setData(keylet.value());
if (!pointer)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto const& m = SField::getKnownCodeToField();
auto const it = m.find(params->data[0].of.i32);
if (it == m.end())
{
RET(HF_ERR_FIELD_NOT_FOUND);
}
auto const& fname(*it->second);
results->data[0] = WASM_I32_VAL(pointer.value());
// out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size());
return nullptr;
int32_t sz = hf->getCurrentLedgerObjArrayLen(fname);
RET(sz);
}
wasm_trap_t*
getLedgerObjArrayLen_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
// auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const& m = SField::getKnownCodeToField();
auto const it = m.find(params->data[1].of.i32);
if (it == m.end())
{
RET(HF_ERR_FIELD_NOT_FOUND);
}
auto const& fname(*it->second);
int32_t sz = hf->getLedgerObjArrayLen(params->data[0].of.i32, fname);
RET(sz);
}
wasm_trap_t*
getTxNestedArrayLen_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!r)
{
RET(r.error());
}
int32_t sz = hf->getTxNestedArrayLen(makeSlice(r.value()));
RET(sz);
}
wasm_trap_t*
getCurrentLedgerObjNestedArrayLen_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!r)
{
RET(r.error());
}
int32_t sz = hf->getCurrentLedgerObjNestedArrayLen(makeSlice(r.value()));
RET(sz);
}
wasm_trap_t*
getLedgerObjNestedArrayLen_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const r = getData(rt, params->data[1].of.i32, params->data[2].of.i32);
if (!r)
{
RET(r.error());
}
int32_t sz = hf->getLedgerObjNestedArrayLen(
params->data[0].of.i32, makeSlice(r.value()));
RET(sz);
}
wasm_trap_t*
@@ -337,17 +489,21 @@ updateData_wrap(
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto fname = getParameterData(params, 0);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
if (params->data[1].of.i32 > maxWasmDataLength)
{
RET(HF_ERR_DATA_FIELD_TOO_LARGE)
}
if (!hf->updateData(fname.value()))
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!r)
{
RET(r.error());
}
return nullptr;
RET(hf->updateData(r.value()));
}
wasm_trap_t*
@@ -356,37 +512,238 @@ computeSha512HalfHash_wrap(
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto fname = getParameterData(params, 0);
if (!fname)
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!r)
{
RET(r.error());
}
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;
auto const hash = hf->computeSha512HalfHash(r.value());
RET(setData(
rt,
params->data[2].of.i32,
params->data[3].of.i32,
hash.data(),
hash.size()));
}
wasm_trap_t*
print_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
accountKeylet_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto& vm = WasmEngine::instance();
// auto* hf = reinterpret_cast<HostFunctions*>(env);
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
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;
auto const acc =
getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!acc)
{
RET(acc.error());
}
auto const k = hf->accountKeylet(acc.value());
if (!k)
{
RET(k.error());
}
RET(setData(
rt,
params->data[2].of.i32,
params->data[3].of.i32,
k->data(),
k->size()));
}
wasm_trap_t*
credentialKeylet_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const subject =
getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!subject)
{
RET(subject.error());
}
auto const issuer =
getDataAccount(rt, params->data[2].of.i32, params->data[3].of.i32);
if (!issuer)
{
RET(issuer.error());
}
auto const credType =
getData(rt, params->data[4].of.i32, params->data[5].of.i32);
if (!credType)
{
RET(credType.error());
}
auto const k =
hf->credentialKeylet(subject.value(), issuer.value(), credType.value());
if (!k)
{
RET(k.error());
}
RET(setData(
rt,
params->data[6].of.i32,
params->data[7].of.i32,
k->data(),
k->size()));
}
wasm_trap_t*
escrowKeylet_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const acc =
getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!acc)
{
RET(acc.error());
}
auto const k = hf->escrowKeylet(acc.value(), params->data[2].of.i32);
if (!k)
{
RET(k.error());
}
RET(setData(
rt,
params->data[3].of.i32,
params->data[4].of.i32,
k->data(),
k->size()));
}
wasm_trap_t*
oracleKeylet_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const acc =
getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!acc)
{
RET(acc.error());
}
auto const k = hf->oracleKeylet(acc.value(), params->data[2].of.i32);
if (!k)
{
RET(k.error());
}
RET(setData(
rt,
params->data[3].of.i32,
params->data[4].of.i32,
k->data(),
k->size()));
}
wasm_trap_t*
getNFT_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const acc =
getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!acc)
{
RET(acc.error());
}
auto const nftRaw =
getData(rt, params->data[2].of.i32, params->data[3].of.i32);
if (!nftRaw)
{
RET(nftRaw.error());
}
if (nftRaw->size() < uint256::bytes * 2)
{
RET(HF_ERR_INVALID_PARAMS);
}
uint256 const ntfId(uint256::fromVoid(nftRaw->data()));
auto const nftURI = hf->getNFT(acc.value(), ntfId);
if (!nftURI)
{
RET(nftURI.error());
}
RET(setData(
rt,
params->data[4].of.i32,
params->data[5].of.i32,
nftURI->data(),
nftURI->size()));
}
wasm_trap_t*
trace_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const msg =
getDataString(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!msg)
{
RET(msg.error());
}
auto const data =
getData(rt, params->data[2].of.i32, params->data[3].of.i32);
if (!data)
{
RET(data.error());
}
auto const e = hf->trace(msg.value(), data.value(), params->data[4].of.i32);
RET(e);
}
wasm_trap_t*
traceNum_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
{
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* rt = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
auto const msg =
getDataString(rt, params->data[0].of.i32, params->data[1].of.i32);
if (!msg)
{
RET(msg.error());
}
auto const e = hf->traceNum(msg.value(), params->data[2].of.i64);
RET(e);
}
} // namespace ripple

View File

@@ -23,91 +23,200 @@
namespace ripple {
using getLedgerSqn_proto = int32_t();
using getLedgerSqnOld_proto = int32_t();
wasm_trap_t*
getLedgerSqnOld_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results);
using getLedgerSqn_proto = int32_t(uint8_t*, 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();
using getParentLedgerTime_proto = int32_t(uint8_t*, 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);
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 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 getLedgerEntryField_proto =
uint32_t*(int32_t, uint8_t const*, int32_t, char const*, int32_t);
using getCurrentLedgerObjField_proto = int32_t(int32_t, uint8_t*, int32_t);
wasm_trap_t*
getLedgerEntryField_wrap(
getCurrentLedgerObjField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results);
using getCurrentLedgerEntryField_proto = uint32_t*(char const*, int32_t);
using getLedgerObjField_proto = int32_t(int32_t, int32_t, uint8_t*, int32_t);
wasm_trap_t*
getCurrentLedgerEntryField_wrap(
getLedgerObjField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results);
using getNFT_proto = uint32_t*(char const*, int32_t, char const*, int32_t);
using getTxNestedField_proto =
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 accountKeylet_proto = uint32_t*(char const*, int32_t);
wasm_trap_t*
accountKeylet_wrap(
getTxNestedField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results);
using credentialKeylet_proto =
uint32_t*(char const*, int32_t, char const*, int32_t, char const*, int32_t);
using getCurrentLedgerObjNestedField_proto =
int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t*
credentialKeylet_wrap(
getCurrentLedgerObjNestedField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results);
using escrowKeylet_proto = uint32_t*(char const*, int32_t, int32_t);
using getLedgerObjNestedField_proto =
int32_t(int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t*
escrowKeylet_wrap(
getLedgerObjNestedField_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results);
using oracleKeylet_proto = uint32_t*(char const*, int32_t, int32_t);
using getTxArrayLen_proto = int32_t(int32_t);
wasm_trap_t*
oracleKeylet_wrap(
getTxArrayLen_wrap(
void* env,
wasm_val_vec_t const* params,
wasm_val_vec_t* results);
using updateData_proto = void(uint8_t const*, int32_t);
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 computeSha512HalfHash_proto = uint32_t*(uint8_t const*, int32_t);
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 print_proto = void(char const*, int32_t);
using accountKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t*
print_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
accountKeylet_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 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 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 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 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);
} // namespace ripple

View File

@@ -0,0 +1,269 @@
//------------------------------------------------------------------------------
/*
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 <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<bool> EscrowResult;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum WasmTypes { WT_I32, WT_I64, WT_F32, WT_F64, 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;
};
#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 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 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)...);
}
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

View File

@@ -18,8 +18,10 @@
//==============================================================================
#include <xrpld/app/misc/WamrVM.h>
#include <xrpld/app/misc/WasmHostFunc.h>
#include <xrpld/app/misc/WasmHostFuncWrapper.h>
#include <xrpl/basics/Log.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/LedgerFormats.h>
@@ -31,32 +33,88 @@ Expected<EscrowResult, TER>
runEscrowWasm(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params,
HostFunctions* hfs,
uint64_t gasLimit)
int64_t gasLimit,
beast::Journal j)
{
// create VM and set cost limit
auto& vm = WasmEngine::instance();
vm.initGas(gasLimit);
vm.initMaxPages(MAX_PAGES);
std::vector<WasmImportFunc> imports;
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)
if (hfs)
{
// TODO: remove after escrow_test wasm module will be updated
WASM_IMPORT_FUNC2(imports, getLedgerSqnOld, "getLedgerSqn", hfs);
std::int64_t const sgas = gasLimit; // vm.getGas();
auto ret = vm.run(wasmCode, funcName, imports, {}, hfs->getJournal());
WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", hfs);
WASM_IMPORT_FUNC2(
imports, getParentLedgerTime, "get_parent_ledger_time", hfs);
WASM_IMPORT_FUNC2(
imports, getParentLedgerHash, "get_parent_ledger_hash", hfs);
WASM_IMPORT_FUNC2(imports, cacheLedgerObj, "cache_ledger_obj", hfs);
WASM_IMPORT_FUNC2(imports, getTxField, "get_tx_field", hfs);
WASM_IMPORT_FUNC2(
imports,
getCurrentLedgerObjField,
"get_current_ledger_obj_field",
hfs);
WASM_IMPORT_FUNC2(
imports, getLedgerObjField, "get_ledger_obj_field", hfs);
WASM_IMPORT_FUNC2(
imports, getTxNestedField, "get_tx_nested_field", hfs);
WASM_IMPORT_FUNC2(
imports,
getCurrentLedgerObjNestedField,
"get_current_ledger_obj_nested_field",
hfs);
WASM_IMPORT_FUNC2(
imports,
getLedgerObjNestedField,
"get_ledger_obj_nested_field",
hfs);
WASM_IMPORT_FUNC2(imports, getTxArrayLen, "get_tx_array_len", hfs);
WASM_IMPORT_FUNC2(
imports,
getCurrentLedgerObjArrayLen,
"get_current_ledger_obj_array_len",
hfs);
WASM_IMPORT_FUNC2(
imports, getLedgerObjArrayLen, "get_ledger_obj_array_len", hfs);
WASM_IMPORT_FUNC2(
imports, getTxNestedArrayLen, "get_tx_nested_array_len", hfs);
WASM_IMPORT_FUNC2(
imports,
getCurrentLedgerObjNestedArrayLen,
"get_current_ledger_obj_nested_array_len",
hfs);
WASM_IMPORT_FUNC2(
imports,
getLedgerObjNestedArrayLen,
"get_ledger_obj_nested_array_len",
hfs);
WASM_IMPORT_FUNC2(imports, updateData, "update_data", hfs);
WASM_IMPORT_FUNC2(
imports, computeSha512HalfHash, "compute_sha512_half", hfs);
WASM_IMPORT_FUNC2(imports, accountKeylet, "account_keylet", hfs);
WASM_IMPORT_FUNC2(imports, credentialKeylet, "credential_keylet", hfs);
WASM_IMPORT_FUNC2(imports, escrowKeylet, "escrow_keylet", hfs);
WASM_IMPORT_FUNC2(imports, oracleKeylet, "oracle_keylet", hfs);
WASM_IMPORT_FUNC2(imports, getNFT, "get_NFT", hfs);
WASM_IMPORT_FUNC(imports, trace, hfs);
WASM_IMPORT_FUNC2(imports, traceNum, "trace_num", hfs);
}
auto ret = vm.run(
wasmCode,
funcName,
params,
imports,
hfs,
gasLimit,
hfs ? hfs->getJournal() : j);
// std::cout << "runEscrowWasm, mod size: " << wasmCode.size()
// << ", gasLimit: " << gasLimit << ", funcName: " << funcName;
@@ -66,12 +124,10 @@ runEscrowWasm(
// std::cout << ", error: " << ret.error() << std::endl;
return Unexpected<TER>(ret.error());
}
std::int64_t const egas = vm.getGas();
std::uint64_t const spent = static_cast<std::uint64_t>(sgas - egas);
// std::cout << ", ret: " << ret.value() << ", gas spent: " << spent
// std::cout << ", ret: " << ret->result << ", gas spent: " << ret->cost
// << std::endl;
return EscrowResult{static_cast<bool>(ret.value()), spent};
return EscrowResult{ret->result > 0, ret->cost};
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -87,21 +143,17 @@ WasmEngine::instance()
return e;
}
Expected<int32_t, TER>
Expected<WasmResult<int32_t>, TER>
WasmEngine::run(
wbytes const& wasmCode,
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmImportFunc> const& imports,
std::vector<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports,
HostFunctions* hfs,
int64_t gasLimit,
beast::Journal j)
{
return impl->run(wasmCode, funcName, imports, params, j);
}
std::int64_t
WasmEngine::initGas(std::int64_t def)
{
return impl->initGas(def);
return impl->run(wasmCode, funcName, params, imports, hfs, gasLimit, j);
}
std::int32_t
@@ -110,35 +162,16 @@ 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);
}
beast::Journal
WasmEngine::getJournal() const
{
return impl->getJournal();
}
} // namespace ripple

View File

@@ -18,22 +18,10 @@
//==============================================================================
#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 <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 <xrpld/app/misc/WasmHostFunc.h>
#include <string_view>
namespace bft = boost::function_types;
namespace ripple {
static std::string_view const W_ENV = "env";
@@ -46,273 +34,8 @@ 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";
using wbytes = std::vector<std::uint8_t>;
struct wmem
{
std::uint8_t* p = nullptr;
std::size_t s = 0;
};
uint32_t const MAX_PAGES = 128; // 8MB = 64KB*128
typedef std::vector<uint8_t> Bytes;
typedef ripple::uint256 Hash;
template <typename T>
struct WasmResult
{
T result;
uint64_t cost;
};
typedef WasmResult<bool> EscrowResult;
struct HostFunctions
{
virtual beast::Journal
getJournal()
{
return beast::Journal{beast::Journal::getNullSink()};
}
virtual int32_t
getLedgerSqn()
{
return 1;
}
virtual int32_t
getParentLedgerTime()
{
return 1;
}
virtual std::optional<Bytes>
getTxField(std::string const& fname)
{
return Bytes{};
}
virtual std::optional<Bytes>
getLedgerEntryField(
int32_t type,
Bytes const& kdata,
std::string const& fname)
{
return Bytes{};
}
virtual std::optional<Bytes>
getCurrentLedgerEntryField(std::string const& fname)
{
return Bytes{};
}
virtual std::optional<Bytes>
getNFT(std::string const& account, std::string const& nftId)
{
return Bytes{};
}
virtual bool
updateData(Bytes const& data)
{
return true;
}
virtual Hash
computeSha512HalfHash(Bytes const& data)
{
return Hash{};
}
virtual std::optional<Bytes>
accountKeylet(std::string const& account)
{
return Bytes{};
}
virtual std::optional<Bytes>
credentialKeylet(
std::string const& subject,
std::string const& issuer,
std::string const& credentialType)
{
return Bytes{};
}
virtual std::optional<Bytes>
escrowKeylet(std::string const& account, std::uint32_t const& seq)
{
return Bytes{};
}
virtual std::optional<Bytes>
oracleKeylet(std::string const& account, std::uint32_t const& docId)
{
return Bytes{};
}
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
{
@@ -333,34 +56,24 @@ public:
static WasmEngine&
instance();
Expected<int32_t, TER>
run(wbytes const& wasmCode,
Expected<WasmResult<int32_t>, TER>
run(Bytes const& wasmCode,
std::string_view funcName = {},
std::vector<WasmImportFunc> const& imports = {},
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()});
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);
// Host functions helper functionality
void*
newTrap(std::string_view msg = {});
beast::Journal
getJournal() const;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -369,7 +82,9 @@ Expected<EscrowResult, TER>
runEscrowWasm(
Bytes const& wasmCode,
std::string_view funcName,
HostFunctions* hfs,
uint64_t gasLimit);
std::vector<WasmParam> const& params = {},
HostFunctions* hfs = nullptr,
int64_t gasLimit = -1,
beast::Journal j = beast::Journal(beast::Journal::getNullSink()));
} // namespace ripple

View File

@@ -629,7 +629,8 @@ EscrowFinish::doApply()
return tecINTERNAL;
}
std::uint32_t allowance = ctx_.tx[sfComputationAllowance];
auto re = runEscrowWasm(wasm, funcName, &ledgerDataProvider, allowance);
auto re =
runEscrowWasm(wasm, funcName, {}, &ledgerDataProvider, allowance);
JLOG(j_.trace()) << "Escrow WASM ran";
if (re.has_value())
{