mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Simplify host function boilerplate (#5534)
* enum for HF errors * switch getData functions to be templates * getData<SField> working * Slice -> Bytes in host functions * RET -> helper function instead of macro * get template function working * more organization/cleanup * fix failures * more cleanup * Bytes -> Slice * SFieldParam macro -> type alias * fix return type * fix bugs * replace std::make_index_sequence * remove `failed` from output * remove complex function * more uniformity * respond to comments * enum class HostFunctionError * rename variable * respond to comments * remove templating * [WIP] basic getData tests * weird linker error * fix issue
This commit is contained in:
@@ -7480,7 +7480,7 @@ private:
|
||||
using namespace test::jtx;
|
||||
|
||||
auto const testCase = [&](std::string suffix, FeatureBitset features) {
|
||||
testcase("Failed pseudo-account allocation " + suffix);
|
||||
testcase("Pseudo-account allocation failure " + suffix);
|
||||
std::string logs;
|
||||
Env env{*this, features, std::make_unique<CaptureLogs>(&logs)};
|
||||
env.fund(XRP(30'000), gw, alice);
|
||||
|
||||
797
src/test/app/TestHostFunctions.h
Normal file
797
src/test/app/TestHostFunctions.h
Normal file
@@ -0,0 +1,797 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/app/wasm_fixtures/fixtures.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpld/app/misc/WasmHostFunc.h>
|
||||
#include <xrpld/app/misc/WasmVM.h>
|
||||
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
||||
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||
|
||||
#include <wasm_c_api.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct TestHostFunctions : public HostFunctions
|
||||
{
|
||||
test::jtx::Env& env_;
|
||||
AccountID accountID_;
|
||||
Bytes data_;
|
||||
int clock_drift_ = 0;
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
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;
|
||||
}
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getLedgerSqn() override
|
||||
{
|
||||
return static_cast<std::uint32_t>(env_.current()->seq());
|
||||
}
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getParentLedgerTime() override
|
||||
{
|
||||
return env_.current()->parentCloseTime().time_since_epoch().count() +
|
||||
clock_drift_;
|
||||
}
|
||||
|
||||
Expected<Hash, HostFunctionError>
|
||||
getParentLedgerHash() override
|
||||
{
|
||||
return env_.current()->info().parentHash;
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
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)
|
||||
{
|
||||
auto const x = getLedgerSqn();
|
||||
if (!x)
|
||||
return Unexpected(x.error());
|
||||
std::uint32_t const data = x.value();
|
||||
auto const* b = reinterpret_cast<uint8_t const*>(&data);
|
||||
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
|
||||
return Bytes{b, e};
|
||||
}
|
||||
return Bytes();
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
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(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
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, HostFunctionError>
|
||||
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, HostFunctionError>
|
||||
getCurrentLedgerObjNestedField(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, HostFunctionError>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, 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<int32_t, HostFunctionError>
|
||||
getTxArrayLen(SField const& fname) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getTxNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
updateData(Slice const& data) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Expected<Hash, HostFunctionError>
|
||||
computeSha512HalfHash(Slice const& data) override
|
||||
{
|
||||
return env_.current()->info().parentHash;
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
accountKeylet(AccountID const& account) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::account(account);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
credentialKeylet(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Slice const& credentialType) override
|
||||
{
|
||||
if (!subject || !issuer || credentialType.empty() ||
|
||||
credentialType.size() > maxCredentialTypeLength)
|
||||
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::credential(subject, issuer, credentialType);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::escrow(account, seq);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t documentId) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::oracle(account, documentId);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) override
|
||||
{
|
||||
if (!account || !nftId)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INVALID_PARAMS);
|
||||
}
|
||||
|
||||
std::string s = "https://ripple.com";
|
||||
return Bytes(s.begin(), s.end());
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
trace(std::string_view const& msg, Slice const& data, bool asHex) override
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
auto& j = std::cerr;
|
||||
#else
|
||||
auto j = getJournal().trace();
|
||||
#endif
|
||||
j << "WASM TRACE: " << msg;
|
||||
if (!asHex)
|
||||
j << std::string_view(
|
||||
reinterpret_cast<char const*>(data.data()), data.size());
|
||||
else
|
||||
{
|
||||
std::string hex;
|
||||
hex.reserve(data.size() * 2);
|
||||
boost::algorithm::hex(data.begin(), data.end(), hex.begin());
|
||||
j << hex;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
j << std::endl;
|
||||
#endif
|
||||
|
||||
return msg.size() + data.size() * (asHex ? 2 : 1);
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceNum(std::string_view const& msg, int64_t data) override
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
auto& j = std::cerr;
|
||||
#else
|
||||
auto j = getJournal().trace();
|
||||
#endif
|
||||
j << "WASM TRACE NUM: " << msg << data;
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
j << std::endl;
|
||||
#endif
|
||||
return msg.size() + sizeof(data);
|
||||
}
|
||||
};
|
||||
|
||||
struct TestHostFunctionsSink : public TestHostFunctions
|
||||
{
|
||||
test::StreamSink sink_;
|
||||
beast::Journal jlog_;
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
explicit TestHostFunctionsSink(test::jtx::Env& env, int cd = 0)
|
||||
: TestHostFunctions(env, cd)
|
||||
, sink_(beast::severities::kDebug)
|
||||
, jlog_(sink_)
|
||||
{
|
||||
}
|
||||
|
||||
test::StreamSink&
|
||||
getSink()
|
||||
{
|
||||
return sink_;
|
||||
}
|
||||
|
||||
beast::Journal
|
||||
getJournal() override
|
||||
{
|
||||
return jlog_;
|
||||
}
|
||||
};
|
||||
|
||||
struct PerfHostFunctions : public TestHostFunctions
|
||||
{
|
||||
Keylet leKey;
|
||||
static int constexpr MAX_CACHE = 256;
|
||||
std::array<std::shared_ptr<SLE const>, MAX_CACHE> cache;
|
||||
std::shared_ptr<STTx const> tx_;
|
||||
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
PerfHostFunctions(
|
||||
test::jtx::Env& env,
|
||||
Keylet const& k,
|
||||
std::shared_ptr<STTx const>&& tx)
|
||||
: TestHostFunctions(env), leKey(k), tx_(std::move(tx))
|
||||
{
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
cacheLedgerObj(uint256 const&, int32_t cacheIdx) override
|
||||
{
|
||||
static int32_t intIdx = 0;
|
||||
|
||||
if (cacheIdx < 0 || cacheIdx > MAX_CACHE)
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
|
||||
if (!cacheIdx)
|
||||
{
|
||||
for (cacheIdx = 0; cacheIdx < MAX_CACHE; ++cacheIdx)
|
||||
if (!cache[cacheIdx])
|
||||
break;
|
||||
if (cacheIdx >= MAX_CACHE)
|
||||
cacheIdx = intIdx++ % MAX_CACHE;
|
||||
}
|
||||
else
|
||||
--cacheIdx;
|
||||
|
||||
if (cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HostFunctionError::SLOTS_FULL);
|
||||
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
if (!cache[cacheIdx])
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
return cacheIdx + 1;
|
||||
}
|
||||
|
||||
static Bytes
|
||||
getAnyFieldData(STBase const& obj)
|
||||
{
|
||||
// auto const& fname = obj.getFName();
|
||||
auto const stype = obj.getSType();
|
||||
switch (stype)
|
||||
{
|
||||
case STI_UNKNOWN:
|
||||
case STI_NOTPRESENT:
|
||||
return {};
|
||||
break;
|
||||
case STI_ACCOUNT: {
|
||||
auto const& super(static_cast<STAccount const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_AMOUNT: {
|
||||
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};
|
||||
}
|
||||
break;
|
||||
case STI_VL: {
|
||||
auto const& super(static_cast<STBlob const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_UINT256: {
|
||||
auto const& super(static_cast<STBitString<256> const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_UINT32: {
|
||||
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};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Serializer msg;
|
||||
obj.add(msg);
|
||||
|
||||
return msg.getData();
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) override
|
||||
{
|
||||
auto const* field = tx_->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HostFunctionError::NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjField(SField const& fname) override
|
||||
{
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const* field = sle->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HostFunctionError::NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HostFunctionError::INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const* field = cache[cacheIdx]->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HostFunctionError::NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
static Expected<STBase const*, HostFunctionError>
|
||||
locateField(STObject const& obj, Slice const& loc)
|
||||
{
|
||||
if (loc.empty() || (loc.size() & 3)) // must be multiple of 4
|
||||
return Unexpected(HostFunctionError::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();
|
||||
|
||||
{
|
||||
int32_t const c = l[0];
|
||||
auto const it = m.find(c);
|
||||
if (it == m.end())
|
||||
return Unexpected(HostFunctionError::LOCATOR_MALFORMED);
|
||||
auto const& fname(*it->second);
|
||||
|
||||
field = obj.peekAtPField(fname);
|
||||
if (!field || (STI_NOTPRESENT == field->getSType()))
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
for (int i = 1; i < sz; ++i)
|
||||
{
|
||||
int32_t const c = l[i];
|
||||
|
||||
if (STI_ARRAY == field->getSType())
|
||||
{
|
||||
auto const* arr = static_cast<STArray const*>(field);
|
||||
if (c >= arr->size())
|
||||
return Unexpected(HostFunctionError::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(HostFunctionError::LOCATOR_MALFORMED);
|
||||
auto const& fname(*it->second);
|
||||
|
||||
field = o->peekAtPField(fname);
|
||||
}
|
||||
else // simple field must be the last one
|
||||
{
|
||||
return Unexpected(HostFunctionError::LOCATOR_MALFORMED);
|
||||
}
|
||||
|
||||
if (!field || (STI_NOTPRESENT == field->getSType()))
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxNestedField(Slice const& locator) override
|
||||
{
|
||||
// std::cout << tx_->getJson(JsonOptions::none).toStyledString() <<
|
||||
// std::endl;
|
||||
auto const r = locateField(*tx_, locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HostFunctionError::NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator) override
|
||||
{
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const r = locateField(*sle, locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HostFunctionError::NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HostFunctionError::INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const r = locateField(*cache[cacheIdx], locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HostFunctionError::NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getTxArrayLen(SField const& fname) override
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
|
||||
auto const* field = tx_->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) override
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const* field = sle->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HostFunctionError::INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const* field = cache[cacheIdx]->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getTxNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
auto const r = locateField(*tx_, locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
auto const* field = r.value();
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
auto const r = locateField(*sle, locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
auto const* field = r.value();
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HostFunctionError::INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const r = locateField(*cache[cacheIdx], locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
updateData(Slice const& data) override
|
||||
{
|
||||
ripple::detail::ApplyViewBase v(
|
||||
env_.app().openLedger().current().get(), tapNONE);
|
||||
|
||||
auto sle = v.peek(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
sle->setFieldVL(sfData, data);
|
||||
v.update(sle);
|
||||
|
||||
return data.size();
|
||||
}
|
||||
|
||||
Expected<Hash, HostFunctionError>
|
||||
computeSha512HalfHash(Slice const& data) override
|
||||
{
|
||||
auto const hash = sha512Half(data);
|
||||
return hash;
|
||||
}
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) override
|
||||
{
|
||||
if (!account || !nftId)
|
||||
{
|
||||
getJournal().trace() << "WAMR getNFT: Invalid account or NFT ID";
|
||||
return Unexpected(HostFunctionError::INVALID_PARAMS);
|
||||
}
|
||||
|
||||
auto obj = nft::findToken(*env_.current(), account, nftId);
|
||||
if (!obj)
|
||||
{
|
||||
getJournal().trace() << "WAMR getNFT: NFT not found";
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
}
|
||||
|
||||
auto ouri = obj->at(~sfURI);
|
||||
if (!ouri)
|
||||
return Bytes();
|
||||
|
||||
Slice const s = ouri->value();
|
||||
return Bytes(s.begin(), s.end());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -2638,7 +2638,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
testcase("failed pseudo-account allocation");
|
||||
testcase("Pseudo-account allocation failure");
|
||||
Env env{*this, supported_amendments() | featureSingleAssetVault};
|
||||
Account const owner{"owner"};
|
||||
Vault vault{env};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
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
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/app/TestHostFunctions.h>
|
||||
#include <test/app/wasm_fixtures/fixtures.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
@@ -34,6 +35,9 @@
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
bool
|
||||
testGetDataIncrement();
|
||||
|
||||
using Add_proto = int32_t(int32_t, int32_t);
|
||||
static wasm_trap_t*
|
||||
Add(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
|
||||
@@ -54,7 +58,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
get_ledger_sqn()
|
||||
{
|
||||
return (int32_t)env->current()->seq();
|
||||
@@ -67,893 +71,21 @@ 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->data[0] = WASM_I32_VAL(sqn.value());
|
||||
results->num_elems = 1;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct TestHostFunctions : public HostFunctions
|
||||
{
|
||||
test::jtx::Env& env_;
|
||||
AccountID accountID_;
|
||||
Bytes data_;
|
||||
int clock_drift_ = 0;
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
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_;
|
||||
}
|
||||
|
||||
Hash
|
||||
getParentLedgerHash() override
|
||||
{
|
||||
return env_.current()->info().parentHash;
|
||||
}
|
||||
|
||||
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>
|
||||
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);
|
||||
}
|
||||
|
||||
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>
|
||||
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>
|
||||
getCurrentLedgerObjNestedField(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>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, 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)]);
|
||||
}
|
||||
|
||||
int32_t
|
||||
getTxArrayLen(SField const& fname) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getTxNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
int32_t
|
||||
updateData(Bytes const& data) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Hash
|
||||
computeSha512HalfHash(Bytes const& data) override
|
||||
{
|
||||
return env_.current()->info().parentHash;
|
||||
}
|
||||
|
||||
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()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
credentialKeylet(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Bytes const& credentialType) override
|
||||
{
|
||||
if (!subject || !issuer || credentialType.empty())
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet =
|
||||
keylet::credential(subject, issuer, makeSlice(credentialType));
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::escrow(account, seq);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t documentId) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::oracle(account, documentId);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) override
|
||||
{
|
||||
if (!account || !nftId)
|
||||
{
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
std::string s = "https://ripple.com";
|
||||
return Bytes(s.begin(), s.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 TestHostFunctionsSink : public TestHostFunctions
|
||||
{
|
||||
test::StreamSink sink_;
|
||||
beast::Journal jlog_;
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
explicit TestHostFunctionsSink(test::jtx::Env& env, int cd = 0)
|
||||
: TestHostFunctions(env, cd)
|
||||
, sink_(beast::severities::kDebug)
|
||||
, jlog_(sink_)
|
||||
{
|
||||
}
|
||||
|
||||
test::StreamSink&
|
||||
getSink()
|
||||
{
|
||||
return sink_;
|
||||
}
|
||||
|
||||
beast::Journal
|
||||
getJournal() override
|
||||
{
|
||||
return jlog_;
|
||||
}
|
||||
};
|
||||
|
||||
struct PerfHostFunctions : public HostFunctions
|
||||
{
|
||||
test::jtx::Env& env_;
|
||||
|
||||
Keylet leKey;
|
||||
static int constexpr MAX_CACHE = 256;
|
||||
std::array<std::shared_ptr<SLE const>, MAX_CACHE> cache;
|
||||
std::shared_ptr<STTx const> tx_;
|
||||
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
PerfHostFunctions(
|
||||
test::jtx::Env& env,
|
||||
Keylet const& k,
|
||||
std::shared_ptr<STTx const>&& tx)
|
||||
: env_(env), leKey(k), tx_(std::move(tx))
|
||||
{
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
Hash
|
||||
getParentLedgerHash() override
|
||||
{
|
||||
return env_.current()->info().parentHash;
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
cacheLedgerObj(Keylet const&, int32_t cacheIdx) override
|
||||
{
|
||||
static int32_t intIdx = 0;
|
||||
|
||||
if (cacheIdx < 0 || cacheIdx > MAX_CACHE)
|
||||
return HF_ERR_SLOT_OUT_RANGE;
|
||||
|
||||
if (!cacheIdx)
|
||||
{
|
||||
for (cacheIdx = 0; cacheIdx < MAX_CACHE; ++cacheIdx)
|
||||
if (!cache[cacheIdx])
|
||||
break;
|
||||
if (cacheIdx >= MAX_CACHE)
|
||||
cacheIdx = intIdx++ % MAX_CACHE;
|
||||
}
|
||||
else
|
||||
--cacheIdx;
|
||||
|
||||
if (cacheIdx >= MAX_CACHE)
|
||||
return HF_ERR_SLOTS_FULL;
|
||||
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
return cache[cacheIdx] ? cacheIdx + 1 : HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
}
|
||||
|
||||
static Bytes
|
||||
getAnyFieldData(STBase const& obj)
|
||||
{
|
||||
// auto const& fname = obj.getFName();
|
||||
auto const stype = obj.getSType();
|
||||
switch (stype)
|
||||
{
|
||||
case STI_UNKNOWN:
|
||||
case STI_NOTPRESENT:
|
||||
return {};
|
||||
break;
|
||||
case STI_ACCOUNT: {
|
||||
auto const& super(static_cast<STAccount const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_AMOUNT: {
|
||||
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};
|
||||
}
|
||||
break;
|
||||
case STI_VL: {
|
||||
auto const& super(static_cast<STBlob const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_UINT256: {
|
||||
auto const& super(static_cast<STBitString<256> const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_UINT32: {
|
||||
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};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Serializer msg;
|
||||
obj.add(msg);
|
||||
|
||||
return msg.getData();
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getTxField(SField const& fname) override
|
||||
{
|
||||
auto const* field = tx_->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getCurrentLedgerObjField(SField const& fname) override
|
||||
{
|
||||
auto const sle = env_.le(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);
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HF_ERR_SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HF_ERR_INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const* field = cache[cacheIdx]->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
static Expected<STBase const*, int32_t>
|
||||
locateField(STObject const& obj, Slice const& loc)
|
||||
{
|
||||
if (loc.empty() || (loc.size() & 3)) // must be multiple of 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();
|
||||
|
||||
{
|
||||
int32_t const c = l[0];
|
||||
auto const it = m.find(c);
|
||||
if (it == m.end())
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
auto const& fname(*it->second);
|
||||
|
||||
field = obj.peekAtPField(fname);
|
||||
if (!field || (STI_NOTPRESENT == field->getSType()))
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
for (int i = 1; i < sz; ++i)
|
||||
{
|
||||
int32_t const c = l[i];
|
||||
|
||||
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_LOCATOR_MALFORMED);
|
||||
auto const& fname(*it->second);
|
||||
|
||||
field = o->peekAtPField(fname);
|
||||
}
|
||||
else // simple field must be the last one
|
||||
{
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
}
|
||||
|
||||
if (!field || (STI_NOTPRESENT == field->getSType()))
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getTxNestedField(Slice const& locator) override
|
||||
{
|
||||
// std::cout << tx_->getJson(JsonOptions::none).toStyledString() <<
|
||||
// std::endl;
|
||||
auto const r = locateField(*tx_, locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator) override
|
||||
{
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const r = locateField(*sle, locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HF_ERR_SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HF_ERR_INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const r = locateField(*cache[cacheIdx], locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
int32_t
|
||||
getTxArrayLen(SField const& fname) override
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
|
||||
auto const* field = 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
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) override
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
|
||||
auto const sle = env_.le(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
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
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 Unexpected(HF_ERR_INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
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
|
||||
getTxNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
auto const r = locateField(*tx_, locator);
|
||||
if (!r)
|
||||
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
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
auto const r = locateField(*sle, locator);
|
||||
if (!r)
|
||||
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
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return HF_ERR_SLOT_OUT_RANGE;
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HF_ERR_INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const r = locateField(*cache[cacheIdx], locator);
|
||||
if (!r)
|
||||
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
|
||||
updateData(Bytes const& data) override
|
||||
{
|
||||
ripple::detail::ApplyViewBase v(
|
||||
env_.app().openLedger().current().get(), tapNONE);
|
||||
|
||||
auto sle = v.peek(leKey);
|
||||
if (!sle)
|
||||
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
|
||||
sle->setFieldVL(sfData, data);
|
||||
v.update(sle);
|
||||
|
||||
return data.size();
|
||||
}
|
||||
|
||||
Hash
|
||||
computeSha512HalfHash(Bytes const& data) override
|
||||
{
|
||||
auto const hash = sha512Half(data);
|
||||
return hash;
|
||||
}
|
||||
|
||||
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()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
credentialKeylet(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Bytes const& credentialType) override
|
||||
{
|
||||
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()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::escrow(account, seq);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t documentId) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::oracle(account, documentId);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) override
|
||||
{
|
||||
if (!account || !nftId)
|
||||
{
|
||||
getJournal().trace() << "WAMR getNFT: Invalid account or NFT ID";
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
auto obj = nft::findToken(*env_.current(), account, nftId);
|
||||
if (!obj)
|
||||
{
|
||||
getJournal().trace() << "WAMR getNFT: NFT not found";
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
}
|
||||
|
||||
auto ouri = obj->at(~sfURI);
|
||||
if (!ouri)
|
||||
return Bytes();
|
||||
|
||||
Slice const s = ouri->value();
|
||||
return Bytes(s.begin(), s.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().error();
|
||||
#endif
|
||||
if (!asHex)
|
||||
j << msg
|
||||
<< 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 << msg << 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().error();
|
||||
#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
|
||||
testGetDataHelperFunctions()
|
||||
{
|
||||
testcase("getData helper functions");
|
||||
BEAST_EXPECT(testGetDataIncrement());
|
||||
}
|
||||
|
||||
void
|
||||
testWasmFib()
|
||||
{
|
||||
@@ -1300,10 +432,10 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env)
|
||||
{
|
||||
}
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) override
|
||||
{
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
}
|
||||
};
|
||||
BadTestHostFunctions nfs(env);
|
||||
@@ -1320,7 +452,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env)
|
||||
{
|
||||
}
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) override
|
||||
{
|
||||
return Bytes((MAX_PAGES + 1) * 64 * 1024, 1);
|
||||
@@ -1361,7 +493,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
|
||||
auto const s = sink.messages().str();
|
||||
BEAST_EXPECT(
|
||||
countSubstr(s, "WAMR Error: failed to call func") == 1);
|
||||
countSubstr(s, "WAMR Error: failure to call func") == 1);
|
||||
BEAST_EXPECT(
|
||||
countSubstr(s, "Exception: wasm operand stack overflow") > 0);
|
||||
}
|
||||
@@ -1387,7 +519,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testEscrowWasmDN2()
|
||||
testEscrowWasmDN3()
|
||||
{
|
||||
testcase("wasm devnet 3 test");
|
||||
|
||||
@@ -1602,6 +734,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
testGetDataHelperFunctions();
|
||||
testWasmLib();
|
||||
testBadWasm();
|
||||
testWasmCheckJson();
|
||||
@@ -1620,7 +753,7 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
|
||||
// TODO: fix result
|
||||
testEscrowWasmDN1();
|
||||
testEscrowWasmDN2();
|
||||
testEscrowWasmDN3();
|
||||
|
||||
// perfTest();
|
||||
}
|
||||
|
||||
@@ -795,7 +795,7 @@ WamrEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in)
|
||||
if (trap)
|
||||
{
|
||||
ret.f = true;
|
||||
print_wasm_error("failed to call func", trap, j_);
|
||||
print_wasm_error("failure to call func", trap, j_);
|
||||
}
|
||||
|
||||
// assert(results[0].kind == WASM_I32);
|
||||
@@ -924,7 +924,7 @@ WamrEngine::runHlp(
|
||||
auto const res = call<1>(f, p);
|
||||
|
||||
if (res.f)
|
||||
throw std::runtime_error("<" + std::string(funcName) + "> failed");
|
||||
throw std::runtime_error("<" + std::string(funcName) + "> failure");
|
||||
else if (!res.r.num_elems)
|
||||
throw std::runtime_error(
|
||||
"<" + std::string(funcName) + "> return nothing");
|
||||
|
||||
@@ -29,25 +29,25 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
enum HostFunctionErrors : int32_t {
|
||||
HF_ERR_INTERNAL = -1,
|
||||
HF_ERR_FIELD_NOT_FOUND = -2,
|
||||
HF_ERR_BUFFER_TOO_SMALL = -3,
|
||||
HF_ERR_NO_ARRAY = -4,
|
||||
HF_ERR_NOT_LEAF_FIELD = -5,
|
||||
HF_ERR_LOCATOR_MALFORMED = -6,
|
||||
HF_ERR_SLOT_OUT_RANGE = -7,
|
||||
HF_ERR_SLOTS_FULL = -8,
|
||||
HF_ERR_EMPTY_SLOT = -9,
|
||||
HF_ERR_LEDGER_OBJ_NOT_FOUND = -10,
|
||||
HF_ERR_DECODING = -11,
|
||||
HF_ERR_DATA_FIELD_TOO_LARGE = -12,
|
||||
HF_ERR_POINTER_OUT_OF_BOUNDS = -13,
|
||||
HF_ERR_NO_MEM_EXPORTED = -14,
|
||||
HF_ERR_INVALID_PARAMS = -15,
|
||||
HF_ERR_INVALID_ACCOUNT = -16,
|
||||
HF_ERR_INVALID_FIELD = -17,
|
||||
HF_ERR_INDEX_OUT_OF_BOUNDS = -18,
|
||||
enum class HostFunctionError : int32_t {
|
||||
INTERNAL = -1,
|
||||
FIELD_NOT_FOUND = -2,
|
||||
BUFFER_TOO_SMALL = -3,
|
||||
NO_ARRAY = -4,
|
||||
NOT_LEAF_FIELD = -5,
|
||||
LOCATOR_MALFORMED = -6,
|
||||
SLOT_OUT_RANGE = -7,
|
||||
SLOTS_FULL = -8,
|
||||
EMPTY_SLOT = -9,
|
||||
LEDGER_OBJ_NOT_FOUND = -10,
|
||||
DECODING = -11,
|
||||
DATA_FIELD_TOO_LARGE = -12,
|
||||
POINTER_OUT_OF_BOUNDS = -13,
|
||||
NO_MEM_EXPORTED = -14,
|
||||
INVALID_PARAMS = -15,
|
||||
INVALID_ACCOUNT = -16,
|
||||
INVALID_FIELD = -17,
|
||||
INDEX_OUT_OF_BOUNDS = -18,
|
||||
};
|
||||
|
||||
struct HostFunctions
|
||||
@@ -69,157 +69,157 @@ struct HostFunctions
|
||||
return beast::Journal{beast::Journal::getNullSink()};
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getLedgerSqn()
|
||||
{
|
||||
return 1;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getParentLedgerTime()
|
||||
{
|
||||
return 1;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Hash
|
||||
virtual Expected<Hash, HostFunctionError>
|
||||
getParentLedgerHash()
|
||||
{
|
||||
return {};
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx)
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjField(SField const& fname)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getTxNestedField(Slice const& locator)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getTxArrayLen(SField const& fname)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjArrayLen(SField const& fname)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getTxNestedArrayLen(Slice const& locator)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
updateData(Bytes const& data)
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
updateData(Slice const& data)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Hash
|
||||
computeSha512HalfHash(Bytes const& data)
|
||||
virtual Expected<Hash, HostFunctionError>
|
||||
computeSha512HalfHash(Slice const& data)
|
||||
{
|
||||
return Hash{};
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
accountKeylet(AccountID const& account)
|
||||
{
|
||||
return Bytes{};
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
credentialKeylet(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Bytes const& credentialType)
|
||||
Slice const& credentialType)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t docId)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, int32_t>
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getNFT(AccountID const& account, uint256 const& nftId)
|
||||
{
|
||||
return Unexpected(HF_ERR_INTERNAL);
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
trace(std::string const& msg, Bytes const& data, bool asHex)
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
trace(std::string_view const& msg, Slice const& data, bool asHex)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
traceNum(std::string const& msg, int64_t data)
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceNum(std::string_view const& msg, int64_t data)
|
||||
{
|
||||
return HF_ERR_INTERNAL;
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual ~HostFunctions() = default;
|
||||
|
||||
@@ -30,29 +30,30 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
int32_t
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getLedgerSqn()
|
||||
{
|
||||
return ctx.view().seq();
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getParentLedgerTime()
|
||||
{
|
||||
return ctx.view().parentCloseTime().time_since_epoch().count();
|
||||
}
|
||||
|
||||
Hash
|
||||
Expected<Hash, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getParentLedgerHash()
|
||||
{
|
||||
return ctx.view().info().parentHash;
|
||||
}
|
||||
|
||||
int32_t
|
||||
WasmHostFunctionsImpl::cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx)
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::cacheLedgerObj(uint256 const& objId, int32_t cacheIdx)
|
||||
{
|
||||
auto const& keylet = keylet::unchecked(objId);
|
||||
if (cacheIdx < 0 || cacheIdx > MAX_CACHE)
|
||||
return HF_ERR_SLOT_OUT_RANGE;
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
|
||||
if (!cacheIdx)
|
||||
{
|
||||
@@ -64,60 +65,61 @@ WasmHostFunctionsImpl::cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx)
|
||||
--cacheIdx;
|
||||
|
||||
if (cacheIdx >= MAX_CACHE)
|
||||
return HF_ERR_SLOTS_FULL;
|
||||
return Unexpected(HostFunctionError::SLOTS_FULL);
|
||||
|
||||
cache[cacheIdx] = ctx.view().read(keylet);
|
||||
return cache[cacheIdx] ? cacheIdx + 1 : HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
if (!cache[cacheIdx])
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
return cacheIdx + 1;
|
||||
}
|
||||
|
||||
static Expected<Bytes, int32_t>
|
||||
static Expected<Bytes, HostFunctionError>
|
||||
getAnyFieldData(STBase const* obj)
|
||||
{
|
||||
// auto const& fname = obj.getFName();
|
||||
if (!obj)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
auto const stype = obj->getSType();
|
||||
switch (stype)
|
||||
{
|
||||
case STI_UNKNOWN:
|
||||
case STI_NOTPRESENT:
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
break;
|
||||
case STI_OBJECT:
|
||||
case STI_ARRAY:
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
return Unexpected(HostFunctionError::NOT_LEAF_FIELD);
|
||||
break;
|
||||
case STI_ACCOUNT: {
|
||||
auto const& super(static_cast<STAccount const*>(obj));
|
||||
auto const& data = super->value();
|
||||
auto const& account(static_cast<STAccount const*>(obj));
|
||||
auto const& data = account->value();
|
||||
return Bytes{data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_AMOUNT: {
|
||||
auto const& super(static_cast<STAmount const*>(obj));
|
||||
int64_t const data = super->xrp().drops();
|
||||
auto const& amount(static_cast<STAmount const*>(obj));
|
||||
int64_t const data = amount->xrp().drops();
|
||||
auto const* b = reinterpret_cast<uint8_t const*>(&data);
|
||||
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
|
||||
return Bytes{b, e};
|
||||
}
|
||||
break;
|
||||
case STI_VL: {
|
||||
auto const& super(static_cast<STBlob const*>(obj));
|
||||
auto const& data = super->value();
|
||||
auto const& vl(static_cast<STBlob const*>(obj));
|
||||
auto const& data = vl->value();
|
||||
return Bytes{data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_UINT256: {
|
||||
auto const& super(static_cast<STBitString<256> const*>(obj));
|
||||
auto const& data = super->value();
|
||||
auto const& num(static_cast<STBitString<256> const*>(obj));
|
||||
auto const& data = num->value();
|
||||
return Bytes{data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_UINT32: {
|
||||
auto const& super(
|
||||
static_cast<STInteger<std::uint32_t> const*>(obj));
|
||||
std::uint32_t const data = super->value();
|
||||
auto const& num(static_cast<STInteger<std::uint32_t> const*>(obj));
|
||||
std::uint32_t const data = num->value();
|
||||
auto const* b = reinterpret_cast<uint8_t const*>(&data);
|
||||
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
|
||||
return Bytes{b, e};
|
||||
@@ -133,29 +135,29 @@ getAnyFieldData(STBase const* obj)
|
||||
return msg.getData();
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getTxField(SField const& fname)
|
||||
{
|
||||
return getAnyFieldData(ctx.tx.peekAtPField(fname));
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getCurrentLedgerObjField(SField const& fname)
|
||||
{
|
||||
auto const sle = ctx.view().read(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
return getAnyFieldData(sle->peekAtPField(fname));
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getLedgerObjField(int32_t cacheIdx, SField const& fname)
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HF_ERR_SLOT_OUT_RANGE);
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
if (!cache[cacheIdx])
|
||||
return Unexpected(HF_ERR_EMPTY_SLOT);
|
||||
return Unexpected(HostFunctionError::EMPTY_SLOT);
|
||||
return getAnyFieldData(cache[cacheIdx]->peekAtPField(fname));
|
||||
}
|
||||
|
||||
@@ -166,11 +168,11 @@ noField(STBase const* field)
|
||||
(STI_UNKNOWN == field->getSType());
|
||||
}
|
||||
|
||||
static Expected<STBase const*, int32_t>
|
||||
static Expected<STBase const*, HostFunctionError>
|
||||
locateField(STObject const& obj, Slice const& loc)
|
||||
{
|
||||
if (loc.empty() || (loc.size() & 3)) // must be multiple of 4
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
return Unexpected(HostFunctionError::LOCATOR_MALFORMED);
|
||||
|
||||
int32_t const* l = reinterpret_cast<int32_t const*>(loc.data());
|
||||
int32_t const sz = loc.size() / 4;
|
||||
@@ -181,12 +183,12 @@ locateField(STObject const& obj, Slice const& loc)
|
||||
int32_t const c = l[0];
|
||||
auto const it = m.find(c);
|
||||
if (it == m.end())
|
||||
return Unexpected(HF_ERR_INVALID_FIELD);
|
||||
return Unexpected(HostFunctionError::INVALID_FIELD);
|
||||
|
||||
auto const& fname(*it->second);
|
||||
field = obj.peekAtPField(fname);
|
||||
if (noField(field))
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
for (int i = 1; i < sz; ++i)
|
||||
@@ -197,7 +199,7 @@ locateField(STObject const& obj, Slice const& loc)
|
||||
{
|
||||
auto const* arr = static_cast<STArray const*>(field);
|
||||
if (c >= arr->size())
|
||||
return Unexpected(HF_ERR_INDEX_OUT_OF_BOUNDS);
|
||||
return Unexpected(HostFunctionError::INDEX_OUT_OF_BOUNDS);
|
||||
field = &(arr->operator[](c));
|
||||
}
|
||||
else if (STI_OBJECT == field->getSType())
|
||||
@@ -206,24 +208,24 @@ locateField(STObject const& obj, Slice const& loc)
|
||||
|
||||
auto const it = m.find(c);
|
||||
if (it == m.end())
|
||||
return Unexpected(HF_ERR_INVALID_FIELD);
|
||||
return Unexpected(HostFunctionError::INVALID_FIELD);
|
||||
|
||||
auto const& fname(*it->second);
|
||||
field = o->peekAtPField(fname);
|
||||
}
|
||||
else // simple field must be the last one
|
||||
{
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
return Unexpected(HostFunctionError::LOCATOR_MALFORMED);
|
||||
}
|
||||
|
||||
if (noField(field))
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getTxNestedField(Slice const& locator)
|
||||
{
|
||||
auto const r = locateField(ctx.tx, locator);
|
||||
@@ -233,12 +235,12 @@ WasmHostFunctionsImpl::getTxNestedField(Slice const& locator)
|
||||
return getAnyFieldData(r.value());
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(Slice const& locator)
|
||||
{
|
||||
auto const sle = ctx.view().read(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const r = locateField(*sle, locator);
|
||||
if (!r)
|
||||
@@ -247,17 +249,17 @@ WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(Slice const& locator)
|
||||
return getAnyFieldData(r.value());
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getLedgerObjNestedField(
|
||||
int32_t cacheIdx,
|
||||
Slice const& locator)
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HF_ERR_SLOT_OUT_RANGE);
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
return Unexpected(HF_ERR_EMPTY_SLOT);
|
||||
return Unexpected(HostFunctionError::EMPTY_SLOT);
|
||||
|
||||
auto const r = locateField(*cache[cacheIdx], locator);
|
||||
if (!r)
|
||||
@@ -266,217 +268,220 @@ WasmHostFunctionsImpl::getLedgerObjNestedField(
|
||||
return getAnyFieldData(r.value());
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getTxArrayLen(SField const& fname)
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
|
||||
auto const* field = ctx.tx.peekAtPField(fname);
|
||||
if (noField(field))
|
||||
return HF_ERR_FIELD_NOT_FOUND;
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getCurrentLedgerObjArrayLen(SField const& fname)
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
|
||||
auto const sle = ctx.view().read(leKey);
|
||||
if (!sle)
|
||||
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const* field = sle->peekAtPField(fname);
|
||||
if (noField(field))
|
||||
return HF_ERR_FIELD_NOT_FOUND;
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getLedgerObjArrayLen(
|
||||
int32_t cacheIdx,
|
||||
SField const& fname)
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return HF_ERR_SLOT_OUT_RANGE;
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
return HF_ERR_EMPTY_SLOT;
|
||||
return Unexpected(HostFunctionError::EMPTY_SLOT);
|
||||
|
||||
auto const* field = cache[cacheIdx]->peekAtPField(fname);
|
||||
if (noField(field))
|
||||
return HF_ERR_FIELD_NOT_FOUND;
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getTxNestedArrayLen(Slice const& locator)
|
||||
{
|
||||
auto const r = locateField(ctx.tx, locator);
|
||||
if (!r)
|
||||
return r.error();
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getCurrentLedgerObjNestedArrayLen(Slice const& locator)
|
||||
{
|
||||
auto const sle = ctx.view().read(leKey);
|
||||
if (!sle)
|
||||
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
auto const r = locateField(*sle, locator);
|
||||
if (!r)
|
||||
return r.error();
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getLedgerObjNestedArrayLen(
|
||||
int32_t cacheIdx,
|
||||
Slice const& locator)
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return HF_ERR_SLOT_OUT_RANGE;
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
return HF_ERR_EMPTY_SLOT;
|
||||
return Unexpected(HostFunctionError::EMPTY_SLOT);
|
||||
|
||||
auto const r = locateField(*cache[cacheIdx], locator);
|
||||
if (!r)
|
||||
return r.error();
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
WasmHostFunctionsImpl::updateData(Bytes const& data)
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::updateData(Slice const& data)
|
||||
{
|
||||
if (data.size() > maxWasmDataLength)
|
||||
{
|
||||
return Unexpected(HostFunctionError::DATA_FIELD_TOO_LARGE);
|
||||
}
|
||||
auto sle = ctx.view().peek(leKey);
|
||||
if (!sle)
|
||||
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
sle->setFieldVL(sfData, data);
|
||||
ctx.view().update(sle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Hash
|
||||
WasmHostFunctionsImpl::computeSha512HalfHash(Bytes const& data)
|
||||
Expected<Hash, HostFunctionError>
|
||||
WasmHostFunctionsImpl::computeSha512HalfHash(Slice const& data)
|
||||
{
|
||||
auto const hash = sha512Half(data);
|
||||
return hash;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::accountKeylet(AccountID const& account)
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::account(account);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::credentialKeylet(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Bytes const& credentialType)
|
||||
Slice const& credentialType)
|
||||
{
|
||||
if (!subject || !issuer)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
|
||||
|
||||
if (credentialType.empty() ||
|
||||
credentialType.size() > maxCredentialTypeLength)
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
return Unexpected(HostFunctionError::INVALID_PARAMS);
|
||||
|
||||
auto const keylet =
|
||||
keylet::credential(subject, issuer, makeSlice(credentialType));
|
||||
auto const keylet = keylet::credential(subject, issuer, credentialType);
|
||||
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::escrowKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::escrow(account, seq);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::oracleKeylet(
|
||||
AccountID const& account,
|
||||
std::uint32_t documentId)
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::oracle(account, documentId);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getNFT(AccountID const& account, uint256 const& nftId)
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
|
||||
|
||||
if (!nftId)
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
return Unexpected(HostFunctionError::INVALID_PARAMS);
|
||||
|
||||
auto obj = nft::findToken(ctx.view(), account, nftId);
|
||||
if (!obj)
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto ouri = obj->at(~sfURI);
|
||||
if (!ouri)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
Slice const s = ouri->value();
|
||||
return Bytes(s.begin(), s.end());
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::trace(
|
||||
std::string const& msg,
|
||||
Bytes const& data,
|
||||
std::string_view const& msg,
|
||||
Slice const& data,
|
||||
bool asHex)
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
@@ -485,21 +490,24 @@ WasmHostFunctionsImpl::trace(
|
||||
auto j = ctx.journal.trace();
|
||||
#endif
|
||||
if (!asHex)
|
||||
{
|
||||
j << "WAMR TRACE (" << leKey.key << "): " << msg << " - "
|
||||
<< std::string_view(
|
||||
reinterpret_cast<char const*>(data.data()), data.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const hex =
|
||||
boost::algorithm::hex(std::string(data.begin(), data.end()));
|
||||
std::string hex;
|
||||
hex.reserve(data.size() * 2);
|
||||
boost::algorithm::hex(data.begin(), data.end(), hex.begin());
|
||||
j << "WAMR DEV TRACE (" << leKey.key << "): " << msg << " - " << hex;
|
||||
}
|
||||
|
||||
return msg.size() + data.size() * (asHex ? 2 : 1);
|
||||
}
|
||||
|
||||
int32_t
|
||||
WasmHostFunctionsImpl::traceNum(std::string const& msg, int64_t data)
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::traceNum(std::string_view const& msg, int64_t data)
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
auto j = ctx.journal.error();
|
||||
|
||||
@@ -57,83 +57,83 @@ public:
|
||||
return ctx.journal;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getLedgerSqn() override;
|
||||
|
||||
int32_t
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getParentLedgerTime() override;
|
||||
|
||||
Hash
|
||||
Expected<Hash, HostFunctionError>
|
||||
getParentLedgerHash() override;
|
||||
|
||||
int32_t
|
||||
cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx) override;
|
||||
Expected<int32_t, HostFunctionError>
|
||||
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjField(SField const& fname) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxNestedField(Slice const& locator) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override;
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getTxArrayLen(SField const& fname) override;
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) override;
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override;
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getTxNestedArrayLen(Slice const& locator) override;
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override;
|
||||
|
||||
int32_t
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override;
|
||||
|
||||
int32_t
|
||||
updateData(Bytes const& data) override;
|
||||
Expected<int32_t, HostFunctionError>
|
||||
updateData(Slice const& data) override;
|
||||
|
||||
Hash
|
||||
computeSha512HalfHash(Bytes const& data) override;
|
||||
Expected<Hash, HostFunctionError>
|
||||
computeSha512HalfHash(Slice const& data) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
accountKeylet(AccountID const& account) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
credentialKeylet(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Bytes const& credentialType) override;
|
||||
Slice const& credentialType) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t documentId) override;
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) override;
|
||||
|
||||
int32_t
|
||||
trace(std::string const& msg, Bytes const& data, bool asHex) override;
|
||||
Expected<int32_t, HostFunctionError>
|
||||
trace(std::string_view const& msg, Slice const& data, bool asHex) override;
|
||||
|
||||
int32_t
|
||||
traceNum(std::string const& msg, int64_t data) override;
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceNum(std::string_view const& msg, int64_t data) override;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user