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:
Mayukha Vadari
2025-07-15 04:28:59 +05:30
committed by GitHub
parent bc445ec6a2
commit 9007097d24
9 changed files with 1531 additions and 1428 deletions

View File

@@ -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);

View 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

View File

@@ -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};

View File

@@ -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();
}

View File

@@ -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");

View File

@@ -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;

View File

@@ -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();

View File

@@ -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