mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 14:20:56 +00:00
develop changed every sequence-based `keylet::` factory to take `SeqProxy const&` instead of `std::uint32_t`, and removed the two-argument `mptokenIssuance(seq, issuer)` overload. The wasm host functions and their tests still passed raw sequences, so the branch merged cleanly but did not compile. Wrap the raw sequences at the call sites, matching the idiom develop adopted in its own tests: - `SeqProxy::rawSequence` for check, escrow, nftokenOffer, offer, payChannel, permissionedDomain and vault - `SeqProxy::rawTicket` for ticket - `keylet::mptokenIssuance(makeMptID(seq, issuer))` for the removed overload No computed keylet changes: the factories only read `seq.value()`, and the removed overload was itself defined as `mptokenIssuance(makeMptID( seq, issuer))`.
6439 lines
237 KiB
C++
6439 lines
237 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/SeqProxy.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/WasmCommon.h>
|
|
#include <xrpl/tx/wasm/WasmImportsHelper.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;
|
|
std::int64_t transferLimit_ = kWasmTransferLimit;
|
|
|
|
public:
|
|
static constexpr std::int64_t transferDiff = 1024;
|
|
|
|
VirtualRuntime() : buffer_(1024 * 1024)
|
|
{
|
|
}
|
|
|
|
Wmem
|
|
getMem() override
|
|
{
|
|
return Wmem(buffer_.data(), 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_;
|
|
}
|
|
|
|
std::int64_t
|
|
getTransferLimit() override
|
|
{
|
|
transferLimit_ -= transferDiff;
|
|
return transferLimit_;
|
|
}
|
|
|
|
[[nodiscard]] std::int64_t
|
|
getTestTransferLimit() const
|
|
{
|
|
return transferLimit_;
|
|
}
|
|
|
|
std::int64_t
|
|
setTransferLimit(std::int64_t x) override
|
|
{
|
|
if (x == -2)
|
|
return -1;
|
|
|
|
if (x < 0)
|
|
{
|
|
transferLimit_ = std::numeric_limits<decltype(transferLimit_)>::max();
|
|
}
|
|
else
|
|
{
|
|
transferLimit_ = x;
|
|
}
|
|
|
|
return transferLimit_;
|
|
}
|
|
|
|
void
|
|
checkIdx(WasmValVec const& params, size_t i) const
|
|
{
|
|
if (i + 1 >= params.size())
|
|
Throw<std::out_of_range>("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::out_of_range>("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 = 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 = 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 E, class P, typename... Args>
|
|
wasm_trap_t*
|
|
ww(E&& e, P&& params, P&& result, Args... args)
|
|
{
|
|
size_t idx = 0;
|
|
(ww_hlp(idx, e, params, std::forward<Args>(args)), ...); // NOLINT
|
|
return HostFuncMain_wrap(std::forward<E>(e), params.get(), result.get()); // NOLINT
|
|
}
|
|
|
|
// ww() packs only integral args as wasm params, so the scoped enum needs widening.
|
|
constexpr int32_t
|
|
traceDataTypeToInt(TraceDataType t)
|
|
{
|
|
return static_cast<int32_t>(t);
|
|
}
|
|
|
|
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, SeqProxy::rawSequence(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(&import.at("ldgr_index"), 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, SeqProxy::rawSequence(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(&import.at("parent_ldgr_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, SeqProxy::rawSequence(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(&import.at("parent_ldgr_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, SeqProxy::rawSequence(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(&import.at("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, SeqProxy::rawSequence(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(&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(&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(&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(&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, SeqProxy::rawSequence(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(&import.at("cache_le"), 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(&import.at("cache_le"), 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(&import.at("cache_le"), 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(&import.at("cache_le"), 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(&import.at("cache_le"), 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(&import.at("cache_le"), 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(&import.at("cache_le"), 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(&import.at("cache_le"), 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, SeqProxy::rawSequence(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(&import.at("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(&import.at("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(&import.at("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(&import.at("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(&import.at("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(&import.at("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(&import.at("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(sfInvalid);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(&import.at("tx_field"), params, result, sfInvalid.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(sfGeneric);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
auto* trap =
|
|
ww(&import.at("tx_field"), params, result, sfGeneric.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(&import.at("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(&import.at("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(&import.at("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(&import.at("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(&import.at("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, SeqProxy::rawSequence(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(&import.at("home_le_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(&import.at("home_le_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(&import.at("home_le_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(&import.at("home_le_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, SeqProxy::rawSequence(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(&import2.at("home_le_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(), SeqProxy::rawSequence(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(&import.at("cache_le"), 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(&import.at("le_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(&import.at("le_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(&import.at("le_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(&import.at("le_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(&import.at("le_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(&import.at("le_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, SeqProxy::rawSequence(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(&import.at("tx_inner"),
|
|
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(&import.at("tx_inner"),
|
|
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(&import.at("tx_inner"),
|
|
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(), sizeof(int32_t) + 1);
|
|
|
|
WasmValVec params(4), result(1);
|
|
auto* trap = ww(&import.at("tx_inner"), params, result, 1, 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(&import.at("tx_inner"),
|
|
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 == hfErrorToInt(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 = sfInvalid, exists in map but not in tx)
|
|
expectError(
|
|
{-1, // sfInvalid's field code
|
|
0,
|
|
sfAccount.getCode()},
|
|
HostFunctionError::FieldNotFound);
|
|
|
|
// hfs.getTxNestedField(locator);
|
|
// Locator for zero base sfield code (0 = sfGeneric, exists in map but not in tx)
|
|
expectError(
|
|
{0, // sfGeneric'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 = sfInvalid, 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(&import.at("tx_inner"), 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::signerList(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(&import.at("home_le_inner"),
|
|
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(&import.at("home_le_inner"),
|
|
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 == hfErrorToInt(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(&import.at("home_le_inner"), 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(&import.at("home_le_inner"), 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, SeqProxy::rawSequence(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(&import2.at("home_le_inner"),
|
|
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, SeqProxy::rawSequence(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::signerList(env.master.id());
|
|
// hfs.cacheLedgerObj(signerListKeylet.key, 1);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, signerListKeylet.key.data(), uint256::size());
|
|
auto* trap = ww(&import.at("cache_le"), 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(&import.at("le_inner"),
|
|
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(&import.at("le_inner"),
|
|
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(&import.at("le_inner"),
|
|
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(&import.at("le_inner"),
|
|
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(&import.at("le_inner"),
|
|
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 == hfErrorToInt(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(&import.at("le_inner"), 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, SeqProxy::rawSequence(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(&import.at("tx_arr_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(&import.at("tx_arr_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(&import.at("tx_arr_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(&import.at("tx_arr_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::signerList(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(&import.at("home_le_arr_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(&import.at("home_le_arr_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(&import.at("home_le_arr_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, SeqProxy::rawSequence(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(&import2.at("home_le_arr_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, SeqProxy::rawSequence(env.seq(env.master)));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(vrt);
|
|
|
|
auto const signerListKeylet = keylet::signerList(env.master.id());
|
|
// hfs.cacheLedgerObj(signerListKeylet.key, 1);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, signerListKeylet.key.data(), uint256::size());
|
|
auto* trap = ww(&import.at("cache_le"), 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(&import.at("le_arr_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(&import.at("le_arr_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(&import.at("le_arr_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(&import.at("le_arr_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(&import.at("le_arr_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, SeqProxy::rawSequence(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(&import.at("tx_inner_arr_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 == hfErrorToInt(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(&import.at("tx_inner_arr_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::signerList(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(&import.at("home_le_inner_arr_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 == hfErrorToInt(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(&import.at("home_le_inner_arr_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, SeqProxy::rawSequence(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(&import2.at("home_le_inner_arr_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, SeqProxy::rawSequence(env.seq(env.master)));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(vrt);
|
|
|
|
auto const signerListKeylet = keylet::signerList(env.master.id());
|
|
// hfs.cacheLedgerObj(signerListKeylet.key, 1);
|
|
{
|
|
WasmValVec params(3), result(1);
|
|
vrt.setBytes(0, signerListKeylet.key.data(), uint256::size());
|
|
auto* trap = ww(&import.at("cache_le"), 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(&import.at("le_inner_arr_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(&import.at("le_inner_arr_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 == hfErrorToInt(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(&import.at("le_inner_arr_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, SeqProxy::rawSequence(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(&import.at("set_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(kMaxWasmDataLength + 1, 0x42);
|
|
// hfs.updateData(Slice(bigData.data(), bigData.size()));
|
|
{
|
|
vrt.setBytes(0, bigData.data(), bigData.size());
|
|
WasmValVec params(2), result(1);
|
|
auto* trap = ww(&import.at("set_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, SeqProxy::rawSequence(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(&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(&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(&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(&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(
|
|
&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(&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, SeqProxy::rawSequence(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(&import.at("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, SeqProxy::rawSequence(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(&imp.at("accountroot_id"), 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(&imp.at("accountroot_id"), 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(&imp.at("amm_id"), 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(&imp.at("amm_id"), 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(&imp.at("amm_id"), 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, SeqProxy::rawSequence(1u));
|
|
WasmValVec params(6), result(1);
|
|
auto* trap = ww(&imp.at("check_id"), 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(&imp.at("check_id"), 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(
|
|
&imp.at("credential_id"), 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(&imp.at("credential_id"),
|
|
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(&imp.at("credential_id"),
|
|
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(&imp.at("credential_id"),
|
|
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(&imp.at("did_id"), 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(&imp.at("did_id"), 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(&imp.at("delegate_id"), 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(&imp.at("delegate_id"), 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(&imp.at("delegate_id"), 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(&imp.at("delegate_id"), 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(&imp.at("deposit_preauth_id"), 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(&imp.at("deposit_preauth_id"), 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(&imp.at("deposit_preauth_id"), 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(&imp.at("deposit_preauth_id"), 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, SeqProxy::rawSequence(1u));
|
|
WasmValVec params(6), result(1);
|
|
auto* trap = ww(&imp.at("escrow_id"), 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(&imp.at("escrow_id"), 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::trustLine(masterID, alice.id(), usd);
|
|
WasmValVec params(8), result(1);
|
|
auto* trap =
|
|
ww(&imp.at("trustline_id"), 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(&imp.at("trustline_id"), 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(&imp.at("trustline_id"), 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(&imp.at("trustline_id"), 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(&imp.at("trustline_id"),
|
|
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::mptokenIssuance(makeMptID(1u, masterID));
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(&imp.at("mpt_issuance_id"), 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(&imp.at("mpt_issuance_id"), 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(&imp.at("mptoken_id"), 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(&imp.at("mptoken_id"), 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(&imp.at("mptoken_id"), 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::nftokenOffer(masterID, SeqProxy::rawSequence(1u));
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(&imp.at("nft_offer_id"), 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(&imp.at("nft_offer_id"), 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, SeqProxy::rawSequence(1u));
|
|
WasmValVec params(6), result(1);
|
|
auto* trap = ww(&imp.at("offer_id"), 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(&imp.at("offer_id"), 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(&imp.at("oracle_id"), 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(&imp.at("oracle_id"), 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::payChannel(masterID, alice.id(), SeqProxy::rawSequence(1u));
|
|
WasmValVec params(8), result(1);
|
|
auto* trap = ww(
|
|
&imp.at("paychan_id"), 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(
|
|
&imp.at("paychan_id"), 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(&imp.at("paychan_id"),
|
|
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(&imp.at("paychan_id"),
|
|
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, SeqProxy::rawSequence(1u));
|
|
WasmValVec params(6), result(1);
|
|
auto* trap = ww(
|
|
&imp.at("permissioned_domain_id"), 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(&imp.at("permissioned_domain_id"),
|
|
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::signerList(masterID);
|
|
WasmValVec params(4), result(1);
|
|
auto* trap = ww(&imp.at("signers_id"), 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(&imp.at("signers_id"), 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::ticket(masterID, SeqProxy::rawTicket(1u));
|
|
WasmValVec params(6), result(1);
|
|
auto* trap = ww(&imp.at("ticket_id"), 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(&imp.at("ticket_id"), 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, SeqProxy::rawSequence(1u));
|
|
WasmValVec params(6), result(1);
|
|
auto* trap = ww(&imp.at("vault_id"), 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(&imp.at("vault_id"), 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, SeqProxy::rawSequence(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(&import.at("nft_uri"),
|
|
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(&import.at("nft_uri"),
|
|
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(&import.at("nft_uri"),
|
|
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(&import.at("nft_uri"),
|
|
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(&import.at("nft_uri"),
|
|
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, SeqProxy::rawSequence(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(&import.at("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(&import.at("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, SeqProxy::rawSequence(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(&import.at("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, SeqProxy::rawSequence(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(&import.at("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(&import.at("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, SeqProxy::rawSequence(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(&import.at("nft_xfer_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(&import.at("nft_xfer_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("getNFTSequence");
|
|
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, SeqProxy::rawSequence(env.seq(env.master)));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(vrt);
|
|
|
|
{
|
|
// hfs.getNFTSequence(nftId);
|
|
vrt.setBytes(0, nftId.data(), uint256::size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(&import.at("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.getNFTSequence(uint256());
|
|
uint256 zeroId;
|
|
vrt.setBytes(0, zeroId.data(), uint256::size());
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(&import.at("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, SeqProxy::rawSequence(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());
|
|
|
|
// AsText: data printed verbatim (was trace with as_hex = 0)
|
|
{
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, slice.data(), slice.size());
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::AsText),
|
|
256,
|
|
slice.size());
|
|
|
|
if (BEAST_EXPECT(!trap))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.contains(msg));
|
|
BEAST_EXPECT(messages.contains(data));
|
|
}
|
|
}
|
|
|
|
// AsHex: host hex-encodes data (was trace with as_hex = 1)
|
|
{
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, slice.data(), slice.size());
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::AsHex),
|
|
256,
|
|
slice.size());
|
|
|
|
if (BEAST_EXPECT(!trap))
|
|
{
|
|
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.contains(msg));
|
|
BEAST_EXPECT(messages.contains(hex));
|
|
}
|
|
}
|
|
|
|
// Unknown data_type: logged as invalid, never a trap
|
|
{
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, slice.data(), slice.size());
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"), params, result, 0, msg.size(), 9999, 256, slice.size());
|
|
BEAST_EXPECT(!trap);
|
|
}
|
|
|
|
// msg and data each fit, but their combined size exceeds
|
|
// kMaxWasmDataLength, so nothing is logged
|
|
{
|
|
std::string const longMsg(kMaxWasmDataLength, 'x');
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(longMsg.data()), longMsg.size());
|
|
vrt.setBytes(2048, slice.data(), slice.size());
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
longMsg.size(),
|
|
traceDataTypeToInt(TraceDataType::AsText),
|
|
2048,
|
|
slice.size());
|
|
|
|
if (BEAST_EXPECT(!trap))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.contains("message and data too long"));
|
|
BEAST_EXPECT(!messages.contains(longMsg));
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// 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, SeqProxy::rawSequence(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());
|
|
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, slice.data(), slice.size());
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::AsText),
|
|
256,
|
|
slice.size());
|
|
|
|
BEAST_EXPECT(!trap);
|
|
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, SeqProxy::rawSequence(env.seq(env.master)));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(vrt);
|
|
|
|
std::string const msg = "trace number";
|
|
|
|
// adjustWasmEndianess is its own inverse, so writing the adjusted value
|
|
// lets the wrapper's adjustment recover it on either endianness.
|
|
auto const traceNum = [&](TraceDataType type, auto value) {
|
|
auto const wire = adjustWasmEndianess(value);
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, reinterpret_cast<uint8_t const*>(&wire), sizeof(wire));
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(type),
|
|
256,
|
|
sizeof(wire));
|
|
|
|
if (BEAST_EXPECT(!trap))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.contains(msg));
|
|
BEAST_EXPECT(messages.contains(std::to_string(value)));
|
|
}
|
|
};
|
|
|
|
traceNum(TraceDataType::Int64, int64_t{123456789});
|
|
traceNum(TraceDataType::Int64, int64_t{-42});
|
|
// Above int64 max -- unreachable through the old trace_num
|
|
traceNum(TraceDataType::Uint64, std::numeric_limits<std::uint64_t>::max());
|
|
|
|
// Wrong buffer length for the type: logged as invalid, no trap
|
|
{
|
|
std::int32_t const tooShort = 7;
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, reinterpret_cast<uint8_t const*>(&tooShort), sizeof(tooShort));
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Int64),
|
|
256,
|
|
sizeof(tooShort));
|
|
BEAST_EXPECT(!trap);
|
|
}
|
|
}
|
|
|
|
{
|
|
// 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, SeqProxy::rawSequence(env.seq(env.master)));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(vrt);
|
|
|
|
std::string const msg = "trace number";
|
|
auto const wire = adjustWasmEndianess(int64_t{123456789});
|
|
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, reinterpret_cast<uint8_t const*>(&wire), sizeof(wire));
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Int64),
|
|
256,
|
|
sizeof(wire));
|
|
|
|
BEAST_EXPECT(!trap);
|
|
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, SeqProxy::rawSequence(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();
|
|
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, accountId.data(), accountId.size());
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Account),
|
|
256,
|
|
accountId.size());
|
|
|
|
if (BEAST_EXPECT(!trap))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.contains(msg));
|
|
BEAST_EXPECT(messages.contains(env.master.human()));
|
|
}
|
|
}
|
|
|
|
{
|
|
// 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, SeqProxy::rawSequence(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();
|
|
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, accountId.data(), accountId.size());
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Account),
|
|
256,
|
|
accountId.size());
|
|
|
|
BEAST_EXPECT(!trap);
|
|
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, SeqProxy::rawSequence(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);
|
|
{
|
|
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(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Amount),
|
|
256,
|
|
amountBytes.size());
|
|
|
|
if (BEAST_EXPECT(!trap))
|
|
{
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.contains(msg));
|
|
BEAST_EXPECT(messages.contains(amount.getFullText()));
|
|
}
|
|
}
|
|
|
|
// IOU amount
|
|
Account const alice("alice");
|
|
env.fund(XRP(1000), alice);
|
|
env.close();
|
|
STAmount const iouAmount = env.master["USD"](100);
|
|
{
|
|
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(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Amount),
|
|
256,
|
|
amountBytes.size());
|
|
|
|
BEAST_EXPECT(!trap);
|
|
}
|
|
|
|
// MPT amount
|
|
{
|
|
auto const mptId = makeMptID(42, env.master.id());
|
|
Asset const mptAsset = Asset(mptId);
|
|
STAmount const mptAmount(mptAsset, 123456);
|
|
|
|
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(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Amount),
|
|
256,
|
|
amountBytes.size());
|
|
|
|
BEAST_EXPECT(!trap);
|
|
}
|
|
}
|
|
|
|
{
|
|
// 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, SeqProxy::rawSequence(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);
|
|
|
|
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(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Amount),
|
|
256,
|
|
amountBytes.size());
|
|
|
|
BEAST_EXPECT(!trap);
|
|
auto const messages = sink.messages().str();
|
|
BEAST_EXPECT(messages.empty());
|
|
}
|
|
}
|
|
|
|
// clang-format off
|
|
|
|
int const normalExp = 18;
|
|
|
|
Bytes const floatIntMin = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; // -2^63 (rounds to nearest: -(2^63-1))
|
|
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);
|
|
}
|
|
|
|
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, SeqProxy::rawSequence(env.seq(env.master)));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(vrt);
|
|
|
|
std::string const msg = "trace float";
|
|
|
|
{
|
|
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(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Xfloat),
|
|
256,
|
|
invalid.size());
|
|
|
|
BEAST_EXPECT(!trap);
|
|
}
|
|
|
|
{
|
|
vrt.setBytes(0, reinterpret_cast<uint8_t const*>(msg.data()), msg.size());
|
|
vrt.setBytes(256, floatMaxExp.data(), floatMaxExp.size());
|
|
WasmValVec params(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Xfloat),
|
|
256,
|
|
floatMaxExp.size());
|
|
|
|
BEAST_EXPECT(!trap);
|
|
}
|
|
}
|
|
|
|
{
|
|
// 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, SeqProxy::rawSequence(env.seq(env.master)));
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
VirtualRuntime vrt;
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(vrt);
|
|
|
|
std::string const msg = "trace float";
|
|
|
|
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(5), result(0);
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
msg.size(),
|
|
traceDataTypeToInt(TraceDataType::Xfloat),
|
|
256,
|
|
invalid.size());
|
|
|
|
BEAST_EXPECT(!trap);
|
|
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, SeqProxy::rawSequence(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(&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(&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(&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(&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(&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, SeqProxy::rawSequence(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(&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(&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(&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(&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, SeqProxy::rawSequence(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(&import.at("float_from_mant_exp"), 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(&import.at("float_from_mant_exp"), 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(&import.at("float_from_mant_exp"),
|
|
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(&import.at("float_from_mant_exp"),
|
|
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(&import.at("float_from_mant_exp"),
|
|
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(&import.at("float_from_mant_exp"),
|
|
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(&import.at("float_from_mant_exp"),
|
|
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(&import.at("float_from_mant_exp"),
|
|
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(&import.at("float_from_mant_exp"),
|
|
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(&import.at("float_from_mant_exp"), 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(&import.at("float_from_mant_exp"),
|
|
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, SeqProxy::rawSequence(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(&import.at("float_cmp"), 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(&import.at("float_cmp"), 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(
|
|
&import.at("float_cmp"), 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(&import.at("float_cmp"), 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(&import.at("float_cmp"), 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(&import.at("float_cmp"), 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, SeqProxy::rawSequence(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(&import.at("float_add"), 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(&import.at("float_add"), 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(&import.at("float_add"),
|
|
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(&import.at("float_add"),
|
|
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(&import.at("float_add"),
|
|
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);//
|
|
// int64.min is rounded to nearest: -(2^63-1), so max + min == 0
|
|
WasmValVec params(7), result(1);
|
|
vrt.setBytes(0, floatIntMax.data(), floatIntMax.size());
|
|
vrt.setBytes(floatSize, floatIntMin.data(), floatIntMin.size());
|
|
auto* trap =
|
|
ww(&import.at("float_add"),
|
|
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);
|
|
}
|
|
}
|
|
|
|
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, SeqProxy::rawSequence(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(&import.at("float_sub"), 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(&import.at("float_sub"), 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(&import.at("float_sub"),
|
|
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(&import.at("float_sub"),
|
|
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(&import.at("float_sub"),
|
|
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(&import.at("float_sub"),
|
|
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, SeqProxy::rawSequence(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(&import.at("float_mult"), 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(&import.at("float_mult"), 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(&import.at("float_mult"),
|
|
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(&import.at("float_mult"),
|
|
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(&import.at("float_mult"),
|
|
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(&import.at("float_mult"),
|
|
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(&import.at("float_mult"),
|
|
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, SeqProxy::rawSequence(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(&import.at("float_div"), 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(&import.at("float_div"), 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(&import.at("float_div"),
|
|
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(&import.at("float_div"),
|
|
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(&import.at("float_div"),
|
|
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(&import.at("float_div"),
|
|
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(&import.at("float_div"),
|
|
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, SeqProxy::rawSequence(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(&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(&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(&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(&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(&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(&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(&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(&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, SeqProxy::rawSequence(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(&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(&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(&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(
|
|
&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(&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(
|
|
&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(
|
|
&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(&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(&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, SeqProxy::rawSequence(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, SeqProxy::rawSequence(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(&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(&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(&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(&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(&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(&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(&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, SeqProxy::rawSequence(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(&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(&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(&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(&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, SeqProxy::rawSequence(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(&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(&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(&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(&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(&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(&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(&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(&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);
|
|
}
|
|
|
|
{
|
|
// int64.min is rounded to nearest: -(2^63-1), which fits into int64
|
|
// hfs.floatToInt(makeSlice(floatIntMin), 0);
|
|
vrt.setBytes(0, floatIntMin.data(), floatIntMin.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap = ww(&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 == floatIntMin);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToInt(makeSlice(floatUIntMax), 0);
|
|
vrt.setBytes(0, floatUIntMax.data(), floatUIntMax.size());
|
|
WasmValVec params(5), result(1);
|
|
auto* trap = ww(&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(&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(&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(&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(&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, SeqProxy::rawSequence(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(&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(&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(&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(&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(&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(&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(&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(&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 == floatIntMin);
|
|
}
|
|
|
|
{
|
|
// hfs.floatToMantExp(makeSlice(floatMax));
|
|
vrt.setBytes(0, floatMax.data(), floatMax.size());
|
|
WasmValVec params(6), result(1);
|
|
auto* trap =
|
|
ww(&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, SeqProxy::rawSequence(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(&import.at("ldgr_index"), 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
|
|
testTransferLimit()
|
|
{
|
|
testcase("transferLimit");
|
|
using namespace test::jtx;
|
|
|
|
Env env{*this};
|
|
OpenView ov{*env.current()};
|
|
ApplyContext ac = createApplyContext(env, ov);
|
|
auto const dummyEscrow =
|
|
keylet::escrow(env.master, SeqProxy::rawSequence(env.seq(env.master)));
|
|
VirtualRuntime vrt;
|
|
WasmHostFunctionsImpl hfs(ac, dummyEscrow);
|
|
|
|
auto import = xrpl::createWasmImport(hfs);
|
|
hfs.setRT(vrt);
|
|
|
|
// Test 1: Test setData() - copying FROM host TO wasm
|
|
// Multiple calls to getLedgerSqn() which uses setData() to write result to WASM memory
|
|
vrt.setTransferLimit(kWasmTransferLimit + 1024);
|
|
|
|
// hfs.getLedgerSqn();
|
|
for (int i = 0; i < (kWasmTransferLimit / vrt.transferDiff) - 3; ++i)
|
|
{
|
|
WasmValVec params(2), result(1);
|
|
|
|
auto* trap = ww(&import.at("ldgr_index"), 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);
|
|
}
|
|
|
|
BEAST_EXPECT((vrt.getTestTransferLimit() >= 0) && (vrt.getTestTransferLimit() < 1024));
|
|
|
|
// Next call should hit OutOfTransferLimit
|
|
{
|
|
WasmValVec params(2), result(1);
|
|
auto* trap = ww(&import.at("ldgr_index"), params, result, 0, sizeof(std::uint32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == hfErrorToInt(HostFunctionError::OutOfTransferLimit));
|
|
}
|
|
|
|
// After limit exhausted, all next call return OutOfTransferLimit
|
|
{
|
|
WasmValVec params(2), result(1);
|
|
auto* trap = ww(&import.at("ldgr_index"), params, result, 0, sizeof(std::uint32_t));
|
|
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == hfErrorToInt(HostFunctionError::OutOfTransferLimit));
|
|
}
|
|
|
|
// Reset transfer limit to a small value that can accommodate overhead but not AccountID
|
|
// copy
|
|
vrt.setTransferLimit(vrt.transferDiff + 10);
|
|
|
|
Account const alice("alice");
|
|
auto const aliceID = env.master.id();
|
|
vrt.setBytes(0, aliceID.data(), AccountID::size());
|
|
|
|
// This should fail because getDataAccountID() needs to copy AccountID (20 bytes)
|
|
// After getTransferLimit() overhead (1024), we only have 10 bytes left, not enough for 20
|
|
{
|
|
WasmValVec params(4), result(1);
|
|
auto* trap =
|
|
ww(&import.at("accountroot_id"), params, result, 0, AccountID::size(), 100, 32);
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == hfErrorToInt(HostFunctionError::OutOfTransferLimit));
|
|
}
|
|
|
|
// Verify that reading slices (without copying) does NOT consume transfer limit
|
|
vrt.setTransferLimit(vrt.transferDiff + 10);
|
|
|
|
// trace() uses getDataString() -> getDataSlice() which does NOT check transfer limit
|
|
std::string testMsg = "This message is longer than 10 bytes to prove slices don't count";
|
|
vrt.setBytes(0, testMsg.data(), testMsg.size());
|
|
vrt.setBytes(
|
|
100,
|
|
reinterpret_cast<uint8_t const*>("dummy"),
|
|
5); // Empty data slice for trace
|
|
{
|
|
WasmValVec params(5), result(0);
|
|
// trace(msg_ptr, msg_len, data_type, data_ptr, data_len) -- returns nothing
|
|
auto* trap =
|
|
ww(&import.at("trace"),
|
|
params,
|
|
result,
|
|
0,
|
|
testMsg.size(),
|
|
traceDataTypeToInt(TraceDataType::AsText),
|
|
100,
|
|
5);
|
|
|
|
// Should not trap even though the message is >10 bytes, because trace only reads
|
|
// slices (no transfer limit check in getDataSlice) and never charges the limit.
|
|
BEAST_EXPECT(!trap);
|
|
}
|
|
|
|
// setData should return OutOfTransferLimit when the transfer limit is exhausted.
|
|
// trace left the limit untouched, so the next getTransferLimit() overhead (1024)
|
|
// takes 1034 down to 10 -- not enough for the 32-byte hash copy.
|
|
{
|
|
WasmValVec params(2), result(1);
|
|
auto* trap = ww(&import.at("parent_ldgr_hash"), params, result, 500, 32);
|
|
|
|
// the transfer limit went negative
|
|
BEAST_EXPECT(!trap) && BEAST_EXPECT(result[0].kind == WASM_I32) &&
|
|
BEAST_EXPECT(
|
|
result[0].of.i32 == hfErrorToInt(HostFunctionError::OutOfTransferLimit));
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
testTransferLimit();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(HostFuncImpl, app, xrpl);
|
|
|
|
} // namespace xrpl::test
|