Latests HF perf test (#5789)

This commit is contained in:
Olek
2025-09-18 15:51:39 -04:00
committed by GitHub
parent edfed06001
commit 6be8f2124c
8 changed files with 611 additions and 234 deletions

View File

@@ -1356,8 +1356,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
BEAST_EXPECT(result.has_value() && result.value() == 0);
// Should fail for too large data
std::vector<uint8_t> bigData(
1024 * 1024 + 1, 0x42); // > maxWasmDataLength
std::vector<uint8_t> bigData(maxWasmDataLength + 1, 0x42);
auto const tooBig =
hfs.updateData(Slice(bigData.data(), bigData.size()));
if (BEAST_EXPECT(!tooBig.has_value()))
@@ -2102,6 +2101,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
Bytes const float2 = {0xD4, 0x87, 0x1A, 0xFD, 0x49, 0x8D, 0x00, 0x00}; // 2
Bytes const float10 = {0xD4, 0xC3, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // 10
Bytes const floatInvalidZero = {0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // INVALID
Bytes const floatPi = {0xD4, 0x8B, 0x29, 0x43, 0x0A, 0x25, 0x6D, 0x21}; // 3.141592653589793
std::string const invalid = "invalid_data";
@@ -2744,8 +2744,14 @@ struct HostFuncImpl_test : public beast::unit_test::suite
}
{
auto const result =
hfs.floatPower(makeSlice(floatMaxIOU), 40000, 0);
auto const result = hfs.floatPower(makeSlice(floatMaxIOU), 81, 0);
BEAST_EXPECT(!result) &&
BEAST_EXPECT(
result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED);
}
{
auto const result = hfs.floatPower(makeSlice(floatMaxIOU), 2, 0);
BEAST_EXPECT(!result) &&
BEAST_EXPECT(
result.error() ==
@@ -2809,6 +2815,46 @@ struct HostFuncImpl_test : public beast::unit_test::suite
result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED);
}
// perf test logs
// {
// auto const result = hfs.floatLog(makeSlice(floatPi), 0);
// if (BEAST_EXPECT(result))
// {
// std::cout << "lg(" << floatToString(makeSlice(floatPi))
// << ") = " << floatToString(makeSlice(*result))
// << std::endl;
// }
// }
// {
// auto const result = hfs.floatLog(makeSlice(floatIntMax), 0);
// if (BEAST_EXPECT(result))
// {
// std::cout << "lg(" << floatToString(makeSlice(floatIntMax))
// << ") = " << floatToString(makeSlice(*result))
// << std::endl;
// }
// }
// {
// auto const result = hfs.floatLog(makeSlice(floatMaxExp), 0);
// if (BEAST_EXPECT(result))
// {
// std::cout << "lg(" << floatToString(makeSlice(floatMaxExp))
// << ") = " << floatToString(makeSlice(*result))
// << std::endl;
// }
// }
// {
// auto const result = hfs.floatLog(makeSlice(floatMaxIOU), 0);
// if (BEAST_EXPECT(result))
// {
// std::cout << "lg(" << floatToString(makeSlice(floatMaxIOU))
// << ") = " << floatToString(makeSlice(*result))
// << std::endl;
// }
// }
{
auto const x =
hfs.floatSet(9'500'000'000'000'001, -14, 0); // almost 80+15

View File

@@ -605,13 +605,17 @@ public:
struct PerfHostFunctions : public TestHostFunctions
{
Keylet leKey;
std::shared_ptr<SLE const> currentLedgerObj = nullptr;
bool isLedgerObjCached = false;
static int constexpr MAX_CACHE = 256;
std::array<std::shared_ptr<SLE const>, MAX_CACHE> cache;
// std::optional<Bytes> data_; // deferred data update, not used in
// performance
std::shared_ptr<STTx const> tx_;
void const* rt_ = nullptr;
public:
PerfHostFunctions(
test::jtx::Env& env,
Keylet const& k,
@@ -620,9 +624,113 @@ public:
{
}
Expected<std::int32_t, HostFunctionError>
getLedgerSqn() override
{
auto seq = env_.current()->seq();
if (seq > std::numeric_limits<int32_t>::max())
return Unexpected(HostFunctionError::INTERNAL); // LCOV_EXCL_LINE
return static_cast<int32_t>(seq);
}
Expected<std::int32_t, HostFunctionError>
getParentLedgerTime() override
{
auto time =
env_.current()->parentCloseTime().time_since_epoch().count();
if (time > std::numeric_limits<int32_t>::max())
return Unexpected(HostFunctionError::INTERNAL);
return static_cast<int32_t>(time);
}
Expected<Hash, HostFunctionError>
getParentLedgerHash() override
{
return env_.current()->info().parentHash;
}
Expected<Hash, HostFunctionError>
getLedgerAccountHash() override
{
return env_.current()->info().accountHash;
}
Expected<Hash, HostFunctionError>
getLedgerTransactionHash() override
{
return env_.current()->info().txHash;
}
Expected<int32_t, HostFunctionError>
getBaseFee() override
{
auto fee = env_.current()->fees().base.drops();
if (fee > std::numeric_limits<int32_t>::max())
return Unexpected(HostFunctionError::INTERNAL);
return static_cast<int32_t>(fee);
}
Expected<int32_t, HostFunctionError>
isAmendmentEnabled(uint256 const& amendmentId) override
{
return env_.current()->rules().enabled(amendmentId);
}
Expected<int32_t, HostFunctionError>
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<std::shared_ptr<SLE const>, HostFunctionError>
getCurrentLedgerObj()
{
if (!isLedgerObjCached)
{
isLedgerObjCached = true;
currentLedgerObj = env_.le(leKey);
}
if (currentLedgerObj)
return currentLedgerObj;
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
}
Expected<std::shared_ptr<SLE const>, 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<int32_t, HostFunctionError>
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<int32_t, HostFunctionError>
cacheLedgerObj(uint256 const&, int32_t cacheIdx) override
{
// auto const& keylet = keylet::unchecked(objId);
static int32_t intIdx = 0;
if (cacheIdx < 0 || cacheIdx > MAX_CACHE)
@@ -648,158 +756,157 @@ public:
return cacheIdx + 1;
}
static Bytes
getAnyFieldData(STBase const& obj)
static Expected<Bytes, HostFunctionError>
getAnyFieldData(STBase const* obj)
{
// auto const& fname = obj.getFName();
auto const stype = obj.getSType();
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 {};
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& super(static_cast<STAccount const&>(obj));
auto const& data = super.value();
return {data.begin(), data.end()};
auto const* account(static_cast<STAccount const*>(obj));
auto const& data = account->value();
return Bytes{data.begin(), data.end()};
}
break;
case STI_AMOUNT: {
auto const& super(static_cast<STAmount const&>(obj));
int64_t const data = super.xrp().drops();
auto const* b = reinterpret_cast<uint8_t const*>(&data);
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
return {b, e};
case STI_AMOUNT:
// will be processed by serializer
break;
case STI_ISSUE: {
auto const* issue(static_cast<STIssue const*>(obj));
Asset const& asset(issue->value());
// XRP and IOU will be processed by serializer
if (asset.holds<MPTIssue>())
{
// MPT
auto const& mptIssue = asset.get<MPTIssue>();
auto const& mptID = mptIssue.getMptID();
return Bytes{mptID.cbegin(), mptID.cend()};
}
}
break;
case STI_VL: {
auto const& super(static_cast<STBlob const&>(obj));
auto const& data = super.value();
return {data.begin(), data.end()};
auto const* vl(static_cast<STBlob const*>(obj));
auto const& data = vl->value();
return Bytes{data.begin(), data.end()};
}
break;
case STI_UINT256: {
auto const& super(static_cast<STBitString<256> const&>(obj));
auto const& data = super.value();
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();
case STI_UINT16: {
auto const& num(
static_cast<STInteger<std::uint16_t> const*>(obj));
std::uint16_t const data = num->value();
auto const* b = reinterpret_cast<uint8_t const*>(&data);
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
return {b, e};
return Bytes{b, e};
}
case STI_UINT32: {
auto const* num(
static_cast<STInteger<std::uint32_t> const*>(obj));
std::uint32_t const data = num->value();
auto const* b = reinterpret_cast<uint8_t const*>(&data);
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
return Bytes{b, e};
}
break;
default:
break;
break; // default to serializer
}
Serializer msg;
obj.add(msg);
obj->add(msg);
auto const data = msg.getData();
return msg.getData();
return data;
}
Expected<Bytes, HostFunctionError>
getTxField(SField const& fname) override
{
auto const* field = tx_->peekAtPField(fname);
if (!field)
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
if ((STI_OBJECT == field->getSType()) ||
(STI_ARRAY == field->getSType()))
return Unexpected(HostFunctionError::NOT_LEAF_FIELD);
return getAnyFieldData(*field);
return getAnyFieldData(tx_->peekAtPField(fname));
}
Expected<Bytes, HostFunctionError>
getCurrentLedgerObjField(SField const& fname) override
{
auto const sle = env_.le(leKey);
auto const sle = getCurrentLedgerObj();
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);
return Unexpected(sle.error());
return getAnyFieldData((*sle)->peekAtPField(fname));
}
Expected<Bytes, HostFunctionError>
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
{
--cacheIdx;
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
auto const sle = peekCurrentLedgerObj(cacheIdx);
if (!sle)
return Unexpected(sle.error());
return getAnyFieldData((*sle)->peekAtPField(fname));
}
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 inline bool
noField(STBase const* field)
{
return !field || (STI_NOTPRESENT == field->getSType()) ||
(STI_UNKNOWN == field->getSType());
}
static Expected<STBase const*, HostFunctionError>
locateField(STObject const& obj, Slice const& loc)
locateField(STObject const& obj, Slice const& locator)
{
if (loc.empty() || (loc.size() & 3)) // must be multiple of 4
if (locator.empty() || (locator.size() & 3)) // must be multiple of 4
return Unexpected(HostFunctionError::LOCATOR_MALFORMED);
int32_t const* l = reinterpret_cast<int32_t const*>(loc.data());
int32_t const sz = loc.size() / 4;
int32_t const* locPtr =
reinterpret_cast<int32_t const*>(locator.data());
int32_t const locSize = locator.size() / 4;
STBase const* field = nullptr;
auto const& m = SField::getKnownCodeToField();
auto const& knownSFields = 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);
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 (!field || (STI_NOTPRESENT == field->getSType()))
if (noField(field))
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
}
for (int i = 1; i < sz; ++i)
for (int i = 1; i < locSize; ++i)
{
int32_t const c = l[i];
int32_t const sfieldCode = locPtr[i];
if (STI_ARRAY == field->getSType())
{
auto const* arr = static_cast<STArray const*>(field);
if (c >= arr->size())
return Unexpected(HostFunctionError::LOCATOR_MALFORMED);
field = &(arr->operator[](c));
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<STObject const*>(field);
auto const it = m.find(c);
if (it == m.end())
return Unexpected(HostFunctionError::LOCATOR_MALFORMED);
auto const& fname(*it->second);
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
@@ -807,7 +914,7 @@ public:
return Unexpected(HostFunctionError::LOCATOR_MALFORMED);
}
if (!field || (STI_NOTPRESENT == field->getSType()))
if (noField(field))
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
}
@@ -822,57 +929,35 @@ public:
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);
return getAnyFieldData(*r);
}
Expected<Bytes, HostFunctionError>
getCurrentLedgerObjNestedField(Slice const& locator) override
{
auto const sle = env_.le(leKey);
auto const sle = getCurrentLedgerObj();
if (!sle)
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
return Unexpected(sle.error());
auto const r = locateField(*sle, locator);
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);
return getAnyFieldData(*r);
}
Expected<Bytes, HostFunctionError>
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override
{
--cacheIdx;
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
auto const sle = peekCurrentLedgerObj(cacheIdx);
if (!sle)
return Unexpected(sle.error());
if (!cache[cacheIdx])
{
// return Unexpected(HostFunctionError::INVALID_SLOT);
cache[cacheIdx] = env_.le(leKey);
}
auto const r = locateField(*cache[cacheIdx], locator);
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);
return getAnyFieldData(*r);
}
Expected<int32_t, HostFunctionError>
@@ -882,7 +967,7 @@ public:
return Unexpected(HostFunctionError::NO_ARRAY);
auto const* field = tx_->peekAtPField(fname);
if (!field)
if (noField(field))
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
if (field->getSType() != STI_ARRAY)
@@ -898,12 +983,12 @@ public:
if (fname.fieldType != STI_ARRAY)
return Unexpected(HostFunctionError::NO_ARRAY);
auto const sle = env_.le(leKey);
auto const sle = getCurrentLedgerObj();
if (!sle)
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
return Unexpected(sle.error());
auto const* field = sle->peekAtPField(fname);
if (!field)
auto const* field = (*sle)->peekAtPField(fname);
if (noField(field))
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
if (field->getSType() != STI_ARRAY)
@@ -919,17 +1004,12 @@ public:
if (fname.fieldType != STI_ARRAY)
return Unexpected(HostFunctionError::NO_ARRAY);
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
auto const sle = peekCurrentLedgerObj(cacheIdx);
if (!sle)
return Unexpected(sle.error());
if (!cache[cacheIdx])
{
// return Unexpected(HostFunctionError::INVALID_SLOT);
cache[cacheIdx] = env_.le(leKey);
}
auto const* field = cache[cacheIdx]->peekAtPField(fname);
if (!field)
auto const* field = (*sle)->peekAtPField(fname);
if (noField(field))
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
if (field->getSType() != STI_ARRAY)
@@ -957,10 +1037,11 @@ public:
Expected<int32_t, HostFunctionError>
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override
{
auto const sle = env_.le(leKey);
auto const sle = getCurrentLedgerObj();
if (!sle)
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
auto const r = locateField(*sle, locator);
return Unexpected(sle.error());
auto const r = locateField(**sle, locator);
if (!r)
return Unexpected(r.error());
auto const* field = r.value();
@@ -975,17 +1056,11 @@ public:
Expected<int32_t, HostFunctionError>
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override
{
--cacheIdx;
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
auto const sle = peekCurrentLedgerObj(cacheIdx);
if (!sle)
return Unexpected(sle.error());
if (!cache[cacheIdx])
{
// return Unexpected(HostFunctionError::INVALID_SLOT);
cache[cacheIdx] = env_.le(leKey);
}
auto const r = locateField(*cache[cacheIdx], locator);
auto const r = locateField(**sle, locator);
if (!r)
return Unexpected(r.error());
@@ -1001,6 +1076,9 @@ public:
Expected<int32_t, HostFunctionError>
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);
@@ -1014,6 +1092,19 @@ public:
return data.size();
}
Expected<int32_t, HostFunctionError>
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, /*canonical*/ true);
}
Expected<Hash, HostFunctionError>
computeSha512HalfHash(Slice const& data) override
{
@@ -1021,6 +1112,215 @@ public:
return hash;
}
Expected<Bytes, HostFunctionError>
accountKeylet(AccountID const& account) override
{
if (!account)
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
auto const keylet = keylet::account(account);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
Expected<Bytes, HostFunctionError>
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<MPTIssue>() || issue2.holds<MPTIssue>())
return Unexpected(HostFunctionError::INVALID_PARAMS);
auto const keylet = keylet::amm(issue1, issue2);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
Expected<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
escrowKeylet(AccountID const& account, std::uint32_t seq) override
{
if (!account)
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
auto const keylet = keylet::escrow(account, seq);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
Expected<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
oracleKeylet(AccountID const& account, std::uint32_t documentId) override
{
if (!account)
return Unexpected(HostFunctionError::INVALID_ACCOUNT);
auto const keylet = keylet::oracle(account, documentId);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
Expected<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
getNFT(AccountID const& account, uint256 const& nftId) override
{
@@ -1044,6 +1344,40 @@ public:
Slice const s = ouri->value();
return Bytes(s.begin(), s.end());
}
Expected<Bytes, HostFunctionError>
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<std::uint32_t, HostFunctionError>
getNFTTaxon(uint256 const& nftId) override
{
return nft::toUInt32(nft::getTaxon(nftId));
}
Expected<int32_t, HostFunctionError>
getNFTFlags(uint256 const& nftId) override
{
return nft::getFlags(nftId);
}
Expected<int32_t, HostFunctionError>
getNFTTransferFee(uint256 const& nftId) override
{
return nft::getTransferFee(nftId);
}
Expected<std::uint32_t, HostFunctionError>
getNFTSerial(uint256 const& nftId) override
{
return nft::getSerial(nftId);
}
};
} // namespace test

