From 9007097d247eec1cf9b3442390c7209418ea805b Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 15 Jul 2025 04:28:59 +0530 Subject: [PATCH] Simplify host function boilerplate (#5534) * enum for HF errors * switch getData functions to be templates * getData 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 --- src/test/app/AMM_test.cpp | 2 +- src/test/app/TestHostFunctions.h | 797 ++++++++++++++++++ src/test/app/Vault_test.cpp | 2 +- src/test/app/Wasm_test.cpp | 909 +-------------------- src/xrpld/app/misc/WamrVM.cpp | 4 +- src/xrpld/app/misc/WasmHostFunc.h | 150 ++-- src/xrpld/app/misc/WasmHostFuncImpl.cpp | 206 ++--- src/xrpld/app/misc/WasmHostFuncImpl.h | 62 +- src/xrpld/app/misc/WasmHostFuncWrapper.cpp | 827 +++++++++++-------- 9 files changed, 1531 insertions(+), 1428 deletions(-) create mode 100644 src/test/app/TestHostFunctions.h diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index 2ee9e5f1f3..93bb57a081 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -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(&logs)}; env.fund(XRP(30'000), gw, alice); diff --git a/src/test/app/TestHostFunctions.h b/src/test/app/TestHostFunctions.h new file mode 100644 index 0000000000..680ec03896 --- /dev/null +++ b/src/test/app/TestHostFunctions.h @@ -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 +#include + +#include +#include +#include +#include + +#include + +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("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 + getLedgerSqn() override + { + return static_cast(env_.current()->seq()); + } + + Expected + getParentLedgerTime() override + { + return env_.current()->parentCloseTime().time_since_epoch().count() + + clock_drift_; + } + + Expected + getParentLedgerHash() override + { + return env_.current()->info().parentHash; + } + + virtual Expected + cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override + { + return 1; + } + + Expected + 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(&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(&data); + auto const* e = reinterpret_cast(&data + 1); + return Bytes{b, e}; + } + return Bytes(); + } + + Expected + 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 + 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(&x); + return Bytes{p, p + sizeof(x)}; + } + return data_; + } + + Expected + 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 + 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 + 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 + getTxArrayLen(SField const& fname) override + { + return 32; + } + + Expected + getCurrentLedgerObjArrayLen(SField const& fname) override + { + return 32; + } + + Expected + getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override + { + return 32; + } + + Expected + getTxNestedArrayLen(Slice const& locator) override + { + return 32; + } + + Expected + getCurrentLedgerObjNestedArrayLen(Slice const& locator) override + { + return 32; + } + + Expected + getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override + { + return 32; + } + + Expected + updateData(Slice const& data) override + { + return 0; + } + + Expected + computeSha512HalfHash(Slice const& data) override + { + return env_.current()->info().parentHash; + } + + Expected + 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 + 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 + 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 + 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 + 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 + 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(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 + 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, MAX_CACHE> cache; + std::shared_ptr tx_; + + void const* rt_ = nullptr; + +public: + PerfHostFunctions( + test::jtx::Env& env, + Keylet const& k, + std::shared_ptr&& tx) + : TestHostFunctions(env), leKey(k), tx_(std::move(tx)) + { + } + + virtual Expected + 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(obj)); + auto const& data = super.value(); + return {data.begin(), data.end()}; + } + break; + case STI_AMOUNT: { + auto const& super(static_cast(obj)); + int64_t const data = super.xrp().drops(); + auto const* b = reinterpret_cast(&data); + auto const* e = reinterpret_cast(&data + 1); + return {b, e}; + } + break; + case STI_VL: { + auto const& super(static_cast(obj)); + auto const& data = super.value(); + return {data.begin(), data.end()}; + } + break; + case STI_UINT256: { + auto const& super(static_cast const&>(obj)); + auto const& data = super.value(); + return {data.begin(), data.end()}; + } + break; + case STI_UINT32: { + auto const& super( + static_cast const&>(obj)); + std::uint32_t const data = super.value(); + auto const* b = reinterpret_cast(&data); + auto const* e = reinterpret_cast(&data + 1); + return {b, e}; + } + break; + default: + break; + } + + Serializer msg; + obj.add(msg); + + return msg.getData(); + } + + Expected + 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 + 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 + 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 + 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(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(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(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 + 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 + 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 + 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 + 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(field)->size(); + + return sz; + } + + Expected + 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(field)->size(); + + return sz; + } + + Expected + 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(field)->size(); + + return sz; + } + + Expected + 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(field)->size(); + + return sz; + } + + Expected + 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(field)->size(); + + return sz; + } + + Expected + 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(field)->size(); + + return sz; + } + + Expected + 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 + computeSha512HalfHash(Slice const& data) override + { + auto const hash = sha512Half(data); + return hash; + } + + Expected + 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 diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index ccac0e2819..24a951c83c 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -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}; diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp index e5d0a9f29f..6c77706b8b 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -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 #include #include @@ -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 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(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("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(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 - 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(&x); - return Bytes{p, p + sizeof(x)}; - } - else if (fname == sfSequence) - { - int32_t x = getLedgerSqn(); - uint8_t const* p = reinterpret_cast(&x); - return Bytes{p, p + sizeof(x)}; - } - return Bytes(); - } - - Expected - 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 - 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(&x); - return Bytes{p, p + sizeof(x)}; - } - return data_; - } - - Expected - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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(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, MAX_CACHE> cache; - std::shared_ptr tx_; - - void const* rt_ = nullptr; - -public: - PerfHostFunctions( - test::jtx::Env& env, - Keylet const& k, - std::shared_ptr&& 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(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(obj)); - auto const& data = super.value(); - return {data.begin(), data.end()}; - } - break; - case STI_AMOUNT: { - auto const& super(static_cast(obj)); - int64_t const data = super.xrp().drops(); - auto const* b = reinterpret_cast(&data); - auto const* e = reinterpret_cast(&data + 1); - return {b, e}; - } - break; - case STI_VL: { - auto const& super(static_cast(obj)); - auto const& data = super.value(); - return {data.begin(), data.end()}; - } - break; - case STI_UINT256: { - auto const& super(static_cast const&>(obj)); - auto const& data = super.value(); - return {data.begin(), data.end()}; - } - break; - case STI_UINT32: { - auto const& super( - static_cast const&>(obj)); - std::uint32_t const data = super.value(); - auto const* b = reinterpret_cast(&data); - auto const* e = reinterpret_cast(&data + 1); - return {b, e}; - } - break; - default: - break; - } - - Serializer msg; - obj.add(msg); - - return msg.getData(); - } - - Expected - 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 - 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 - 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 - 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(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(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(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 - 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 - 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 - 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(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(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(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(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(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(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 - 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 - 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 - 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 - 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 - 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(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 + Expected 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 + Expected 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(); } diff --git a/src/xrpld/app/misc/WamrVM.cpp b/src/xrpld/app/misc/WamrVM.cpp index 9ad697b1c1..64759ff8d0 100644 --- a/src/xrpld/app/misc/WamrVM.cpp +++ b/src/xrpld/app/misc/WamrVM.cpp @@ -795,7 +795,7 @@ WamrEngine::call(FuncInfo const& f, std::vector& 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"); diff --git a/src/xrpld/app/misc/WasmHostFunc.h b/src/xrpld/app/misc/WasmHostFunc.h index 86eb174451..b507d6fe69 100644 --- a/src/xrpld/app/misc/WasmHostFunc.h +++ b/src/xrpld/app/misc/WasmHostFunc.h @@ -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 getLedgerSqn() { - return 1; + return Unexpected(HostFunctionError::INTERNAL); } - virtual int32_t + virtual Expected getParentLedgerTime() { - return 1; + return Unexpected(HostFunctionError::INTERNAL); } - virtual Hash + virtual Expected getParentLedgerHash() { - return {}; + return Unexpected(HostFunctionError::INTERNAL); } - virtual int32_t - cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx) + virtual Expected + cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) { - return HF_ERR_INTERNAL; + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected getTxField(SField const& fname) { - return Unexpected(HF_ERR_INTERNAL); + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected getCurrentLedgerObjField(SField const& fname) { - return Unexpected(HF_ERR_INTERNAL); + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected getLedgerObjField(int32_t cacheIdx, SField const& fname) { - return Unexpected(HF_ERR_INTERNAL); + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected getTxNestedField(Slice const& locator) { - return Unexpected(HF_ERR_INTERNAL); + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected getCurrentLedgerObjNestedField(Slice const& locator) { - return Unexpected(HF_ERR_INTERNAL); + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) { - return Unexpected(HF_ERR_INTERNAL); + return Unexpected(HostFunctionError::INTERNAL); } - virtual int32_t + virtual Expected getTxArrayLen(SField const& fname) { - return HF_ERR_INTERNAL; + return Unexpected(HostFunctionError::INTERNAL); } - virtual int32_t + virtual Expected getCurrentLedgerObjArrayLen(SField const& fname) { - return HF_ERR_INTERNAL; + return Unexpected(HostFunctionError::INTERNAL); } - virtual int32_t + virtual Expected getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) { - return HF_ERR_INTERNAL; + return Unexpected(HostFunctionError::INTERNAL); } - virtual int32_t + virtual Expected getTxNestedArrayLen(Slice const& locator) { - return HF_ERR_INTERNAL; + return Unexpected(HostFunctionError::INTERNAL); } - virtual int32_t + virtual Expected getCurrentLedgerObjNestedArrayLen(Slice const& locator) { - return HF_ERR_INTERNAL; + return Unexpected(HostFunctionError::INTERNAL); } - virtual int32_t + virtual Expected getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) { - return HF_ERR_INTERNAL; + return Unexpected(HostFunctionError::INTERNAL); } - virtual int32_t - updateData(Bytes const& data) + virtual Expected + updateData(Slice const& data) { - return HF_ERR_INTERNAL; + return Unexpected(HostFunctionError::INTERNAL); } - virtual Hash - computeSha512HalfHash(Bytes const& data) + virtual Expected + computeSha512HalfHash(Slice const& data) { - return Hash{}; + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected accountKeylet(AccountID const& account) { - return Bytes{}; + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected credentialKeylet( AccountID const& subject, AccountID const& issuer, - Bytes const& credentialType) + Slice const& credentialType) { - return Unexpected(HF_ERR_INTERNAL); + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected escrowKeylet(AccountID const& account, std::uint32_t seq) { - return Unexpected(HF_ERR_INTERNAL); + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected oracleKeylet(AccountID const& account, std::uint32_t docId) { - return Unexpected(HF_ERR_INTERNAL); + return Unexpected(HostFunctionError::INTERNAL); } - virtual Expected + virtual Expected 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 + 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 + traceNum(std::string_view const& msg, int64_t data) { - return HF_ERR_INTERNAL; + return Unexpected(HostFunctionError::INTERNAL); } virtual ~HostFunctions() = default; diff --git a/src/xrpld/app/misc/WasmHostFuncImpl.cpp b/src/xrpld/app/misc/WasmHostFuncImpl.cpp index 7c804f376d..6c7ae43d91 100644 --- a/src/xrpld/app/misc/WasmHostFuncImpl.cpp +++ b/src/xrpld/app/misc/WasmHostFuncImpl.cpp @@ -30,29 +30,30 @@ namespace ripple { -int32_t +Expected WasmHostFunctionsImpl::getLedgerSqn() { return ctx.view().seq(); } -int32_t +Expected WasmHostFunctionsImpl::getParentLedgerTime() { return ctx.view().parentCloseTime().time_since_epoch().count(); } -Hash +Expected WasmHostFunctionsImpl::getParentLedgerHash() { return ctx.view().info().parentHash; } -int32_t -WasmHostFunctionsImpl::cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx) +Expected +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 +static Expected 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(obj)); - auto const& data = super->value(); + auto const& account(static_cast(obj)); + auto const& data = account->value(); return Bytes{data.begin(), data.end()}; } break; case STI_AMOUNT: { - auto const& super(static_cast(obj)); - int64_t const data = super->xrp().drops(); + auto const& amount(static_cast(obj)); + int64_t const data = amount->xrp().drops(); auto const* b = reinterpret_cast(&data); auto const* e = reinterpret_cast(&data + 1); return Bytes{b, e}; } break; case STI_VL: { - auto const& super(static_cast(obj)); - auto const& data = super->value(); + auto const& vl(static_cast(obj)); + auto const& data = vl->value(); return Bytes{data.begin(), data.end()}; } break; case STI_UINT256: { - auto const& super(static_cast const*>(obj)); - auto const& data = super->value(); + auto const& num(static_cast const*>(obj)); + auto const& data = num->value(); return Bytes{data.begin(), data.end()}; } break; case STI_UINT32: { - auto const& super( - static_cast const*>(obj)); - std::uint32_t const data = super->value(); + auto const& num(static_cast const*>(obj)); + std::uint32_t const data = num->value(); auto const* b = reinterpret_cast(&data); auto const* e = reinterpret_cast(&data + 1); return Bytes{b, e}; @@ -133,29 +135,29 @@ getAnyFieldData(STBase const* obj) return msg.getData(); } -Expected +Expected WasmHostFunctionsImpl::getTxField(SField const& fname) { return getAnyFieldData(ctx.tx.peekAtPField(fname)); } -Expected +Expected 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 +Expected 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 +static Expected 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(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(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 +Expected 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 +Expected 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 +Expected 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 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(field)->size(); return sz; } -int32_t +Expected 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(field)->size(); return sz; } -int32_t +Expected 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(field)->size(); return sz; } -int32_t +Expected 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(field)->size(); return sz; } -int32_t +Expected 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(field)->size(); return sz; } -int32_t +Expected 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(field)->size(); return sz; } -int32_t -WasmHostFunctionsImpl::updateData(Bytes const& data) +Expected +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 +WasmHostFunctionsImpl::computeSha512HalfHash(Slice const& data) { auto const hash = sha512Half(data); return hash; } -Expected +Expected 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 +Expected 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 +Expected 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 +Expected 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 +Expected 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 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(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 +WasmHostFunctionsImpl::traceNum(std::string_view const& msg, int64_t data) { #ifdef DEBUG_OUTPUT auto j = ctx.journal.error(); diff --git a/src/xrpld/app/misc/WasmHostFuncImpl.h b/src/xrpld/app/misc/WasmHostFuncImpl.h index 091ca24934..58bf4c194a 100644 --- a/src/xrpld/app/misc/WasmHostFuncImpl.h +++ b/src/xrpld/app/misc/WasmHostFuncImpl.h @@ -57,83 +57,83 @@ public: return ctx.journal; } - int32_t + Expected getLedgerSqn() override; - int32_t + Expected getParentLedgerTime() override; - Hash + Expected getParentLedgerHash() override; - int32_t - cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx) override; + Expected + cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override; - Expected + Expected getTxField(SField const& fname) override; - Expected + Expected getCurrentLedgerObjField(SField const& fname) override; - Expected + Expected getLedgerObjField(int32_t cacheIdx, SField const& fname) override; - Expected + Expected getTxNestedField(Slice const& locator) override; - Expected + Expected getCurrentLedgerObjNestedField(Slice const& locator) override; - Expected + Expected getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override; - int32_t + Expected getTxArrayLen(SField const& fname) override; - int32_t + Expected getCurrentLedgerObjArrayLen(SField const& fname) override; - int32_t + Expected getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override; - int32_t + Expected getTxNestedArrayLen(Slice const& locator) override; - int32_t + Expected getCurrentLedgerObjNestedArrayLen(Slice const& locator) override; - int32_t + Expected getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override; - int32_t - updateData(Bytes const& data) override; + Expected + updateData(Slice const& data) override; - Hash - computeSha512HalfHash(Bytes const& data) override; + Expected + computeSha512HalfHash(Slice const& data) override; - Expected + Expected accountKeylet(AccountID const& account) override; - Expected + Expected credentialKeylet( AccountID const& subject, AccountID const& issuer, - Bytes const& credentialType) override; + Slice const& credentialType) override; - Expected + Expected escrowKeylet(AccountID const& account, std::uint32_t seq) override; - Expected + Expected oracleKeylet(AccountID const& account, std::uint32_t documentId) override; - Expected + Expected getNFT(AccountID const& account, uint256 const& nftId) override; - int32_t - trace(std::string const& msg, Bytes const& data, bool asHex) override; + Expected + trace(std::string_view const& msg, Slice const& data, bool asHex) override; - int32_t - traceNum(std::string const& msg, int64_t data) override; + Expected + traceNum(std::string_view const& msg, int64_t data) override; }; } // namespace ripple diff --git a/src/xrpld/app/misc/WasmHostFuncWrapper.cpp b/src/xrpld/app/misc/WasmHostFuncWrapper.cpp index 0c50273362..f1de9b2235 100644 --- a/src/xrpld/app/misc/WasmHostFuncWrapper.cpp +++ b/src/xrpld/app/misc/WasmHostFuncWrapper.cpp @@ -26,6 +26,8 @@ namespace ripple { +using SFieldCRef = std::reference_wrapper; + static int32_t setData( InstanceWrapper const* rt, @@ -38,70 +40,199 @@ setData( return 0; if (dst < 0 || dsz < 0 || !src || ssz < 0) - return HF_ERR_INVALID_PARAMS; + return static_cast>( + HostFunctionError::INVALID_PARAMS); auto mem = rt ? rt->getMem() : wmem(); if (!mem.s) - return HF_ERR_NO_MEM_EXPORTED; + return static_cast>( + HostFunctionError::NO_MEM_EXPORTED); if (dst + dsz > mem.s) - return HF_ERR_POINTER_OUT_OF_BOUNDS; + return static_cast>( + HostFunctionError::POINTER_OUT_OF_BOUNDS); if (ssz > dsz) - return HF_ERR_BUFFER_TOO_SMALL; + return static_cast>( + HostFunctionError::BUFFER_TOO_SMALL); memcpy(mem.p + dst, src, ssz); return ssz; } -static Expected -getData(InstanceWrapper const* rt, int32_t src, int32_t ssz) +template +Expected +getDataInt32(IW const* _rt, wasm_val_vec_t const* params, int32_t& i) { + auto const result = params->data[i].of.i32; + i++; + return result; +} + +template +Expected +getDataInt64(IW const* _rt, wasm_val_vec_t const* params, int32_t& i) +{ + auto const result = params->data[i].of.i64; + i++; + return result; +} + +template +Expected +getDataSField(IW const* _rt, wasm_val_vec_t const* params, int32_t& i) +{ + auto const& m = SField::getKnownCodeToField(); + auto const it = m.find(params->data[i].of.i32); + if (it == m.end()) + { + return Unexpected(HostFunctionError::INVALID_FIELD); + } + i++; + return *it->second; +} + +template +Expected +getDataSlice(IW const* rt, wasm_val_vec_t const* params, int32_t& i) +{ + auto const src = params->data[i].of.i32; + auto const ssz = params->data[i + 1].of.i32; if (src < 0 || ssz <= 0) - return Unexpected(HF_ERR_INVALID_PARAMS); + return Unexpected(HostFunctionError::INVALID_PARAMS); + + if (ssz > maxWasmDataLength) + { + return Unexpected(HostFunctionError::DATA_FIELD_TOO_LARGE); + } auto mem = rt ? rt->getMem() : wmem(); if (!mem.s) - return Unexpected(HF_ERR_NO_MEM_EXPORTED); + return Unexpected(HostFunctionError::NO_MEM_EXPORTED); if (src + ssz > mem.s) - return Unexpected(HF_ERR_POINTER_OUT_OF_BOUNDS); + return Unexpected(HostFunctionError::POINTER_OUT_OF_BOUNDS); - Bytes data(mem.p + src, mem.p + src + ssz); + Slice data(mem.p + src, ssz); + i += 2; return data; } -static Expected -getDataAccount(InstanceWrapper const* rt, int32_t ptr, int32_t sz) +template +Expected +getDataUInt256(IW const* rt, wasm_val_vec_t const* params, int32_t& i) { - auto const r = getData(rt, ptr, sz); + auto const r = getDataSlice(rt, params, i); + if (!r) + { + return Unexpected(r.error()); + } + + if (r->size() != uint256::bytes) + { + return Unexpected(HostFunctionError::INVALID_PARAMS); + } + return uint256::fromVoid(r->data()); +} + +template +Expected +getDataAccountID(IW const* rt, wasm_val_vec_t const* params, int32_t& i) +{ + auto const r = getDataSlice(rt, params, i); if (!r || (r->size() != AccountID::bytes)) - return Unexpected(HF_ERR_INVALID_PARAMS); + return Unexpected(HostFunctionError::INVALID_PARAMS); return AccountID::fromVoid(r->data()); } -static Expected -getDataString(InstanceWrapper const* rt, int32_t src, int32_t ssz) +template +Expected +getDataString(IW const* rt, wasm_val_vec_t const* params, int32_t& i) { - if (src < 0 || ssz <= 0) - return Unexpected(HF_ERR_INVALID_PARAMS); - - auto mem = rt ? rt->getMem() : wmem(); - if (!mem.s) - return Unexpected(HF_ERR_NO_MEM_EXPORTED); - - if (src + ssz > mem.s) - return Unexpected(HF_ERR_POINTER_OUT_OF_BOUNDS); - - std::string data(mem.p + src, mem.p + src + ssz); - return data; + auto const r = getDataSlice(rt, params, i); + if (!r) + return Unexpected(r.error()); + return std::string_view( + reinterpret_cast(r->data()), r->size()); } -#define RET(x) \ - results->data[0] = WASM_I32_VAL(x); \ - results->num_elems = 1; \ +std::nullptr_t +hfResult(wasm_val_vec_t* results, int32_t value) +{ + results->data[0] = WASM_I32_VAL(value); + results->num_elems = 1; return nullptr; +} + +std::nullptr_t +hfResult(wasm_val_vec_t* results, HostFunctionError value) +{ + results->data[0] = WASM_I32_VAL( + static_cast>(value)); + results->num_elems = 1; + return nullptr; +} + +template +std::nullptr_t +returnResult( + InstanceWrapper const* rt, + wasm_val_vec_t const* params, + wasm_val_vec_t* results, + Expected const& res, + int32_t index) +{ + if (!res) + { + return hfResult(results, res.error()); + } + if constexpr (std::is_same_v, Bytes>) + { + return hfResult( + results, + setData( + rt, + params->data[index].of.i32, + params->data[index + 1].of.i32, + res->data(), + res->size())); + } + else if constexpr (std::is_same_v, Hash>) + { + return hfResult( + results, + setData( + rt, + params->data[index].of.i32, + params->data[index + 1].of.i32, + res->data(), + res->size())); + } + else if constexpr (std::is_same_v, int32_t>) + { + return hfResult(results, res.value()); + } + else if constexpr (std::is_same_v< + std::decay_t, + std::uint32_t>) + { + auto const resultValue = res.value(); + return hfResult( + results, + setData( + rt, + params->data[index].of.i32, + params->data[index + 1].of.i32, + reinterpret_cast(&resultValue), + static_cast(sizeof(resultValue)))); + } + else + { + static_assert( + [] { return false; }(), "Unhandled return type in returnResult"); + } +} wasm_trap_t* getLedgerSqnOld_wrap( @@ -111,8 +242,12 @@ getLedgerSqnOld_wrap( { auto* hf = reinterpret_cast(env); // auto const* rt = reinterpret_cast(hf->getRT()); - int32_t const sqn = hf->getLedgerSqn(); - RET(sqn); + auto const sqn = hf->getLedgerSqn(); + if (!sqn) + { + return hfResult(results, sqn.error()); + } + return hfResult(results, static_cast(sqn.value())); } wasm_trap_t* @@ -123,14 +258,9 @@ getLedgerSqn_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); - int32_t const sqn = hf->getLedgerSqn(); + int index = 0; - RET(setData( - rt, - params->data[0].of.i32, - params->data[1].of.i32, - reinterpret_cast(&sqn), - static_cast(sizeof(sqn)))); + return returnResult(rt, params, results, hf->getLedgerSqn(), index); } wasm_trap_t* @@ -141,13 +271,9 @@ getParentLedgerTime_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); - int32_t const ltime = hf->getParentLedgerTime(); - RET(setData( - rt, - params->data[0].of.i32, - params->data[1].of.i32, - reinterpret_cast(<ime), - static_cast(sizeof(ltime)))); + int index = 0; + + return returnResult(rt, params, results, hf->getParentLedgerTime(), index); } wasm_trap_t* @@ -158,13 +284,9 @@ getParentLedgerHash_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); - Hash const hash = hf->getParentLedgerHash(); - RET(setData( - rt, - params->data[0].of.i32, - params->data[1].of.i32, - hash.data(), - static_cast(hash.size()))); + int index = 0; + + return returnResult(rt, params, results, hf->getParentLedgerHash(), index); } wasm_trap_t* @@ -175,22 +297,22 @@ cacheLedgerObj_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32); - if (!r) + auto const id = getDataUInt256(rt, params, index); + if (!id) { - RET(r.error()); + return hfResult(results, id.error()); } - if (r->size() != uint256::bytes) + auto const cache = getDataInt32(rt, params, index); + if (!cache) { - RET(HF_ERR_INVALID_PARAMS); + return hfResult(results, cache.error()); } - uint256 const key(uint256::fromVoid(r->data())); - int32_t const idx = - hf->cacheLedgerObj(keylet::unchecked(key), params->data[2].of.i32); - RET(idx); + return returnResult( + rt, params, results, hf->cacheLedgerObj(*id, *cache), index); } wasm_trap_t* @@ -201,27 +323,14 @@ getTxField_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const& m = SField::getKnownCodeToField(); - auto const it = m.find(params->data[0].of.i32); - if (it == m.end()) + auto const fname = getDataSField(rt, params, index); + if (!fname) { - RET(HF_ERR_INVALID_FIELD); + return hfResult(results, fname.error()); } - auto const& fname(*it->second); - - auto fieldData = hf->getTxField(fname); - if (!fieldData) - { - RET(fieldData.error()); - } - - RET(setData( - rt, - params->data[1].of.i32, - params->data[2].of.i32, - fieldData->data(), - fieldData->size())); + return returnResult(rt, params, results, hf->getTxField(*fname), index); } wasm_trap_t* @@ -232,27 +341,16 @@ getCurrentLedgerObjField_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const& m = SField::getKnownCodeToField(); - auto const it = m.find(params->data[0].of.i32); - if (it == m.end()) + auto const fname = getDataSField(rt, params, index); + if (!fname) { - RET(HF_ERR_INVALID_FIELD); - } - auto const& fname(*it->second); - - auto fieldData = hf->getCurrentLedgerObjField(fname); - if (!fieldData) - { - RET(fieldData.error()); + return hfResult(results, fname.error()); } - RET(setData( - rt, - params->data[1].of.i32, - params->data[2].of.i32, - fieldData->data(), - fieldData->size())); + return returnResult( + rt, params, results, hf->getCurrentLedgerObjField(*fname), index); } wasm_trap_t* @@ -263,27 +361,22 @@ getLedgerObjField_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const& m = SField::getKnownCodeToField(); - auto const it = m.find(params->data[1].of.i32); - if (it == m.end()) + auto const cache = getDataInt32(rt, params, index); + if (!cache) { - RET(HF_ERR_INVALID_FIELD); - } - auto const& fname(*it->second); - - auto fieldData = hf->getLedgerObjField(params->data[0].of.i32, fname); - if (!fieldData) - { - RET(fieldData.error()); + return hfResult(results, cache.error()); } - RET(setData( - rt, - params->data[2].of.i32, - params->data[3].of.i32, - fieldData->data(), - fieldData->size())); + auto const fname = getDataSField(rt, params, index); + if (!fname) + { + return hfResult(results, fname.error()); + } + + return returnResult( + rt, params, results, hf->getLedgerObjField(*cache, *fname), index); } wasm_trap_t* @@ -294,25 +387,16 @@ getTxNestedField_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32); - if (!r) + auto const bytes = getDataSlice(rt, params, index); + if (!bytes) { - RET(r.error()); + return hfResult(results, bytes.error()); } - auto fieldData = hf->getTxNestedField(makeSlice(r.value())); - if (!fieldData) - { - RET(fieldData.error()); - } - - RET(setData( - rt, - params->data[2].of.i32, - params->data[3].of.i32, - fieldData->data(), - fieldData->size())); + return returnResult( + rt, params, results, hf->getTxNestedField(*bytes), index); } wasm_trap_t* @@ -323,25 +407,15 @@ getCurrentLedgerObjNestedField_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32); - if (!r) + auto const bytes = getDataSlice(rt, params, index); + if (!bytes) { - RET(r.error()); + return hfResult(results, bytes.error()); } - - auto fieldData = hf->getCurrentLedgerObjNestedField(makeSlice(r.value())); - if (!fieldData) - { - RET(fieldData.error()); - } - - RET(setData( - rt, - params->data[2].of.i32, - params->data[3].of.i32, - fieldData->data(), - fieldData->size())); + return returnResult( + rt, params, results, hf->getCurrentLedgerObjNestedField(*bytes), index); } wasm_trap_t* @@ -352,26 +426,26 @@ getLedgerObjNestedField_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const r = getData(rt, params->data[1].of.i32, params->data[2].of.i32); - if (!r) + auto const cache = getDataInt32(rt, params, index); + if (!cache) { - RET(r.error()); + return hfResult(results, cache.error()); } - auto fieldData = hf->getLedgerObjNestedField( - params->data[0].of.i32, makeSlice(r.value())); - if (!fieldData) + auto const bytes = getDataSlice(rt, params, index); + if (!bytes) { - RET(fieldData.error()); + return hfResult(results, bytes.error()); } - RET(setData( + return returnResult( rt, - params->data[3].of.i32, - params->data[4].of.i32, - fieldData->data(), - fieldData->size())); + params, + results, + hf->getLedgerObjNestedField(*cache, *bytes), + index); } wasm_trap_t* @@ -381,18 +455,16 @@ getTxArrayLen_wrap( wasm_val_vec_t* results) { auto* hf = reinterpret_cast(env); - // auto const* rt = reinterpret_cast(hf->getRT()); + auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const& m = SField::getKnownCodeToField(); - auto const it = m.find(params->data[0].of.i32); - if (it == m.end()) + auto const fname = getDataSField(rt, params, index); + if (!fname) { - RET(HF_ERR_INVALID_FIELD); + return hfResult(results, fname.error()); } - auto const& fname(*it->second); - int32_t sz = hf->getTxArrayLen(fname); - RET(sz); + return returnResult(rt, params, results, hf->getTxArrayLen(*fname), index); } wasm_trap_t* @@ -402,18 +474,17 @@ getCurrentLedgerObjArrayLen_wrap( wasm_val_vec_t* results) { auto* hf = reinterpret_cast(env); - // auto const* rt = reinterpret_cast(hf->getRT()); + auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const& m = SField::getKnownCodeToField(); - auto const it = m.find(params->data[0].of.i32); - if (it == m.end()) + auto const fname = getDataSField(rt, params, index); + if (!fname) { - RET(HF_ERR_INVALID_FIELD); + return hfResult(results, fname.error()); } - auto const& fname(*it->second); - int32_t sz = hf->getCurrentLedgerObjArrayLen(fname); - RET(sz); + return returnResult( + rt, params, results, hf->getCurrentLedgerObjArrayLen(*fname), index); } wasm_trap_t* @@ -423,18 +494,23 @@ getLedgerObjArrayLen_wrap( wasm_val_vec_t* results) { auto* hf = reinterpret_cast(env); - // auto const* rt = reinterpret_cast(hf->getRT()); + auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const& m = SField::getKnownCodeToField(); - auto const it = m.find(params->data[1].of.i32); - if (it == m.end()) + auto const cache = getDataInt32(rt, params, index); + if (!cache) { - RET(HF_ERR_INVALID_FIELD); + return hfResult(results, cache.error()); } - auto const& fname(*it->second); - int32_t sz = hf->getLedgerObjArrayLen(params->data[0].of.i32, fname); - RET(sz); + auto const fname = getDataSField(rt, params, index); + if (!fname) + { + return hfResult(results, fname.error()); + } + + return returnResult( + rt, params, results, hf->getLedgerObjArrayLen(*cache, *fname), index); } wasm_trap_t* @@ -445,15 +521,16 @@ getTxNestedArrayLen_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32); - if (!r) + auto const bytes = getDataSlice(rt, params, index); + if (!bytes) { - RET(r.error()); + return hfResult(results, bytes.error()); } - int32_t sz = hf->getTxNestedArrayLen(makeSlice(r.value())); - RET(sz); + return returnResult( + rt, params, results, hf->getTxNestedArrayLen(*bytes), index); } wasm_trap_t* @@ -464,17 +541,21 @@ getCurrentLedgerObjNestedArrayLen_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32); - if (!r) + auto const bytes = getDataSlice(rt, params, index); + if (!bytes) { - RET(r.error()); + return hfResult(results, bytes.error()); } - int32_t sz = hf->getCurrentLedgerObjNestedArrayLen(makeSlice(r.value())); - RET(sz); + return returnResult( + rt, + params, + results, + hf->getCurrentLedgerObjNestedArrayLen(*bytes), + index); } - wasm_trap_t* getLedgerObjNestedArrayLen_wrap( void* env, @@ -483,16 +564,25 @@ getLedgerObjNestedArrayLen_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const r = getData(rt, params->data[1].of.i32, params->data[2].of.i32); - if (!r) + auto const cache = getDataInt32(rt, params, index); + if (!cache) { - RET(r.error()); + return hfResult(results, cache.error()); } - int32_t sz = hf->getLedgerObjNestedArrayLen( - params->data[0].of.i32, makeSlice(r.value())); - RET(sz); + auto const bytes = getDataSlice(rt, params, index); + if (!bytes) + { + return hfResult(results, bytes.error()); + } + return returnResult( + rt, + params, + results, + hf->getLedgerObjNestedArrayLen(*cache, *bytes), + index); } wasm_trap_t* @@ -503,19 +593,15 @@ updateData_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - if (params->data[1].of.i32 > maxWasmDataLength) + auto const bytes = getDataSlice(rt, params, index); + if (!bytes) { - RET(HF_ERR_DATA_FIELD_TOO_LARGE) + return hfResult(results, bytes.error()); } - auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32); - if (!r) - { - RET(r.error()); - } - - RET(hf->updateData(r.value())); + return returnResult(rt, params, results, hf->updateData(*bytes), index); } wasm_trap_t* @@ -526,25 +612,15 @@ computeSha512HalfHash_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - if (params->data[1].of.i32 > maxWasmDataLength) + auto const bytes = getDataSlice(rt, params, index); + if (!bytes) { - RET(HF_ERR_DATA_FIELD_TOO_LARGE); + return hfResult(results, bytes.error()); } - - auto const r = getData(rt, params->data[0].of.i32, params->data[1].of.i32); - if (!r) - { - RET(r.error()); - } - - auto const hash = hf->computeSha512HalfHash(r.value()); - RET(setData( - rt, - params->data[2].of.i32, - params->data[3].of.i32, - hash.data(), - hash.size())); + return returnResult( + rt, params, results, hf->computeSha512HalfHash(*bytes), index); } wasm_trap_t* @@ -555,26 +631,15 @@ accountKeylet_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const acc = - getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32); + auto const acc = getDataAccountID(rt, params, index); if (!acc) { - RET(acc.error()); + return hfResult(results, acc.error()); } - auto const k = hf->accountKeylet(acc.value()); - if (!k) - { - RET(k.error()); - } - - RET(setData( - rt, - params->data[2].of.i32, - params->data[3].of.i32, - k->data(), - k->size())); + return returnResult(rt, params, results, hf->accountKeylet(*acc), index); } wasm_trap_t* @@ -585,41 +650,32 @@ credentialKeylet_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const subject = - getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32); - if (!subject) + auto const subj = getDataAccountID(rt, params, index); + if (!subj) { - RET(subject.error()); + return hfResult(results, subj.error()); } - auto const issuer = - getDataAccount(rt, params->data[2].of.i32, params->data[3].of.i32); - if (!issuer) + auto const iss = getDataAccountID(rt, params, index); + if (!iss) { - RET(issuer.error()); + return hfResult(results, iss.error()); } - auto const credType = - getData(rt, params->data[4].of.i32, params->data[5].of.i32); + auto const credType = getDataSlice(rt, params, index); if (!credType) { - RET(credType.error()); + return hfResult(results, credType.error()); } - auto const k = - hf->credentialKeylet(subject.value(), issuer.value(), credType.value()); - if (!k) - { - RET(k.error()); - } - - RET(setData( + return returnResult( rt, - params->data[6].of.i32, - params->data[7].of.i32, - k->data(), - k->size())); + params, + results, + hf->credentialKeylet(*subj, *iss, *credType), + index); } wasm_trap_t* @@ -630,26 +686,22 @@ escrowKeylet_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const acc = - getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32); + auto const acc = getDataAccountID(rt, params, index); if (!acc) { - RET(acc.error()); + return hfResult(results, acc.error()); } - auto const k = hf->escrowKeylet(acc.value(), params->data[2].of.i32); - if (!k) + auto const seq = getDataInt32(rt, params, index); + if (!seq) { - RET(k.error()); + return hfResult(results, seq.error()); } - RET(setData( - rt, - params->data[3].of.i32, - params->data[4].of.i32, - k->data(), - k->size())); + return returnResult( + rt, params, results, hf->escrowKeylet(*acc, *seq), index); } wasm_trap_t* @@ -660,26 +712,21 @@ oracleKeylet_wrap( { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const acc = - getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32); + auto const acc = getDataAccountID(rt, params, index); if (!acc) { - RET(acc.error()); + return hfResult(results, acc.error()); } - auto const k = hf->oracleKeylet(acc.value(), params->data[2].of.i32); - if (!k) + auto const seq = getDataInt32(rt, params, index); + if (!seq) { - RET(k.error()); + return hfResult(results, seq.error()); } - - RET(setData( - rt, - params->data[3].of.i32, - params->data[4].of.i32, - k->data(), - k->size())); + return returnResult( + rt, params, results, hf->oracleKeylet(*acc, *seq), index); } wasm_trap_t* @@ -687,42 +734,21 @@ getNFT_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; - auto const acc = - getDataAccount(rt, params->data[0].of.i32, params->data[1].of.i32); + auto const acc = getDataAccountID(rt, params, index); if (!acc) { - RET(acc.error()); + return hfResult(results, acc.error()); } - auto const nftRaw = - getData(rt, params->data[2].of.i32, params->data[3].of.i32); - if (!nftRaw) + auto const nftId = getDataUInt256(rt, params, index); + if (!nftId) { - RET(nftRaw.error()); + return hfResult(results, nftId.error()); } - if (nftRaw->size() != uint256::bytes) - { - hf->getJournal().trace() - << "WAMR getNFT: Invalid NFT data size: " << nftRaw->size() - << ", expected " << (uint256::bytes); - RET(HF_ERR_INVALID_PARAMS); - } - - uint256 const ntfId(uint256::fromVoid(nftRaw->data())); - auto const nftURI = hf->getNFT(acc.value(), ntfId); - if (!nftURI) - { - RET(nftURI.error()); - } - - RET(setData( - rt, - params->data[4].of.i32, - params->data[5].of.i32, - nftURI->data(), - nftURI->size())); + return returnResult(rt, params, results, hf->getNFT(*acc, *nftId), index); } wasm_trap_t* @@ -730,28 +756,33 @@ trace_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); + int index = 0; if (params->data[1].of.i32 + params->data[3].of.i32 > maxWasmDataLength) { - RET(HF_ERR_DATA_FIELD_TOO_LARGE); + return hfResult(results, HostFunctionError::DATA_FIELD_TOO_LARGE); } - auto const msg = - getDataString(rt, params->data[0].of.i32, params->data[1].of.i32); + auto const msg = getDataString(rt, params, index); if (!msg) { - RET(msg.error()); + return hfResult(results, msg.error()); } - auto const data = - getData(rt, params->data[2].of.i32, params->data[3].of.i32); + auto const data = getDataSlice(rt, params, index); if (!data) { - RET(data.error()); + return hfResult(results, data.error()); } - auto const e = hf->trace(msg.value(), data.value(), params->data[4].of.i32); - RET(e); + auto const asHex = getDataInt32(rt, params, index); + if (!asHex) + { + return hfResult(results, asHex.error()); + } + + return returnResult( + rt, params, results, hf->trace(*msg, *data, *asHex), index); } wasm_trap_t* @@ -759,21 +790,155 @@ traceNum_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) { auto* hf = reinterpret_cast(env); auto const* rt = reinterpret_cast(hf->getRT()); - + int index = 0; if (params->data[1].of.i32 > maxWasmDataLength) { - RET(HF_ERR_DATA_FIELD_TOO_LARGE); + return hfResult(results, HostFunctionError::DATA_FIELD_TOO_LARGE); } - auto const msg = - getDataString(rt, params->data[0].of.i32, params->data[1].of.i32); + auto const msg = getDataString(rt, params, index); if (!msg) { - RET(msg.error()); + return hfResult(results, msg.error()); } - auto const e = hf->traceNum(msg.value(), params->data[2].of.i64); - RET(e); + auto const number = getDataInt64(rt, params, index); + if (!number) + { + return hfResult(results, number.error()); + } + + return returnResult( + rt, params, results, hf->traceNum(*msg, *number), index); } +class MockInstanceWrapper +{ + wmem mem_; + +public: + MockInstanceWrapper(wmem mem) : mem_(mem) + { + } + + // Mock methods to simulate the behavior of InstanceWrapper + wmem + getMem() const + { + return mem_; + } +}; + +namespace test { +bool +testGetDataIncrement() +{ + wasm_val_t values[4]; + + std::array buffer = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; + MockInstanceWrapper rt(wmem{buffer.data(), buffer.size()}); + + { + // test int32_t + wasm_val_vec_t params = {1, &values[0], 1, sizeof(wasm_val_t), nullptr}; + + values[0] = WASM_I32_VAL(42); + + int index = 0; + auto const result = getDataInt32(&rt, ¶ms, index); + if (!result || result.value() != 42 || index != 1) + return false; + } + + { + // test int64_t + wasm_val_vec_t params = {1, &values[0], 1, sizeof(wasm_val_t), nullptr}; + + values[0] = WASM_I64_VAL(1234); + + int index = 0; + auto const result = getDataInt64(&rt, ¶ms, index); + if (!result || result.value() != 1234 || index != 1) + return false; + } + + { + // test SFieldCRef + wasm_val_vec_t params = {1, &values[0], 1, sizeof(wasm_val_t), nullptr}; + + values[0] = WASM_I32_VAL(sfAccount.fieldCode); + + int index = 0; + auto const result = getDataSField(&rt, ¶ms, index); + if (!result || result.value().get() != sfAccount || index != 1) + return false; + } + + { + // test Slice + wasm_val_vec_t params = {2, &values[0], 2, sizeof(wasm_val_t), nullptr}; + + values[0] = WASM_I32_VAL(0); + values[1] = WASM_I32_VAL(3); + + int index = 0; + auto const result = getDataSlice(&rt, ¶ms, index); + if (!result || result.value() != Slice(buffer.data(), 3)) + return false; + } + + { + // test string + wasm_val_vec_t params = {2, &values[0], 2, sizeof(wasm_val_t), nullptr}; + + values[0] = WASM_I32_VAL(0); + values[1] = WASM_I32_VAL(5); + + int index = 0; + auto const result = getDataString(&rt, ¶ms, index); + if (!result || + result.value() != + std::string_view( + reinterpret_cast(buffer.data()), 5)) + return false; + } + + { + // test account + AccountID const id(calcAccountID( + generateKeyPair(KeyType::secp256k1, generateSeed("alice")).first)); + + wasm_val_vec_t params = {2, &values[0], 2, sizeof(wasm_val_t), nullptr}; + + values[0] = WASM_I32_VAL(0); + values[1] = WASM_I32_VAL(id.bytes); + memcpy(&buffer[0], id.data(), id.bytes); + + int index = 0; + auto const result = getDataAccountID(&rt, ¶ms, index); + if (!result || result.value() != id) + return false; + } + + { + // test uint256 + + Hash h1 = sha512Half(Slice(buffer.data(), 8)); + wasm_val_vec_t params = {2, &values[0], 2, sizeof(wasm_val_t), nullptr}; + + values[0] = WASM_I32_VAL(0); + values[1] = WASM_I32_VAL(h1.bytes); + memcpy(&buffer[0], h1.data(), h1.bytes); + + int index = 0; + auto const result = getDataUInt256(&rt, ¶ms, index); + if (!result || result.value() != h1) + return false; + } + + return true; +} + +} // namespace test } // namespace ripple