mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
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:
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
wasm_trap_delete(trap);
|
||||
j.debug() << "WAMR trap: " << error_message.data;
|
||||
wasm_byte_vec_delete(&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);
|
||||
}
|
||||
|
||||
#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)
|
||||
[[maybe_unused]] int const m = addModule(wasmCode, true, gas, imports);
|
||||
}
|
||||
|
||||
if (!module || !module->mod_inst)
|
||||
{
|
||||
print_wasm_error("no instance to run", nullptr, j_);
|
||||
return Unexpected<TER>(tecFAILED_PROCESSING);
|
||||
}
|
||||
|
||||
if (!module)
|
||||
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)
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
std::int64_t
|
||||
WamrEngine::initGas(std::int64_t def)
|
||||
{
|
||||
defGas = def;
|
||||
return def;
|
||||
assert(res.r.data[0].kind == WASM_I32);
|
||||
|
||||
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
|
||||
|
||||
@@ -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); }
|
||||
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&
|
||||
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;}
|
||||
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
|
||||
|
||||
224
src/xrpld/app/misc/WasmHostFunc.h
Normal file
224
src/xrpld/app/misc/WasmHostFunc.h
Normal 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
|
||||
@@ -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
|
||||
Hash
|
||||
WasmHostFunctionsImpl::getParentLedgerHash()
|
||||
{
|
||||
return ctx.view().info().parentHash;
|
||||
}
|
||||
|
||||
std::optional<Bytes>
|
||||
WasmHostFunctionsImpl::getTxField(std::string const& fname)
|
||||
int32_t
|
||||
WasmHostFunctionsImpl::cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx)
|
||||
{
|
||||
auto js = ctx.tx.getJson(JsonOptions::none);
|
||||
if (js.isMember(fname))
|
||||
if (cacheIdx < 0 || cacheIdx > MAX_CACHE)
|
||||
return HF_ERR_SLOT_OUT_RANGE;
|
||||
|
||||
if (!cacheIdx)
|
||||
{
|
||||
auto s = js.get(fname, Json::Value::null).asString();
|
||||
return Bytes{s.begin(), s.end()};
|
||||
for (cacheIdx = 0; cacheIdx < MAX_CACHE; ++cacheIdx)
|
||||
if (!cache[cacheIdx])
|
||||
break;
|
||||
}
|
||||
else
|
||||
return std::nullopt;
|
||||
--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;
|
||||
}
|
||||
|
||||
std::optional<Bytes>
|
||||
WasmHostFunctionsImpl::getLedgerEntryField(
|
||||
int32_t type,
|
||||
Bytes const& kdata,
|
||||
std::string const& fname)
|
||||
Bytes
|
||||
getAnyFieldData(STBase const& obj)
|
||||
{
|
||||
auto kl = [&]() -> std::optional<ripple::Keylet> {
|
||||
if (type == ltACCOUNT_ROOT)
|
||||
// auto const& fname = obj.getFName();
|
||||
if (STI_ACCOUNT == obj.getSType())
|
||||
{
|
||||
std::string s(kdata.begin(), kdata.end());
|
||||
auto const account = parseBase58<AccountID>(s);
|
||||
if (account)
|
||||
{
|
||||
return keylet::account(account.value());
|
||||
auto const& super(static_cast<STAccount const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}();
|
||||
|
||||
if (!kl || !ctx.view().exists(kl.value()))
|
||||
return std::nullopt;
|
||||
|
||||
auto js = ctx.view().read(kl.value())->getJson(JsonOptions::none);
|
||||
if (js.isMember(fname))
|
||||
else if (STI_AMOUNT == obj.getSType())
|
||||
{
|
||||
auto s = js.get(fname, Json::Value::null).asString();
|
||||
return Bytes{s.begin(), s.end()};
|
||||
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
|
||||
return std::nullopt;
|
||||
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};
|
||||
}
|
||||
|
||||
std::optional<Bytes>
|
||||
WasmHostFunctionsImpl::getCurrentLedgerEntryField(std::string const& fname)
|
||||
{
|
||||
if (!ctx.view().exists(leKey))
|
||||
return std::nullopt;
|
||||
Serializer msg;
|
||||
obj.add(msg);
|
||||
|
||||
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;
|
||||
return msg.getData();
|
||||
}
|
||||
|
||||
std::optional<Bytes>
|
||||
WasmHostFunctionsImpl::getNFT(
|
||||
std::string const& account,
|
||||
std::string const& nftId)
|
||||
Expected<Bytes, int32_t>
|
||||
WasmHostFunctionsImpl::getTxField(SField const& fname)
|
||||
{
|
||||
auto const accountId = parseBase58<AccountID>(account);
|
||||
if (!accountId || accountId->isZero())
|
||||
{
|
||||
return std::nullopt;
|
||||
auto const* field = ctx.tx.peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
uint256 nftHash;
|
||||
if (!nftHash.parseHex(nftId))
|
||||
Expected<Bytes, int32_t>
|
||||
WasmHostFunctionsImpl::getCurrentLedgerObjField(SField const& fname)
|
||||
{
|
||||
return std::nullopt;
|
||||
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);
|
||||
}
|
||||
|
||||
auto jv = nft::findToken(ctx.view(), accountId.value(), nftHash);
|
||||
if (!jv)
|
||||
Expected<Bytes, int32_t>
|
||||
WasmHostFunctionsImpl::getLedgerObjField(int32_t cacheIdx, SField const& fname)
|
||||
{
|
||||
return std::nullopt;
|
||||
--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);
|
||||
}
|
||||
|
||||
Slice const s = (*jv)[sfURI];
|
||||
return Bytes{s.begin(), s.end()};
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
auto const it = m.find(c);
|
||||
if (it == m.end())
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
auto const& fname(*it->second);
|
||||
|
||||
field = o->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
if (!field || (STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
WasmHostFunctionsImpl::getTxNestedField(Slice const& locator)
|
||||
{
|
||||
auto const r = locateField(&ctx.tx, locator);
|
||||
if (!r.has_value())
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(Slice const& locator)
|
||||
{
|
||||
auto const sle = ctx.view().read(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const r = locateField(sle.get(), locator);
|
||||
if (!r.has_value())
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
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;
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::account(account);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
auto keylet = keylet::account(*accountId).key;
|
||||
if (!keylet)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return Bytes{keylet.begin(), keylet.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 keylet =
|
||||
keylet::credential(subject, issuer, makeSlice(credentialType));
|
||||
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
auto const issuerId = parseBase58<AccountID>(issuer);
|
||||
if (!issuerId || issuerId->isZero())
|
||||
Expected<Bytes, int32_t>
|
||||
WasmHostFunctionsImpl::escrowKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
return std::nullopt;
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::escrow(account, seq);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
auto keylet =
|
||||
keylet::credential(*subjectId, *issuerId, makeSlice(credentialType))
|
||||
.key;
|
||||
if (!keylet)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return Bytes{keylet.begin(), keylet.end()};
|
||||
}
|
||||
|
||||
std::optional<Bytes>
|
||||
WasmHostFunctionsImpl::escrowKeylet(
|
||||
std::string const& account,
|
||||
std::uint32_t const& 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()};
|
||||
}
|
||||
|
||||
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;
|
||||
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()};
|
||||
}
|
||||
|
||||
auto keylet = keylet::oracle(*accountId, documentId).key;
|
||||
if (!keylet)
|
||||
Expected<Bytes, int32_t>
|
||||
WasmHostFunctionsImpl::getNFT(AccountID const& account, uint256 const& nftId)
|
||||
{
|
||||
return std::nullopt;
|
||||
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());
|
||||
}
|
||||
|
||||
return Bytes{keylet.begin(), keylet.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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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*>(<ime),
|
||||
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());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& vm = WasmEngine::instance();
|
||||
return Unexpected<wasm_trap_t*>(
|
||||
reinterpret_cast<wasm_trap_t*>(vm.newTrap(dataRes.error())));
|
||||
}
|
||||
RET(r.error());
|
||||
}
|
||||
|
||||
static Expected<int32_t, std::string>
|
||||
setData(Bytes const& data)
|
||||
if (r->size() < uint256::bytes)
|
||||
{
|
||||
auto& vm = WasmEngine::instance();
|
||||
auto mem = vm.getMem();
|
||||
if (!mem.s)
|
||||
return Unexpected<std::string>("No memory exported");
|
||||
RET(HF_ERR_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
auto pointer = setData(fieldData.value());
|
||||
if (!pointer)
|
||||
return reinterpret_cast<wasm_trap_t*>(vm.newTrap());
|
||||
|
||||
results->data[0] = WASM_I32_VAL(pointer.value());
|
||||
// out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wasm_trap_t*
|
||||
getNFT_wrap(void* env, 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());
|
||||
RET(fieldData.error());
|
||||
}
|
||||
|
||||
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;
|
||||
RET(setData(
|
||||
rt,
|
||||
params->data[2].of.i32,
|
||||
params->data[3].of.i32,
|
||||
fieldData->data(),
|
||||
fieldData->size()));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
269
src/xrpld/app/misc/WasmParamsHelper.h
Normal file
269
src/xrpld/app/misc/WasmParamsHelper.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user