feat: Porting wasm host function tests to new design

This commit is contained in:
TimothyBanks
2026-08-15 19:41:32 -04:00
parent d469fc2cdf
commit e863db5061
33 changed files with 1661 additions and 1019 deletions

View File

@@ -821,4 +821,24 @@ mod tests {
"one {MAX_FIELD_BYTES}-byte value against a {TRANSFER_LIMIT_BYTES}-byte budget"
);
}
#[test]
fn read_u32_arg_success() {
let number: u32 = 0x12345678;
let le_array: [u8; 4] = number.to_le_bytes();
assert_eq!(le_array, [0x78, 0x56, 0x34, 0x12]);
let result = read_u32_arg(&le_array);
assert!(result.is_ok());
assert_eq!(result.unwrap(), number.try_into().unwrap());
}
#[test]
fn read_u32_arg_invalid_length() {
let le_array = [0x56, 0x34, 0x12];
let result = read_u32_arg(&le_array);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), HostError::InvalidParams);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,22 @@
#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/HashRouter.h>
#include <xrpl/core/NetworkIDService.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/AmendmentTable.h>
#include <xrpl/ledger/PendingSaves.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/STValidation.h>
#include <xrpl/server/LoadFeeTrack.h>
#include <boost/asio/io_context.hpp>
@@ -15,11 +24,15 @@
#include <helpers/TestFamily.h>
#include <helpers/TestSink.h>
#include <chrono>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <stdexcept>
#include <string>
#include <vector>
namespace xrpl::test {
@@ -40,6 +53,107 @@ public:
}
};
/**
* Minimal AmendmentTable for tests.
*
* The real table is built by `makeAmendmentTable`, which lives in the app (xrpld) tier and
* so cannot link into a libxrpl-tier test binary. But the wasm host only ever calls
* `find(name)` — a name -> amendment-id resolve — and whether an amendment is *enabled* is
* read from the ledger's `Rules`, never from here. So `find` delegates to the feature
* registry (the same source `makeAmendmentTable` would seed from) and every other method,
* unused by these tests, throws if reached.
*/
class TestAmendmentTable final : public AmendmentTable
{
public:
[[nodiscard]] uint256
find(std::string const& name) const override
{
return getRegisteredFeature(name).value_or(uint256{});
}
bool
veto(uint256 const&) override
{
throw std::logic_error("TestAmendmentTable::veto not implemented");
}
bool
unVeto(uint256 const&) override
{
throw std::logic_error("TestAmendmentTable::unVeto not implemented");
}
bool
enable(uint256 const&) override
{
throw std::logic_error("TestAmendmentTable::enable not implemented");
}
[[nodiscard]] bool
isEnabled(uint256 const&) const override
{
throw std::logic_error("TestAmendmentTable::isEnabled not implemented");
}
[[nodiscard]] bool
isSupported(uint256 const&) const override
{
throw std::logic_error("TestAmendmentTable::isSupported not implemented");
}
[[nodiscard]] bool
hasUnsupportedEnabled() const override
{
throw std::logic_error("TestAmendmentTable::hasUnsupportedEnabled not implemented");
}
[[nodiscard]] std::optional<NetClock::time_point>
firstUnsupportedExpected() const override
{
throw std::logic_error("TestAmendmentTable::firstUnsupportedExpected not implemented");
}
[[nodiscard]] json::Value
getJson(bool) const override
{
throw std::logic_error("TestAmendmentTable::getJson not implemented");
}
[[nodiscard]] json::Value
getJson(uint256 const&, bool) const override
{
throw std::logic_error("TestAmendmentTable::getJson(amendment) not implemented");
}
[[nodiscard]] bool
needValidatedLedger(LedgerIndex) const override
{
throw std::logic_error("TestAmendmentTable::needValidatedLedger not implemented");
}
void
doValidatedLedger(LedgerIndex, std::set<uint256> const&, majorityAmendments_t const&) override
{
throw std::logic_error("TestAmendmentTable::doValidatedLedger not implemented");
}
void
trustChanged(hash_set<PublicKey> const&) override
{
throw std::logic_error("TestAmendmentTable::trustChanged not implemented");
}
std::map<uint256, std::uint32_t>
doVoting(
Rules const&,
NetClock::time_point,
std::set<uint256> const&,
majorityAmendments_t const&,
std::vector<std::shared_ptr<STValidation>> const&) override
{
throw std::logic_error("TestAmendmentTable::doVoting not implemented");
}
[[nodiscard]] std::vector<uint256>
doValidation(std::set<uint256> const&) const override
{
throw std::logic_error("TestAmendmentTable::doValidation not implemented");
}
[[nodiscard]] std::vector<uint256>
getDesired() const override
{
throw std::logic_error("TestAmendmentTable::getDesired not implemented");
}
};
/**
* Simple NetworkIDService implementation for tests.
*/
@@ -91,6 +205,7 @@ class TestServiceRegistry : public ServiceRegistry
logs_.journal("TaggedCache")};
PendingSaves pendingSaves_;
std::optional<uint256> trapTxID_;
TestAmendmentTable amendmentTable_;
public:
TestServiceRegistry() = default;
@@ -140,10 +255,13 @@ public:
}
// Protocol and validation services
// See `TestAmendmentTable`: the wasm host only resolves a name -> id here; enabled
// state is read from the ledger's `Rules`. The real factory (`makeAmendmentTable`) is
// app-tier and won't link into a libxrpl test binary, so a stub table is used.
AmendmentTable&
getAmendmentTable() override
{
throw std::logic_error("TestServiceRegistry::getAmendmentTable() not implemented");
return amendmentTable_;
}
HashRouter&

