mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 17:27:00 +00:00
7492 lines
259 KiB
C++
7492 lines
259 KiB
C++
|
|
#include <test/jtx/Env.h>
|
|
#include <test/jtx/amount.h>
|
|
#include <test/jtx/escrow.h>
|
|
#include <test/jtx/multisign.h>
|
|
#include <test/jtx/token.h>
|
|
#include <test/jtx/txflags.h>
|
|
#include <test/unit_test/SuiteJournal.h>
|
|
|
|
#include <xrpl/basics/Number.h>
|
|
#include <xrpl/basics/Slice.h>
|
|
#include <xrpl/basics/base_uint.h>
|
|
#include <xrpl/basics/contract.h>
|
|
#include <xrpl/basics/strHex.h>
|
|
#include <xrpl/beast/unit_test/suite.h>
|
|
#include <xrpl/beast/utility/Journal.h>
|
|
#include <xrpl/ledger/ApplyView.h>
|
|
#include <xrpl/ledger/OpenView.h>
|
|
#include <xrpl/protocol/AccountID.h>
|
|
#include <xrpl/protocol/Asset.h>
|
|
#include <xrpl/protocol/Feature.h>
|
|
#include <xrpl/protocol/Indexes.h>
|
|
#include <xrpl/protocol/Issue.h>
|
|
#include <xrpl/protocol/KeyType.h>
|
|
#include <xrpl/protocol/MPTIssue.h>
|
|
#include <xrpl/protocol/Protocol.h>
|
|
#include <xrpl/protocol/PublicKey.h>
|
|
#include <xrpl/protocol/SField.h>
|
|
#include <xrpl/protocol/STAmount.h>
|
|
#include <xrpl/protocol/STIssue.h>
|
|
#include <xrpl/protocol/STNumber.h>
|
|
#include <xrpl/protocol/STObject.h>
|
|
#include <xrpl/protocol/STTx.h>
|
|
#include <xrpl/protocol/STVector256.h>
|
|
#include <xrpl/protocol/SecretKey.h>
|
|
#include <xrpl/protocol/Seed.h>
|
|
#include <xrpl/protocol/Serializer.h>
|
|
#include <xrpl/protocol/TER.h>
|
|
#include <xrpl/protocol/TxFlags.h>
|
|
#include <xrpl/protocol/TxFormats.h>
|
|
#include <xrpl/protocol/UintTypes.h>
|
|
#include <xrpl/protocol/digest.h>
|
|
#include <xrpl/tx/ApplyContext.h>
|
|
#include <xrpl/tx/wasm/HostFunc.h>
|
|
#include <xrpl/tx/wasm/HostFuncImpl.h>
|
|
#include <xrpl/tx/wasm/HostFuncWrapper.h>
|
|
#include <xrpl/tx/wasm/ParamsHelper.h>
|
|
#include <xrpl/tx/wasm/WasmVM.h>
|
|
#include <xrpl/tx/wasm/WasmiVM.h>
|
|
|
|
#include <boost/algorithm/hex.hpp>
|
|
|
|
#include <wasm.h>
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <exception>
|
|
#include <iomanip>
|
|
#include <ios>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace xrpl::test {
|
|
|
|
static Bytes
|
|
toBytes(std::uint8_t value)
|
|
{
|
|
return {value};
|
|
}
|
|
|
|
static Bytes
|
|
toBytes(std::uint16_t value)
|
|
{
|
|
auto const* b = reinterpret_cast<uint8_t const*>(&value);
|
|
auto const* e = reinterpret_cast<uint8_t const*>(&value + 1);
|
|
return Bytes{b, e};
|
|
}
|
|
|
|
static Bytes
|
|
toBytes(std::uint32_t value)
|
|
{
|
|
auto const* b = reinterpret_cast<uint8_t const*>(&value);
|
|
auto const* e = reinterpret_cast<uint8_t const*>(&value + 1);
|
|
return Bytes{b, e};
|
|
}
|
|
|
|
static Bytes
|
|
toBytes(uint256 const& value)
|
|
{
|
|
return Bytes{value.begin(), value.end()};
|
|
}
|
|
|
|
static Bytes
|
|
toBytes(Issue const& issue)
|
|
{
|
|
Serializer s;
|
|
s.addBitString(issue.currency);
|
|
if (!isXRP(issue.currency))
|
|
s.addBitString(issue.account);
|
|
auto const data = s.getData();
|
|
return data;
|
|
}
|
|
|
|
static Bytes
|
|
toBytes(Asset const& asset)
|
|
{
|
|
if (asset.holds<Issue>())
|
|
return toBytes(asset.get<Issue>());
|
|
|
|
auto const& mptIssue = asset.get<MPTIssue>();
|
|
auto const& mptID = mptIssue.getMptID();
|
|
return Bytes{mptID.cbegin(), mptID.cend()};
|
|
}
|
|
|
|
static Bytes
|
|
toBytes(STAmount const& amount)
|
|
{
|
|
Serializer msg;
|
|
amount.add(msg);
|
|
auto const data = msg.getData();
|
|
|
|
return data;
|
|
}
|
|
|
|
static Bytes
|
|
toBytes(STNumber const& number)
|
|
{
|
|
Serializer msg;
|
|
number.add(msg);
|
|
auto const data = msg.getData();
|
|
|
|
return data;
|
|
}
|
|
|
|
static ApplyContext
|
|
createApplyContext(
|
|
test::jtx::Env& env,
|
|
OpenView& ov,
|
|
beast::Journal j,
|
|
STTx const& tx = STTx(ttESCROW_FINISH, [](STObject&) {}))
|
|
{
|
|
ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, j};
|
|
return ac;
|
|
}
|
|
|
|
static ApplyContext
|
|
createApplyContext(
|
|
test::jtx::Env& env,
|
|
OpenView& ov,
|
|
STTx const& tx = STTx(ttESCROW_FINISH, [](STObject&) {}))
|
|
{
|
|
return createApplyContext(env, ov, env.journal, tx);
|
|
}
|
|
|
|
class VirtualRuntime : public WasmRuntimeWrapper
|
|
{
|
|
Bytes buffer_;
|
|
std::int64_t gas_ = 1'000'000;
|
|
|
|
public:
|
|
VirtualRuntime() : buffer_(1024 * 1024)
|
|
{
|
|
}
|
|
|
|
Wmem
|
|
getMem() override
|
|
{
|
|
return {.p = buffer_.data(), .s = buffer_.size()};
|
|
}
|
|
|
|
std::int64_t
|
|
getGas() override
|
|
{
|
|
gas_ -= 100;
|
|
return gas_;
|
|
}
|
|
|
|
std::int64_t
|
|
setGas(std::int64_t gas) override
|
|
{
|
|
if (gas == -2)
|
|
return -1;
|
|
|
|
if (gas < 0)
|
|
{
|
|
gas_ = std::numeric_limits<decltype(gas)>::max();
|
|
}
|
|
else
|
|
{
|
|
gas_ = gas;
|
|
}
|
|
|
|
return gas_;
|
|
}
|
|
|
|
void
|
|
checkIdx(WasmValVec const& params, size_t i) const
|
|
{
|
|
if (i + 1 >= params.size())
|
|
Throw<std::runtime_error>("Out of bounds");
|
|
if (params[i].kind != WASM_I32 || params[i + 1].kind != WASM_I32)
|
|
Throw<std::runtime_error>("Invalid params");
|
|
std::int32_t const ptr = params[i].of.i32;
|
|
std::int32_t const size = params[i + 1].of.i32;
|
|
std::int64_t const offset = (std::int64_t)ptr + size;
|
|
if (ptr < 0 || size < 0 || std::cmp_greater_equal(offset, buffer_.size()))
|
|
Throw<std::runtime_error>("Out of bounds");
|
|
}
|
|
|
|
[[nodiscard]] Slice
|
|
getBuffer(WasmValVec const& params, size_t i) const
|
|
{
|
|
checkIdx(params, i);
|
|
std::int32_t const ptr = params[i].of.i32;
|
|
std::int32_t const size = params[i + 1].of.i32;
|
|
return {&buffer_[ptr], static_cast<size_t>(size)};
|
|
}
|
|
|
|
[[nodiscard]] Bytes
|
|
getBytes(WasmValVec const& params, size_t i) const
|
|
{
|
|
checkIdx(params, i);
|
|
std::int32_t const ptr = params[i].of.i32;
|
|
std::int32_t const size = params[i + 1].of.i32;
|
|
return {&buffer_[ptr], &buffer_[ptr + size]};
|
|
}
|
|
|
|
void
|
|
setBytes(size_t ptr, void const* bytes, size_t size)
|
|
{
|
|
if (ptr + size >= buffer_.size())
|
|
Throw<std::runtime_error>("Out of bounds");
|
|
memcpy(&buffer_[ptr], bytes, size);
|
|
}
|
|
|
|
template <class T>
|
|
[[nodiscard]] [[nodiscard]] [[nodiscard]] [[nodiscard]] T
|
|
getInt(WasmValVec const& params, size_t i) const
|
|
{
|
|
checkIdx(params, i);
|
|
std::int32_t const ptr = params[i].of.i32;
|
|
std::int32_t const size = params[i + 1].of.i32;
|
|
if (size != sizeof(T))
|
|
Throw<std::runtime_error>("Invalid size");
|
|
return *reinterpret_cast<T const*>(&buffer_[ptr]);
|
|
}
|
|
|
|
[[nodiscard]] std::int32_t
|
|
getInt32(WasmValVec const& params, size_t i) const
|
|
{
|
|
return getInt<std::int32_t>(params, i);
|
|
}
|
|
|
|
[[nodiscard]] std::uint32_t
|
|
getUint32(WasmValVec const& params, size_t i) const
|
|
{
|
|
return getInt<std::uint32_t>(params, i);
|
|
}
|
|
|
|
[[nodiscard]] std::int64_t
|
|
getInt64(WasmValVec const& params, size_t i) const
|
|
{
|
|
return getInt<std::int64_t>(params, i);
|
|
}
|
|
|
|
[[nodiscard]] std::uint64_t
|
|
getUint64(WasmValVec const& params, size_t i) const
|
|
{
|
|
return getInt<std::uint64_t>(params, i);
|
|
}
|
|
};
|
|
|
|
template <class P, class E, typename Arg>
|
|
void
|
|
ww_hlp(size_t& idx, E&& e, P&& params, Arg&& arg)
|
|
{
|
|
if constexpr (std::is_integral_v<Arg>)
|
|
{
|
|
params[idx++] = std::is_same_v<Arg, int64_t> || std::is_same_v<Arg, long long>
|
|
? wasm_val_t WASM_I64_VAL(static_cast<int64_t>(arg))
|
|
: wasm_val_t WASM_I32_VAL(static_cast<int32_t>(arg));
|
|
}
|
|
else if constexpr (std::is_same_v<Arg, Issue>)
|
|
{
|
|
auto const* udata = reinterpret_cast<WasmUserData*>(e);
|
|
HostFunctions const* hf = reinterpret_cast<HostFunctions*>(udata->first);
|
|
auto* vrt = reinterpret_cast<VirtualRuntime*>(hf->getRT());
|
|
|
|
auto const data = toBytes(std::forward<Arg>(arg));
|
|
|
|
size_t const ptr = (idx << 10);
|
|
vrt->setBytes(ptr, data.data(), data.size());
|
|
params[idx++] = wasm_val_t WASM_I32_VAL(static_cast<int32_t>(ptr));
|
|
params[idx++] = wasm_val_t WASM_I32_VAL(static_cast<int32_t>(data.size()));
|
|
}
|
|
else
|
|
{
|
|
auto const* udata = reinterpret_cast<WasmUserData*>(e);
|
|
HostFunctions const* hf = reinterpret_cast<HostFunctions*>(udata->first);
|
|
auto* vrt = reinterpret_cast<VirtualRuntime*>(hf->getRT());
|
|
|
|
size_t const ptr = (idx << 10);
|
|
vrt->setBytes(ptr, arg.data(), arg.size());
|
|
params[idx++] = wasm_val_t WASM_I32_VAL(static_cast<int32_t>(ptr));
|
|
params[idx++] = wasm_val_t WASM_I32_VAL(static_cast<int32_t>(arg.size()));
|
|
}
|
|
}
|
|
|
|
// Helper wrapper to call WASM wrapper functions with automatic parameter packing
|
|
template <class F, class E, class P, typename... Args>
|
|
wasm_trap_t*
|
|
ww(F&& f, E&& e, P&& params, P&& result, Args... args)
|
|
{
|
|
size_t idx = 0;
|
|
(ww_hlp(idx, e, params, std::forward<Args>(args)), ...); // NOLINT
|
|
return f(std::forward<E>(e), params.get(), result.get()); // NOLINT
|
|
}
|
|
|
|
constexpr int64_t min64 = std::numeric_limits<int64_t>::min();
|
|
constexpr int64_t max64 = std::numeric_limits<int64_t>::max();
|
|
constexpr int32_t floatSize = 12;
|
|
|
|
struct HostFuncImpl_test : public beast::unit_test::Suite
|
|
{
|
|
void
|
|
testGetLedgerSqn()
|
|
{
|
|
testcase("getLedgerSqn");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.getLedgerSqn();
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getLedgerSqn_wrap,
|
|
&import.at("get_ledger_sqn"),
|
|
params,
|
|
result,
|
|
0,
|
|
sizeof(std::uint32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == sizeof(std::uint32_t)) &&
|
|
BEAST_EXPECT(vrt.getUint32(params, 0) == env.current()->header().seq);
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetParentLedgerTime()
|
|
{
|
|
testcase("getParentLedgerTime");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.getParentLedgerTime();
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getParentLedgerTime_wrap,
|
|
&import.at("get_parent_ledger_time"),
|
|
params,
|
|
result,
|
|
0,
|
|
sizeof(std::uint32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == sizeof(std::uint32_t)) &&
|
|
BEAST_EXPECT(
|
|
vrt.getUint32(params, 0) ==
|
|
env.current()->parentCloseTime().time_since_epoch().count());
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetParentLedgerHash()
|
|
{
|
|
testcase("getParentLedgerHash");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.getParentLedgerHash();
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getParentLedgerHash_wrap,
|
|
&import.at("get_parent_ledger_hash"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == uint256::size());
|
|
auto const resultBytes = vrt.getBytes(params, 0);
|
|
auto const expectedHash = env.current()->header().parentHash;
|
|
BEAST_EXPECT(
|
|
resultBytes.size() == uint256::size() &&
|
|
std::memcmp(resultBytes.data(), expectedHash.data(), uint256::size()) == 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetBaseFee()
|
|
{
|
|
testcase("getBaseFee");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getBaseFee();
|
|
{
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getBaseFee_wrap,
|
|
&import.at("get_base_fee"),
|
|
params,
|
|
result,
|
|
0,
|
|
sizeof(std::uint32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == sizeof(std::uint32_t)) &&
|
|
BEAST_EXPECT(vrt.getUint32(params, 0) == env.current()->fees().base.drops());
|
|
}
|
|
}
|
|
|
|
void
|
|
testIsAmendmentEnabled()
|
|
{
|
|
testcase("isAmendmentEnabled");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Use featureTokenEscrow for testing
|
|
auto const amendmentId = featureTokenEscrow;
|
|
|
|
// hfs.isAmendmentEnabled(amendmentId);
|
|
{
|
|
WasmValVec params(2), result(1);
|
|
vrt.setBytes(0, amendmentId.data(), uint256::size());
|
|
auto* trap =
|
|
ww(isAmendmentEnabled_wrap,
|
|
&import.at("amendment_enabled"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
std::string const amendmentName = "TokenEscrow";
|
|
// hfs.isAmendmentEnabled(amendmentName);
|
|
{
|
|
WasmValVec params(2), result(1);
|
|
vrt.setBytes(0, amendmentName.data(), amendmentName.size());
|
|
auto* trap =
|
|
ww(isAmendmentEnabled_wrap,
|
|
&import.at("amendment_enabled"),
|
|
params,
|
|
result,
|
|
0,
|
|
amendmentName.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
uint256 const fakeId;
|
|
// hfs.isAmendmentEnabled(fakeId);
|
|
{
|
|
WasmValVec params(2), result(1);
|
|
vrt.setBytes(0, fakeId.data(), uint256::size());
|
|
auto* trap =
|
|
ww(isAmendmentEnabled_wrap,
|
|
&import.at("amendment_enabled"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
|
|
std::string const fakeName = "FakeAmendment";
|
|
// hfs.isAmendmentEnabled(fakeName);
|
|
{
|
|
WasmValVec params(2), result(1);
|
|
vrt.setBytes(0, fakeName.data(), fakeName.size());
|
|
auto* trap =
|
|
ww(isAmendmentEnabled_wrap,
|
|
&import.at("amendment_enabled"),
|
|
params,
|
|
result,
|
|
0,
|
|
fakeName.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
testCacheLedgerObj()
|
|
{
|
|
testcase("cacheLedgerObj");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, 2);
|
|
auto const accountKeylet = keylet::account(env.master);
|
|
{
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.cacheLedgerObj(accountKeylet.key, -1);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, accountKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::SlotOutRange));
|
|
}
|
|
|
|
// hfs.cacheLedgerObj(accountKeylet.key, 257);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, accountKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
257);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::SlotOutRange));
|
|
}
|
|
|
|
// hfs.cacheLedgerObj(dummyEscrow.key, 0);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, dummyEscrow.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::LedgerObjNotFound));
|
|
}
|
|
|
|
// hfs.cacheLedgerObj(accountKeylet.key, 0);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, accountKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
vrt.setGas(2'000'000);
|
|
for (int i = 1; i <= 256; ++i)
|
|
{
|
|
// hfs.cacheLedgerObj(accountKeylet.key, i);
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, accountKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
i);
|
|
|
|
if (!(BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == i,
|
|
"result: " + std::to_string(result[0].of.i32) +
|
|
", expected: " + std::to_string(i))))
|
|
break;
|
|
}
|
|
|
|
// hfs.cacheLedgerObj(accountKeylet.key, 0);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, accountKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::SlotsFull));
|
|
}
|
|
}
|
|
|
|
{
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
vrt.setGas(2'000'000);
|
|
for (int i = 1; i <= 256; ++i)
|
|
{
|
|
// hfs.cacheLedgerObj(accountKeylet.key, 0);
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, accountKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
0);
|
|
|
|
if (!(BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == i,
|
|
"result: " + std::to_string(result[0].of.i32) +
|
|
", expected: " + std::to_string(i))))
|
|
break;
|
|
}
|
|
|
|
// hfs.cacheLedgerObj(accountKeylet.key, 0);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, accountKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::SlotsFull));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetTxField()
|
|
{
|
|
testcase("getTxField");
|
|
using namespace test::jtx;
|
|
|
|
std::string const credIdHex =
|
|
"0011223344556677889900112233445566778899001122334455667788990011";
|
|
uint256 credId;
|
|
BEAST_EXPECT(credId.parseHex(credIdHex));
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
STTx const stx = STTx(ttESCROW_FINISH, [&](auto& obj) {
|
|
obj.setAccountID(sfAccount, env.master.id());
|
|
obj.setAccountID(sfOwner, env.master.id());
|
|
obj.setFieldU32(sfOfferSequence, env.seq(env.master));
|
|
obj.setFieldArray(sfMemos, STArray{});
|
|
STVector256 credIds;
|
|
credIds.pushBack(credId);
|
|
obj.setFieldV256(sfCredentialIDs, credIds);
|
|
});
|
|
ApplyContext ac = createApplyContext(env, ov, stx);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
|
|
{
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getTxField(sfAccount);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfAccount.getCode(),
|
|
0,
|
|
AccountID::size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == AccountID::size());
|
|
auto const accountBytes = vrt.getBytes(params, 1);
|
|
BEAST_EXPECT(std::ranges::equal(accountBytes, env.master.id()));
|
|
}
|
|
|
|
// hfs.getTxField(sfOwner);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfOwner.getCode(),
|
|
0,
|
|
AccountID::size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == AccountID::size());
|
|
auto const ownerBytes = vrt.getBytes(params, 1);
|
|
BEAST_EXPECT(std::ranges::equal(ownerBytes, env.master.id()));
|
|
}
|
|
|
|
// hfs.getTxField(sfTransactionType);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfTransactionType.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 > 0);
|
|
auto txTypeBytes = vrt.getBytes(params, 1);
|
|
txTypeBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(txTypeBytes == toBytes(ttESCROW_FINISH));
|
|
}
|
|
|
|
// hfs.getTxField(sfOfferSequence);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfOfferSequence.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 > 0);
|
|
auto offerSeqBytes = vrt.getBytes(params, 1);
|
|
offerSeqBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(offerSeqBytes == toBytes(env.seq(env.master)));
|
|
}
|
|
|
|
// hfs.getTxField(sfDestination);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfDestination.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::FieldNotFound));
|
|
}
|
|
|
|
// hfs.getTxField(sfMemos);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfMemos.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::NotLeafField));
|
|
}
|
|
|
|
// hfs.getTxField(sfCredentialIDs);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfCredentialIDs.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::NotLeafField),
|
|
std::to_string(result[0].of.i32));
|
|
}
|
|
|
|
// hfs.getTxField(kSfInvalid);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
kSfInvalid.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::FieldNotFound));
|
|
}
|
|
|
|
// hfs.getTxField(kSfGeneric);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
kSfGeneric.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::FieldNotFound));
|
|
}
|
|
}
|
|
|
|
{
|
|
auto const iouAsset = env.master["USD"];
|
|
STTx const stx2 = STTx(ttAMM_DEPOSIT, [&](auto& obj) {
|
|
obj.setAccountID(sfAccount, env.master.id());
|
|
obj.setFieldIssue(sfAsset, STIssue{sfAsset, xrpIssue()});
|
|
obj.setFieldIssue(sfAsset2, STIssue{sfAsset2, iouAsset.issue()});
|
|
});
|
|
ApplyContext ac2 = createApplyContext(env, ov, stx2);
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac2, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getTxField(sfAsset);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfAsset.getCode(),
|
|
0,
|
|
256);
|
|
|
|
std::vector<std::uint8_t> const expectedAsset(20, 0);
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 > 0);
|
|
auto assetBytes = vrt.getBytes(params, 1);
|
|
assetBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(assetBytes == expectedAsset);
|
|
}
|
|
|
|
// hfs.getTxField(sfAsset2);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfAsset2.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 > 0);
|
|
auto asset2Bytes = vrt.getBytes(params, 1);
|
|
asset2Bytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(asset2Bytes == toBytes(Asset(iouAsset)));
|
|
}
|
|
}
|
|
|
|
{
|
|
auto const iouAsset = env.master["GBP"];
|
|
auto const mptId = makeMptID(1, env.master);
|
|
STTx const stx2 = STTx(ttAMM_DEPOSIT, [&](auto& obj) {
|
|
obj.setAccountID(sfAccount, env.master.id());
|
|
obj.setFieldIssue(sfAsset, STIssue{sfAsset, iouAsset.issue()});
|
|
obj.setFieldIssue(sfAsset2, STIssue{sfAsset2, MPTIssue{mptId}});
|
|
});
|
|
ApplyContext ac2 = createApplyContext(env, ov, stx2);
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac2, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getTxField(sfAsset);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfAsset.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
{
|
|
auto assetBytes = vrt.getBytes(params, 1);
|
|
assetBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(assetBytes == toBytes(Asset(iouAsset)));
|
|
}
|
|
}
|
|
|
|
// hfs.getTxField(sfAsset2);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfAsset2.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
{
|
|
auto assetBytes = vrt.getBytes(params, 1);
|
|
assetBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(assetBytes == toBytes(Asset(mptId)));
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
std::uint8_t const expectedScale = 8;
|
|
STTx const stx2 = STTx(ttMPTOKEN_ISSUANCE_CREATE, [&](auto& obj) {
|
|
obj.setAccountID(sfAccount, env.master.id());
|
|
obj.setFieldU8(sfAssetScale, expectedScale);
|
|
});
|
|
ApplyContext ac2 = createApplyContext(env, ov, stx2);
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac2, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getTxField(sfAssetScale);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getTxField_wrap,
|
|
&import.at("get_tx_field"),
|
|
params,
|
|
result,
|
|
sfAssetScale.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
{
|
|
auto assetBytes = vrt.getBytes(params, 1);
|
|
assetBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(std::ranges::equal(assetBytes, toBytes(expectedScale)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetCurrentLedgerObjField()
|
|
{
|
|
testcase("getCurrentLedgerObjField");
|
|
using namespace test::jtx;
|
|
using namespace std::chrono;
|
|
|
|
Env env{*this};
|
|
|
|
// Fund the account and create an escrow so the ledger object exists
|
|
env(escrow::create(env.master, env.master, XRP(100)), escrow::kFinishTime(env.now() + 1s));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
// Find the escrow ledger object
|
|
auto const escrowKeylet = keylet::escrow(env.master, env.seq(env.master) - 1);
|
|
BEAST_EXPECT(env.le(escrowKeylet));
|
|
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, escrowKeylet);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getCurrentLedgerObjField(sfAccount);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjField_wrap,
|
|
&import.at("get_current_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
sfAccount.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto accountBytes = vrt.getBytes(params, 1);
|
|
accountBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(std::ranges::equal(accountBytes, env.master.id()));
|
|
}
|
|
}
|
|
|
|
// hfs.getCurrentLedgerObjField(sfAmount);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjField_wrap,
|
|
&import.at("get_current_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
sfAmount.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
{
|
|
auto amountBytes = vrt.getBytes(params, 1);
|
|
amountBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(amountBytes == toBytes(XRP(100)));
|
|
}
|
|
}
|
|
|
|
// hfs.getCurrentLedgerObjField(sfPreviousTxnID);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjField_wrap,
|
|
&import.at("get_current_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
sfPreviousTxnID.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
{
|
|
auto previousTxnIdBytes = vrt.getBytes(params, 1);
|
|
previousTxnIdBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(previousTxnIdBytes == toBytes(env.tx()->getTransactionID()));
|
|
}
|
|
}
|
|
|
|
// hfs.getCurrentLedgerObjField(sfOwner);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjField_wrap,
|
|
&import.at("get_current_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
sfOwner.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::FieldNotFound));
|
|
}
|
|
|
|
{
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master) + 5);
|
|
VirtualRuntime vrt2;
|
|
WasmHostFunctionsImpl hfs2(ac, dummyEscrow);
|
|
|
|
auto import2 = xrpl::createWasmImport(hfs2);
|
|
hfs2.setRT(&vrt2);
|
|
|
|
// hfs2.getCurrentLedgerObjField(sfAccount);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjField_wrap,
|
|
&import2.at("get_current_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
sfAccount.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::LedgerObjNotFound));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetLedgerObjField()
|
|
{
|
|
testcase("getLedgerObjField");
|
|
using namespace test::jtx;
|
|
using namespace std::chrono;
|
|
|
|
Env env{*this};
|
|
// Fund the account and create an escrow so the ledger object exists
|
|
env(escrow::create(env.master, env.master, XRP(100)), escrow::kFinishTime(env.now() + 1s));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const accountKeylet = keylet::account(env.master.id());
|
|
auto const escrowKeylet = keylet::escrow(env.master.id(), env.seq(env.master) - 1);
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, escrowKeylet);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.cacheLedgerObj(accountKeylet.key, 1);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, accountKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
// hfs.getLedgerObjField(1, sfAccount);
|
|
{
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjField_wrap,
|
|
&import.at("get_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
1,
|
|
sfAccount.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto accountBytes = vrt.getBytes(params, 2);
|
|
accountBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(std::ranges::equal(accountBytes, env.master.id()));
|
|
}
|
|
}
|
|
|
|
// hfs.getLedgerObjField(1, sfBalance);
|
|
{
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjField_wrap,
|
|
&import.at("get_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
1,
|
|
sfBalance.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
{
|
|
auto balanceBytes = vrt.getBytes(params, 2);
|
|
balanceBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(balanceBytes == toBytes(env.balance(env.master)));
|
|
}
|
|
}
|
|
|
|
// hfs.getLedgerObjField(0, sfAccount);
|
|
{
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjField_wrap,
|
|
&import.at("get_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
sfAccount.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::SlotOutRange));
|
|
}
|
|
|
|
// hfs.getLedgerObjField(257, sfAccount);
|
|
{
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjField_wrap,
|
|
&import.at("get_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
257,
|
|
sfAccount.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::SlotOutRange));
|
|
}
|
|
|
|
// hfs.getLedgerObjField(2, sfAccount);
|
|
{
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjField_wrap,
|
|
&import.at("get_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
2,
|
|
sfAccount.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::EmptySlot));
|
|
}
|
|
|
|
// hfs.getLedgerObjField(1, sfOwner);
|
|
{
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjField_wrap,
|
|
&import.at("get_ledger_obj_field"),
|
|
params,
|
|
result,
|
|
1,
|
|
sfOwner.getCode(),
|
|
0,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::FieldNotFound));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetTxNestedField()
|
|
{
|
|
testcase("getTxNestedField");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
|
|
std::string const credIdHex =
|
|
"0011223344556677889900112233445566778899001122334455667788990011";
|
|
uint256 credId;
|
|
BEAST_EXPECT(credId.parseHex(credIdHex));
|
|
|
|
// Create a transaction with a nested array field
|
|
STTx const stx = STTx(ttESCROW_FINISH, [&](auto& obj) {
|
|
obj.setAccountID(sfAccount, env.master.id());
|
|
STArray memos;
|
|
STObject memoObj(sfMemo);
|
|
memoObj.setFieldVL(sfMemoData, Slice("hello", 5));
|
|
memos.push_back(memoObj);
|
|
obj.setFieldArray(sfMemos, memos);
|
|
STVector256 credIds;
|
|
credIds.pushBack(credId);
|
|
obj.setFieldV256(sfCredentialIDs, credIds);
|
|
});
|
|
|
|
ApplyContext ac = createApplyContext(env, ov, stx);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
{
|
|
// Locator for sfMemos[0].sfMemo.sfMemoData
|
|
// Locator is a sequence of int32_t codes:
|
|
// [sfMemos.getCode(), 0, sfMemoData.getCode()]
|
|
std::vector<int32_t> const locatorVec = {sfMemos.getCode(), 0, sfMemoData.getCode()};
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getTxNestedField_wrap,
|
|
&import.at("get_tx_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto memoDataBytes = vrt.getBytes(params, 2);
|
|
memoDataBytes.resize(result[0].of.i32);
|
|
std::string const memoData(memoDataBytes.begin(), memoDataBytes.end());
|
|
BEAST_EXPECT(memoData == "hello");
|
|
}
|
|
}
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
{
|
|
// Locator for sfCredentialIDs[0]
|
|
std::vector<int32_t> locatorVec = {sfCredentialIDs.getCode(), 0};
|
|
vrt.setBytes(
|
|
0,
|
|
reinterpret_cast<uint8_t const*>(locatorVec.data()),
|
|
locatorVec.size() * sizeof(int32_t));
|
|
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getTxNestedField_wrap,
|
|
&import.at("get_tx_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto credIdBytes = vrt.getBytes(params, 2);
|
|
credIdBytes.resize(result[0].of.i32);
|
|
std::string const credIdResult(credIdBytes.begin(), credIdBytes.end());
|
|
BEAST_EXPECT(strHex(credIdResult) == credIdHex);
|
|
}
|
|
}
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
{
|
|
// can use the nested locator for base fields too
|
|
std::vector<int32_t> locatorVec = {sfAccount.getCode()};
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getTxNestedField_wrap,
|
|
&import.at("get_tx_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto accountBytes = vrt.getBytes(params, 2);
|
|
accountBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(std::ranges::equal(accountBytes, env.master.id()));
|
|
}
|
|
}
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
{
|
|
// unaligned locator
|
|
std::vector<uint8_t> locatorVec(sizeof(int32_t) + 1);
|
|
auto const accountFieldCode = sfAccount.getCode();
|
|
memcpy(locatorVec.data() + 1, &accountFieldCode, sizeof(int32_t));
|
|
vrt.setBytes(0, locatorVec.data() + 1, sizeof(int32_t));
|
|
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getTxNestedField_wrap,
|
|
&import.at("get_tx_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto accountBytes = vrt.getBytes(params, 2);
|
|
accountBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(std::ranges::equal(accountBytes, env.master.id()));
|
|
}
|
|
}
|
|
|
|
auto expectError = [&](std::vector<int32_t> const& locatorVec,
|
|
HostFunctionError expectedError) {
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
|
|
WasmValVec params(4), result(1);
|
|
// hfs.getTxNestedField(locator);
|
|
auto* trap =
|
|
ww(getTxNestedField_wrap,
|
|
&import.at("get_tx_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == static_cast<int32_t>(expectedError),
|
|
std::to_string(result[0].of.i32));
|
|
};
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for non-existent base field
|
|
expectError(
|
|
{sfSigners.getCode(), // sfSigners does not exist
|
|
0,
|
|
sfAccount.getCode()},
|
|
HostFunctionError::FieldNotFound);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for non-existent index
|
|
expectError(
|
|
{sfMemos.getCode(),
|
|
1, // index 1 does not exist
|
|
sfMemoData.getCode()},
|
|
HostFunctionError::IndexOutOfBounds);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for non-existent index
|
|
expectError(
|
|
{sfCredentialIDs.getCode(), 1}, // index 1 does not exist
|
|
HostFunctionError::IndexOutOfBounds);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for negative index (STArray)
|
|
expectError(
|
|
{sfMemos.getCode(),
|
|
-1, // negative index
|
|
sfMemoData.getCode()},
|
|
HostFunctionError::IndexOutOfBounds);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for negative index (STVector256)
|
|
expectError(
|
|
{sfCredentialIDs.getCode(), -1}, // negative index
|
|
HostFunctionError::IndexOutOfBounds);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for non-existent nested field
|
|
expectError(
|
|
{sfMemos.getCode(), 0, sfURI.getCode()}, // sfURI does not exist in the memo
|
|
HostFunctionError::FieldNotFound);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for non-existent base sfield
|
|
expectError(
|
|
{fieldCode(20000, 20000), // nonexistent SField code
|
|
0,
|
|
sfAccount.getCode()},
|
|
HostFunctionError::InvalidField);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for non-existent nested sfield
|
|
expectError(
|
|
{sfMemos.getCode(), // nonexistent SField code
|
|
0,
|
|
fieldCode(20000, 20000)},
|
|
HostFunctionError::InvalidField);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for negative base sfield code (-1 = kSfInvalid, exists in map but not in tx)
|
|
expectError(
|
|
{-1, // kSfInvalid's field code
|
|
0,
|
|
sfAccount.getCode()},
|
|
HostFunctionError::FieldNotFound);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for zero base sfield code (0 = kSfGeneric, exists in map but not in tx)
|
|
expectError(
|
|
{0, // kSfGeneric's field code
|
|
0,
|
|
sfAccount.getCode()},
|
|
HostFunctionError::FieldNotFound);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for very negative base sfield code (not in knownCodeToField map)
|
|
expectError(
|
|
{std::numeric_limits<int32_t>::min(), 0, sfAccount.getCode()},
|
|
HostFunctionError::InvalidField);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for negative nested sfield code in STObject context
|
|
// (sfMemos[0] is an STObject, then -1 is looked up as SField)
|
|
expectError(
|
|
{sfMemos.getCode(), 0, -1}, // -1 = kSfInvalid, exists in map but not in memo object
|
|
HostFunctionError::FieldNotFound);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for STArray
|
|
expectError({sfMemos.getCode()}, HostFunctionError::NotLeafField);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for STVector256
|
|
expectError({sfCredentialIDs.getCode()}, HostFunctionError::NotLeafField);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for nesting into non-array/object field
|
|
expectError(
|
|
{sfAccount.getCode(), // sfAccount is not an array or object
|
|
0,
|
|
sfAccount.getCode()},
|
|
HostFunctionError::LocatorMalformed);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for empty locator
|
|
expectError({}, HostFunctionError::LocatorMalformed);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for malformed locator (not multiple of 4)
|
|
{
|
|
std::vector<int32_t> locatorVec = {sfMemos.getCode()};
|
|
vrt.setBytes(0, locatorVec.data(), 3);
|
|
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getTxNestedField_wrap,
|
|
&import.at("get_tx_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
3,
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::LocatorMalformed));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetCurrentLedgerObjNestedField()
|
|
{
|
|
testcase("getCurrentLedgerObjNestedField");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
Account const alice("alice");
|
|
Account const becky("becky");
|
|
// Create a SignerList for env.master
|
|
env(signers(env.master, 2, {{alice, 1}, {becky, 1}}));
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
// Find the signer ledger object
|
|
auto const signerKeylet = keylet::signers(env.master.id());
|
|
BEAST_EXPECT(env.le(signerKeylet));
|
|
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, signerKeylet);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getCurrentLedgerObjNestedField(baseLocatorSlice);
|
|
// Locator for base field
|
|
{
|
|
std::vector<int32_t> baseLocator = {sfSignerQuorum.getCode()};
|
|
vrt.setBytes(0, baseLocator.data(), baseLocator.size() * sizeof(int32_t));
|
|
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjNestedField_wrap,
|
|
&import.at("get_current_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
baseLocator.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto signerQuorumBytes = vrt.getBytes(params, 2);
|
|
signerQuorumBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(signerQuorumBytes == toBytes(static_cast<uint32_t>(2)));
|
|
}
|
|
}
|
|
|
|
auto expectError = [&](std::vector<int32_t> const& locatorVec,
|
|
HostFunctionError expectedError) {
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
|
|
WasmValVec params(4), result(1);
|
|
// hfs.getCurrentLedgerObjNestedField(locator);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjNestedField_wrap,
|
|
&import.at("get_current_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == static_cast<int32_t>(expectedError),
|
|
std::to_string(result[0].of.i32));
|
|
};
|
|
// hfs.getCurrentLedgerObjNestedField(locator);
|
|
// Locator for non-existent base field
|
|
expectError(
|
|
{sfSigners.getCode(), // sfSigners does not exist
|
|
0,
|
|
sfAccount.getCode()},
|
|
HostFunctionError::FieldNotFound);
|
|
|
|
// hfs.getCurrentLedgerObjNestedField(locator);
|
|
// Locator for nesting into non-array/object field
|
|
expectError(
|
|
{sfSignerQuorum.getCode(), // sfSignerQuorum is not an array or object
|
|
0,
|
|
sfAccount.getCode()},
|
|
HostFunctionError::LocatorMalformed);
|
|
|
|
// hfs.getCurrentLedgerObjNestedField(emptyLocator);
|
|
// Locator for empty locator
|
|
{
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjNestedField_wrap,
|
|
&import.at("get_current_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::LocatorMalformed));
|
|
}
|
|
|
|
// hfs.getCurrentLedgerObjNestedField(malformedLocator);
|
|
// Locator for malformed locator (not multiple of 4)
|
|
{
|
|
std::vector<int32_t> malformedLocatorVec = {sfMemos.getCode()};
|
|
vrt.setBytes(0, malformedLocatorVec.data(), 3);
|
|
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjNestedField_wrap,
|
|
&import.at("get_current_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
3,
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::LocatorMalformed));
|
|
}
|
|
|
|
// hfs.getCurrentLedgerObjNestedField(locator);
|
|
{
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master) + 5);
|
|
VirtualRuntime vrt2;
|
|
WasmHostFunctionsImpl dummyHfs(ac, dummyEscrow);
|
|
|
|
auto import2 = xrpl::createWasmImport(dummyHfs);
|
|
dummyHfs.setRT(&vrt2);
|
|
|
|
std::vector<int32_t> const locatorVec = {sfAccount.getCode()};
|
|
vrt2.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjNestedField_wrap,
|
|
&import2.at("get_current_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::LedgerObjNotFound),
|
|
std::to_string(result[0].of.i32));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetLedgerObjNestedField()
|
|
{
|
|
testcase("getLedgerObjNestedField");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
Account const alice("alice");
|
|
Account const becky("becky");
|
|
// Create a SignerList for env.master
|
|
env(signers(env.master, 2, {{alice, 1}, {becky, 1}}));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Cache the SignerList ledger object in slot 1
|
|
auto const signerListKeylet = keylet::signers(env.master.id());
|
|
// hfs.cacheLedgerObj(signerListKeylet.key, 1);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, signerListKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
1);
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
// Locator for sfSignerEntries[0].sfAccount
|
|
{
|
|
std::vector<int32_t> const locatorVec = {
|
|
sfSignerEntries.getCode(), 0, sfAccount.getCode()};
|
|
// hfs.getLedgerObjNestedField(1, locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjNestedField_wrap,
|
|
&import.at("get_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
1,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto aliceIdBytes = vrt.getBytes(params, 3);
|
|
aliceIdBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(std::ranges::equal(aliceIdBytes, alice.id()));
|
|
}
|
|
}
|
|
|
|
// Locator for sfSignerEntries[1].sfAccount
|
|
{
|
|
std::vector<int32_t> const locatorVec = {
|
|
sfSignerEntries.getCode(), 1, sfAccount.getCode()};
|
|
// hfs.getLedgerObjNestedField(1, locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjNestedField_wrap,
|
|
&import.at("get_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
1,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto beckyIdBytes = vrt.getBytes(params, 3);
|
|
beckyIdBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(std::ranges::equal(beckyIdBytes, becky.id()));
|
|
}
|
|
}
|
|
|
|
// Locator for sfSignerEntries[0].sfSignerWeight
|
|
{
|
|
std::vector<int32_t> const locatorVec = {
|
|
sfSignerEntries.getCode(), 0, sfSignerWeight.getCode()};
|
|
// hfs.getLedgerObjNestedField(1, locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjNestedField_wrap,
|
|
&import.at("get_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
1,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
// Should be 1
|
|
auto const expected = toBytes(static_cast<std::uint16_t>(1));
|
|
auto weightBytes = vrt.getBytes(params, 3);
|
|
weightBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(weightBytes == expected);
|
|
}
|
|
}
|
|
|
|
// Locator for base field sfSignerQuorum
|
|
{
|
|
std::vector<int32_t> const locatorVec = {sfSignerQuorum.getCode()};
|
|
// hfs.getLedgerObjNestedField(1, locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjNestedField_wrap,
|
|
&import.at("get_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
1,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECTS(result[0].of.i32 > 0, std::to_string(result[0].of.i32)))
|
|
{
|
|
auto const expected = toBytes(static_cast<std::uint32_t>(2));
|
|
auto quorumBytes = vrt.getBytes(params, 3);
|
|
quorumBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(quorumBytes == expected);
|
|
}
|
|
}
|
|
|
|
// Helper for error checks
|
|
auto expectError = [&](std::vector<int32_t> const& locatorVec,
|
|
HostFunctionError expectedError,
|
|
int slot = 1) {
|
|
// hfs.getLedgerObjNestedField(slot, locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjNestedField_wrap,
|
|
&import.at("get_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
slot,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t),
|
|
256,
|
|
256);
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == static_cast<int32_t>(expectedError),
|
|
std::to_string(result[0].of.i32));
|
|
};
|
|
|
|
// Error: base field not found
|
|
expectError(
|
|
{sfSigners.getCode(), // sfSigners does not exist
|
|
0,
|
|
sfAccount.getCode()},
|
|
HostFunctionError::FieldNotFound);
|
|
|
|
// Error: index out of bounds
|
|
expectError(
|
|
{sfSignerEntries.getCode(),
|
|
2, // index 2 does not exist
|
|
sfAccount.getCode()},
|
|
HostFunctionError::IndexOutOfBounds);
|
|
|
|
// Error: nested field not found
|
|
expectError(
|
|
{
|
|
sfSignerEntries.getCode(),
|
|
0,
|
|
sfDestination.getCode() // sfDestination does not exist
|
|
},
|
|
HostFunctionError::FieldNotFound);
|
|
|
|
// Error: invalid field code
|
|
expectError(
|
|
{fieldCode(99999, 99999), 0, sfAccount.getCode()}, HostFunctionError::InvalidField);
|
|
|
|
// Error: invalid nested field code
|
|
expectError(
|
|
{sfSignerEntries.getCode(), 0, fieldCode(99999, 99999)},
|
|
HostFunctionError::InvalidField);
|
|
|
|
// Error: slot out of range
|
|
expectError({sfSignerQuorum.getCode()}, HostFunctionError::SlotOutRange, 0);
|
|
expectError({sfSignerQuorum.getCode()}, HostFunctionError::SlotOutRange, 257);
|
|
|
|
// Error: empty slot
|
|
expectError({sfSignerQuorum.getCode()}, HostFunctionError::EmptySlot, 2);
|
|
|
|
// Error: locator for STArray (not leaf field)
|
|
expectError({sfSignerEntries.getCode()}, HostFunctionError::NotLeafField);
|
|
|
|
// Error: nesting into non-array/object field
|
|
expectError(
|
|
{sfSignerQuorum.getCode(), 0, sfAccount.getCode()},
|
|
HostFunctionError::LocatorMalformed);
|
|
|
|
// Error: empty locator
|
|
expectError({}, HostFunctionError::LocatorMalformed);
|
|
|
|
// Error: locator malformed (not multiple of 4)
|
|
{
|
|
std::vector<int32_t> const locatorVec = {sfSignerEntries.getCode()};
|
|
// hfs.getLedgerObjNestedField(1, locator);
|
|
vrt.setBytes(0, locatorVec.data(), 3);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjNestedField_wrap,
|
|
&import.at("get_ledger_obj_nested_field"),
|
|
params,
|
|
result,
|
|
1,
|
|
0,
|
|
3,
|
|
256,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::LocatorMalformed));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetTxArrayLen()
|
|
{
|
|
testcase("getTxArrayLen");
|
|
using namespace test::jtx;
|
|
|
|
std::string const credIdHex =
|
|
"0011223344556677889900112233445566778899001122334455667788990011";
|
|
uint256 credId;
|
|
BEAST_EXPECT(credId.parseHex(credIdHex));
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
|
|
// Transaction with an array field
|
|
STTx const stx = STTx(ttESCROW_FINISH, [&](auto& obj) {
|
|
obj.setAccountID(sfAccount, env.master.id());
|
|
STArray memos;
|
|
{
|
|
STObject memoObj(sfMemo);
|
|
memoObj.setFieldVL(sfMemoData, Slice("hello", 5));
|
|
memos.push_back(memoObj);
|
|
}
|
|
{
|
|
STObject memoObj(sfMemo);
|
|
memoObj.setFieldVL(sfMemoData, Slice("world", 5));
|
|
memos.push_back(memoObj);
|
|
}
|
|
obj.setFieldArray(sfMemos, memos);
|
|
STVector256 credIds;
|
|
credIds.pushBack(credId);
|
|
obj.setFieldV256(sfCredentialIDs, credIds);
|
|
});
|
|
|
|
ApplyContext ac = createApplyContext(env, ov, stx);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Should return 2 for sfMemos
|
|
// hfs.getTxArrayLen(sfMemos);
|
|
{
|
|
WasmValVec params(1), result(1);
|
|
auto* trap =
|
|
ww(getTxArrayLen_wrap,
|
|
&import.at("get_tx_array_len"),
|
|
params,
|
|
result,
|
|
sfMemos.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
BEAST_EXPECT(result[0].of.i32 == 2);
|
|
}
|
|
|
|
// Should return error for non-array field
|
|
// hfs.getTxArrayLen(sfAccount);
|
|
{
|
|
WasmValVec params(1), result(1);
|
|
auto* trap =
|
|
ww(getTxArrayLen_wrap,
|
|
&import.at("get_tx_array_len"),
|
|
params,
|
|
result,
|
|
sfAccount.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(result[0].of.i32 == static_cast<int32_t>(HostFunctionError::NoArray));
|
|
}
|
|
|
|
// Should return error for missing array field
|
|
// hfs.getTxArrayLen(sfSigners);
|
|
{
|
|
WasmValVec params(1), result(1);
|
|
auto* trap =
|
|
ww(getTxArrayLen_wrap,
|
|
&import.at("get_tx_array_len"),
|
|
params,
|
|
result,
|
|
sfSigners.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::FieldNotFound));
|
|
}
|
|
|
|
// Should return 1 for sfCredentialIDs
|
|
// hfs.getTxArrayLen(sfCredentialIDs);
|
|
{
|
|
WasmValVec params(1), result(1);
|
|
auto* trap =
|
|
ww(getTxArrayLen_wrap,
|
|
&import.at("get_tx_array_len"),
|
|
params,
|
|
result,
|
|
sfCredentialIDs.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetCurrentLedgerObjArrayLen()
|
|
{
|
|
testcase("getCurrentLedgerObjArrayLen");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
Account const alice("alice");
|
|
Account const becky("becky");
|
|
// Create a SignerList for env.master
|
|
env(signers(env.master, 2, {{alice, 1}, {becky, 1}}));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const signerKeylet = keylet::signers(env.master.id());
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, signerKeylet);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getCurrentLedgerObjArrayLen(sfSignerEntries);
|
|
{
|
|
WasmValVec params(1), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjArrayLen_wrap,
|
|
&import.at("get_current_ledger_obj_array_len"),
|
|
params,
|
|
result,
|
|
sfSignerEntries.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
BEAST_EXPECT(result[0].of.i32 == 2);
|
|
}
|
|
|
|
// hfs.getCurrentLedgerObjArrayLen(sfMemos);
|
|
{
|
|
WasmValVec params(1), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjArrayLen_wrap,
|
|
&import.at("get_current_ledger_obj_array_len"),
|
|
params,
|
|
result,
|
|
sfMemos.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::FieldNotFound));
|
|
}
|
|
|
|
// Should return NO_ARRAY for non-array field
|
|
// hfs.getCurrentLedgerObjArrayLen(sfAccount);
|
|
{
|
|
WasmValVec params(1), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjArrayLen_wrap,
|
|
&import.at("get_current_ledger_obj_array_len"),
|
|
params,
|
|
result,
|
|
sfAccount.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(result[0].of.i32 == static_cast<int32_t>(HostFunctionError::NoArray));
|
|
}
|
|
|
|
{
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master) + 5);
|
|
VirtualRuntime vrt2;
|
|
WasmHostFunctionsImpl dummyHfs(ac, dummyEscrow);
|
|
|
|
auto import2 = xrpl::createWasmImport(dummyHfs);
|
|
dummyHfs.setRT(&vrt2);
|
|
|
|
// auto const len = dummyHfs.getCurrentLedgerObjArrayLen(sfMemos);
|
|
WasmValVec params(1), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjArrayLen_wrap,
|
|
&import2.at("get_current_ledger_obj_array_len"),
|
|
params,
|
|
result,
|
|
sfMemos.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::LedgerObjNotFound));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetLedgerObjArrayLen()
|
|
{
|
|
testcase("getLedgerObjArrayLen");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
Account const alice("alice");
|
|
Account const becky("becky");
|
|
// Create a SignerList for env.master
|
|
env(signers(env.master, 2, {{alice, 1}, {becky, 1}}));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
auto const signerListKeylet = keylet::signers(env.master.id());
|
|
// hfs.cacheLedgerObj(signerListKeylet.key, 1);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, signerListKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
1);
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
{
|
|
// hfs.getLedgerObjArrayLen(1, sfSignerEntries);
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjArrayLen_wrap,
|
|
&import.at("get_ledger_obj_array_len"),
|
|
params,
|
|
result,
|
|
1,
|
|
sfSignerEntries.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
{
|
|
// Should return 2 for sfSignerEntries
|
|
BEAST_EXPECT(result[0].of.i32 == 2);
|
|
}
|
|
}
|
|
{
|
|
// hfs.getLedgerObjArrayLen(0, sfSignerEntries);
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjArrayLen_wrap,
|
|
&import.at("get_ledger_obj_array_len"),
|
|
params,
|
|
result,
|
|
0,
|
|
sfSignerEntries.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(result[0].of.i32 == static_cast<int32_t>(HostFunctionError::SlotOutRange));
|
|
}
|
|
|
|
{
|
|
// Should return error for non-array field
|
|
// hfs.getLedgerObjArrayLen(1, sfAccount);
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjArrayLen_wrap,
|
|
&import.at("get_ledger_obj_array_len"),
|
|
params,
|
|
result,
|
|
1,
|
|
sfAccount.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(result[0].of.i32 == static_cast<int32_t>(HostFunctionError::NoArray));
|
|
}
|
|
|
|
{
|
|
// Should return error for empty slot
|
|
// hfs.getLedgerObjArrayLen(2, sfSignerEntries);
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjArrayLen_wrap,
|
|
&import.at("get_ledger_obj_array_len"),
|
|
params,
|
|
result,
|
|
2,
|
|
sfSignerEntries.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(result[0].of.i32 == static_cast<int32_t>(HostFunctionError::EmptySlot));
|
|
}
|
|
|
|
{
|
|
// Should return error for missing array field
|
|
// hfs.getLedgerObjArrayLen(1, sfMemos);
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjArrayLen_wrap,
|
|
&import.at("get_ledger_obj_array_len"),
|
|
params,
|
|
result,
|
|
1,
|
|
sfMemos.getCode());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::FieldNotFound));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetTxNestedArrayLen()
|
|
{
|
|
testcase("getTxNestedArrayLen");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
|
|
STTx const stx = STTx(ttESCROW_FINISH, [&](auto& obj) {
|
|
STArray memos;
|
|
STObject memoObj(sfMemo);
|
|
memoObj.setFieldVL(sfMemoData, Slice("hello", 5));
|
|
memos.push_back(memoObj);
|
|
obj.setFieldArray(sfMemos, memos);
|
|
});
|
|
|
|
ApplyContext ac = createApplyContext(env, ov, stx);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Helper for error checks
|
|
auto expectError = [&](std::vector<int32_t> const& locatorVec,
|
|
HostFunctionError expectedError) {
|
|
// hfs.getTxNestedArrayLen(locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getTxNestedArrayLen_wrap,
|
|
&import.at("get_tx_nested_array_len"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == static_cast<int32_t>(expectedError),
|
|
std::to_string(result[0].of.i32));
|
|
};
|
|
|
|
// Locator for sfMemos
|
|
{
|
|
std::vector<int32_t> locatorVec = {sfMemos.getCode()};
|
|
// hfs.getTxNestedArrayLen(locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getTxNestedArrayLen_wrap,
|
|
&import.at("get_tx_nested_array_len"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
// Error: non-array field
|
|
expectError({sfAccount.getCode()}, HostFunctionError::NoArray);
|
|
|
|
// Error: missing field
|
|
expectError({sfSigners.getCode()}, HostFunctionError::FieldNotFound);
|
|
}
|
|
|
|
void
|
|
testGetCurrentLedgerObjNestedArrayLen()
|
|
{
|
|
testcase("getCurrentLedgerObjNestedArrayLen");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
Account const alice("alice");
|
|
Account const becky("becky");
|
|
// Create a SignerList for env.master
|
|
env(signers(env.master, 2, {{alice, 1}, {becky, 1}}));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const signerKeylet = keylet::signers(env.master.id());
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, signerKeylet);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Helper for error checks
|
|
auto expectError = [&](std::vector<int32_t> const& locatorVec,
|
|
HostFunctionError expectedError) {
|
|
// hfs.getCurrentLedgerObjNestedArrayLen(locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjNestedArrayLen_wrap,
|
|
&import.at("get_current_ledger_obj_nested_array_len"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == static_cast<int32_t>(expectedError),
|
|
std::to_string(result[0].of.i32));
|
|
};
|
|
|
|
// Locator for sfSignerEntries
|
|
{
|
|
std::vector<int32_t> locatorVec = {sfSignerEntries.getCode()};
|
|
// hfs.getCurrentLedgerObjNestedArrayLen(locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjNestedArrayLen_wrap,
|
|
&import.at("get_current_ledger_obj_nested_array_len"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECT(result[0].of.i32 == 2);
|
|
}
|
|
|
|
// Error: non-array field
|
|
expectError({sfSignerQuorum.getCode()}, HostFunctionError::NoArray);
|
|
|
|
// Error: missing field
|
|
expectError({sfSigners.getCode()}, HostFunctionError::FieldNotFound);
|
|
|
|
{
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master) + 5);
|
|
VirtualRuntime vrt2;
|
|
WasmHostFunctionsImpl dummyHfs(ac, dummyEscrow);
|
|
|
|
auto import2 = xrpl::createWasmImport(dummyHfs);
|
|
dummyHfs.setRT(&vrt2);
|
|
|
|
std::vector<int32_t> locatorVec = {sfAccount.getCode()};
|
|
// auto const result = dummyHfs.getCurrentLedgerObjNestedArrayLen(locator);
|
|
vrt2.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getCurrentLedgerObjNestedArrayLen_wrap,
|
|
&import2.at("get_current_ledger_obj_nested_array_len"),
|
|
params,
|
|
result,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::LedgerObjNotFound),
|
|
std::to_string(result[0].of.i32));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetLedgerObjNestedArrayLen()
|
|
{
|
|
testcase("getLedgerObjNestedArrayLen");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
Account const alice("alice");
|
|
Account const becky("becky");
|
|
env(signers(env.master, 2, {{alice, 1}, {becky, 1}}));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
auto const signerListKeylet = keylet::signers(env.master.id());
|
|
// hfs.cacheLedgerObj(signerListKeylet.key, 1);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, signerListKeylet.key.data(), uint256::size());
|
|
auto* trap =
|
|
ww(cacheLedgerObj_wrap,
|
|
&import.at("cache_ledger_obj"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
1);
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
// Locator for sfSignerEntries
|
|
std::vector<int32_t> locatorVec = {sfSignerEntries.getCode()};
|
|
// hfs.getLedgerObjNestedArrayLen(1, locator);
|
|
{
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjNestedArrayLen_wrap,
|
|
&import.at("get_ledger_obj_nested_array_len"),
|
|
params,
|
|
result,
|
|
1,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
if (BEAST_EXPECT(result[0].of.i32 > 0))
|
|
BEAST_EXPECT(result[0].of.i32 == 2);
|
|
}
|
|
|
|
// Helper for error checks
|
|
auto expectError = [&](std::vector<int32_t> const& locatorVec,
|
|
HostFunctionError expectedError,
|
|
int slot = 1) {
|
|
// hfs.getLedgerObjNestedArrayLen(slot, locator);
|
|
vrt.setBytes(0, locatorVec.data(), locatorVec.size() * sizeof(int32_t));
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjNestedArrayLen_wrap,
|
|
&import.at("get_ledger_obj_nested_array_len"),
|
|
params,
|
|
result,
|
|
slot,
|
|
0,
|
|
locatorVec.size() * sizeof(int32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32);
|
|
BEAST_EXPECTS(
|
|
result[0].of.i32 == static_cast<int32_t>(expectedError),
|
|
std::to_string(result[0].of.i32));
|
|
};
|
|
|
|
// Error: non-array field
|
|
expectError({sfSignerQuorum.getCode()}, HostFunctionError::NoArray);
|
|
|
|
// Error: missing field
|
|
expectError({sfSigners.getCode()}, HostFunctionError::FieldNotFound);
|
|
|
|
// Slot out of range
|
|
expectError(locatorVec, HostFunctionError::SlotOutRange, 0);
|
|
expectError(locatorVec, HostFunctionError::SlotOutRange, 257);
|
|
|
|
// Empty slot
|
|
expectError(locatorVec, HostFunctionError::EmptySlot, 2);
|
|
|
|
// Error: empty locator
|
|
expectError({}, HostFunctionError::LocatorMalformed);
|
|
|
|
// Error: locator malformed (not multiple of 4)
|
|
{
|
|
// hfs.getLedgerObjNestedArrayLen(1, malformedLocator);
|
|
vrt.setBytes(0, locatorVec.data(), 3);
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(getLedgerObjNestedArrayLen_wrap,
|
|
&import.at("get_ledger_obj_nested_array_len"),
|
|
params,
|
|
result,
|
|
1,
|
|
0,
|
|
3);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::LocatorMalformed));
|
|
}
|
|
|
|
// Error: locator for non-STArray field
|
|
expectError(
|
|
{sfSignerQuorum.getCode(), 0, sfAccount.getCode()},
|
|
HostFunctionError::LocatorMalformed);
|
|
}
|
|
|
|
void
|
|
testUpdateData()
|
|
{
|
|
testcase("updateData");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
env(escrow::create(env.master, env.master, XRP(100)),
|
|
escrow::kFinishTime(env.now() + std::chrono::seconds(1)));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const escrowKeylet = keylet::escrow(env.master, env.seq(env.master) - 1);
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, escrowKeylet);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Should succeed for small data
|
|
Bytes data(10, 0x42);
|
|
// hfs.updateData(Slice(data.data(), data.size()));
|
|
{
|
|
vrt.setBytes(0, data.data(), data.size());
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(updateData_wrap, &import.at("update_data"), params, result, 0, data.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == data.size());
|
|
BEAST_EXPECT(hfs.getData() && *hfs.getData() == data);
|
|
}
|
|
|
|
// Should fail for too large data
|
|
Bytes bigData(maxWasmDataLength + 1, 0x42);
|
|
// hfs.updateData(Slice(bigData.data(), bigData.size()));
|
|
{
|
|
vrt.setBytes(0, bigData.data(), bigData.size());
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(updateData_wrap, &import.at("update_data"), params, result, 0, bigData.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == HfErrorToInt(HostFunctionError::DataFieldTooLarge));
|
|
}
|
|
}
|
|
|
|
void
|
|
testCheckSignature()
|
|
{
|
|
testcase("checkSignature");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Generate a keypair and sign a message
|
|
auto const kp = generateKeyPair(KeyType::Secp256k1, randomSeed());
|
|
PublicKey const& pk = kp.first;
|
|
SecretKey const& sk = kp.second;
|
|
std::string const& message = "hello signature";
|
|
auto const sig = sign(pk, sk, Slice(message.data(), message.size()));
|
|
|
|
// Should succeed for valid signature
|
|
{
|
|
// hfs.checkSignature(
|
|
// Slice(message.data(), message.size()),
|
|
// Slice(sig.data(), sig.size()),
|
|
// Slice(pk.data(), pk.size()));
|
|
vrt.setBytes(0, message.data(), message.size());
|
|
vrt.setBytes(256, sig.data(), sig.size());
|
|
vrt.setBytes(512, pk.data(), pk.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(checkSignature_wrap,
|
|
&import.at("check_sig"),
|
|
params,
|
|
result,
|
|
0,
|
|
message.size(),
|
|
256,
|
|
sig.size(),
|
|
512,
|
|
pk.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
// Should fail for invalid signature
|
|
{
|
|
std::string badSig(sig.size(), 0xFF);
|
|
// hfs.checkSignature(
|
|
// Slice(message.data(), message.size()),
|
|
// Slice(badSig.data(), badSig.size()),
|
|
// Slice(pk.data(), pk.size()));
|
|
vrt.setBytes(0, message.data(), message.size());
|
|
vrt.setBytes(256, badSig.data(), badSig.size());
|
|
vrt.setBytes(512, pk.data(), pk.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(checkSignature_wrap,
|
|
&import.at("check_sig"),
|
|
params,
|
|
result,
|
|
0,
|
|
message.size(),
|
|
256,
|
|
badSig.size(),
|
|
512,
|
|
pk.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
|
|
// Should fail for invalid public key
|
|
{
|
|
std::string badPk(pk.size(), 0x00);
|
|
// hfs.checkSignature(
|
|
// Slice(message.data(), message.size()),
|
|
// Slice(sig.data(), sig.size()),
|
|
// Slice(badPk.data(), badPk.size()));
|
|
vrt.setBytes(0, message.data(), message.size());
|
|
vrt.setBytes(256, sig.data(), sig.size());
|
|
vrt.setBytes(512, badPk.data(), badPk.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(checkSignature_wrap,
|
|
&import.at("check_sig"),
|
|
params,
|
|
result,
|
|
0,
|
|
message.size(),
|
|
256,
|
|
sig.size(),
|
|
512,
|
|
badPk.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == HfErrorToInt(HostFunctionError::InvalidParams));
|
|
}
|
|
|
|
// Should fail for empty public key
|
|
{
|
|
// hfs.checkSignature(
|
|
// Slice(message.data(), message.size()),
|
|
// Slice(sig.data(), sig.size()),
|
|
// Slice(nullptr, 0));
|
|
vrt.setBytes(0, message.data(), message.size());
|
|
vrt.setBytes(256, sig.data(), sig.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(checkSignature_wrap,
|
|
&import.at("check_sig"),
|
|
params,
|
|
result,
|
|
0,
|
|
message.size(),
|
|
256,
|
|
sig.size(),
|
|
512,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == HfErrorToInt(HostFunctionError::InvalidParams));
|
|
}
|
|
|
|
// Should fail for empty signature
|
|
{
|
|
// hfs.checkSignature(
|
|
// Slice(message.data(), message.size()),
|
|
// Slice(nullptr, 0),
|
|
// Slice(pk.data(), pk.size()));
|
|
vrt.setBytes(0, message.data(), message.size());
|
|
vrt.setBytes(512, pk.data(), pk.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(checkSignature_wrap,
|
|
&import.at("check_sig"),
|
|
params,
|
|
result,
|
|
0,
|
|
message.size(),
|
|
256,
|
|
0,
|
|
512,
|
|
pk.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
|
|
// Should fail for empty message
|
|
{
|
|
// hfs.checkSignature(
|
|
// Slice(nullptr, 0), Slice(sig.data(), sig.size()), Slice(pk.data(), pk.size()));
|
|
vrt.setBytes(256, sig.data(), sig.size());
|
|
vrt.setBytes(512, pk.data(), pk.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(checkSignature_wrap,
|
|
&import.at("check_sig"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
256,
|
|
sig.size(),
|
|
512,
|
|
pk.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
testComputeSha512HalfHash()
|
|
{
|
|
testcase("computeSha512HalfHash");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string data = "hello world";
|
|
// hfs.computeSha512HalfHash(Slice(data.data(), data.size()));
|
|
{
|
|
vrt.setBytes(0, data.data(), data.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(computeSha512HalfHash_wrap,
|
|
&import.at("compute_sha512_half"),
|
|
params,
|
|
result,
|
|
0,
|
|
data.size(),
|
|
256,
|
|
uint256::size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == uint256::size());
|
|
|
|
// Should match direct call to sha512Half
|
|
auto expected = sha512Half(Slice(data.data(), data.size()));
|
|
auto hashBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(std::ranges::equal(hashBytes, expected));
|
|
}
|
|
}
|
|
|
|
void
|
|
testKeyletFunctions()
|
|
{
|
|
testcase("keylet functions");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
VirtualRuntime vrt;
|
|
|
|
auto const usdIssue = env.master["USD"].issue();
|
|
auto const masterID = env.master.id();
|
|
auto const baseMpt = makeMptID(1, masterID);
|
|
|
|
auto imp = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Lambda to compare a Bytes (std::vector<uint8_t>) to a keylet
|
|
auto compareKeylet = [](std::vector<uint8_t> const& bytes, Keylet const& kl) {
|
|
return std::ranges::equal(bytes, kl.key);
|
|
};
|
|
|
|
{
|
|
auto const expected = keylet::account(masterID);
|
|
WasmValVec params(4), result(1);
|
|
auto* trap = ww(
|
|
accountKeylet_wrap, &imp.at("account_keylet"), params, result, masterID, 1024, 32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(accountKeylet_wrap,
|
|
&imp.at("account_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::amm(xrpIssue(), usdIssue);
|
|
WasmValVec params(6), result(1);
|
|
|
|
auto* trap =
|
|
ww(ammKeylet_wrap,
|
|
&imp.at("amm_keylet"),
|
|
params,
|
|
result,
|
|
xrpIssue(),
|
|
usdIssue,
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(ammKeylet_wrap,
|
|
&imp.at("amm_keylet"),
|
|
params,
|
|
result,
|
|
xrpIssue(),
|
|
xrpIssue(),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidParams));
|
|
|
|
auto* trap3 =
|
|
ww(ammKeylet_wrap,
|
|
&imp.at("amm_keylet"),
|
|
params,
|
|
result,
|
|
baseMpt,
|
|
xrpIssue(),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap3 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidParams));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::check(masterID, 1u);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(checkKeylet_wrap,
|
|
&imp.at("check_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(checkKeylet_wrap,
|
|
&imp.at("check_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
std::string const credTypeStr = "test";
|
|
Slice const credType(credTypeStr.data(), credTypeStr.size());
|
|
Account const alice("alice");
|
|
{
|
|
auto const expected = keylet::credential(masterID, masterID, credType);
|
|
WasmValVec params(8), result(1);
|
|
auto* trap =
|
|
ww(credentialKeylet_wrap,
|
|
&imp.at("credential_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
masterID,
|
|
credType,
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 6);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
std::string_view constexpr longCredTypeStr =
|
|
"abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
|
|
"asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p";
|
|
Slice const longCredType(longCredTypeStr.data(), longCredTypeStr.size());
|
|
static_assert(longCredTypeStr.size() > kMaxCredentialTypeLength);
|
|
auto* trap2 =
|
|
ww(credentialKeylet_wrap,
|
|
&imp.at("credential_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
alice.id(),
|
|
longCredType,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidParams));
|
|
|
|
auto* trap3 =
|
|
ww(credentialKeylet_wrap,
|
|
&imp.at("credential_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
alice.id(),
|
|
credType,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap3 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
|
|
auto* trap4 =
|
|
ww(credentialKeylet_wrap,
|
|
&imp.at("credential_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
xrpAccount(),
|
|
credType,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap4 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::did(masterID);
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(didKeylet_wrap, &imp.at("did_keylet"), params, result, masterID, 1024, 32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(didKeylet_wrap, &imp.at("did_keylet"), params, result, xrpAccount(), 1024, 32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::delegate(masterID, alice.id());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(delegateKeylet_wrap,
|
|
&imp.at("delegate_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
alice.id(),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(delegateKeylet_wrap,
|
|
&imp.at("delegate_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
masterID,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidParams));
|
|
|
|
auto* trap3 =
|
|
ww(delegateKeylet_wrap,
|
|
&imp.at("delegate_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
xrpAccount(),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap3 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
|
|
auto* trap4 =
|
|
ww(delegateKeylet_wrap,
|
|
&imp.at("delegate_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
masterID,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap4 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::depositPreauth(masterID, alice.id());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(depositPreauthKeylet_wrap,
|
|
&imp.at("deposit_preauth_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
alice.id(),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(depositPreauthKeylet_wrap,
|
|
&imp.at("deposit_preauth_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
masterID,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidParams));
|
|
|
|
auto* trap3 =
|
|
ww(depositPreauthKeylet_wrap,
|
|
&imp.at("deposit_preauth_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
xrpAccount(),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap3 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
|
|
auto* trap4 =
|
|
ww(depositPreauthKeylet_wrap,
|
|
&imp.at("deposit_preauth_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
masterID,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap4 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::escrow(masterID, 1u);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(escrowKeylet_wrap,
|
|
&imp.at("escrow_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(escrowKeylet_wrap,
|
|
&imp.at("escrow_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
Currency const usd = toCurrency("USD");
|
|
{
|
|
auto const expected = keylet::line(masterID, alice.id(), usd);
|
|
WasmValVec params(8), result(1);
|
|
auto* trap =
|
|
ww(lineKeylet_wrap,
|
|
&imp.at("line_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
alice.id(),
|
|
usd,
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 6);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(lineKeylet_wrap,
|
|
&imp.at("line_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
masterID,
|
|
usd,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidParams));
|
|
|
|
auto* trap3 =
|
|
ww(lineKeylet_wrap,
|
|
&imp.at("line_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
xrpAccount(),
|
|
usd,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap3 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
|
|
auto* trap4 =
|
|
ww(lineKeylet_wrap,
|
|
&imp.at("line_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
masterID,
|
|
usd,
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap4 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
|
|
auto* trap5 =
|
|
ww(lineKeylet_wrap,
|
|
&imp.at("line_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
alice.id(),
|
|
toCurrency(""),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap5 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidParams));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::mptIssuance(1u, masterID);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(mptIssuanceKeylet_wrap,
|
|
&imp.at("mpt_issuance_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(mptIssuanceKeylet_wrap,
|
|
&imp.at("mpt_issuance_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::mptoken(baseMpt, alice.id());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(mptokenKeylet_wrap,
|
|
&imp.at("mptoken_keylet"),
|
|
params,
|
|
result,
|
|
baseMpt,
|
|
alice.id(),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(mptokenKeylet_wrap,
|
|
&imp.at("mptoken_keylet"),
|
|
params,
|
|
result,
|
|
MPTID{},
|
|
alice.id(),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidParams));
|
|
|
|
auto* trap3 =
|
|
ww(mptokenKeylet_wrap,
|
|
&imp.at("mptoken_keylet"),
|
|
params,
|
|
result,
|
|
baseMpt,
|
|
xrpAccount(),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap3 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::nftoffer(masterID, 1u);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(nftOfferKeylet_wrap,
|
|
&imp.at("nft_offer_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(nftOfferKeylet_wrap,
|
|
&imp.at("nft_offer_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::offer(masterID, 1u);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(offerKeylet_wrap,
|
|
&imp.at("offer_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(offerKeylet_wrap,
|
|
&imp.at("offer_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::oracle(masterID, 1u);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(oracleKeylet_wrap,
|
|
&imp.at("oracle_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(oracleKeylet_wrap,
|
|
&imp.at("oracle_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::payChan(masterID, alice.id(), 1u);
|
|
WasmValVec params(8), result(1);
|
|
auto* trap =
|
|
ww(paychanKeylet_wrap,
|
|
&imp.at("paychan_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
alice.id(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 6);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(paychanKeylet_wrap,
|
|
&imp.at("paychan_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidParams));
|
|
|
|
auto* trap3 =
|
|
ww(paychanKeylet_wrap,
|
|
&imp.at("paychan_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap3 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
|
|
auto* trap4 =
|
|
ww(paychanKeylet_wrap,
|
|
&imp.at("paychan_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap4 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::permissionedDomain(masterID, 1u);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(permissionedDomainKeylet_wrap,
|
|
&imp.at("permissioned_domain_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(permissionedDomainKeylet_wrap,
|
|
&imp.at("permissioned_domain_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::signers(masterID);
|
|
WasmValVec params(4), result(1);
|
|
auto* trap = ww(
|
|
signersKeylet_wrap, &imp.at("signers_keylet"), params, result, masterID, 1024, 32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(signersKeylet_wrap,
|
|
&imp.at("signers_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::kTicket(masterID, 1u);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(ticketKeylet_wrap,
|
|
&imp.at("ticket_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(ticketKeylet_wrap,
|
|
&imp.at("ticket_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
{
|
|
auto const expected = keylet::vault(masterID, 1u);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(vaultKeylet_wrap,
|
|
&imp.at("vault_keylet"),
|
|
params,
|
|
result,
|
|
masterID,
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
if (BEAST_EXPECT(!trap && result[0].kind == WASM_I32 && result[0].of.i32 == 32))
|
|
{
|
|
auto const actual = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(compareKeylet(actual, expected));
|
|
}
|
|
|
|
auto* trap2 =
|
|
ww(vaultKeylet_wrap,
|
|
&imp.at("vault_keylet"),
|
|
params,
|
|
result,
|
|
xrpAccount(),
|
|
toBytes(1u),
|
|
1024,
|
|
32);
|
|
BEAST_EXPECT(
|
|
!trap2 && result[0].kind == WASM_I32 &&
|
|
result[0].of.i32 == static_cast<int32_t>(HostFunctionError::InvalidAccount));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetNFT()
|
|
{
|
|
testcase("getNFT");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
Account const alice("alice");
|
|
env.fund(XRP(1000), alice);
|
|
env.close();
|
|
|
|
// Mint NFT for alice
|
|
uint256 const nftId = token::getNextID(env, alice, 0u, 0u);
|
|
std::string const uri = "https://example.com/nft";
|
|
env(token::mint(alice), token::Uri(uri));
|
|
env.close();
|
|
uint256 const nftId2 = token::getNextID(env, alice, 0u, 0u);
|
|
env(token::mint(alice));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(alice, env.seq(alice));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Should succeed for valid NFT
|
|
{
|
|
// hfs.getNFT(alice.id(), nftId);
|
|
vrt.setBytes(0, alice.id().data(), AccountID::size());
|
|
vrt.setBytes(256, nftId.data(), uint256::size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(getNFT_wrap,
|
|
&import.at("get_nft"),
|
|
params,
|
|
result,
|
|
0,
|
|
AccountID::size(),
|
|
256,
|
|
uint256::size(),
|
|
512,
|
|
256);
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 > 0))
|
|
{
|
|
auto uriBytes = vrt.getBytes(params, 4);
|
|
uriBytes.resize(result[0].of.i32);
|
|
BEAST_EXPECT(std::ranges::equal(uriBytes, uri));
|
|
}
|
|
}
|
|
|
|
// Should fail for invalid account
|
|
{
|
|
// hfs.getNFT(xrpAccount(), nftId);
|
|
vrt.setBytes(0, xrpAccount().data(), AccountID::size());
|
|
vrt.setBytes(256, nftId.data(), uint256::size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(getNFT_wrap,
|
|
&import.at("get_nft"),
|
|
params,
|
|
result,
|
|
0,
|
|
AccountID::size(),
|
|
256,
|
|
uint256::size(),
|
|
512,
|
|
256);
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32))
|
|
BEAST_EXPECT(result[0].of.i32 == HfErrorToInt(HostFunctionError::InvalidAccount));
|
|
}
|
|
|
|
// Should fail for invalid nftId
|
|
{
|
|
// hfs.getNFT(alice.id(), uint256());
|
|
uint256 zeroId;
|
|
vrt.setBytes(0, alice.id().data(), AccountID::size());
|
|
vrt.setBytes(256, zeroId.data(), uint256::size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(getNFT_wrap,
|
|
&import.at("get_nft"),
|
|
params,
|
|
result,
|
|
0,
|
|
AccountID::size(),
|
|
256,
|
|
uint256::size(),
|
|
512,
|
|
256);
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32))
|
|
BEAST_EXPECT(result[0].of.i32 == HfErrorToInt(HostFunctionError::InvalidParams));
|
|
}
|
|
|
|
// Should fail for invalid nftId
|
|
{
|
|
auto const badId = token::getNextID(env, alice, 0u, 1u);
|
|
// hfs.getNFT(alice.id(), badId);
|
|
vrt.setBytes(0, alice.id().data(), AccountID::size());
|
|
vrt.setBytes(256, badId.data(), uint256::size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(getNFT_wrap,
|
|
&import.at("get_nft"),
|
|
params,
|
|
result,
|
|
0,
|
|
AccountID::size(),
|
|
256,
|
|
uint256::size(),
|
|
512,
|
|
256);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == HfErrorToInt(HostFunctionError::LedgerObjNotFound));
|
|
}
|
|
|
|
{
|
|
// hfs.getNFT(alice.id(), nftId2);
|
|
vrt.setBytes(0, alice.id().data(), AccountID::size());
|
|
vrt.setBytes(256, nftId2.data(), uint256::size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(getNFT_wrap,
|
|
&import.at("get_nft"),
|
|
params,
|
|
result,
|
|
0,
|
|
AccountID::size(),
|
|
256,
|
|
uint256::size(),
|
|
512,
|
|
256);
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32))
|
|
BEAST_EXPECT(result[0].of.i32 == HfErrorToInt(HostFunctionError::FieldNotFound));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetNFTIssuer()
|
|
{
|
|
testcase("getNFTIssuer");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
// Mint NFT for env.master
|
|
uint32_t const taxon = 12345;
|
|
uint256 const nftId = token::getNextID(env, env.master, taxon);
|
|
env(token::mint(env.master, taxon));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Should succeed for valid NFT id
|
|
{
|
|
// hfs.getNFTIssuer(nftId);
|
|
vrt.setBytes(0, nftId.data(), uint256::size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getNFTIssuer_wrap,
|
|
&import.at("get_nft_issuer"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
256,
|
|
AccountID::size());
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == AccountID::size()))
|
|
{
|
|
auto issuerBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(std::ranges::equal(issuerBytes, env.master.id()));
|
|
}
|
|
}
|
|
|
|
// Should fail for zero NFT id
|
|
{
|
|
// hfs.getNFTIssuer(uint256());
|
|
uint256 zeroId;
|
|
vrt.setBytes(0, zeroId.data(), uint256::size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getNFTIssuer_wrap,
|
|
&import.at("get_nft_issuer"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
256,
|
|
AccountID::size());
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32))
|
|
BEAST_EXPECT(result[0].of.i32 == HfErrorToInt(HostFunctionError::InvalidParams));
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetNFTTaxon()
|
|
{
|
|
testcase("getNFTTaxon");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
|
|
uint32_t const taxon = 54321;
|
|
uint256 const nftId = token::getNextID(env, env.master, taxon);
|
|
env(token::mint(env.master, taxon));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// hfs.getNFTTaxon(nftId);
|
|
vrt.setBytes(0, nftId.data(), uint256::size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getNFTTaxon_wrap,
|
|
&import.at("get_nft_taxon"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
256,
|
|
sizeof(uint32_t));
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == sizeof(uint32_t)))
|
|
{
|
|
BEAST_EXPECT(vrt.getUint32(params, 2) == taxon);
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetNFTFlags()
|
|
{
|
|
testcase("getNFTFlags");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
|
|
// Mint NFT with default flags
|
|
uint256 const nftId = token::getNextID(env, env.master, 0u, tfTransferable);
|
|
env(token::mint(env.master, 0), Txflags(tfTransferable));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.getNFTFlags(nftId);
|
|
vrt.setBytes(0, nftId.data(), uint256::size());
|
|
WasmValVec params(2), result(1);
|
|
auto* trap = ww(
|
|
getNFTFlags_wrap, &import.at("get_nft_flags"), params, result, 0, uint256::size());
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32))
|
|
BEAST_EXPECT(result[0].of.i32 == tfTransferable);
|
|
}
|
|
|
|
// Should return 0 for zero NFT id
|
|
{
|
|
// hfs.getNFTFlags(uint256());
|
|
uint256 zeroId;
|
|
vrt.setBytes(0, zeroId.data(), uint256::size());
|
|
WasmValVec params(2), result(1);
|
|
auto* trap = ww(
|
|
getNFTFlags_wrap, &import.at("get_nft_flags"), params, result, 0, uint256::size());
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32))
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetNFTTransferFee()
|
|
{
|
|
testcase("getNFTTransferFee");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
|
|
uint16_t const transferFee = 250;
|
|
uint256 const nftId = token::getNextID(env, env.master, 0u, tfTransferable, transferFee);
|
|
env(token::mint(env.master, 0), token::XferFee(transferFee), Txflags(tfTransferable));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.getNFTTransferFee(nftId);
|
|
vrt.setBytes(0, nftId.data(), uint256::size());
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getNFTTransferFee_wrap,
|
|
&import.at("get_nft_transfer_fee"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size());
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32))
|
|
BEAST_EXPECT(result[0].of.i32 == transferFee);
|
|
}
|
|
|
|
// Should return 0 for zero NFT id
|
|
{
|
|
// hfs.getNFTTransferFee(uint256());
|
|
uint256 zeroId;
|
|
vrt.setBytes(0, zeroId.data(), uint256::size());
|
|
WasmValVec params(2), result(1);
|
|
auto* trap =
|
|
ww(getNFTTransferFee_wrap,
|
|
&import.at("get_nft_transfer_fee"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size());
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32))
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
testGetNFTSerial()
|
|
{
|
|
testcase("getNFTSerial");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
|
|
// Mint NFT with serial 0
|
|
uint256 const nftId = token::getNextID(env, env.master, 0u);
|
|
auto const serial = env.seq(env.master);
|
|
env(token::mint(env.master));
|
|
env.close();
|
|
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.getNFTSerial(nftId);
|
|
vrt.setBytes(0, nftId.data(), uint256::size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getNFTSerial_wrap,
|
|
&import.at("get_nft_serial"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
256,
|
|
sizeof(uint32_t));
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == sizeof(uint32_t)))
|
|
{
|
|
BEAST_EXPECT(vrt.getUint32(params, 2) == serial);
|
|
}
|
|
}
|
|
|
|
// Should return 0 for zero NFT id
|
|
{
|
|
// hfs.getNFTSerial(uint256());
|
|
uint256 zeroId;
|
|
vrt.setBytes(0, zeroId.data(), uint256::size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(getNFTSerial_wrap,
|
|
&import.at("get_nft_serial"),
|
|
params,
|
|
result,
|
|
0,
|
|
uint256::size(),
|
|
256,
|
|
sizeof(uint32_t));
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == sizeof(uint32_t)))
|
|
{
|
|
BEAST_EXPECT(vrt.getUint32(params, 2) == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
testTrace()
|
|
{
|
|
testcase("trace");
|
|
using namespace test::jtx;
|
|
|
|
{
|
|
Env env(*this);
|
|
OpenView ov{*env.current()};
|
|
test::StreamSink sink{beast::Severity::Trace};
|
|
beast::Journal const jlog{sink};
|
|
ApplyContext ac = createApplyContext(env, ov, jlog);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string const msg = "test trace";
|
|
std::string data = "abc";
|
|
auto const slice = Slice(data.data(), data.size());
|
|
|
|
// hfs.trace(msg, slice, false);
|
|
{
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, slice.data(), slice.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(trace_wrap,
|
|
&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
slice.size(),
|
|
0);
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.find(msg) != std::string::npos);
|
|
}
|
|
}
|
|
|
|
// hfs.trace(msg, slice, true);
|
|
{
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, slice.data(), slice.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(trace_wrap,
|
|
&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
slice.size(),
|
|
1);
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
std::string hex;
|
|
hex.reserve(data.size() * 2);
|
|
boost::algorithm::hex(data.begin(), data.end(), std::back_inserter(hex));
|
|
BEAST_EXPECT(messages.find(msg) != std::string::npos);
|
|
BEAST_EXPECT(messages.find(hex) != std::string::npos);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// logs disabled (trace < error)
|
|
Env env(*this);
|
|
OpenView ov{*env.current()};
|
|
test::StreamSink sink{beast::Severity::Error};
|
|
beast::Journal const jlog{sink};
|
|
ApplyContext ac = createApplyContext(env, ov, jlog);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string const msg = "test trace";
|
|
std::string data = "abc";
|
|
auto const slice = Slice(data.data(), data.size());
|
|
|
|
// hfs.trace(msg, slice, false);
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, slice.data(), slice.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(trace_wrap,
|
|
&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
slice.size(),
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.empty());
|
|
}
|
|
}
|
|
|
|
void
|
|
testTraceNum()
|
|
{
|
|
testcase("traceNum");
|
|
using namespace test::jtx;
|
|
|
|
{
|
|
Env env(*this);
|
|
OpenView ov{*env.current()};
|
|
test::StreamSink sink{beast::Severity::Trace};
|
|
beast::Journal const jlog{sink};
|
|
ApplyContext ac = createApplyContext(env, ov, jlog);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string const msg = "trace number";
|
|
int64_t const num = 123456789;
|
|
|
|
// hfs.traceNum(msg, num);
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(traceNum_wrap, &import.at("trace_num"), params, result, 0, msg.size(), num);
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.find(msg) != std::string::npos);
|
|
BEAST_EXPECT(messages.find(std::to_string(num)) != std::string::npos);
|
|
}
|
|
}
|
|
|
|
{
|
|
// logs disabled
|
|
Env env(*this);
|
|
OpenView ov{*env.current()};
|
|
test::StreamSink sink{beast::Severity::Error};
|
|
beast::Journal const jlog{sink};
|
|
ApplyContext ac = createApplyContext(env, ov, jlog);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string const msg = "trace number";
|
|
int64_t const num = 123456789;
|
|
|
|
// hfs.traceNum(msg, num);
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(traceNum_wrap, &import.at("trace_num"), params, result, 0, msg.size(), num);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.empty());
|
|
}
|
|
}
|
|
|
|
void
|
|
testTraceAccount()
|
|
{
|
|
testcase("traceAccount");
|
|
using namespace test::jtx;
|
|
|
|
{
|
|
Env env(*this);
|
|
OpenView ov{*env.current()};
|
|
test::StreamSink sink{beast::Severity::Trace};
|
|
beast::Journal const jlog{sink};
|
|
ApplyContext ac = createApplyContext(env, ov, jlog);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string const msg = "trace account";
|
|
auto const& accountId = env.master.id();
|
|
|
|
// hfs.traceAccount(msg, env.master.id());
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, accountId.data(), accountId.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(traceAccount_wrap,
|
|
&import.at("trace_account"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
accountId.size());
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.find(msg) != std::string::npos);
|
|
BEAST_EXPECT(messages.find(env.master.human()) != std::string::npos);
|
|
}
|
|
}
|
|
|
|
{
|
|
// logs disabled
|
|
Env env(*this);
|
|
OpenView ov{*env.current()};
|
|
test::StreamSink sink{beast::Severity::Error};
|
|
beast::Journal const jlog{sink};
|
|
ApplyContext ac = createApplyContext(env, ov, jlog);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string msg = "trace account";
|
|
auto const& accountId = env.master.id();
|
|
|
|
// hfs.traceAccount(msg, env.master.id());
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, accountId.data(), accountId.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(traceAccount_wrap,
|
|
&import.at("trace_account"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
accountId.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.empty());
|
|
}
|
|
}
|
|
|
|
void
|
|
testTraceAmount()
|
|
{
|
|
testcase("traceAmount");
|
|
using namespace test::jtx;
|
|
|
|
{
|
|
Env env(*this);
|
|
OpenView ov{*env.current()};
|
|
test::StreamSink sink{beast::Severity::Trace};
|
|
beast::Journal const jlog{sink};
|
|
ApplyContext ac = createApplyContext(env, ov, jlog);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string const msg = "trace amount";
|
|
STAmount const amount = XRP(12345);
|
|
{
|
|
// hfs.traceAmount(msg, amount);
|
|
Bytes amountBytes = toBytes(amount);
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(traceAmount_wrap,
|
|
&import.at("trace_amount"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
amountBytes.size());
|
|
|
|
if (BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.find(msg) != std::string::npos);
|
|
BEAST_EXPECT(messages.find(amount.getFullText()) != std::string::npos);
|
|
}
|
|
}
|
|
|
|
// IOU amount
|
|
Account const alice("alice");
|
|
env.fund(XRP(1000), alice);
|
|
env.close();
|
|
STAmount const iouAmount = env.master["USD"](100);
|
|
{
|
|
// hfs.traceAmount(msg, iouAmount);
|
|
Bytes amountBytes = toBytes(iouAmount);
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(traceAmount_wrap,
|
|
&import.at("trace_amount"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
amountBytes.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
|
|
// MPT amount
|
|
{
|
|
auto const mptId = makeMptID(42, env.master.id());
|
|
Asset const mptAsset = Asset(mptId);
|
|
STAmount const mptAmount(mptAsset, 123456);
|
|
|
|
// hfs.traceAmount(msg, mptAmount);
|
|
Bytes amountBytes = toBytes(mptAmount);
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(traceAmount_wrap,
|
|
&import.at("trace_amount"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
amountBytes.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
}
|
|
|
|
{
|
|
// logs disabled
|
|
Env env(*this);
|
|
OpenView ov{*env.current()};
|
|
test::StreamSink sink{beast::Severity::Error};
|
|
beast::Journal const jlog{sink};
|
|
ApplyContext ac = createApplyContext(env, ov, jlog);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string const msg = "trace amount";
|
|
STAmount const amount = XRP(12345);
|
|
|
|
// hfs.traceAmount(msg, amount);
|
|
Bytes amountBytes = toBytes(amount);
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(traceAmount_wrap,
|
|
&import.at("trace_amount"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
amountBytes.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.empty());
|
|
}
|
|
}
|
|
|
|
// clang-format off
|
|
|
|
int const normalExp = 18;
|
|
|
|
Bytes const floatIntMin = {0xF3, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, 0x01}; // -2^63
|
|
Bytes const floatIntZero = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}; // 0
|
|
Bytes const floatIntMax = {0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; // 2^63-1
|
|
Bytes const floatUIntMax = {0x19, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, 0x00, 0x00, 0x00, 0x01}; // 2^64-1
|
|
|
|
Bytes const floatMaxExp = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00}; // 1e(Number::kMaxExponent + normalExp)
|
|
Bytes const floatPreMaxExp = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF}; // 1e(Number::kMaxExponent + normalExp - 1)
|
|
Bytes const floatMinusMaxExp = {0xF2, 0x1F, 0x49, 0x4C, 0x58, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00}; // -1e(Number::kMaxExponent + normalExp)
|
|
Bytes const floatMinExp = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}; // 1e(Number::kMinExponent - normalExp)
|
|
Bytes const floatMax = {0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x00}; // Number::kMaxRep e(Number::kMaxExponent - normalExp)
|
|
|
|
Bytes const floatMaxIOU = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x63, 0xFF, 0x9C, 0x00, 0x00, 0x00, 0x4E}; // 9999999999999999e(96)
|
|
Bytes const floatMinIOU = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x9D}; // 1e(-96 - 3 + normalExp = -81)
|
|
|
|
Bytes const float1 = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE}; // 1
|
|
Bytes const floatMinus1 = {0xF2, 0x1F, 0x49, 0x4C, 0x58, 0x9C, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE}; // -1
|
|
Bytes const float1More = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x03, 0xE8, 0xFF, 0xFF, 0xFF, 0xEE}; // 1.000 000 000 000 001
|
|
Bytes const float2 = {0x1B, 0xC1, 0x6D, 0x67, 0x4E, 0xC8, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE}; // 2
|
|
Bytes const float10 = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEF}; // 10
|
|
Bytes const floatPi = {0x2B, 0x99, 0x2D, 0xDF, 0xA2, 0x32, 0x48, 0xE8, 0xFF, 0xFF, 0xFF, 0xEE}; // 3.141592653589793
|
|
Bytes const floatInvalidZero = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00}; // INVALID
|
|
Bytes const floatMinus3 = {0xD6, 0x5D, 0xDB, 0xE5, 0x09, 0xD4, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE}; // -3
|
|
|
|
std::string const invalid = "invalid_data";
|
|
|
|
// clang-format on
|
|
|
|
template <class T>
|
|
void
|
|
printFloats(std::string_view descr, T m, int e)
|
|
{
|
|
Serializer msg;
|
|
Number n;
|
|
|
|
if constexpr (std::is_signed_v<T>)
|
|
{
|
|
n = Number(static_cast<int64_t>(m), e);
|
|
}
|
|
else
|
|
{
|
|
n = Number(static_cast<uint64_t>(m), e, Number::Normalized{});
|
|
}
|
|
|
|
STNumber(sfNumber, n).add(msg);
|
|
auto const& data = msg.modData();
|
|
std::cout << std::setw(24) << descr << " m: " << std::setw(20) << n.mantissa()
|
|
<< ", e: " << std::setw(8) << n.exponent() << ", hex: ";
|
|
std::cout << std::hex << std::uppercase << std::setfill('0');
|
|
for (auto const& c : data)
|
|
std::cout << std::setw(2) << (unsigned)c << " ";
|
|
std::cout << std::dec << std::setfill(' ') << std::endl;
|
|
}
|
|
|
|
void
|
|
printNumbersBin()
|
|
{
|
|
printFloats("int64.min", std::numeric_limits<int64_t>::min(), 0);
|
|
printFloats("zero", 0, 0);
|
|
printFloats("int64.max", std::numeric_limits<int64_t>::max(), 0);
|
|
printFloats("uint64.max", std::numeric_limits<uint64_t>::max(), 0);
|
|
|
|
printFloats("Number 1 max exp", 1, Number::kMaxExponent + normalExp);
|
|
printFloats("Number (max exp - 1)", 1, Number::kMaxExponent + normalExp - 1);
|
|
printFloats("Number -1 max exp", -1, Number::kMaxExponent + normalExp);
|
|
|
|
printFloats("Number.max", Number::kMaxRep, Number::kMaxExponent);
|
|
printFloats("Number min positive", 1, Number::kMinExponent + normalExp);
|
|
printFloats(
|
|
"Number.min", std::numeric_limits<int64_t>::min(), Number::kMaxExponent - normalExp);
|
|
printFloats("STAmount.max", STAmount::kMaxValue, STAmount::kMaxOffset);
|
|
printFloats("STAmount min positive", STAmount::kMinValue, STAmount::kMinOffset);
|
|
|
|
printFloats("one", 1, 0);
|
|
printFloats("-one", -1, 0);
|
|
printFloats("1,00...01", 1'000'000'000'000'001, -15);
|
|
printFloats("two", 2, 0);
|
|
printFloats("ten", 10, 0);
|
|
printFloats("pi", 3141592653589793, -15);
|
|
printFloats("-three", -3, 0);
|
|
return;
|
|
}
|
|
|
|
void
|
|
testTraceFloat()
|
|
{
|
|
testcase("traceFloat");
|
|
using namespace test::jtx;
|
|
|
|
{
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string const msg = "trace float";
|
|
|
|
{
|
|
// hfs.traceFloat(msg, makeSlice(invalid));
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, reinterpret_cast<uint8_t const*>(invalid.data()), invalid.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(traceFloat_wrap,
|
|
&import.at("trace_opaque_float"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
invalid.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
|
|
{
|
|
// hfs.traceFloat(msg, makeSlice(floatMaxExp));
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, floatMaxExp.data(), floatMaxExp.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(traceFloat_wrap,
|
|
&import.at("trace_opaque_float"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
floatMaxExp.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
}
|
|
|
|
{
|
|
// logs disabled
|
|
Env env(*this);
|
|
OpenView ov{*env.current()};
|
|
test::StreamSink sink{beast::Severity::Error};
|
|
beast::Journal const jlog{sink};
|
|
ApplyContext ac = createApplyContext(env, ov, jlog);
|
|
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
std::string const msg = "trace float";
|
|
|
|
// hfs.traceFloat(msg, makeSlice(invalid));
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, reinterpret_cast<uint8_t const*>(invalid.data()), invalid.size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(traceFloat_wrap,
|
|
&import.at("trace_opaque_float"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
256,
|
|
invalid.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.empty());
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatFromInt()
|
|
{
|
|
testcase("floatFromInt");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatFromInt(min64, -1);
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(floatFromInt_wrap,
|
|
&import.at("float_from_int"),
|
|
params,
|
|
result,
|
|
min64,
|
|
0,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromInt(min64, 4);
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(floatFromInt_wrap,
|
|
&import.at("float_from_int"),
|
|
params,
|
|
result,
|
|
min64,
|
|
0,
|
|
floatSize,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromInt(min64, 0);
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(floatFromInt_wrap,
|
|
&import.at("float_from_int"),
|
|
params,
|
|
result,
|
|
min64,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 1);
|
|
BEAST_EXPECT(resultBytes == floatIntMin);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromInt(0, 0);
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(floatFromInt_wrap,
|
|
&import.at("float_from_int"),
|
|
params,
|
|
result,
|
|
0ll,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 1);
|
|
BEAST_EXPECT(resultBytes == floatIntZero);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromInt(max64, 0);
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(floatFromInt_wrap,
|
|
&import.at("float_from_int"),
|
|
params,
|
|
result,
|
|
max64,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 1);
|
|
BEAST_EXPECT(resultBytes == floatIntMax);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatFromUint()
|
|
{
|
|
testcase("floatFromUint");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatFromUint(std::numeric_limits<uint64_t>::min(), -1);
|
|
WasmValVec params(5), result(1);
|
|
uint64_t val = std::numeric_limits<uint64_t>::min();
|
|
vrt.setBytes(0, &val, sizeof(val));
|
|
auto* trap =
|
|
ww(floatFromUint_wrap,
|
|
&import.at("float_from_uint"),
|
|
params,
|
|
result,
|
|
0,
|
|
8,
|
|
16,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromUint(std::numeric_limits<uint64_t>::min(), 4);
|
|
WasmValVec params(5), result(1);
|
|
uint64_t val = std::numeric_limits<uint64_t>::min();
|
|
vrt.setBytes(0, &val, sizeof(val));
|
|
auto* trap =
|
|
ww(floatFromUint_wrap,
|
|
&import.at("float_from_uint"),
|
|
params,
|
|
result,
|
|
0,
|
|
8,
|
|
16,
|
|
floatSize,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromUint(0, 0);
|
|
WasmValVec params(5), result(1);
|
|
uint64_t val = 0;
|
|
vrt.setBytes(0, &val, sizeof(val));
|
|
auto* trap =
|
|
ww(floatFromUint_wrap,
|
|
&import.at("float_from_uint"),
|
|
params,
|
|
result,
|
|
0,
|
|
8,
|
|
16,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatIntZero);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromUint(std::numeric_limits<uint64_t>::max(), 0);
|
|
WasmValVec params(5), result(1);
|
|
uint64_t val = std::numeric_limits<uint64_t>::max();
|
|
vrt.setBytes(0, &val, sizeof(val));
|
|
auto* trap =
|
|
ww(floatFromUint_wrap,
|
|
&import.at("float_from_uint"),
|
|
params,
|
|
result,
|
|
0,
|
|
8,
|
|
16,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatUIntMax);
|
|
}
|
|
}
|
|
|
|
void
|
|
testfloatFromMantExp()
|
|
{
|
|
testcase("floatFromMantExp");
|
|
using namespace test::jtx;
|
|
using namespace wasm_float;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatFromMantExp(1, 0, -1);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
1ll,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(1, 0, 4);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
1ll,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(1, Number::kMaxExponent + normalExp + 1, 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
1ll,
|
|
Number::kMaxExponent + normalExp + 1,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(1, Number::kMinExponent + normalExp - 1, 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
1ll,
|
|
Number::kMinExponent + normalExp - 1,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatIntZero);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(1, Number::kMaxExponent + normalExp, 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
1ll,
|
|
Number::kMaxExponent + normalExp,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatMaxExp);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(-1, Number::kMaxExponent + normalExp, 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
-1ll,
|
|
Number::kMaxExponent + normalExp,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatMinusMaxExp);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(1, Number::kMaxExponent + normalExp - 1, 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
1ll,
|
|
Number::kMaxExponent + normalExp - 1,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatPreMaxExp);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(STAmount::kMaxValue, STAmount::kMaxOffset, 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
static_cast<int64_t>(STAmount::kMaxValue),
|
|
STAmount::kMaxOffset,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatMaxIOU);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(1, Number::kMinExponent + normalExp, 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
1ll,
|
|
Number::kMinExponent - normalExp,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatMinExp);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(10, -1, 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
10ll,
|
|
-1,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == float1);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromMantExp(1, Number::kMaxExponent + normalExp + 1, 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromMantExp_wrap,
|
|
&import.at("float_compare"),
|
|
params,
|
|
result,
|
|
1ll,
|
|
Number::kMaxExponent + normalExp + 1,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatCompare()
|
|
{
|
|
testcase("floatCompare");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatCompare(Slice(), Slice());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap = ww(floatCompare_wrap, &import.at("float_add"), params, result, 0, 0, 0, 0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatCompare(makeSlice(floatInvalidZero), Slice());
|
|
WasmValVec params(4), result(1);
|
|
vrt.setBytes(0, floatInvalidZero.data(), floatInvalidZero.size());
|
|
auto* trap =
|
|
ww(floatCompare_wrap, &import.at("float_add"), params, result, 0, floatSize, 0, 0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatCompare(makeSlice(float1), makeSlice(invalid));
|
|
WasmValVec params(4), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
vrt.setBytes(floatSize, invalid.data(), invalid.size());
|
|
auto* trap =
|
|
ww(floatCompare_wrap,
|
|
&import.at("float_add"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
invalid.size());
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatCompare(makeSlice(floatIntMin), makeSlice(floatIntZero));
|
|
WasmValVec params(4), result(1);
|
|
vrt.setBytes(0, floatIntMin.data(), floatIntMin.size());
|
|
vrt.setBytes(floatSize, floatIntZero.data(), floatIntZero.size());
|
|
auto* trap =
|
|
ww(floatCompare_wrap,
|
|
&import.at("float_add"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 2);
|
|
}
|
|
|
|
{
|
|
// hfs.floatCompare(makeSlice(floatIntMax), makeSlice(floatIntZero));
|
|
WasmValVec params(4), result(1);
|
|
vrt.setBytes(0, floatIntMax.data(), floatIntMax.size());
|
|
vrt.setBytes(floatSize, floatIntZero.data(), floatIntZero.size());
|
|
auto* trap =
|
|
ww(floatCompare_wrap,
|
|
&import.at("float_add"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 1);
|
|
}
|
|
|
|
{
|
|
// hfs.floatCompare(makeSlice(float1), makeSlice(float1));
|
|
WasmValVec params(4), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
vrt.setBytes(floatSize, float1.data(), float1.size());
|
|
auto* trap =
|
|
ww(floatCompare_wrap,
|
|
&import.at("float_add"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatAdd()
|
|
{
|
|
testcase("floatAdd");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatAdd(Slice(), Slice(), -1);
|
|
WasmValVec params(7), result(1);
|
|
auto* trap =
|
|
ww(floatAdd_wrap,
|
|
&import.at("float_subtract"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatAdd(Slice(), Slice(), 0);
|
|
WasmValVec params(7), result(1);
|
|
auto* trap =
|
|
ww(floatAdd_wrap,
|
|
&import.at("float_subtract"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatAdd(makeSlice(float1), makeSlice(invalid), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
vrt.setBytes(floatSize, invalid.data(), invalid.size());
|
|
auto* trap =
|
|
ww(floatAdd_wrap,
|
|
&import.at("float_subtract"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
invalid.size(),
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatAdd(makeSlice(floatMaxIOU), makeSlice(floatMaxExp), 0);
|
|
// max IOU is too small to make any change
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatMaxIOU.data(), floatMaxIOU.size());
|
|
vrt.setBytes(floatSize, floatMaxExp.data(), floatMaxExp.size());
|
|
auto* trap =
|
|
ww(floatAdd_wrap,
|
|
&import.at("float_subtract"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatMaxExp);
|
|
}
|
|
|
|
{
|
|
// hfs.floatAdd(makeSlice(floatIntMin), makeSlice(floatIntZero), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatIntMin.data(), floatIntMin.size());
|
|
vrt.setBytes(floatSize, floatIntZero.data(), floatIntZero.size());
|
|
auto* trap =
|
|
ww(floatAdd_wrap,
|
|
&import.at("float_subtract"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatIntMin);
|
|
}
|
|
|
|
{
|
|
// hfs.floatAdd(makeSlice(floatIntMax), makeSlice(floatIntMin), 0);//
|
|
// Number can't hold int64.min, it is rounded and we get -3, not -1
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatIntMax.data(), floatIntMax.size());
|
|
vrt.setBytes(floatSize, floatIntMin.data(), floatIntMin.size());
|
|
auto* trap =
|
|
ww(floatAdd_wrap,
|
|
&import.at("float_subtract"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatMinus3);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatSubtract()
|
|
{
|
|
testcase("floatSubtract");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatSubtract(Slice(), Slice(), -1);
|
|
WasmValVec params(7), result(1);
|
|
auto* trap =
|
|
ww(floatSubtract_wrap,
|
|
&import.at("float_from_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatSubtract(Slice(), Slice(), 0);
|
|
WasmValVec params(7), result(1);
|
|
auto* trap =
|
|
ww(floatSubtract_wrap,
|
|
&import.at("float_from_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatSubtract(makeSlice(float1), makeSlice(invalid), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
vrt.setBytes(floatSize, invalid.data(), invalid.size());
|
|
auto* trap =
|
|
ww(floatSubtract_wrap,
|
|
&import.at("float_from_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
invalid.size(),
|
|
floatSize * 2,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatSubtract(makeSlice(floatMinusMaxExp), makeSlice(floatMaxIOU), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatMinusMaxExp.data(), floatMinusMaxExp.size());
|
|
vrt.setBytes(floatSize, floatMaxIOU.data(), floatMaxIOU.size());
|
|
auto* trap =
|
|
ww(floatSubtract_wrap,
|
|
&import.at("float_from_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatMinusMaxExp);
|
|
}
|
|
|
|
{
|
|
// hfs.floatSubtract(makeSlice(floatIntMin), makeSlice(floatIntZero), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatIntMin.data(), floatIntMin.size());
|
|
vrt.setBytes(floatSize, floatIntZero.data(), floatIntZero.size());
|
|
auto* trap =
|
|
ww(floatSubtract_wrap,
|
|
&import.at("float_from_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatIntMin);
|
|
}
|
|
|
|
{
|
|
// hfs.floatSubtract(makeSlice(floatIntZero), makeSlice(float1), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatIntZero.data(), floatIntZero.size());
|
|
vrt.setBytes(floatSize, float1.data(), float1.size());
|
|
auto* trap =
|
|
ww(floatSubtract_wrap,
|
|
&import.at("float_from_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatMinus1);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatMultiply()
|
|
{
|
|
testcase("floatMultiply");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatMultiply(Slice(), Slice(), -1);
|
|
WasmValVec params(7), result(1);
|
|
auto* trap =
|
|
ww(floatMultiply_wrap,
|
|
&import.at("float_multiply"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatMultiply(Slice(), Slice(), 0);
|
|
WasmValVec params(7), result(1);
|
|
auto* trap =
|
|
ww(floatMultiply_wrap,
|
|
&import.at("float_multiply"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatMultiply(makeSlice(float1), makeSlice(invalid), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
vrt.setBytes(floatSize, invalid.data(), invalid.size());
|
|
auto* trap =
|
|
ww(floatMultiply_wrap,
|
|
&import.at("float_multiply"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
invalid.size(),
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatMultiply(makeSlice(floatMax), makeSlice(float1More), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatMax.data(), floatMax.size());
|
|
vrt.setBytes(floatSize, float1More.data(), float1More.size());
|
|
auto* trap =
|
|
ww(floatMultiply_wrap,
|
|
&import.at("float_multiply"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatComputationError));
|
|
}
|
|
|
|
{
|
|
// hfs.floatMultiply(makeSlice(float1), makeSlice(float1), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
vrt.setBytes(floatSize, float1.data(), float1.size());
|
|
auto* trap =
|
|
ww(floatMultiply_wrap,
|
|
&import.at("float_multiply"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == float1);
|
|
}
|
|
|
|
{
|
|
// hfs.floatMultiply(makeSlice(floatIntZero), makeSlice(floatMaxIOU), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatIntZero.data(), floatIntZero.size());
|
|
vrt.setBytes(floatSize, floatMaxIOU.data(), floatMaxIOU.size());
|
|
auto* trap =
|
|
ww(floatMultiply_wrap,
|
|
&import.at("float_multiply"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatIntZero);
|
|
}
|
|
|
|
{
|
|
// hfs.floatMultiply(makeSlice(float10), makeSlice(floatPreMaxExp), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, float10.data(), float10.size());
|
|
vrt.setBytes(floatSize, floatPreMaxExp.data(), floatPreMaxExp.size());
|
|
auto* trap =
|
|
ww(floatMultiply_wrap,
|
|
&import.at("float_multiply"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatMaxExp);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatDivide()
|
|
{
|
|
testcase("floatDivide");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatDivide(Slice(), Slice(), -1);
|
|
WasmValVec params(7), result(1);
|
|
auto* trap =
|
|
ww(floatDivide_wrap,
|
|
&import.at("float_divide"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatDivide(Slice(), Slice(), 0);
|
|
WasmValVec params(7), result(1);
|
|
auto* trap =
|
|
ww(floatDivide_wrap,
|
|
&import.at("float_divide"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{ // hfs.floatDivide(makeSlice(float1), makeSlice(invalid), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
vrt.setBytes(floatSize, invalid.data(), invalid.size());
|
|
auto* trap =
|
|
ww(floatDivide_wrap,
|
|
&import.at("float_divide"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
invalid.size(),
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{ // hfs.floatDivide(makeSlice(float1), makeSlice(floatIntZero), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
vrt.setBytes(floatSize, floatIntZero.data(), floatIntZero.size());
|
|
auto* trap =
|
|
ww(floatDivide_wrap,
|
|
&import.at("float_divide"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatComputationError));
|
|
}
|
|
|
|
{ // hfs.floatDivide(makeSlice(floatMax), makeSlice(*y), 0);
|
|
auto const y =
|
|
hfs.floatFromMantExp(STAmount::kMaxValue, -normalExp - 1, 0); // 0.9999999...
|
|
if (BEAST_EXPECT(y))
|
|
{
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatMax.data(), floatMax.size());
|
|
vrt.setBytes(floatSize, y->data(), y->size());
|
|
auto* trap =
|
|
ww(floatDivide_wrap,
|
|
&import.at("float_divide"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatComputationError));
|
|
}
|
|
}
|
|
|
|
{ // hfs.floatDivide(makeSlice(floatIntZero), makeSlice(float1), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatIntZero.data(), floatIntZero.size());
|
|
vrt.setBytes(floatSize, float1.data(), float1.size());
|
|
auto* trap =
|
|
ww(floatDivide_wrap,
|
|
&import.at("float_divide"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatIntZero);
|
|
}
|
|
|
|
{ // hfs.floatDivide(makeSlice(floatMaxExp), makeSlice(float10), 0);
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatMaxExp.data(), floatMaxExp.size());
|
|
vrt.setBytes(floatSize, float10.data(), float10.size());
|
|
auto* trap =
|
|
ww(floatDivide_wrap,
|
|
&import.at("float_divide"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
floatSize,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 4);
|
|
BEAST_EXPECT(resultBytes == floatPreMaxExp);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatRoot()
|
|
{
|
|
testcase("floatRoot");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{ // hfs.floatRoot(Slice(), 2, -1);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatRoot_wrap,
|
|
&import.at("float_root"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
2,
|
|
0,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{ // hfs.floatRoot(makeSlice(invalid), 3, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, invalid.data(), invalid.size());
|
|
auto* trap =
|
|
ww(floatRoot_wrap,
|
|
&import.at("float_root"),
|
|
params,
|
|
result,
|
|
0,
|
|
invalid.size(),
|
|
3,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{ // hfs.floatRoot(makeSlice(float1), -2, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
auto* trap =
|
|
ww(floatRoot_wrap,
|
|
&import.at("float_root"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
-2,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{ // hfs.floatRoot(makeSlice(floatIntZero), 2, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, floatIntZero.data(), floatIntZero.size());
|
|
auto* trap =
|
|
ww(floatRoot_wrap,
|
|
&import.at("float_root"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
2,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 3);
|
|
BEAST_EXPECT(resultBytes == floatIntZero);
|
|
}
|
|
|
|
{ // hfs.floatRoot(makeSlice(floatMaxIOU), 1, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, floatMaxIOU.data(), floatMaxIOU.size());
|
|
auto* trap =
|
|
ww(floatRoot_wrap,
|
|
&import.at("float_root"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
1,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 3);
|
|
BEAST_EXPECT(resultBytes == floatMaxIOU);
|
|
}
|
|
|
|
{
|
|
// hfs.floatRoot(makeSlice(*x), 2, 0);
|
|
auto const x = hfs.floatFromMantExp(100, 0, 0); // 100
|
|
if (BEAST_EXPECT(x))
|
|
{
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, x->data(), x->size());
|
|
auto* trap =
|
|
ww(floatRoot_wrap,
|
|
&import.at("float_root"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
2,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 3);
|
|
BEAST_EXPECT(resultBytes == float10);
|
|
}
|
|
}
|
|
|
|
{
|
|
// hfs.floatRoot(makeSlice(*x), 3, 0);
|
|
auto const x = hfs.floatFromMantExp(1000, 0, 0); // 1000
|
|
if (BEAST_EXPECT(x))
|
|
{
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, x->data(), x->size());
|
|
auto* trap =
|
|
ww(floatRoot_wrap,
|
|
&import.at("float_root"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
3,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 3);
|
|
BEAST_EXPECT(resultBytes == float10);
|
|
}
|
|
}
|
|
|
|
{
|
|
// hfs.floatRoot(makeSlice(*x), 2, 0);
|
|
auto const x = hfs.floatFromMantExp(1, -2, 0); // 0.01
|
|
auto const y = hfs.floatFromMantExp(1, -1, 0); // 0.1
|
|
if (BEAST_EXPECT(x && y))
|
|
{
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, x->data(), x->size());
|
|
auto* trap =
|
|
ww(floatRoot_wrap,
|
|
&import.at("float_root"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
2,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 3);
|
|
BEAST_EXPECT(resultBytes == *y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatPower()
|
|
{
|
|
testcase("floatPower");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{ // hfs.floatPower(Slice(), 2, -1);
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatPower_wrap,
|
|
&import.at("float_pow"),
|
|
params,
|
|
result,
|
|
0,
|
|
0,
|
|
2,
|
|
0,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{ // hfs.floatPower(makeSlice(invalid), 3, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, invalid.data(), invalid.size());
|
|
auto* trap =
|
|
ww(floatPower_wrap,
|
|
&import.at("float_pow"),
|
|
params,
|
|
result,
|
|
0,
|
|
invalid.size(),
|
|
3,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{ // hfs.floatPower(makeSlice(float1), -2, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
auto* trap =
|
|
ww(floatPower_wrap,
|
|
&import.at("float_pow"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
-2,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatPower(makeSlice(floatMax), 2, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, floatMax.data(), floatMax.size());
|
|
auto* trap =
|
|
ww(floatPower_wrap,
|
|
&import.at("float_pow"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
2,
|
|
floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatComputationError));
|
|
}
|
|
|
|
{
|
|
// hfs.floatPower(makeSlice(floatMax), Number::kMaxExponent + 1, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, floatMax.data(), floatMax.size());
|
|
auto* trap =
|
|
ww(floatPower_wrap,
|
|
&import.at("float_pow"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
Number::kMaxExponent + 1,
|
|
floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatPower(makeSlice(floatMaxIOU), 0, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, floatMaxIOU.data(), floatMaxIOU.size());
|
|
auto* trap =
|
|
ww(floatPower_wrap,
|
|
&import.at("float_pow"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
0,
|
|
floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 3);
|
|
BEAST_EXPECT(resultBytes == float1);
|
|
}
|
|
|
|
{ // hfs.floatPower(makeSlice(floatMaxIOU), 1, 0);
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, floatMaxIOU.data(), floatMaxIOU.size());
|
|
auto* trap =
|
|
ww(floatPower_wrap,
|
|
&import.at("float_pow"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
1,
|
|
floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 3);
|
|
BEAST_EXPECT(resultBytes == floatMaxIOU);
|
|
}
|
|
|
|
{
|
|
// hfs.floatPower(makeSlice(float10), 2, 0);
|
|
auto const x = hfs.floatFromMantExp(100, 0, 0); // 100
|
|
if (BEAST_EXPECT(x))
|
|
{
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, float10.data(), float10.size());
|
|
auto* trap =
|
|
ww(floatPower_wrap,
|
|
&import.at("float_pow"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
2,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 3);
|
|
BEAST_EXPECT(resultBytes == *x);
|
|
}
|
|
}
|
|
|
|
{
|
|
// hfs.floatPower(makeSlice(*x), 2, 0);
|
|
auto const x = hfs.floatFromMantExp(1, -1, 0); // 0.1
|
|
auto const y = hfs.floatFromMantExp(1, -2, 0); // 0.01
|
|
if (BEAST_EXPECT(x && y))
|
|
{
|
|
WasmValVec params(6), result(1);
|
|
vrt.setBytes(0, x->data(), x->size());
|
|
auto* trap =
|
|
ww(floatPower_wrap,
|
|
&import.at("float_pow"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
2,
|
|
2 * floatSize,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 3);
|
|
BEAST_EXPECT(resultBytes == *y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatSpecialCases()
|
|
{
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
WasmHostFunctionsImpl const hfs(ac, dummyEscrow);
|
|
|
|
testcase("float non-canonical");
|
|
|
|
{ // non-canonical mantissa 100000e-4
|
|
Bytes const y = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xA0, 0xFF, 0xFF, 0xFF, 0xFC};
|
|
auto const result = hfs.floatCompare(makeSlice(y), makeSlice(float10));
|
|
BEAST_EXPECT(result && *result == 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatFromSTAmount()
|
|
{
|
|
testcase("floatFromSTAmount");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatFromSTAmount(amount, -1);
|
|
STAmount const amount = XRP(100);
|
|
Bytes amountBytes = toBytes(amount);
|
|
vrt.setBytes(0, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTAmount_wrap,
|
|
&import.at("float_from_stamount"),
|
|
params,
|
|
result,
|
|
0,
|
|
amountBytes.size(),
|
|
256,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromSTAmount(amount, 4);
|
|
STAmount const amount = XRP(100);
|
|
Bytes amountBytes = toBytes(amount);
|
|
vrt.setBytes(0, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTAmount_wrap,
|
|
&import.at("float_from_stamount"),
|
|
params,
|
|
result,
|
|
0,
|
|
amountBytes.size(),
|
|
256,
|
|
floatSize,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromSTAmount(amount, 0);
|
|
STAmount const amount = XRP(0);
|
|
Bytes amountBytes = toBytes(amount);
|
|
vrt.setBytes(0, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTAmount_wrap,
|
|
&import.at("float_from_stamount"),
|
|
params,
|
|
result,
|
|
0,
|
|
amountBytes.size(),
|
|
256,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatIntZero);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromSTAmount(amount, 0);
|
|
STAmount const amount = XRP(-1);
|
|
auto const y = hfs.floatFromMantExp(-1 * 1'000'000, 0, 0);
|
|
if (BEAST_EXPECT(y))
|
|
{
|
|
Bytes amountBytes = toBytes(amount);
|
|
vrt.setBytes(0, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTAmount_wrap,
|
|
&import.at("float_from_stamount"),
|
|
params,
|
|
result,
|
|
0,
|
|
amountBytes.size(),
|
|
256,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == *y);
|
|
}
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromSTAmount(amount, 0);
|
|
auto const y = hfs.floatFromMantExp(9223372036854776, 3, 0);
|
|
STAmount const amount(noIssue(), std::numeric_limits<int64_t>::max());
|
|
Bytes amountBytes = toBytes(amount);
|
|
vrt.setBytes(0, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTAmount_wrap,
|
|
&import.at("float_from_stamount"),
|
|
params,
|
|
result,
|
|
0,
|
|
amountBytes.size(),
|
|
256,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == *y);
|
|
}
|
|
|
|
{
|
|
bool ex = false;
|
|
try
|
|
{
|
|
STAmount const amount(noIssue(), -1, Number::kMaxExponent + normalExp);
|
|
[[maybe_unused]] Bytes const amountBytes = toBytes(amount);
|
|
}
|
|
catch (...)
|
|
{
|
|
ex = true;
|
|
}
|
|
|
|
BEAST_EXPECT(ex);
|
|
}
|
|
|
|
auto const usd = env.master["USD"];
|
|
{
|
|
// hfs.floatFromSTAmount(amount, 0);
|
|
STAmount const amount(
|
|
IOUAmount(STAmount::kMinValue, STAmount::kMinOffset), usd.issue());
|
|
Bytes amountBytes = toBytes(amount);
|
|
vrt.setBytes(0, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTAmount_wrap,
|
|
&import.at("float_from_stamount"),
|
|
params,
|
|
result,
|
|
0,
|
|
amountBytes.size(),
|
|
256,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatMinIOU);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromSTAmount(amount, 0);
|
|
STAmount const amount(
|
|
IOUAmount(STAmount::kMaxValue, STAmount::kMaxOffset), usd.issue());
|
|
Bytes amountBytes = toBytes(amount);
|
|
vrt.setBytes(0, amountBytes.data(), amountBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTAmount_wrap,
|
|
&import.at("float_from_stamount"),
|
|
params,
|
|
result,
|
|
0,
|
|
amountBytes.size(),
|
|
256,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatMaxIOU);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatFromSTNumber()
|
|
{
|
|
testcase("floatFromSTNumber");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
// Test with invalid rounding mode
|
|
{
|
|
// hfs.floatFromSTNumber(num, -1);
|
|
STNumber const num(sfNumber, Number(123, 0));
|
|
Bytes numBytes = toBytes(num);
|
|
vrt.setBytes(0, numBytes.data(), numBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTNumber_wrap,
|
|
&import.at("float_from_stnumber"),
|
|
params,
|
|
result,
|
|
0,
|
|
numBytes.size(),
|
|
256,
|
|
floatSize,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromSTNumber(num, 4);
|
|
STNumber const num(sfNumber, Number(123, 0));
|
|
Bytes numBytes = toBytes(num);
|
|
vrt.setBytes(0, numBytes.data(), numBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTNumber_wrap,
|
|
&import.at("float_from_stnumber"),
|
|
params,
|
|
result,
|
|
0,
|
|
numBytes.size(),
|
|
256,
|
|
floatSize,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromSTNumber(num, 0);
|
|
STNumber const num(
|
|
sfNumber, Number(std::numeric_limits<uint64_t>::max(), 0, Number::Normalized{}));
|
|
Bytes numBytes = toBytes(num);
|
|
vrt.setBytes(0, numBytes.data(), numBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTNumber_wrap,
|
|
&import.at("float_from_stnumber"),
|
|
params,
|
|
result,
|
|
0,
|
|
numBytes.size(),
|
|
256,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatUIntMax);
|
|
}
|
|
|
|
{
|
|
// hfs.floatFromSTNumber(num, 0);
|
|
STNumber const num(sfNumber, Number(-1, Number::kMaxExponent + normalExp));
|
|
Bytes numBytes = toBytes(num);
|
|
vrt.setBytes(0, numBytes.data(), numBytes.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatFromSTNumber_wrap,
|
|
&import.at("float_from_stnumber"),
|
|
params,
|
|
result,
|
|
0,
|
|
numBytes.size(),
|
|
256,
|
|
floatSize,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const resultBytes = vrt.getBytes(params, 2);
|
|
BEAST_EXPECT(resultBytes == floatMinusMaxExp);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatToInt()
|
|
{
|
|
testcase("floatToInt");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatToInt(makeSlice(float1), -1);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
-1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatToInt(makeSlice(float1), 4);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatToInt(Slice(), 0);
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap, &import.at("float_to_int"), params, result, 0, 0, 256, 8, 0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatToInt(makeSlice(invalid), 0);
|
|
vrt.setBytes(0, invalid.data(), invalid.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatToInt(makeSlice(floatIntZero), 0);
|
|
vrt.setBytes(0, floatIntZero.data(), floatIntZero.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 8);
|
|
auto const resultVal = vrt.getInt64(params, 2);
|
|
BEAST_EXPECT(resultVal == 0);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromInt(resultVal, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntZero);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToInt(makeSlice(float1), 0);
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 8);
|
|
auto const resultVal = vrt.getInt64(params, 2);
|
|
BEAST_EXPECT(resultVal == 1);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromInt(resultVal, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == float1);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToInt(makeSlice(floatMinus1), 0);
|
|
vrt.setBytes(0, floatMinus1.data(), floatMinus1.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 8);
|
|
auto const resultVal = vrt.getInt64(params, 2);
|
|
BEAST_EXPECT(resultVal == -1);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromInt(resultVal, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatMinus1);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToInt(makeSlice(floatIntMax), 0);
|
|
vrt.setBytes(0, floatIntMax.data(), floatIntMax.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 8);
|
|
auto const resultVal = vrt.getInt64(params, 2);
|
|
BEAST_EXPECT(resultVal == std::numeric_limits<int64_t>::max());
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromInt(resultVal, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntMax);
|
|
}
|
|
|
|
{
|
|
// Number can't hold int64.min, it is rounded and we get int64_t.min - 3, which doesn't
|
|
// fit into int64
|
|
// hfs.floatToInt(makeSlice(floatIntMin), 0);
|
|
vrt.setBytes(0, floatIntMin.data(), floatIntMin.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatComputationError));
|
|
}
|
|
|
|
{
|
|
// hfs.floatToInt(makeSlice(floatUIntMax), 0);
|
|
vrt.setBytes(0, floatUIntMax.data(), floatUIntMax.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatComputationError));
|
|
}
|
|
|
|
// Test rounding modes with pi (3.141592653589793)
|
|
{
|
|
// to_nearest (mode 0): should round to 3
|
|
// hfs.floatToInt(makeSlice(floatPi), 0);
|
|
vrt.setBytes(0, floatPi.data(), floatPi.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
0);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 8);
|
|
auto const resultVal = vrt.getInt64(params, 2);
|
|
BEAST_EXPECT(resultVal == 3);
|
|
}
|
|
|
|
{
|
|
// towards_zero (mode 1): should truncate to 3
|
|
// hfs.floatToInt(makeSlice(floatPi), 1);
|
|
vrt.setBytes(0, floatPi.data(), floatPi.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 8);
|
|
auto const resultVal = vrt.getInt64(params, 2);
|
|
BEAST_EXPECT(resultVal == 3);
|
|
}
|
|
|
|
{
|
|
// downward (mode 2): should round down to 3
|
|
// hfs.floatToInt(makeSlice(floatPi), 2);
|
|
vrt.setBytes(0, floatPi.data(), floatPi.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
2);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 8);
|
|
auto const resultVal = vrt.getInt64(params, 2);
|
|
BEAST_EXPECT(resultVal == 3);
|
|
}
|
|
|
|
{
|
|
// upward (mode 3): should round up to 4
|
|
// hfs.floatToInt(makeSlice(floatPi), 3);
|
|
vrt.setBytes(0, floatPi.data(), floatPi.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap =
|
|
ww(floatToInt_wrap,
|
|
&import.at("float_to_int"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
3);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == 8);
|
|
auto const resultVal = vrt.getInt64(params, 2);
|
|
BEAST_EXPECT(resultVal == 4);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloatToMantExp()
|
|
{
|
|
testcase("floatToMantExp");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(invalid));
|
|
vrt.setBytes(0, invalid.data(), invalid.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatToMantExp_wrap,
|
|
&import.at("float_to_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
512,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 ==
|
|
static_cast<int32_t>(HostFunctionError::FloatInputMalformed));
|
|
}
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(floatIntZero));
|
|
vrt.setBytes(0, floatIntZero.data(), floatIntZero.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatToMantExp_wrap,
|
|
&import.at("float_to_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
512,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const mantissa = vrt.getInt64(params, 2);
|
|
auto const exponent = vrt.getInt32(params, 4);
|
|
BEAST_EXPECT(mantissa == 0) &&
|
|
BEAST_EXPECT(exponent == std::numeric_limits<int32_t>::min());
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromMantExp(mantissa, exponent, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntZero);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(float1));
|
|
vrt.setBytes(0, float1.data(), float1.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatToMantExp_wrap,
|
|
&import.at("float_to_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
512,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const mantissa = vrt.getInt64(params, 2);
|
|
auto const exponent = vrt.getInt32(params, 4);
|
|
BEAST_EXPECT(mantissa == 1000000000000000000) && BEAST_EXPECT(exponent == -normalExp);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromMantExp(mantissa, exponent, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == float1);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(floatMinus1));
|
|
vrt.setBytes(0, floatMinus1.data(), floatMinus1.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatToMantExp_wrap,
|
|
&import.at("float_to_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
512,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const mantissa = vrt.getInt64(params, 2);
|
|
auto const exponent = vrt.getInt32(params, 4);
|
|
BEAST_EXPECT(mantissa == -1000000000000000000) && BEAST_EXPECT(exponent == -normalExp);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromMantExp(mantissa, exponent, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatMinus1);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(float10));
|
|
vrt.setBytes(0, float10.data(), float10.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatToMantExp_wrap,
|
|
&import.at("float_to_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
512,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const mantissa = vrt.getInt64(params, 2);
|
|
auto const exponent = vrt.getInt32(params, 4);
|
|
BEAST_EXPECT(mantissa == 1000000000000000000) &&
|
|
BEAST_EXPECT(exponent == -normalExp + 1);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromMantExp(mantissa, exponent, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == float10);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(floatPi));
|
|
vrt.setBytes(0, floatPi.data(), floatPi.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatToMantExp_wrap,
|
|
&import.at("float_to_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
512,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const mantissa = vrt.getInt64(params, 2);
|
|
auto const exponent = vrt.getInt32(params, 4);
|
|
BEAST_EXPECT(mantissa == 3141592653589793000) && BEAST_EXPECT(exponent == -normalExp);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromMantExp(mantissa, exponent, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatPi);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(floatIntMax));
|
|
vrt.setBytes(0, floatIntMax.data(), floatIntMax.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatToMantExp_wrap,
|
|
&import.at("float_to_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
512,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const mantissa = vrt.getInt64(params, 2);
|
|
auto const exponent = vrt.getInt32(params, 4);
|
|
BEAST_EXPECT(mantissa == std::numeric_limits<int64_t>::max()) &&
|
|
BEAST_EXPECT(exponent == 0);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromMantExp(mantissa, exponent, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntMax);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(floatIntMin));
|
|
vrt.setBytes(0, floatIntMin.data(), floatIntMin.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatToMantExp_wrap,
|
|
&import.at("float_to_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
512,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const mantissa = vrt.getInt64(params, 2);
|
|
auto const exponent = vrt.getInt32(params, 4);
|
|
BEAST_EXPECT(mantissa == (std::numeric_limits<int64_t>::min() / 10) - 1) &&
|
|
BEAST_EXPECT(exponent == 1);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromMantExp(mantissa, exponent, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntMin);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(floatMax));
|
|
vrt.setBytes(0, floatMax.data(), floatMax.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(floatToMantExp_wrap,
|
|
&import.at("float_to_mant_exp"),
|
|
params,
|
|
result,
|
|
0,
|
|
floatSize,
|
|
256,
|
|
8,
|
|
512,
|
|
4);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == floatSize);
|
|
auto const mantissa = vrt.getInt64(params, 2);
|
|
auto const exponent = vrt.getInt32(params, 4);
|
|
BEAST_EXPECT(mantissa == Number::kMaxRep) &&
|
|
BEAST_EXPECT(exponent == Number::kMaxExponent);
|
|
|
|
// roundtrip
|
|
auto const result2 = hfs.floatFromMantExp(mantissa, exponent, 0);
|
|
BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatMax);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFloats()
|
|
{
|
|
// for checking binary formats manually
|
|
// printNumbersBin();
|
|
|
|
testTraceFloat();
|
|
testFloatFromInt();
|
|
testFloatFromUint();
|
|
testFloatFromSTAmount();
|
|
testFloatFromSTNumber();
|
|
testFloatToInt();
|
|
testFloatToMantExp();
|
|
testfloatFromMantExp();
|
|
testFloatCompare();
|
|
testFloatAdd();
|
|
testFloatSubtract();
|
|
testFloatMultiply();
|
|
testFloatDivide();
|
|
testFloatRoot();
|
|
testFloatPower();
|
|
testFloatSpecialCases();
|
|
}
|
|
|
|
void
|
|
testVectorIndexes()
|
|
{
|
|
testcase("WasmValVec indicies");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(&vrt);
|
|
|
|
bool ex = false;
|
|
try
|
|
{
|
|
// hfs.getLedgerSqn();
|
|
WasmValVec params(2), result(1);
|
|
// 3 parameters instead of 2
|
|
auto* trap =
|
|
ww(getLedgerSqn_wrap,
|
|
&import.at("get_ledger_sqn"),
|
|
params,
|
|
result,
|
|
0,
|
|
sizeof(std::uint32_t),
|
|
1);
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(result[0].of.i32 == sizeof(std::uint32_t)) &&
|
|
BEAST_EXPECT(vrt.getUint32(params, 0) == env.current()->header().seq);
|
|
}
|
|
catch (std::exception const& e)
|
|
{
|
|
BEAST_EXPECTS(e.what() == std::string("Out of bound"), e.what());
|
|
ex = true;
|
|
}
|
|
|
|
// const version
|
|
ex = false;
|
|
try
|
|
{
|
|
WasmValVec params(2);
|
|
[[maybe_unused]] auto const x = params[2];
|
|
}
|
|
catch (std::exception const& e)
|
|
{
|
|
BEAST_EXPECTS(e.what() == std::string("Out of bound"), e.what());
|
|
ex = true;
|
|
}
|
|
|
|
BEAST_EXPECT(ex);
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
testGetLedgerSqn();
|
|
testGetParentLedgerTime();
|
|
testGetParentLedgerHash();
|
|
testGetBaseFee();
|
|
testIsAmendmentEnabled();
|
|
testCacheLedgerObj();
|
|
testGetTxField();
|
|
testGetCurrentLedgerObjField();
|
|
testGetLedgerObjField();
|
|
testGetTxNestedField();
|
|
testGetCurrentLedgerObjNestedField();
|
|
testGetLedgerObjNestedField();
|
|
testGetTxArrayLen();
|
|
testGetCurrentLedgerObjArrayLen();
|
|
testGetLedgerObjArrayLen();
|
|
testGetTxNestedArrayLen();
|
|
testGetCurrentLedgerObjNestedArrayLen();
|
|
testGetLedgerObjNestedArrayLen();
|
|
testUpdateData();
|
|
testCheckSignature();
|
|
testComputeSha512HalfHash();
|
|
testKeyletFunctions();
|
|
testGetNFT();
|
|
testGetNFTIssuer();
|
|
testGetNFTTaxon();
|
|
testGetNFTFlags();
|
|
testGetNFTTransferFee();
|
|
testGetNFTSerial();
|
|
testTrace();
|
|
testTraceNum();
|
|
testTraceAccount();
|
|
testTraceAmount();
|
|
testFloats();
|
|
|
|
testVectorIndexes();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(HostFuncImpl, app, xrpl);
|
|
|
|
} // namespace xrpl::test
|