mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Host-functions perf test fixes (#5514)
This commit is contained in:
@@ -30,7 +30,6 @@
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <iterator>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include <xrpld/app/misc/WasmHostFunc.h>
|
||||
#include <xrpld/app/misc/WasmVM.h>
|
||||
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
||||
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||
|
||||
#include <wasm_c_api.h>
|
||||
|
||||
@@ -385,6 +387,571 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct PerfHostFunctions : public HostFunctions
|
||||
{
|
||||
test::jtx::Env& env_;
|
||||
|
||||
Keylet leKey;
|
||||
static int constexpr MAX_CACHE = 256;
|
||||
std::array<std::shared_ptr<SLE const>, MAX_CACHE> cache;
|
||||
std::shared_ptr<STTx const> tx_;
|
||||
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
public:
|
||||
PerfHostFunctions(
|
||||
test::jtx::Env& env,
|
||||
Keylet const& k,
|
||||
std::shared_ptr<STTx const>&& tx)
|
||||
: env_(env), leKey(k), tx_(std::move(tx))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
setRT(void const* rt) override
|
||||
{
|
||||
rt_ = rt;
|
||||
}
|
||||
|
||||
virtual void const*
|
||||
getRT() const override
|
||||
{
|
||||
return rt_;
|
||||
}
|
||||
|
||||
beast::Journal
|
||||
getJournal() override
|
||||
{
|
||||
return env_.journal;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getLedgerSqn() override
|
||||
{
|
||||
return static_cast<int32_t>(env_.current()->seq());
|
||||
}
|
||||
|
||||
int32_t
|
||||
getParentLedgerTime() override
|
||||
{
|
||||
return env_.current()->parentCloseTime().time_since_epoch().count();
|
||||
}
|
||||
|
||||
Hash
|
||||
getParentLedgerHash() override
|
||||
{
|
||||
return env_.current()->info().parentHash;
|
||||
}
|
||||
|
||||
virtual int32_t
|
||||
cacheLedgerObj(Keylet const&, int32_t cacheIdx) override
|
||||
{
|
||||
static int32_t intIdx = 0;
|
||||
|
||||
if (cacheIdx < 0 || cacheIdx > MAX_CACHE)
|
||||
return HF_ERR_SLOT_OUT_RANGE;
|
||||
|
||||
if (!cacheIdx)
|
||||
{
|
||||
for (cacheIdx = 0; cacheIdx < MAX_CACHE; ++cacheIdx)
|
||||
if (!cache[cacheIdx])
|
||||
break;
|
||||
if (cacheIdx >= MAX_CACHE)
|
||||
cacheIdx = intIdx++ % MAX_CACHE;
|
||||
}
|
||||
else
|
||||
--cacheIdx;
|
||||
|
||||
if (cacheIdx >= MAX_CACHE)
|
||||
return HF_ERR_SLOTS_FULL;
|
||||
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
return cache[cacheIdx] ? cacheIdx + 1 : HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
}
|
||||
|
||||
static Bytes
|
||||
getAnyFieldData(STBase const& obj)
|
||||
{
|
||||
// auto const& fname = obj.getFName();
|
||||
auto const stype = obj.getSType();
|
||||
switch (stype)
|
||||
{
|
||||
case STI_UNKNOWN:
|
||||
case STI_NOTPRESENT:
|
||||
return {};
|
||||
break;
|
||||
case STI_ACCOUNT: {
|
||||
auto const& super(static_cast<STAccount const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_AMOUNT: {
|
||||
auto const& super(static_cast<STAmount const&>(obj));
|
||||
int64_t const data = super.xrp().drops();
|
||||
auto const* b = reinterpret_cast<uint8_t const*>(&data);
|
||||
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
|
||||
return {b, e};
|
||||
}
|
||||
break;
|
||||
case STI_VL: {
|
||||
auto const& super(static_cast<STBlob const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_UINT256: {
|
||||
auto const& super(static_cast<STBitString<256> const&>(obj));
|
||||
auto const& data = super.value();
|
||||
return {data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
case STI_UINT32: {
|
||||
auto const& super(
|
||||
static_cast<STInteger<std::uint32_t> const&>(obj));
|
||||
std::uint32_t const data = super.value();
|
||||
auto const* b = reinterpret_cast<uint8_t const*>(&data);
|
||||
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
|
||||
return {b, e};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Serializer msg;
|
||||
obj.add(msg);
|
||||
|
||||
return msg.getData();
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getTxField(SField const& fname) override
|
||||
{
|
||||
auto const* field = tx_->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getCurrentLedgerObjField(SField const& fname) override
|
||||
{
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const* field = sle->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HF_ERR_SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HF_ERR_INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const* field = cache[cacheIdx]->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
static Expected<STBase const*, int32_t>
|
||||
locateField(STObject const& obj, Slice const& loc)
|
||||
{
|
||||
if (loc.empty() || (loc.size() & 3)) // must be multiple of 4
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
|
||||
int32_t const* l = reinterpret_cast<int32_t const*>(loc.data());
|
||||
int32_t const sz = loc.size() / 4;
|
||||
STBase const* field = nullptr;
|
||||
auto const& m = SField::getKnownCodeToField();
|
||||
|
||||
{
|
||||
int32_t const c = l[0];
|
||||
auto const it = m.find(c);
|
||||
if (it == m.end())
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
auto const& fname(*it->second);
|
||||
|
||||
field = obj.peekAtPField(fname);
|
||||
if (!field || (STI_NOTPRESENT == field->getSType()))
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
for (int i = 1; i < sz; ++i)
|
||||
{
|
||||
int32_t const c = l[i];
|
||||
|
||||
if (STI_ARRAY == field->getSType())
|
||||
{
|
||||
auto const* arr = static_cast<STArray const*>(field);
|
||||
if (c >= arr->size())
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
field = &(arr->operator[](c));
|
||||
}
|
||||
else if (STI_OBJECT == field->getSType())
|
||||
{
|
||||
auto const* o = static_cast<STObject const*>(field);
|
||||
|
||||
auto const it = m.find(c);
|
||||
if (it == m.end())
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
auto const& fname(*it->second);
|
||||
|
||||
field = o->peekAtPField(fname);
|
||||
}
|
||||
else // simple field must be the last one
|
||||
{
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
}
|
||||
|
||||
if (!field || (STI_NOTPRESENT == field->getSType()))
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getTxNestedField(Slice const& locator) override
|
||||
{
|
||||
// std::cout << tx_->getJson(JsonOptions::none).toStyledString() <<
|
||||
// std::endl;
|
||||
auto const r = locateField(*tx_, locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator) override
|
||||
{
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const r = locateField(*sle, locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HF_ERR_SLOT_OUT_RANGE);
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HF_ERR_INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const r = locateField(*cache[cacheIdx], locator);
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if ((STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
int32_t
|
||||
getTxArrayLen(SField const& fname) override
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
|
||||
auto const* field = tx_->peekAtPField(fname);
|
||||
if (!field)
|
||||
return HF_ERR_FIELD_NOT_FOUND;
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) override
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
|
||||
auto const* field = sle->peekAtPField(fname);
|
||||
if (!field)
|
||||
return HF_ERR_FIELD_NOT_FOUND;
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return HF_ERR_SLOT_OUT_RANGE;
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HF_ERR_INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const* field = cache[cacheIdx]->peekAtPField(fname);
|
||||
if (!field)
|
||||
return HF_ERR_FIELD_NOT_FOUND;
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getTxNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
auto const r = locateField(*tx_, locator);
|
||||
if (!r)
|
||||
return r.error();
|
||||
auto const* field = r.value();
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override
|
||||
{
|
||||
auto const sle = env_.le(leKey);
|
||||
if (!sle)
|
||||
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
auto const r = locateField(*sle, locator);
|
||||
if (!r)
|
||||
return r.error();
|
||||
auto const* field = r.value();
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return HF_ERR_SLOT_OUT_RANGE;
|
||||
|
||||
if (!cache[cacheIdx])
|
||||
{
|
||||
// return Unexpected(HF_ERR_INVALID_SLOT);
|
||||
cache[cacheIdx] = env_.le(leKey);
|
||||
}
|
||||
|
||||
auto const r = locateField(*cache[cacheIdx], locator);
|
||||
if (!r)
|
||||
return r.error();
|
||||
|
||||
auto const* field = r.value();
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return HF_ERR_NO_ARRAY;
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int32_t
|
||||
updateData(Bytes const& data) override
|
||||
{
|
||||
ripple::detail::ApplyViewBase v(
|
||||
env_.app().openLedger().current().get(), tapNONE);
|
||||
|
||||
auto sle = v.peek(leKey);
|
||||
if (!sle)
|
||||
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
|
||||
sle->setFieldVL(sfData, data);
|
||||
v.update(sle);
|
||||
|
||||
return data.size();
|
||||
}
|
||||
|
||||
Hash
|
||||
computeSha512HalfHash(Bytes const& data) override
|
||||
{
|
||||
auto const hash = sha512Half(data);
|
||||
return hash;
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
accountKeylet(AccountID const& account) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::account(account);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
credentialKeylet(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Bytes const& credentialType) override
|
||||
{
|
||||
if (!subject || !issuer || credentialType.empty() ||
|
||||
credentialType.size() > maxCredentialTypeLength)
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
|
||||
auto const keylet =
|
||||
keylet::credential(subject, issuer, makeSlice(credentialType));
|
||||
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::escrow(account, seq);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t documentId) override
|
||||
{
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::oracle(account, documentId);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
}
|
||||
|
||||
Expected<Bytes, int32_t>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) override
|
||||
{
|
||||
if (!account || !nftId)
|
||||
{
|
||||
getJournal().trace() << "WAMR getNFT: Invalid account or NFT ID";
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
auto obj = nft::findToken(*env_.current(), account, nftId);
|
||||
if (!obj)
|
||||
{
|
||||
getJournal().trace() << "WAMR getNFT: NFT not found";
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
}
|
||||
|
||||
auto ouri = obj->at(~sfURI);
|
||||
if (!ouri)
|
||||
return Bytes();
|
||||
|
||||
Slice const s = ouri->value();
|
||||
return Bytes(s.begin(), s.end());
|
||||
}
|
||||
|
||||
int32_t
|
||||
trace(std::string const& msg, Bytes const& data, bool asHex) override
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
auto& j = std::cerr;
|
||||
#else
|
||||
auto j = getJournal().error();
|
||||
#endif
|
||||
if (!asHex)
|
||||
j << msg
|
||||
<< std::string_view(
|
||||
reinterpret_cast<char const*>(data.data()), data.size());
|
||||
else
|
||||
{
|
||||
auto const hex =
|
||||
boost::algorithm::hex(std::string(data.begin(), data.end()));
|
||||
j << msg << hex;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
j << std::endl;
|
||||
#endif
|
||||
|
||||
return msg.size() + data.size() * (asHex ? 2 : 1);
|
||||
}
|
||||
|
||||
int32_t
|
||||
traceNum(std::string const& msg, int64_t data) override
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
auto& j = std::cerr;
|
||||
#else
|
||||
auto j = getJournal().error();
|
||||
#endif
|
||||
j << msg << data;
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
j << std::endl;
|
||||
#endif
|
||||
return msg.size() + sizeof(data);
|
||||
}
|
||||
};
|
||||
|
||||
struct Wasm_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
@@ -831,6 +1398,103 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
perfTest()
|
||||
{
|
||||
testcase("Perf test host functions");
|
||||
|
||||
using namespace jtx;
|
||||
using namespace std::chrono;
|
||||
|
||||
std::string const funcName("finish");
|
||||
// std::string const funcName("test");
|
||||
auto const& wasmHex = hfPerfTest;
|
||||
// auto const& wasmHex = opcCallPerfTest;
|
||||
std::string const wasmStr = boost::algorithm::unhex(wasmHex);
|
||||
std::vector<uint8_t> const wasm(wasmStr.begin(), wasmStr.end());
|
||||
|
||||
std::string const credType = "abcde";
|
||||
std::string const credType2 = "fghijk";
|
||||
std::string const credType3 = "0123456";
|
||||
// char const uri[] = "uri";
|
||||
|
||||
Account const alan{"alan"};
|
||||
Account const bob{"bob"};
|
||||
Account const issuer{"issuer"};
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
// Env env(*this, envconfig(), {}, nullptr,
|
||||
// beast::severities::kTrace);
|
||||
env.fund(XRP(5000), alan, bob, issuer);
|
||||
env.close();
|
||||
|
||||
// // create escrow
|
||||
// auto const seq = env.seq(alan);
|
||||
// auto const k = keylet::escrow(alan, seq);
|
||||
// // auto const allowance = 3'600;
|
||||
// auto escrowCreate = escrow::create(alan, bob, XRP(1000));
|
||||
// XRPAmount txnFees = env.current()->fees().base + 1000;
|
||||
// env(escrowCreate,
|
||||
// escrow::finish_function(wasmHex),
|
||||
// escrow::finish_time(env.now() + 11s),
|
||||
// escrow::cancel_time(env.now() + 100s),
|
||||
// escrow::data("1000000000"), // 1000 XRP in drops
|
||||
// memodata("memo1234567"),
|
||||
// memodata("2memo1234567"),
|
||||
// fee(txnFees));
|
||||
|
||||
// // create depositPreauth
|
||||
// auto const k = keylet::depositPreauth(
|
||||
// bob,
|
||||
// {{issuer.id(), makeSlice(credType)},
|
||||
// {issuer.id(), makeSlice(credType2)},
|
||||
// {issuer.id(), makeSlice(credType3)}});
|
||||
// env(deposit::authCredentials(
|
||||
// bob,
|
||||
// {{issuer, credType},
|
||||
// {issuer, credType2},
|
||||
// {issuer, credType3}}));
|
||||
|
||||
// cREATE nft
|
||||
[[maybe_unused]] uint256 const nft0{
|
||||
token::getNextID(env, alan, 0u)};
|
||||
env(token::mint(alan, 0u));
|
||||
auto const k = keylet::nftoffer(alan, 0);
|
||||
[[maybe_unused]] uint256 const nft1{
|
||||
token::getNextID(env, alan, 0u)};
|
||||
|
||||
env(token::mint(alan, 0u),
|
||||
token::uri(
|
||||
"https://github.com/XRPLF/XRPL-Standards/discussions/"
|
||||
"279?id=github.com/XRPLF/XRPL-Standards/discussions/"
|
||||
"279&ut=github.com/XRPLF/XRPL-Standards/discussions/"
|
||||
"279&sid=github.com/XRPLF/XRPL-Standards/discussions/"
|
||||
"279&aot=github.com/XRPLF/XRPL-Standards/disc"));
|
||||
[[maybe_unused]] uint256 const nft2{
|
||||
token::getNextID(env, alan, 0u)};
|
||||
env(token::mint(alan, 0u));
|
||||
env.close();
|
||||
|
||||
PerfHostFunctions nfs(env, k, env.tx());
|
||||
|
||||
auto re = runEscrowWasm(wasm, funcName, {}, &nfs);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(re->result);
|
||||
std::cout << "Res: " << re->result << " cost: " << re->cost
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// env(escrow::finish(alan, alan, seq),
|
||||
// escrow::comp_allowance(allowance),
|
||||
// fee(txnFees),
|
||||
// ter(tesSUCCESS));
|
||||
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -853,6 +1517,8 @@ struct Wasm_test : public beast::unit_test::suite
|
||||
// TODO: fix result
|
||||
testEscrowWasmDN1();
|
||||
testEscrowWasmDN2();
|
||||
|
||||
// perfTest();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11709,3 +11709,50 @@ extern std::string const reqNonexistentField =
|
||||
"2e31202834656231363132353020323032352d30332d31352900490f7461726765745f6665"
|
||||
"617475726573042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f72"
|
||||
"65666572656e63652d74797065732b0a6d756c746976616c7565";
|
||||
|
||||
extern std::string const hfPerfTest =
|
||||
"0061736d0100000001180460037f7f7e017f60057f7f7f7f7f017f600000"
|
||||
"6000017f021d0203656e760974726163655f6e756d000003656e76057472"
|
||||
"616365000103030202030503010002068601167f0041a0090b7f0041a011"
|
||||
"0b7f0041e0080b7f0041a0080b7f0041c0080b7f004180080b7f00418408"
|
||||
"0b7f004188080b7f00418c080b7f004190080b7f004194080b7f00419808"
|
||||
"0b7f004180090b7f004180080b7f0041e0110b7f0041e0110b7f0041e091"
|
||||
"040b7f004180080b7f0041e091040b7f00418080080b7f0041000b7f0041"
|
||||
"010b07c50219066d656d6f72790200115f5f7761736d5f63616c6c5f6374"
|
||||
"6f727300020666696e6973680003036275660300076c6f635f6275660301"
|
||||
"036d73670302086572725f686561640303096572725f646174613103040a"
|
||||
"53465f4163636f756e7403050e53465f44657374696e6174696f6e030608"
|
||||
"53465f4d656d6f7303070753465f4d656d6f03080b53465f4d656d6f4461"
|
||||
"746103090753465f44617461030a1753465f417574686f72697a65437265"
|
||||
"64656e7469616c73030b066163635f6964030c0c5f5f64736f5f68616e64"
|
||||
"6c65030d0a5f5f646174615f656e64030e0b5f5f737461636b5f6c6f7703"
|
||||
"0f0c5f5f737461636b5f6869676803100d5f5f676c6f62616c5f62617365"
|
||||
"03110b5f5f686561705f6261736503120a5f5f686561705f656e6403130d"
|
||||
"5f5f6d656d6f72795f6261736503140c5f5f7461626c655f626173650315"
|
||||
"0aa2010202000b9c0102017f017e41807821000340200041a0116a420037"
|
||||
"0300200041086a22000d000b41d811420037030041d011420037030041c8"
|
||||
"11420037030041c011420037030041b811420037030041b0114200370300"
|
||||
"41a811428d801c37030041a011429a803c3703000240034041e008411020"
|
||||
"0110004118460440200142017c220142c0843d520d010c020b0b41a00841"
|
||||
"1441c0084113410010011a0b41010b0b840104004180080b340100080003"
|
||||
"00080009000f000a000e000d0007001b0007001a000f0000000000676574"
|
||||
"5f6c65646765725f73716e206572726f720041c0080b13696e76616c6964"
|
||||
"2072657475726e2073697a650041e0080b104e6577206163636f756e7420"
|
||||
"69643a20004180090b14fc4f9afac9f1a8db807fda7dc9247bb557569d58"
|
||||
"007f0970726f647563657273010c70726f6365737365642d62790105636c"
|
||||
"616e675f31392e312e352d776173692d73646b202868747470733a2f2f67"
|
||||
"69746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a656374206162"
|
||||
"346235613264623538323935386166316565333038613739306366646234"
|
||||
"32626432343732302900490f7461726765745f6665617475726573042b0f"
|
||||
"6d757461626c652d676c6f62616c732b087369676e2d6578742b0f726566"
|
||||
"6572656e63652d74797065732b0a6d756c746976616c7565";
|
||||
|
||||
extern std::string const opcCallPerfTest =
|
||||
"0061736d010000000112046000017e60027f7f017f6000017f6000000304"
|
||||
"0303020104050170010101050401008002061c047f0141b08880040b7f00"
|
||||
"41ac080b7f0041b08880040b7f01410a0b073305066d656d6f7279020004"
|
||||
"6d61696e0002047465737400010a5f5f646174615f656e6403010b5f5f68"
|
||||
"6561705f6261736503020a4e030400010f0b3b04017f017c017f027e4180"
|
||||
"84afdf0021004494b768e11b6c9b42210141d1e3c6012102024003401000"
|
||||
"200041016b220041004a0d000b0b41010f0b0b01017f1001210220020f0b"
|
||||
"0b0e01004180080b07623a2025660a00";
|
||||
|
||||
@@ -56,3 +56,7 @@ extern std::string const xrplStdExampleHex;
|
||||
extern std::string const hostFunctions2Hex;
|
||||
|
||||
extern std::string const reqNonexistentField;
|
||||
|
||||
extern std::string const hfPerfTest;
|
||||
|
||||
extern std::string const opcCallPerfTest;
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
// #define DEBUG_OUTPUT_WAMR 1
|
||||
#endif
|
||||
|
||||
// #define SHOW_CALL_TIME 1
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace {
|
||||
@@ -738,6 +740,18 @@ WamrEngine::call(FuncInfo const& f, Types&&... args)
|
||||
return call<NR>(f, in, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
#ifdef SHOW_CALL_TIME
|
||||
static inline uint64_t
|
||||
usecs()
|
||||
{
|
||||
uint64_t x =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count();
|
||||
return x;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <int NR, class... Types>
|
||||
WamrResult
|
||||
WamrEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in)
|
||||
@@ -762,7 +776,19 @@ WamrEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in)
|
||||
in.size(),
|
||||
sizeof(std::remove_reference_t<decltype(in)>::value_type),
|
||||
nullptr};
|
||||
|
||||
#ifdef SHOW_CALL_TIME
|
||||
auto const start = usecs();
|
||||
#endif
|
||||
|
||||
wasm_trap_t* trap = wasm_func_call(f.first, &inv, &ret.r);
|
||||
|
||||
#ifdef SHOW_CALL_TIME
|
||||
auto const finish = usecs();
|
||||
auto const delta_ms = (finish - start) / 1000;
|
||||
std::cout << "wasm_func_call: " << delta_ms << "ms" << std::endl;
|
||||
#endif
|
||||
|
||||
if (trap)
|
||||
{
|
||||
ret.f = true;
|
||||
|
||||
@@ -39,7 +39,7 @@ WasmHostFunctionsImpl::getLedgerSqn()
|
||||
int32_t
|
||||
WasmHostFunctionsImpl::getParentLedgerTime()
|
||||
{
|
||||
return ctx.view().parentCloseTime().time_since_epoch().count(); // TODO try
|
||||
return ctx.view().parentCloseTime().time_since_epoch().count();
|
||||
}
|
||||
|
||||
Hash
|
||||
@@ -70,7 +70,7 @@ WasmHostFunctionsImpl::cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx)
|
||||
return cache[cacheIdx] ? cacheIdx + 1 : HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
}
|
||||
|
||||
Bytes
|
||||
static Bytes
|
||||
getAnyFieldData(STBase const& obj)
|
||||
{
|
||||
// auto const& fname = obj.getFName();
|
||||
@@ -132,6 +132,9 @@ WasmHostFunctionsImpl::getTxField(SField const& fname)
|
||||
auto const* field = ctx.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);
|
||||
}
|
||||
|
||||
@@ -145,6 +148,8 @@ WasmHostFunctionsImpl::getCurrentLedgerObjField(SField const& fname)
|
||||
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);
|
||||
}
|
||||
@@ -162,14 +167,16 @@ WasmHostFunctionsImpl::getLedgerObjField(int32_t cacheIdx, SField const& fname)
|
||||
auto const* field = cache[cacheIdx]->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
if ((STI_OBJECT == field->getSType()) || (STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_NOT_LEAF_FIELD);
|
||||
|
||||
return getAnyFieldData(*field);
|
||||
}
|
||||
|
||||
static Expected<STBase const*, int32_t>
|
||||
locateField(STObject const* obj, Slice const& loc)
|
||||
locateField(STObject const& obj, Slice const& loc)
|
||||
{
|
||||
if (loc.size() % 4)
|
||||
if (loc.empty() || (loc.size() & 3)) // must be multiple of 4
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
|
||||
int32_t const* l = reinterpret_cast<int32_t const*>(loc.data());
|
||||
@@ -177,22 +184,23 @@ locateField(STObject const* obj, Slice const& loc)
|
||||
STBase const* field = nullptr;
|
||||
auto const& m = SField::getKnownCodeToField();
|
||||
|
||||
for (int i = 0; i < sz; ++i)
|
||||
{
|
||||
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 (!field)
|
||||
{
|
||||
auto const it = m.find(c);
|
||||
if (it == m.end())
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
auto const& fname(*it->second);
|
||||
|
||||
field = obj->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
}
|
||||
else if (STI_ARRAY == field->getSType())
|
||||
if (STI_ARRAY == field->getSType())
|
||||
{
|
||||
auto const* arr = static_cast<STArray const*>(field);
|
||||
if (c >= arr->size())
|
||||
@@ -205,18 +213,19 @@ locateField(STObject const* obj, Slice const& loc)
|
||||
|
||||
auto const it = m.find(c);
|
||||
if (it == m.end())
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
auto const& fname(*it->second);
|
||||
|
||||
field = o->peekAtPField(fname);
|
||||
if (!field)
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
else // simple field must be the last one
|
||||
{
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
}
|
||||
|
||||
if (!field || (STI_OBJECT == field->getSType()) ||
|
||||
(STI_ARRAY == field->getSType()))
|
||||
return Unexpected(HF_ERR_LOCATOR_MALFORMED);
|
||||
if (!field || (STI_NOTPRESENT == field->getSType()))
|
||||
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
@@ -224,11 +233,14 @@ locateField(STObject const* obj, Slice const& loc)
|
||||
Expected<Bytes, int32_t>
|
||||
WasmHostFunctionsImpl::getTxNestedField(Slice const& locator)
|
||||
{
|
||||
auto const r = locateField(&ctx.tx, locator);
|
||||
if (!r.has_value())
|
||||
auto const r = locateField(ctx.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);
|
||||
}
|
||||
|
||||
@@ -239,11 +251,14 @@ WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(Slice const& locator)
|
||||
if (!sle)
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
|
||||
auto const r = locateField(sle.get(), locator);
|
||||
if (!r.has_value())
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -259,11 +274,14 @@ WasmHostFunctionsImpl::getLedgerObjNestedField(
|
||||
if (!cache[cacheIdx])
|
||||
return Unexpected(HF_ERR_INVALID_SLOT);
|
||||
|
||||
auto const r = locateField(cache[cacheIdx].get(), locator);
|
||||
if (!r.has_value())
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -333,8 +351,8 @@ WasmHostFunctionsImpl::getLedgerObjArrayLen(
|
||||
int32_t
|
||||
WasmHostFunctionsImpl::getTxNestedArrayLen(Slice const& locator)
|
||||
{
|
||||
auto const r = locateField(&ctx.tx, locator);
|
||||
if (!r.has_value())
|
||||
auto const r = locateField(ctx.tx, locator);
|
||||
if (!r)
|
||||
return r.error();
|
||||
auto const* field = r.value();
|
||||
|
||||
@@ -351,8 +369,8 @@ WasmHostFunctionsImpl::getCurrentLedgerObjNestedArrayLen(Slice const& locator)
|
||||
auto const sle = ctx.view().read(leKey);
|
||||
if (!sle)
|
||||
return HF_ERR_LEDGER_OBJ_NOT_FOUND;
|
||||
auto const r = locateField(sle.get(), locator);
|
||||
if (!r.has_value())
|
||||
auto const r = locateField(*sle, locator);
|
||||
if (!r)
|
||||
return r.error();
|
||||
auto const* field = r.value();
|
||||
|
||||
@@ -375,8 +393,8 @@ WasmHostFunctionsImpl::getLedgerObjNestedArrayLen(
|
||||
if (!cache[cacheIdx])
|
||||
return HF_ERR_INVALID_SLOT;
|
||||
|
||||
auto const r = locateField(cache[cacheIdx].get(), locator);
|
||||
if (!r.has_value())
|
||||
auto const r = locateField(*cache[cacheIdx], locator);
|
||||
if (!r)
|
||||
return r.error();
|
||||
|
||||
auto const* field = r.value();
|
||||
@@ -445,7 +463,7 @@ WasmHostFunctionsImpl::oracleKeylet(
|
||||
AccountID const& account,
|
||||
std::uint32_t documentId)
|
||||
{
|
||||
if (!account || account.isZero())
|
||||
if (!account)
|
||||
return Unexpected(HF_ERR_INVALID_ACCOUNT);
|
||||
auto const keylet = keylet::oracle(account, documentId);
|
||||
return Bytes{keylet.key.begin(), keylet.key.end()};
|
||||
@@ -467,7 +485,11 @@ WasmHostFunctionsImpl::getNFT(AccountID const& account, uint256 const& nftId)
|
||||
return Unexpected(HF_ERR_LEDGER_OBJ_NOT_FOUND);
|
||||
}
|
||||
|
||||
Slice const s = obj->at(sfURI);
|
||||
auto ouri = obj->at(~sfURI);
|
||||
if (!ouri)
|
||||
return Bytes();
|
||||
|
||||
Slice const s = ouri->value();
|
||||
return Bytes(s.begin(), s.end());
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@ setData(
|
||||
uint8_t const* src,
|
||||
int32_t ssz)
|
||||
{
|
||||
if (!ssz)
|
||||
return 0;
|
||||
|
||||
auto mem = rt ? rt->getMem() : wmem();
|
||||
|
||||
if (!mem.s)
|
||||
@@ -51,6 +54,9 @@ setData(
|
||||
static Expected<Bytes, int32_t>
|
||||
getData(InstanceWrapper const* rt, int32_t src, int32_t ssz)
|
||||
{
|
||||
if (!ssz)
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
|
||||
auto mem = rt ? rt->getMem() : wmem();
|
||||
if (!mem.s)
|
||||
return Unexpected(HF_ERR_NO_MEM_EXPORTED);
|
||||
@@ -66,7 +72,7 @@ static Expected<AccountID, int32_t>
|
||||
getDataAccount(InstanceWrapper const* rt, int32_t ptr, int32_t sz)
|
||||
{
|
||||
auto const r = getData(rt, ptr, sz);
|
||||
if (r->size() < AccountID::bytes)
|
||||
if (!r || (r->size() < AccountID::bytes))
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
|
||||
return AccountID::fromVoid(r->data());
|
||||
@@ -75,6 +81,9 @@ getDataAccount(InstanceWrapper const* rt, int32_t ptr, int32_t sz)
|
||||
static Expected<std::string, int32_t>
|
||||
getDataString(InstanceWrapper const* rt, int32_t src, int32_t ssz)
|
||||
{
|
||||
if (!ssz)
|
||||
return Unexpected(HF_ERR_INVALID_PARAMS);
|
||||
|
||||
auto mem = rt ? rt->getMem() : wmem();
|
||||
if (!mem.s)
|
||||
return Unexpected(HF_ERR_NO_MEM_EXPORTED);
|
||||
|
||||
Reference in New Issue
Block a user