View File

@@ -0,0 +1,150 @@
#pragma once
// Base for the "impl" wasm tests: a *real* `WasmHostFunctionsImpl` over a *real* ledger,
// built with no Application / jtx / beast::unit_test::Suite. The ledger is a `TxTest`
// (genesis ledger + OpenView + real transactor dispatch); the host is constructed from
// its `ServiceRegistry` and `OpenView` through an `ApplyContext`.
//
// This is the counterpart to `HostContextFixture` (mock host, interop): here the host
// really computes, so a test asserts a value against the ledger's own source of truth
// (`keylet::escrow`, a real field's bytes, `wasm_float`), rather than what a mock was
// asked. Pure-computation host functions (keylets, floats, check_sig, sha512_half, nft
// decoders) ignore the ledger; the reading getters read the object `leKey` points at.
#include <xrpl/basics/base_uint.h>
#include <xrpl/ledger/ApplyView.h> // TapNone
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Fees.h>
#include <xrpl/protocol/Indexes.h> // keylet::account
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STNumber.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/wasm/HostFuncImpl.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/TxTest.h>
#include <cstdint>
#include <memory>
#include <optional>
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;
}
class WasmImplTest : public testing::Test
{
public:
// The real ledger. Tests populate it with `ledger.createAccount()` / `submit()` /
// `close()` before reading through the host.
TxTest ledger;
// A real host bound to `leKey` — the "current"/home object the `*_field` and
// `*_arr_len` getters read. Defaults to a throwaway keylet for the many functions
// (keylets, floats, sig, hash, nft decoders) that never touch the current object.
//
// Returns a reference into a fixture-owned host so its `ApplyContext&` outlives it;
// call once per test.
WasmHostFunctionsImpl&
host(Keylet const& leKey = keylet::account(AccountID{}))
{
// The finish tx the host runs under. Its contents are irrelevant to the
// functions these tests exercise; it only has to be a well-formed shell.
finishTx_ = std::make_shared<STTx>(ttESCROW_FINISH, [](STObject&) {});
context_.emplace(
ledger.getServiceRegistry(),
ledger.getOpenLedger(),
*finishTx_,
tesSUCCESS,
ledger.getOpenLedger().fees().base,
TapNone,
beast::Journal{beast::Journal::getNullSink()});
host_.emplace(*context_, leKey);
return *host_;
}
private:
std::shared_ptr<STTx const> finishTx_;
std::optional<ApplyContext> context_;
std::optional<WasmHostFunctionsImpl> host_;
};
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct AccountKeyletImpl : WasmImplTest
{
};
TEST_F(AccountKeyletImpl, MatchesAccountKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::account(owner.id());
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().accountKeylet(owner);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(AccountKeyletImpl, UnsetAccountIsInvalidAccount)
{
auto const result = host().accountKeylet(AccountID{});
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,53 @@
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct AmmKeyletImpl : WasmImplTest
{
};
TEST_F(AmmKeyletImpl, MatchesAmmKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto usdIssue = Issue{toCurrency("USD"), owner.id()};
auto const expected = keylet::amm(xrpIssue(), usdIssue);
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().ammKeylet(usdIssue, xrpIssue());
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(AmmKeyletImpl, InvalidIssue1)
{
auto const result = host().ammKeylet(xrpIssue(), xrpIssue());
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidParams);
}
TEST_F(AmmKeyletImpl, InvalidIssue2)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto baseMpt = makeMptID(1, owner.id());
auto const result = host().ammKeylet(baseMpt, xrpIssue());
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidParams);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,19 @@
#include <gtest/gtest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
namespace xrpl::test {
struct BaseFeeImpl : WasmImplTest
{
};
TEST_F(BaseFeeImpl, MatchesLedger)
{
auto const result = host().getBaseFee();
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, ledger.getOpenLedger().fees().base.drops());
}
} // namespace xrpl::test

