#include #include #include #include #include #include #include #include namespace ripple { namespace test { struct TestLedgerDataProvider : public HostFunctions { jtx::Env* env_; void const* rt_ = nullptr; public: TestLedgerDataProvider(jtx::Env* env) : env_(env) { } virtual void setRT(void const* rt) override { rt_ = rt; } virtual void const* getRT() const override { return rt_; } Expected getLedgerSqn() override { return env_->current()->seq(); } }; 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) { accountID_ = env_.master.id(); 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 12345; } Expected getParentLedgerTime() override { return 67890; } Expected getParentLedgerHash() override { return env_.current()->info().parentHash; } Expected getBaseFee() override { return 10; } Expected isAmendmentEnabled(uint256 const& amendmentId) override { return 1; } Expected isAmendmentEnabled(std::string_view const& amendmentName) override { return 1; } 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 { if (fname == sfBalance) { int64_t x = 10'000; uint8_t const* p = reinterpret_cast(&x); return Bytes{p, p + sizeof(x)}; } else if (fname == sfAccount) { return Bytes(accountID_.begin(), accountID_.end()); } return data_; } Expected getTxNestedField(Slice const& locator) override { if (locator.size() == 4) { int32_t const* l = reinterpret_cast(locator.data()); int32_t const sfield = l[0]; if (sfield == sfAccount.fieldCode) { return Bytes(accountID_.begin(), accountID_.end()); } } 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 { if (locator.size() == 4) { int32_t const* l = reinterpret_cast(locator.data()); int32_t const sfield = l[0]; if (sfield == sfAccount.fieldCode) { return Bytes(accountID_.begin(), accountID_.end()); } } 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 { if (locator.size() == 4) { int32_t const* l = reinterpret_cast(locator.data()); int32_t const sfield = l[0]; if (sfield == sfAccount.fieldCode) { return Bytes(accountID_.begin(), accountID_.end()); } } 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 data.size(); } Expected checkSignature( Slice const& message, Slice const& signature, Slice const& pubkey) override { return 1; } 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 ammKeylet(Asset const& issue1, Asset const& issue2) override { if (issue1 == issue2) return Unexpected(HostFunctionError::INVALID_PARAMS); if (issue1.holds() || issue2.holds()) return Unexpected(HostFunctionError::INVALID_PARAMS); auto const keylet = keylet::amm(issue1, issue2); 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 getNFTIssuer(uint256 const& nftId) override { return Bytes(accountID_.begin(), accountID_.end()); } Expected getNFTTaxon(uint256 const& nftId) override { return 4; } Expected getNFTFlags(uint256 const& nftId) override { return 8; } Expected getNFTTransferFee(uint256 const& nftId) override { return 10; } Expected getNFTSerial(uint256 const& nftId) override { return 4; } 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 if (!asHex) { j << "WAMR TRACE: " << msg << " " << 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(), std::back_inserter(hex)); j << "WAMR DEV TRACE: " << msg << " " << 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 << "WAMR TRACE NUM: " << msg << " " << data; #ifdef DEBUG_OUTPUT j << std::endl; #endif return msg.size() + sizeof(data); } Expected traceAccount(std::string_view const& msg, AccountID const& account) override { #ifdef DEBUG_OUTPUT auto j = getJournal().error(); #else auto j = getJournal().trace(); #endif if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const accountStr = toBase58(account); j << "WAMR TRACE ACCOUNT: " << msg << " " << accountStr; return msg.size() + accountStr.size(); } Expected traceFloat(std::string_view const& msg, Slice const& data) override { #ifdef DEBUG_OUTPUT auto& j = std::cerr; #else auto j = getJournal().trace(); #endif auto const s = floatToString(data); j << "WAMR TRACE FLOAT: " << msg << " " << s; #ifdef DEBUG_OUTPUT j << std::endl; #endif return msg.size() + s.size(); } Expected traceAmount(std::string_view const& msg, STAmount const& amount) override { #ifdef DEBUG_OUTPUT auto j = getJournal().error(); #else auto j = getJournal().trace(); #endif auto const amountStr = amount.getFullText(); j << "WAMR TRACE AMOUNT: " << msg << " " << amountStr; return msg.size() + amountStr.size(); } Expected floatFromInt(int64_t x, int32_t mode) override { return floatFromIntImpl(x, mode); } Expected floatFromUint(uint64_t x, int32_t mode) override { return floatFromUintImpl(x, mode); } Expected floatSet(int64_t mantissa, int32_t exponent, int32_t mode) override { return floatSetImpl(mantissa, exponent, mode); } Expected floatCompare(Slice const& x, Slice const& y) override { return floatCompareImpl(x, y); } Expected floatAdd(Slice const& x, Slice const& y, int32_t mode) override { return floatAddImpl(x, y, mode); } Expected floatSubtract(Slice const& x, Slice const& y, int32_t mode) override { return floatSubtractImpl(x, y, mode); } Expected floatMultiply(Slice const& x, Slice const& y, int32_t mode) override { return floatMultiplyImpl(x, y, mode); } Expected floatDivide(Slice const& x, Slice const& y, int32_t mode) override { return floatDivideImpl(x, y, mode); } Expected floatRoot(Slice const& x, int32_t n, int32_t mode) override { return floatRootImpl(x, n, mode); } Expected floatPower(Slice const& x, int32_t n, int32_t mode) override { return floatPowerImpl(x, n, mode); } Expected floatLog(Slice const& x, int32_t mode) override { return floatLogImpl(x, mode); } }; 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; std::shared_ptr currentLedgerObj = nullptr; bool isLedgerObjCached = false; static int constexpr MAX_CACHE = 256; std::array, MAX_CACHE> cache; // std::optional data_; // deferred data update, not used in // performance std::shared_ptr tx_; void const* rt_ = nullptr; PerfHostFunctions( test::jtx::Env& env, Keylet const& k, std::shared_ptr&& tx) : TestHostFunctions(env), leKey(k), tx_(std::move(tx)) { } Expected getLedgerSqn() override { auto seq = env_.current()->seq(); if (seq > std::numeric_limits::max()) return Unexpected(HostFunctionError::INTERNAL); // LCOV_EXCL_LINE return static_cast(seq); } Expected getParentLedgerTime() override { auto time = env_.current()->parentCloseTime().time_since_epoch().count(); if (time > std::numeric_limits::max()) return Unexpected(HostFunctionError::INTERNAL); return static_cast(time); } Expected getParentLedgerHash() override { return env_.current()->info().parentHash; } Expected getBaseFee() override { auto fee = env_.current()->fees().base.drops(); if (fee > std::numeric_limits::max()) return Unexpected(HostFunctionError::INTERNAL); return static_cast(fee); } Expected isAmendmentEnabled(uint256 const& amendmentId) override { return env_.current()->rules().enabled(amendmentId); } Expected isAmendmentEnabled(std::string_view const& amendmentName) override { auto const& table = env_.app().getAmendmentTable(); auto const amendment = table.find(std::string(amendmentName)); return env_.current()->rules().enabled(amendment); } Expected, HostFunctionError> getCurrentLedgerObj() { if (!isLedgerObjCached) { isLedgerObjCached = true; currentLedgerObj = env_.le(leKey); } if (currentLedgerObj) return currentLedgerObj; return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND); } Expected, HostFunctionError> peekCurrentLedgerObj(int32_t cacheIdx) { --cacheIdx; if (cacheIdx < 0 || cacheIdx >= MAX_CACHE) return Unexpected(HostFunctionError::SLOT_OUT_RANGE); if (!cache[cacheIdx]) { // return Unexpected(HostFunctionError::INVALID_SLOT); auto const r = getCurrentLedgerObj(); if (!r) return Unexpected(r.error()); cache[cacheIdx] = *r; } return cache[cacheIdx]; } Expected normalizeCacheIndex(int32_t cacheIdx) { --cacheIdx; if (cacheIdx < 0 || cacheIdx >= MAX_CACHE) return Unexpected(HostFunctionError::SLOT_OUT_RANGE); if (!cache[cacheIdx]) return Unexpected(HostFunctionError::EMPTY_SLOT); return cacheIdx; } virtual Expected cacheLedgerObj(uint256 const&, int32_t cacheIdx) override { // auto const& keylet = keylet::unchecked(objId); 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 Expected getAnyFieldData(STBase const* obj) { // auto const& fname = obj.getFName(); if (!obj) return Unexpected(HostFunctionError::FIELD_NOT_FOUND); auto const stype = obj->getSType(); switch (stype) { // LCOV_EXCL_START case STI_UNKNOWN: case STI_NOTPRESENT: return Unexpected(HostFunctionError::FIELD_NOT_FOUND); break; // LCOV_EXCL_STOP case STI_OBJECT: case STI_ARRAY: return Unexpected(HostFunctionError::NOT_LEAF_FIELD); break; case STI_ACCOUNT: { auto const* account(static_cast(obj)); auto const& data = account->value(); return Bytes{data.begin(), data.end()}; } break; case STI_AMOUNT: // will be processed by serializer break; case STI_ISSUE: { auto const* issue(static_cast(obj)); Asset const& asset(issue->value()); // XRP and IOU will be processed by serializer if (asset.holds()) { // MPT auto const& mptIssue = asset.get(); auto const& mptID = mptIssue.getMptID(); return Bytes{mptID.cbegin(), mptID.cend()}; } } break; case STI_VL: { auto const* vl(static_cast(obj)); auto const& data = vl->value(); return Bytes{data.begin(), data.end()}; } break; case STI_UINT16: { auto const& num( static_cast const*>(obj)); std::uint16_t const data = num->value(); auto const* b = reinterpret_cast(&data); auto const* e = reinterpret_cast(&data + 1); return Bytes{b, e}; } case STI_UINT32: { 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}; } break; default: break; // default to serializer } Serializer msg; obj->add(msg); auto const data = msg.getData(); return data; } Expected getTxField(SField const& fname) override { return getAnyFieldData(tx_->peekAtPField(fname)); } Expected getCurrentLedgerObjField(SField const& fname) override { auto const sle = getCurrentLedgerObj(); if (!sle) return Unexpected(sle.error()); return getAnyFieldData((*sle)->peekAtPField(fname)); } Expected getLedgerObjField(int32_t cacheIdx, SField const& fname) override { auto const sle = peekCurrentLedgerObj(cacheIdx); if (!sle) return Unexpected(sle.error()); return getAnyFieldData((*sle)->peekAtPField(fname)); } static inline bool noField(STBase const* field) { return !field || (STI_NOTPRESENT == field->getSType()) || (STI_UNKNOWN == field->getSType()); } static Expected locateField(STObject const& obj, Slice const& locator) { if (locator.empty() || (locator.size() & 3)) // must be multiple of 4 return Unexpected(HostFunctionError::LOCATOR_MALFORMED); int32_t const* locPtr = reinterpret_cast(locator.data()); int32_t const locSize = locator.size() / 4; STBase const* field = nullptr; auto const& knownSFields = SField::getKnownCodeToField(); { int32_t const sfieldCode = locPtr[0]; auto const it = knownSFields.find(sfieldCode); if (it == knownSFields.end()) return Unexpected(HostFunctionError::INVALID_FIELD); auto const& fname(*it->second); field = obj.peekAtPField(fname); if (noField(field)) return Unexpected(HostFunctionError::FIELD_NOT_FOUND); } for (int i = 1; i < locSize; ++i) { int32_t const sfieldCode = locPtr[i]; if (STI_ARRAY == field->getSType()) { auto const* arr = static_cast(field); if (sfieldCode >= arr->size()) return Unexpected(HostFunctionError::INDEX_OUT_OF_BOUNDS); field = &(arr->operator[](sfieldCode)); } else if (STI_OBJECT == field->getSType()) { auto const* o = static_cast(field); auto const it = knownSFields.find(sfieldCode); if (it == knownSFields.end()) return Unexpected(HostFunctionError::INVALID_FIELD); auto const& fname(*it->second); field = o->peekAtPField(fname); } else // simple field must be the last one { return Unexpected(HostFunctionError::LOCATOR_MALFORMED); } if (noField(field)) 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()); return getAnyFieldData(*r); } Expected getCurrentLedgerObjNestedField(Slice const& locator) override { auto const sle = getCurrentLedgerObj(); if (!sle) return Unexpected(sle.error()); auto const r = locateField(**sle, locator); if (!r) return Unexpected(r.error()); return getAnyFieldData(*r); } Expected getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override { auto const sle = peekCurrentLedgerObj(cacheIdx); if (!sle) return Unexpected(sle.error()); auto const r = locateField(**sle, locator); if (!r) return Unexpected(r.error()); return getAnyFieldData(*r); } Expected getTxArrayLen(SField const& fname) override { if (fname.fieldType != STI_ARRAY) return Unexpected(HostFunctionError::NO_ARRAY); auto const* field = tx_->peekAtPField(fname); if (noField(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 = getCurrentLedgerObj(); if (!sle) return Unexpected(sle.error()); auto const* field = (*sle)->peekAtPField(fname); if (noField(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); auto const sle = peekCurrentLedgerObj(cacheIdx); if (!sle) return Unexpected(sle.error()); auto const* field = (*sle)->peekAtPField(fname); if (noField(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 = getCurrentLedgerObj(); if (!sle) return Unexpected(sle.error()); 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 { auto const sle = peekCurrentLedgerObj(cacheIdx); if (!sle) return Unexpected(sle.error()); 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 updateData(Slice const& data) override { if (data.size() > maxWasmDataLength) return Unexpected(HostFunctionError::DATA_FIELD_TOO_LARGE); 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 checkSignature( Slice const& message, Slice const& signature, Slice const& pubkey) override { if (!publicKeyType(pubkey)) return Unexpected(HostFunctionError::INVALID_PARAMS); PublicKey const pk(pubkey); return verify(pk, message, signature); } Expected computeSha512HalfHash(Slice const& data) override { auto const hash = sha512Half(data); return hash; } 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 ammKeylet(Asset const& issue1, Asset const& issue2) override { if (issue1 == issue2) return Unexpected(HostFunctionError::INVALID_PARAMS); // note: this should be removed with the MPT DEX amendment if (issue1.holds() || issue2.holds()) return Unexpected(HostFunctionError::INVALID_PARAMS); auto const keylet = keylet::amm(issue1, issue2); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected checkKeylet(AccountID const& account, std::uint32_t seq) override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::check(account, seq); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected credentialKeylet( AccountID const& subject, AccountID const& issuer, Slice const& credentialType) override { if (!subject || !issuer) return Unexpected(HostFunctionError::INVALID_ACCOUNT); if (credentialType.empty() || credentialType.size() > maxCredentialTypeLength) return Unexpected(HostFunctionError::INVALID_PARAMS); auto const keylet = keylet::credential(subject, issuer, credentialType); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected didKeylet(AccountID const& account) override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::did(account); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected delegateKeylet(AccountID const& account, AccountID const& authorize) override { if (!account || !authorize) return Unexpected(HostFunctionError::INVALID_ACCOUNT); if (account == authorize) return Unexpected(HostFunctionError::INVALID_PARAMS); auto const keylet = keylet::delegate(account, authorize); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected depositPreauthKeylet(AccountID const& account, AccountID const& authorize) override { if (!account || !authorize) return Unexpected(HostFunctionError::INVALID_ACCOUNT); if (account == authorize) return Unexpected(HostFunctionError::INVALID_PARAMS); auto const keylet = keylet::depositPreauth(account, authorize); 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 lineKeylet( AccountID const& account1, AccountID const& account2, Currency const& currency) override { if (!account1 || !account2) return Unexpected(HostFunctionError::INVALID_ACCOUNT); if (account1 == account2) return Unexpected(HostFunctionError::INVALID_PARAMS); if (currency.isZero()) return Unexpected(HostFunctionError::INVALID_PARAMS); auto const keylet = keylet::line(account1, account2, currency); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) override { if (!issuer) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::mptIssuance(seq, issuer); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected mptokenKeylet(MPTID const& mptid, AccountID const& holder) override { if (!mptid) return Unexpected(HostFunctionError::INVALID_PARAMS); if (!holder) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::mptoken(mptid, holder); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected nftOfferKeylet(AccountID const& account, std::uint32_t seq) override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::nftoffer(account, seq); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected offerKeylet(AccountID const& account, std::uint32_t seq) override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::offer(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 paychanKeylet( AccountID const& account, AccountID const& destination, std::uint32_t seq) override { if (!account || !destination) return Unexpected(HostFunctionError::INVALID_ACCOUNT); if (account == destination) return Unexpected(HostFunctionError::INVALID_PARAMS); auto const keylet = keylet::payChan(account, destination, seq); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::permissionedDomain(account, seq); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected signersKeylet(AccountID const& account) override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::signers(account); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected ticketKeylet(AccountID const& account, std::uint32_t seq) override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::ticket(account, seq); return Bytes{keylet.key.begin(), keylet.key.end()}; } Expected vaultKeylet(AccountID const& account, std::uint32_t seq) override { if (!account) return Unexpected(HostFunctionError::INVALID_ACCOUNT); auto const keylet = keylet::vault(account, seq); 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(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()); } Expected getNFTIssuer(uint256 const& nftId) override { auto const issuer = nft::getIssuer(nftId); if (!issuer) return Unexpected(HostFunctionError::INVALID_PARAMS); return Bytes{issuer.begin(), issuer.end()}; } Expected getNFTTaxon(uint256 const& nftId) override { return nft::toUInt32(nft::getTaxon(nftId)); } Expected getNFTFlags(uint256 const& nftId) override { return nft::getFlags(nftId); } Expected getNFTTransferFee(uint256 const& nftId) override { return nft::getTransferFee(nftId); } Expected getNFTSerial(uint256 const& nftId) override { return nft::getSerial(nftId); } }; } // namespace test } // namespace ripple