View File

@@ -519,7 +519,7 @@ struct Wasm_test : public beast::unit_test::suite
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECTS(re->result == 1, std::to_string(re->result));
BEAST_EXPECTS(re->cost == 91'412, std::to_string(re->cost));
BEAST_EXPECTS(re->cost == 96'942, std::to_string(re->cost));
}
env.close();
}
@@ -534,7 +534,7 @@ struct Wasm_test : public beast::unit_test::suite
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECTS(re->result == 1, std::to_string(re->result));
BEAST_EXPECTS(re->cost == 6'533, std::to_string(re->cost));
BEAST_EXPECTS(re->cost == 2'053, std::to_string(re->cost));
}
env.close();
}
@@ -550,13 +550,12 @@ struct Wasm_test : public beast::unit_test::suite
// 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";
// std::string const credType = "abcde";
// std::string const credType2 = "fghijk";
// std::string const credType3 = "0123456";
// char const uri[] = "uri";
Account const alan{"alan"};
@@ -597,7 +596,7 @@ struct Wasm_test : public beast::unit_test::suite
// {issuer, credType2},
// {issuer, credType3}}));
// cREATE nft
// create nft
[[maybe_unused]] uint256 const nft0{
token::getNextID(env, alan, 0u)};
env(token::mint(alan, 0u));
@@ -649,7 +648,7 @@ struct Wasm_test : public beast::unit_test::suite
Bytes const wasm(wasmStr.begin(), wasmStr.end());
TestHostFunctions hfs(env, 0);
auto const allowance = 148'406;
auto const allowance = 153'296;
auto re = runEscrowWasm(
wasm, ESCROW_FUNCTION_NAME, {}, &hfs, allowance, env.journal);