View File

@@ -0,0 +1,81 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <cstdint>
#include <expected>
namespace xrpl::test {
struct CacheLedgerObjImpl : WasmImplTest
{
void
runMatchesLedger(bool implicit)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto& h = host();
auto const key = keylet::account(owner.id()).key;
for (auto i = int32_t{1}; i < 257; ++i)
{
auto const slot = h.cacheLedgerObj(key, i);
ASSERT_TRUE(slot.has_value()) << "cacheLedgerObj should find the created account";
EXPECT_EQ(*slot, i);
auto const account = h.getLedgerObjField(*slot, sfAccount);
ASSERT_TRUE(account.has_value());
Bytes const ownerBytes{owner.id().begin(), owner.id().end()};
EXPECT_EQ(*account, ownerBytes);
auto const sle = ledger.getOpenLedger().read(keylet::account(owner.id()));
ASSERT_NE(sle, nullptr);
auto const& ledgerAccount = sle->getAccountID(sfAccount);
EXPECT_EQ(*account, (Bytes{ledgerAccount.begin(), ledgerAccount.end()}));
}
// Every slot is now occupied, so asking to auto-allocate (cacheIdx == 0) has nowhere
// to put the object.
auto const result = h.cacheLedgerObj(key, 0);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::SlotsFull);
}
};
TEST_F(CacheLedgerObjImpl, MatchesLedgerExplicitIndices)
{
runMatchesLedger(false);
}
TEST_F(CacheLedgerObjImpl, MatchesLedgerImplicitIndices)
{
runMatchesLedger(true);
}
TEST_F(CacheLedgerObjImpl, OutOfRange)
{
auto result = host().cacheLedgerObj(uint256{}, -1);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::SlotOutRange);
result = host().cacheLedgerObj(uint256{}, 257);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::SlotOutRange);
}
TEST_F(CacheLedgerObjImpl, LedgerObjNotFound)
{
auto const ghost = keylet::account(Account{"ghost"}.id()).key;
auto result = host().cacheLedgerObj(ghost, 0);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::LedgerObjNotFound);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,40 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct CheckKeyletImpl : WasmImplTest
{
};
TEST_F(CheckKeyletImpl, MatchesCheckKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::check(owner.id(), SeqProxy::rawSequence(1u));
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().checkKeylet(owner.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(CheckKeyletImpl, UnsetAccountIsInvalidAccount)
{
auto const result = host().checkKeylet(AccountID{}, 1u);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,69 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct CredentialKeyletImpl : WasmImplTest
{
};
TEST_F(CredentialKeyletImpl, MatchesCredentialKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const credTypeStr = std::string{"test"};
auto const credType = Slice{credTypeStr.data(), credTypeStr.size()};
auto const expected = keylet::credential(owner.id(), owner.id(), credType);
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().credentialKeylet(owner.id(), owner.id(), credType);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(CredentialKeyletImpl, CredentialTypeStringTooLong)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto constexpr credTypeStr = std::string_view{
"abcdefghijklmnopqrstuvwxyz01234567890qwertyuiop[]"
"asdfghjkl;'zxcvbnm8237tr28weufwldebvfv8734t07p"};
static_assert(credTypeStr.size() > kMaxCredentialTypeLength);
auto const credType = Slice{credTypeStr.data(), credTypeStr.size()};
auto const result = host().credentialKeylet(owner.id(), owner.id(), credType);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidParams);
}
TEST_F(CredentialKeyletImpl, InvalidAccount)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const credTypeStr = std::string{"test"};
auto const credType = Slice{credTypeStr.data(), credTypeStr.size()};
auto result = host().credentialKeylet(AccountID{}, owner.id(), credType);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
result = host().credentialKeylet(owner.id(), AccountID{}, credType);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,108 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol_autogen/transactions/EscrowCreate.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct CurrentLedgerObjFieldImpl : WasmImplTest
{
// Create an escrow owned by `owner` and return its keylet (the object the host will
// read as its "current" object).
Keylet
makeEscrow(Account const& owner, Account const& dest, uint256* transactionId = nullptr)
{
ledger.createAccount(owner, XRP(1000));
ledger.createAccount(dest, XRP(1000));
auto const ownerSeq = ledger.getAccountRoot(owner.id()).getSequence();
// A finish time comfortably after the genesis close time.
auto const r = ledger.submit(
transactions::EscrowCreateBuilder{owner.id(), dest.id(), XRP(100)}.setFinishAfter(
900'000'000),
owner);
EXPECT_EQ(r.ter, tesSUCCESS) << transToken(r.ter);
if (transactionId != nullptr)
{
*transactionId = r.tx->getTransactionID();
}
ledger.close();
return keylet::escrow(owner.id(), SeqProxy::rawSequence(ownerSeq));
}
};
TEST_F(CurrentLedgerObjFieldImpl, ReadsfAccount)
{
auto const owner = Account{"owner"};
auto const escrow = makeEscrow(owner, Account{"dest"});
ASSERT_NE(ledger.getOpenLedger().read(escrow), nullptr) << "escrow object should exist";
auto const account = host(escrow).getCurrentLedgerObjField(sfAccount);
ASSERT_TRUE(account.has_value());
auto const ownerBytes = Bytes{std::begin(owner.id()), std::end(owner.id())};
EXPECT_EQ(*account, ownerBytes);
}
TEST_F(CurrentLedgerObjFieldImpl, ReadsfAccountDummyEscrow)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const ownerSeq = ledger.getAccountRoot(owner.id()).getSequence();
auto const escrow = keylet::escrow(owner.id(), SeqProxy::rawSequence(ownerSeq));
auto const account = host(escrow).getCurrentLedgerObjField(sfAccount);
ASSERT_TRUE(!account.has_value());
ASSERT_TRUE(account.error() == HostFunctionError::LedgerObjNotFound);
}
TEST_F(CurrentLedgerObjFieldImpl, ReadAmount)
{
auto const owner = Account{"owner"};
auto const escrow = makeEscrow(owner, Account{"dest"});
ASSERT_NE(ledger.getOpenLedger().read(escrow), nullptr) << "escrow object should exist";
auto const amount = host(escrow).getCurrentLedgerObjField(sfAmount);
ASSERT_TRUE(amount.has_value());
EXPECT_EQ(*amount, toBytes(XRP(100)));
}
TEST_F(CurrentLedgerObjFieldImpl, ReadPreviousTxnID)
{
auto const owner = Account{"owner"};
auto transactionId = uint256{};
auto const escrow = makeEscrow(owner, Account{"dest"}, &transactionId);
ASSERT_NE(ledger.getOpenLedger().read(escrow), nullptr) << "escrow object should exist";
auto const previousTxnId = host(escrow).getCurrentLedgerObjField(sfPreviousTxnID);
ASSERT_TRUE(previousTxnId.has_value());
EXPECT_EQ(*previousTxnId, toBytes(transactionId));
}
TEST_F(CurrentLedgerObjFieldImpl, ReadOwner)
{
auto const owner = Account{"owner"};
auto const escrow = makeEscrow(owner, Account{"dest"});
ASSERT_NE(ledger.getOpenLedger().read(escrow), nullptr) << "escrow object should exist";
auto const ownerField = host(escrow).getCurrentLedgerObjField(sfOwner);
ASSERT_TRUE(!ownerField.has_value());
ASSERT_TRUE(ownerField.error() == HostFunctionError::FieldNotFound);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,57 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct DelegateKeyletImpl : WasmImplTest
{
};
TEST_F(DelegateKeyletImpl, MatchesDelegateKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const delegate = Account{"delegate"};
ledger.createAccount(delegate, XRP(1000));
auto const expected = keylet::delegate(owner.id(), delegate.id());
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().delegateKeylet(owner.id(), delegate.id());
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(DelegateKeyletImpl, CantDelegateToSelf)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const result = host().delegateKeylet(owner.id(), owner.id());
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidParams);
}
TEST_F(DelegateKeyletImpl, InvalidAccount)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto result = host().delegateKeylet(AccountID{}, owner.id());
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
result = host().delegateKeylet(owner.id(), AccountID{});
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,57 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct DepositPreauthKeyletImpl : WasmImplTest
{
};
TEST_F(DepositPreauthKeyletImpl, MatchesDepositPreauthKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const destination = Account{"destination"};
ledger.createAccount(destination, XRP(1000));
auto const expected = keylet::depositPreauth(owner.id(), destination.id());
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().depositPreauthKeylet(owner.id(), destination.id());
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(DepositPreauthKeyletImpl, CantPreauthToSelf)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const result = host().depositPreauthKeylet(owner.id(), owner.id());
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidParams);
}
TEST_F(DepositPreauthKeyletImpl, InvalidAccount)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto result = host().depositPreauthKeylet(AccountID{}, owner.id());
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
result = host().depositPreauthKeylet(owner.id(), AccountID{});
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,38 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct DidKeyletImpl : WasmImplTest
{
};
TEST_F(DidKeyletImpl, MatchesDidKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::did(owner.id());
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().didKeylet(owner.id());
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(DidKeyletImpl, InvalidAccount)
{
auto result = host().didKeylet(AccountID{});
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,50 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <tx/wasm/RealHostFixture.h>
#include <cstdint>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct EscrowKeyletImpl : WasmImplTest
{
};
TEST_F(EscrowKeyletImpl, MatchesLedgerKeyletFunction)
{
auto const owner = Account{"owner"};
auto const seq = std::uint32_t{42};
auto const result = host().escrowKeylet(owner.id(), seq);
ASSERT_TRUE(result.has_value());
auto const expected = keylet::escrow(owner.id(), SeqProxy::rawSequence(seq)).key;
auto const expectedBytes = Bytes{std::begin(expected), std::end(expected)};
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(EscrowKeyletImpl, DifferentAccountsGiveDifferentKeylets)
{
auto const a = host().escrowKeylet(Account{"alice"}.id(), 7);
auto const b = host().escrowKeylet(Account{"becky"}.id(), 7);
ASSERT_TRUE(a.has_value() && b.has_value());
EXPECT_NE(*a, *b);
}
TEST_F(EscrowKeyletImpl, UnsetAccountIsInvalidAccount)
{
auto const result = host().escrowKeylet(AccountID{}, 1);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,50 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/Feature.h>
#include <gtest/gtest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <string_view>
namespace xrpl::test {
struct IsAmendmentEnabledImpl : WasmImplTest
{
};
TEST_F(IsAmendmentEnabledImpl, EnabledAmendmentByIdReadsOne)
{
auto const id = getRegisteredFeature("TokenEscrow");
ASSERT_TRUE(id.has_value());
auto const result = host().isAmendmentEnabled(id.value_or(uint256{}));
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, 1);
}
TEST_F(IsAmendmentEnabledImpl, EnabledAmendmentByNameReadsOne)
{
auto const result = host().isAmendmentEnabled(std::string_view{"TokenEscrow"});
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, 1);
}
TEST_F(IsAmendmentEnabledImpl, UnknownAmendmentByIdReadsZero)
{
auto const result = host().isAmendmentEnabled(
uint256{"DEADBEEF00000000000000000000000000000000000000000000000000000000"});
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, 0);
}
TEST_F(IsAmendmentEnabledImpl, UnknownAmendmentNameReadsZero)
{
auto const result = host().isAmendmentEnabled(std::string_view{"DEADBEEF"});
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, 0);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,19 @@
#include <gtest/gtest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
namespace xrpl::test {
struct LedgerSqnImpl : WasmImplTest
{
};
TEST_F(LedgerSqnImpl, MatchesLedger)
{
auto const result = host().getLedgerSqn();
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, ledger.getOpenLedger().header().seq);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,38 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct MptokenIssuanceKeyletImpl : WasmImplTest
{
};
TEST_F(MptokenIssuanceKeyletImpl, MatchesMptokenIssuanceKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::mptokenIssuance(makeMptID(1u, owner.id()));
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().mptokenIssuanceKeylet(owner.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(MptokenIssuanceKeyletImpl, InvalidAccount)
{
auto result = host().mptokenIssuanceKeylet(AccountID{}, 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,54 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct MptokenKeyletImpl : WasmImplTest
{
};
TEST_F(MptokenKeyletImpl, MatchesMptokenKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const anotherAccount = Account{"account"};
ledger.createAccount(anotherAccount, XRP(1000));
auto const mpt = makeMptID(1u, owner.id());
auto const expected = keylet::mptoken(mpt, anotherAccount.id());
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().mptokenKeylet(mpt, anotherAccount.id());
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(MptokenKeyletImpl, InvalidMpt)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto result = host().mptokenKeylet(MPTID{}, owner.id());
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidParams);
}
TEST_F(MptokenKeyletImpl, InvalidAccount)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const mpt = makeMptID(1u, owner.id());
auto result = host().mptokenKeylet(mpt, AccountID{});
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct NFTImpl : WasmImplTest
{
};
TEST_F(NFTImpl, MatchesVaultFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::vault(owner.id(), SeqProxy::rawSequence(1u));
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().vaultKeylet(owner.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(NFTImpl, InvalidAccount)
{
auto result = host().vaultKeylet(AccountID{}, 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct NftokenOfferKeyletImpl : WasmImplTest
{
};
TEST_F(NftokenOfferKeyletImpl, MatchesNftokenOfferFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::nftokenOffer(owner.id(), SeqProxy::rawSequence(1u));
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().nftokenOfferKeylet(owner.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(NftokenOfferKeyletImpl, InvalidAccount)
{
auto result = host().nftokenOfferKeylet(AccountID{}, 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct OfferKeyletImpl : WasmImplTest
{
};
TEST_F(OfferKeyletImpl, MatchesOfferFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::offer(owner.id(), SeqProxy::rawSequence(1u));
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().offerKeylet(owner.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(OfferKeyletImpl, InvalidAccount)
{
auto result = host().offerKeylet(AccountID{}, 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,38 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct OracleKeyletImpl : WasmImplTest
{
};
TEST_F(OracleKeyletImpl, MatchesOracleFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::oracle(owner.id(), 1u);
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().oracleKeylet(owner.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(OracleKeyletImpl, InvalidAccount)
{
auto result = host().oracleKeylet(AccountID{}, 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,19 @@
#include <gtest/gtest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
namespace xrpl::test {
struct ParentLedgerHashImpl : WasmImplTest
{
};
TEST_F(ParentLedgerHashImpl, MatchesLedger)
{
auto const result = host().getParentLedgerHash();
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, ledger.getOpenLedger().header().parentHash);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,19 @@
#include <gtest/gtest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
namespace xrpl::test {
struct ParentLedgerTimeImpl : WasmImplTest
{
};
TEST_F(ParentLedgerTimeImpl, MatchesLedger)
{
auto const result = host().getParentLedgerTime();
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, ledger.getOpenLedger().parentCloseTime().time_since_epoch().count());
}
} // namespace xrpl::test

View File

@@ -0,0 +1,59 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct PaychannelKeyletImpl : WasmImplTest
{
};
TEST_F(PaychannelKeyletImpl, MatchesPaychannelFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const destination = Account{"destination"};
ledger.createAccount(destination, XRP(1000));
auto const expected =
keylet::payChannel(owner.id(), destination.id(), SeqProxy::rawSequence(1u));
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().paychannelKeylet(owner.id(), destination.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(PaychannelKeyletImpl, CantUseSelf)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto result = host().paychannelKeylet(owner.id(), owner.id(), 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidParams);
}
TEST_F(PaychannelKeyletImpl, InvalidAccount)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto result = host().paychannelKeylet(AccountID{}, owner.id(), 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
result = host().paychannelKeylet(owner.id(), AccountID{}, 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct PermissionedDomainKeyletImpl : WasmImplTest
{
};
TEST_F(PermissionedDomainKeyletImpl, MatchesPermissionedDomainFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::permissionedDomain(owner.id(), SeqProxy::rawSequence(1u));
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().permissionedDomainKeylet(owner.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(PermissionedDomainKeyletImpl, InvalidAccount)
{
auto result = host().permissionedDomainKeylet(AccountID{}, 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,38 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct SignerListKeyletImpl : WasmImplTest
{
};
TEST_F(SignerListKeyletImpl, MatchesSignerListFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::signerList(owner.id());
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().signerListKeylet(owner.id());
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(SignerListKeyletImpl, InvalidAccount)
{
auto result = host().signerListKeylet(AccountID{});
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct TicketKeyletImpl : WasmImplTest
{
};
TEST_F(TicketKeyletImpl, MatchesTicketFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::ticket(owner.id(), SeqProxy::rawTicket(1u));
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().ticketKeylet(owner.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(TicketKeyletImpl, InvalidAccount)
{
auto result = host().ticketKeylet(AccountID{}, 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,76 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct TrustlineKeyletImpl : WasmImplTest
{
};
TEST_F(TrustlineKeyletImpl, MatchesTrustlineKeyletFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const destination = Account{"destination"};
ledger.createAccount(destination, XRP(1000));
auto const usd = toCurrency("USD");
auto const expected = keylet::trustLine(owner.id(), destination.id(), usd);
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().trustLineKeylet(owner.id(), destination.id(), usd);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(TrustlineKeyletImpl, InvalidCurrency)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const destination = Account{"destination"};
ledger.createAccount(destination, XRP(1000));
auto const result = host().trustLineKeylet(owner.id(), destination.id(), toCurrency(""));
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidParams);
}
TEST_F(TrustlineKeyletImpl, CantTrustlineToSelf)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const usd = toCurrency("USD");
auto const result = host().trustLineKeylet(owner.id(), owner.id(), usd);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidParams);
}
TEST_F(TrustlineKeyletImpl, InvalidAccount)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const usd = toCurrency("USD");
auto result = host().trustLineKeylet(AccountID{}, owner.id(), usd);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
result = host().trustLineKeylet(owner.id(), AccountID{}, usd);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,82 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <cstdint>
#include <expected>
namespace xrpl::test {
struct CacheLedgerObjImpl : WasmImplTest
{
void
runMatchesLedger(bool implicit)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto& h = host();
auto const key = keylet::account(owner.id()).key;
for (auto i = int32_t{1}; i < 257; ++i)
{
auto const slot = h.cacheLedgerObj(key, i);
ASSERT_TRUE(slot.has_value()) << "cacheLedgerObj should find the created account";
EXPECT_EQ(*slot, i);
auto const account = h.getLedgerObjField(*slot, sfAccount);
ASSERT_TRUE(account.has_value());
Bytes const ownerBytes{owner.id().begin(), owner.id().end()};
EXPECT_EQ(*account, ownerBytes);
auto const sle = ledger.getOpenLedger().read(keylet::account(owner.id()));
ASSERT_NE(sle, nullptr);
auto const& ledgerAccount = sle->getAccountID(sfAccount);
EXPECT_EQ(*account, (Bytes{ledgerAccount.begin(), ledgerAccount.end()}));
}
// Every slot is now occupied, so asking to auto-allocate (cacheIdx == 0) has nowhere
// to put the object.
auto const result = h.cacheLedgerObj(key, 0);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::SlotsFull);
}
};
TEST_F(CacheLedgerObjImpl, MatchesLedgerExplicitIndices)
{
runMatchesLedger(false);
}
TEST_F(CacheLedgerObjImpl, MatchesLedgerImplicitIndices)
{
runMatchesLedger(true);
}
TEST_F(CacheLedgerObjImpl, OutOfRange)
{
auto result = host().cacheLedgerObj(uint256{}, -1);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::SlotOutRange);
result = host().cacheLedgerObj(uint256{}, 257);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::SlotOutRange);
}
TEST_F(CacheLedgerObjImpl, LedgerObjNotFound)
{
auto const ghost = keylet::account(Account{"ghost"}.id()).key;
auto result = host().cacheLedgerObj(ghost, 0);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::LedgerObjNotFound);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,35 @@
#include <xrpl/protocol/Protocol.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
namespace xrpl::test {
struct UpdateDataImpl : WasmImplTest
{
};
TEST_F(UpdateDataImpl, SmallData)
{
auto& h = host();
auto data = Bytes(10, 0x42);
auto result = h.updateData(Slice{data.data(), data.size()});
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, data.size());
// TODO: getData() does not seem to be called when the smart escrow finishes.
EXPECT_EQ(h.getData(), data);
}
TEST_F(UpdateDataImpl, LargeData)
{
auto& h = host();
auto data = Bytes(kMaxWasmDataLength + 1, 0x42);
auto result = h.updateData(Slice{data.data(), data.size()});
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::DataFieldTooLarge);
}
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <gtest/gtest.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/RealHostFixture.h>
#include <expected>
#include <iterator>
namespace xrpl::test {
struct VaultKeyletImpl : WasmImplTest
{
};
TEST_F(VaultKeyletImpl, MatchesVaultFunction)
{
auto const owner = Account{"owner"};
ledger.createAccount(owner, XRP(1000));
auto const expected = keylet::vault(owner.id(), SeqProxy::rawSequence(1u));
auto const expectedBytes = Bytes{std::begin(expected.key), std::end(expected.key)};
auto const result = host().vaultKeylet(owner.id(), 1u);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(*result, expectedBytes);
}
TEST_F(VaultKeyletImpl, InvalidAccount)
{
auto result = host().vaultKeylet(AccountID{}, 1u);
ASSERT_TRUE(!result.has_value());
EXPECT_EQ(result.error(), HostFunctionError::InvalidAccount);
}
} // namespace xrpl::test