View File

@@ -9571,41 +9571,38 @@ extern std::string const deepRecursionHex =
"2d657874";
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";
"0061736d0100000001190460057f7f7f7f7f017f60047f7f7f7f017f6000006000017f0236"
"0303656e7609666c6f61745f6c6f67000003656e76057472616365000003656e7612747261"
"63655f6f70617175655f666c6f617400010303020203050301000206aa011c7f0041b0090b"
"7f004193090b7f0041b0080b7f0041c0080b7f0041e0080b7f004180090b7f004180080b7f"
"004184080b7f004188080b7f00418c080b7f004190080b7f004194080b7f004198080b7f00"
"419c080b7f0041a0080b7f0041a4080b7f0041a8080b7f0041ac080b7f00419b090b7f0041"
"80080b7f0041b0110b7f0041b0110b7f0041b091040b7f004180080b7f0041b091040b7f00"
"418080080b7f0041000b7f0041010b0785031f066d656d6f72790200115f5f7761736d5f63"
"616c6c5f63746f727300030666696e697368000403627566030001610301086572725f6865"
"61640302096572725f6461746131030305696e707574030406726573756c74030508495445"
"525f4d4158030609484153485f53495a450307084143435f53495a4503080d43555252454e"
"43595f53495a4503090b4b45594c45545f53495a45030a0a53465f4163636f756e74030b0e"
"53465f44657374696e6174696f6e030c0853465f4d656d6f73030d0753465f4d656d6f030e"
"0b53465f4d656d6f44617461030f0753465f4461746103101753465f417574686f72697a65"
"43726564656e7469616c730311016203120c5f5f64736f5f68616e646c6503130a5f5f6461"
"74615f656e6403140b5f5f737461636b5f6c6f7703150c5f5f737461636b5f686967680316"
"0d5f5f676c6f62616c5f6261736503170b5f5f686561705f6261736503180a5f5f68656170"
"5f656e6403190d5f5f6d656d6f72795f62617365031a0c5f5f7461626c655f62617365031b"
"0a7c0202000b7701017f41807821000340200041b0116a4200370300200041086a22000d00"
"0b41c0843d210002400340419309410841b009418008410010004108460440200041016b22"
"000d010c020b0b41b008410f41c0084113410010011a0b41e0084111419309410810021a41"
"8009411241b009410810021a41010b0b9f0104004180080b53809698002000000014000000"
"1400000020000000010008000300080009000f000a000e000d0007001b0007001a000f0066"
"6c6f61745f6c6f67206572726f7200696e76616c69642072657475726e2073697a650041e0"
"080b11666c6f61745f6c6f6720696e7075743a20004180090b12666c6f61745f6c6f672072"
"6573756c743a20004193090b10d48b29430a256d21d920c49ba5e353f8007f0970726f6475"
"63657273010c70726f6365737365642d62790105636c616e675f31392e312e352d77617369"
"2d73646b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d7072"
"6f6a6563742061623462356132646235383239353861663165653330386137393063666462"
"3432626432343732302900490f7461726765745f6665617475726573042b0f6d757461626c"
"652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b"
"0a6d756c746976616c7565";
extern std::string const allKeyletsWasmHex =
"0061736d0100000001530a60057f7f7f7f7f017f60047f7f7f7f017f60067f7f7f7f7f7f01"

View File

@@ -50,7 +50,15 @@ class WasmHostFunctionsImpl : public HostFunctions
}
Expected<int32_t, HostFunctionError>
normalizeCacheIndex(int32_t cacheIdx);
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;
}
public:
WasmHostFunctionsImpl(ApplyContext& ctx, Keylet const& leKey)

View File

@@ -90,17 +90,6 @@ WasmHostFunctionsImpl::isAmendmentEnabled(std::string_view const& amendmentName)
return ctx.view().rules().enabled(amendment);
}
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::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;
}
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::cacheLedgerObj(uint256 const& objId, int32_t cacheIdx)
{
@@ -1296,7 +1285,7 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode)
{
try
{
if (n < 0)
if ((n < 0) || (n > IOUAmount::maxExponent))
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
SetRound rm(mode);

View File

@@ -107,7 +107,11 @@ getDataSField(IW const* _runtime, wasm_val_vec_t const* params, int32_t& i)
template <class IW>
Expected<Slice, HostFunctionError>
getDataSlice(IW const* runtime, wasm_val_vec_t const* params, int32_t& i)
getDataSlice(
IW const* runtime,
wasm_val_vec_t const* params,
int32_t& i,
bool isUpdate = false)
{
auto const ptr = params->data[i].of.i32;
auto const size = params->data[i + 1].of.i32;
@@ -117,7 +121,7 @@ getDataSlice(IW const* runtime, wasm_val_vec_t const* params, int32_t& i)
if (!size)
return Slice();
if (size > maxWasmDataLength)
if (size > (isUpdate ? maxWasmDataLength : maxWasmParamLength))
return Unexpected(HostFunctionError::DATA_FIELD_TOO_LARGE);
auto memory = runtime ? runtime->getMem() : wmem();
@@ -751,7 +755,7 @@ updateData_wrap(
auto const* runtime = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
int index = 0;
auto const bytes = getDataSlice(runtime, params, index);
auto const bytes = getDataSlice(runtime, params, index, true);
if (!bytes)
{
return hfResult(results, bytes.error());
@@ -1491,7 +1495,7 @@ trace_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
auto const* runtime = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
int index = 0;
if (params->data[1].of.i32 + params->data[3].of.i32 > maxWasmDataLength)
if (params->data[1].of.i32 + params->data[3].of.i32 > maxWasmParamLength)
{
return hfResult(results, HostFunctionError::DATA_FIELD_TOO_LARGE);
}
@@ -1524,7 +1528,7 @@ traceNum_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* runtime = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
int index = 0;
if (params->data[1].of.i32 > maxWasmDataLength)
if (params->data[1].of.i32 > maxWasmParamLength)
{
return hfResult(results, HostFunctionError::DATA_FIELD_TOO_LARGE);
}
@@ -1554,7 +1558,7 @@ traceAccount_wrap(
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* runtime = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
if (params->data[1].of.i32 > maxWasmDataLength)
if (params->data[1].of.i32 > maxWasmParamLength)
return hfResult(results, HostFunctionError::DATA_FIELD_TOO_LARGE);
int i = 0;
@@ -1579,7 +1583,7 @@ traceFloat_wrap(
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* runtime = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
if (params->data[1].of.i32 > maxWasmDataLength)
if (params->data[1].of.i32 > maxWasmParamLength)
return hfResult(results, HostFunctionError::DATA_FIELD_TOO_LARGE);
int i = 0;
@@ -1604,7 +1608,7 @@ traceAmount_wrap(
auto* hf = reinterpret_cast<HostFunctions*>(env);
auto const* runtime = reinterpret_cast<InstanceWrapper const*>(hf->getRT());
if (params->data[1].of.i32 > maxWasmDataLength)
if (params->data[1].of.i32 > maxWasmParamLength)
return hfResult(results, HostFunctionError::DATA_FIELD_TOO_LARGE);
int i = 0;

View File

@@ -44,9 +44,9 @@ setCommonHostFunctions(HostFunctions* hfs, std::vector<WasmImportFunc>& i)
WASM_IMPORT_FUNC2(i, getLedgerAccountHash, "get_ledger_account_hash", hfs, 60);
WASM_IMPORT_FUNC2(i, getLedgerTransactionHash, "get_ledger_tx_hash", hfs, 60);
WASM_IMPORT_FUNC2(i, getBaseFee, "get_base_fee", hfs, 60);
WASM_IMPORT_FUNC2(i, isAmendmentEnabled, "amendment_enabled", hfs, 60);
WASM_IMPORT_FUNC2(i, isAmendmentEnabled, "amendment_enabled", hfs, 100);
WASM_IMPORT_FUNC2(i, cacheLedgerObj, "cache_ledger_obj", hfs, 5000);
WASM_IMPORT_FUNC2(i, cacheLedgerObj, "cache_ledger_obj", hfs, 5'000);
WASM_IMPORT_FUNC2(i, getTxField, "get_tx_field", hfs, 70);
WASM_IMPORT_FUNC2(i, getCurrentLedgerObjField, "get_current_ledger_obj_field", hfs, 70);
WASM_IMPORT_FUNC2(i, getLedgerObjField, "get_ledger_obj_field", hfs, 70);
@@ -60,20 +60,20 @@ setCommonHostFunctions(HostFunctions* hfs, std::vector<WasmImportFunc>& i)
WASM_IMPORT_FUNC2(i, getCurrentLedgerObjNestedArrayLen, "get_current_ledger_obj_nested_array_len", hfs, 70);
WASM_IMPORT_FUNC2(i, getLedgerObjNestedArrayLen, "get_ledger_obj_nested_array_len", hfs, 70);
WASM_IMPORT_FUNC2(i, checkSignature, "check_sig", hfs, 2000);
WASM_IMPORT_FUNC2(i, checkSignature, "check_sig", hfs, 300);
WASM_IMPORT_FUNC2(i, computeSha512HalfHash, "compute_sha512_half", hfs, 2000);
WASM_IMPORT_FUNC2(i, accountKeylet, "account_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, ammKeylet, "amm_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, ammKeylet, "amm_keylet", hfs, 450);
WASM_IMPORT_FUNC2(i, checkKeylet, "check_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, credentialKeylet, "credential_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, delegateKeylet, "delegate_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, depositPreauthKeylet, "deposit_preauth_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, didKeylet, "did_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, escrowKeylet, "escrow_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, lineKeylet, "line_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, lineKeylet, "line_keylet", hfs, 400);
WASM_IMPORT_FUNC2(i, mptIssuanceKeylet, "mpt_issuance_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, mptokenKeylet, "mptoken_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, mptokenKeylet, "mptoken_keylet", hfs, 500);
WASM_IMPORT_FUNC2(i, nftOfferKeylet, "nft_offer_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, offerKeylet, "offer_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, oracleKeylet, "oracle_keylet", hfs, 350);
@@ -84,7 +84,7 @@ setCommonHostFunctions(HostFunctions* hfs, std::vector<WasmImportFunc>& i)
WASM_IMPORT_FUNC2(i, vaultKeylet, "vault_keylet", hfs, 350);
WASM_IMPORT_FUNC2(i, getNFT, "get_nft", hfs, 1000);
WASM_IMPORT_FUNC2(i, getNFTIssuer, "get_nft_issuer", hfs, 60);
WASM_IMPORT_FUNC2(i, getNFTIssuer, "get_nft_issuer", hfs, 70);
WASM_IMPORT_FUNC2(i, getNFTTaxon, "get_nft_taxon", hfs, 60);
WASM_IMPORT_FUNC2(i, getNFTFlags, "get_nft_flags", hfs, 60);
WASM_IMPORT_FUNC2(i, getNFTTransferFee, "get_nft_transfer_fee", hfs, 60);
@@ -96,17 +96,17 @@ setCommonHostFunctions(HostFunctions* hfs, std::vector<WasmImportFunc>& i)
WASM_IMPORT_FUNC2(i, traceFloat, "trace_opaque_float", hfs, 500);
WASM_IMPORT_FUNC2(i, traceAmount, "trace_amount", hfs, 500);
WASM_IMPORT_FUNC2(i, floatFromInt, "float_from_int", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatFromUint, "float_from_uint", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatSet, "float_set", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatCompare, "float_compare", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatAdd, "float_add", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatSubtract, "float_subtract", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatMultiply, "float_multiply", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatDivide, "float_divide", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatRoot, "float_root", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatPower, "float_pow", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatLog, "float_log", hfs, 1000);
WASM_IMPORT_FUNC2(i, floatFromInt, "float_from_int", hfs, 100);
WASM_IMPORT_FUNC2(i, floatFromUint, "float_from_uint", hfs, 130);
WASM_IMPORT_FUNC2(i, floatSet, "float_set", hfs, 100);
WASM_IMPORT_FUNC2(i, floatCompare, "float_compare", hfs, 80);
WASM_IMPORT_FUNC2(i, floatAdd, "float_add", hfs, 160);
WASM_IMPORT_FUNC2(i, floatSubtract, "float_subtract", hfs, 160);
WASM_IMPORT_FUNC2(i, floatMultiply, "float_multiply", hfs, 300);
WASM_IMPORT_FUNC2(i, floatDivide, "float_divide", hfs, 300);
WASM_IMPORT_FUNC2(i, floatRoot, "float_root", hfs, 5'500);
WASM_IMPORT_FUNC2(i, floatPower, "float_pow", hfs, 5'500);
WASM_IMPORT_FUNC2(i, floatLog, "float_log", hfs, 12'000);
// clang-format on
}