From e863db5061c092ba3d990c71a2dffe77cda7b76d Mon Sep 17 00:00:00 2001 From: TimothyBanks Date: Sat, 15 Aug 2026 19:41:32 -0400 Subject: [PATCH] feat: Porting wasm host function tests to new design --- crates/xrpl-wasm-vm/src/abi.rs | 20 + src/test/app/HostFuncImpl_test.cpp | 1018 ----------------- .../libxrpl/helpers/TestServiceRegistry.h | 120 +- src/tests/libxrpl/tx/wasm/RealHostFixture.h | 150 +++ .../tx/wasm/host_functions/AccountKeylet.cpp | 39 + .../tx/wasm/host_functions/AmmKeylet.cpp | 53 + .../tx/wasm/host_functions/BaseFee.cpp | 19 + .../tx/wasm/host_functions/CacheLedgerObj.cpp | 81 ++ .../tx/wasm/host_functions/CheckKeylet.cpp | 40 + .../wasm/host_functions/CredentialKeylet.cpp | 69 ++ .../host_functions/CurrentLedgerObjField.cpp | 108 ++ .../tx/wasm/host_functions/DelegateKeylet.cpp | 57 + .../host_functions/DepositPreauthKeylet.cpp | 57 + .../tx/wasm/host_functions/DidKeylet.cpp | 38 + .../tx/wasm/host_functions/EscrowKeylet.cpp | 50 + .../host_functions/IsAmendmentEnabled.cpp | 50 + .../tx/wasm/host_functions/LedgerSqn.cpp | 19 + .../host_functions/MptokenIssuanceKeylet.cpp | 38 + .../tx/wasm/host_functions/MptokenKeylet.cpp | 54 + .../libxrpl/tx/wasm/host_functions/NFT.cpp | 39 + .../host_functions/NftokenOfferKeylet.cpp | 39 + .../tx/wasm/host_functions/OfferKeylet.cpp | 39 + .../tx/wasm/host_functions/OracleKeylet.cpp | 38 + .../wasm/host_functions/ParentLedgerHash.cpp | 19 + .../wasm/host_functions/ParentLedgerTime.cpp | 19 + .../wasm/host_functions/PaychannelKeylet.cpp | 59 + .../PermissionedDomainedKeylet.cpp | 39 + .../wasm/host_functions/SignerListKeylet.cpp | 38 + .../tx/wasm/host_functions/TicketKeylet.cpp | 39 + .../wasm/host_functions/TrustLineKeylet.cpp | 76 ++ .../tx/wasm/host_functions/TxField.cpp | 82 ++ .../tx/wasm/host_functions/UpdateData.cpp | 35 + .../tx/wasm/host_functions/VaultKeylet.cpp | 39 + 33 files changed, 1661 insertions(+), 1019 deletions(-) create mode 100644 src/tests/libxrpl/tx/wasm/RealHostFixture.h create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/AccountKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/AmmKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/BaseFee.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/CacheLedgerObj.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/CheckKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/CredentialKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/CurrentLedgerObjField.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/DelegateKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/DepositPreauthKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/DidKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/EscrowKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/IsAmendmentEnabled.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/LedgerSqn.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/MptokenIssuanceKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/MptokenKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/NFT.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/NftokenOfferKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/OfferKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/OracleKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/ParentLedgerHash.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/ParentLedgerTime.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/PaychannelKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/PermissionedDomainedKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/SignerListKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/TicketKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/TrustLineKeylet.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/TxField.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/UpdateData.cpp create mode 100644 src/tests/libxrpl/tx/wasm/host_functions/VaultKeylet.cpp diff --git a/crates/xrpl-wasm-vm/src/abi.rs b/crates/xrpl-wasm-vm/src/abi.rs index 74b52944d4..da0b47d704 100644 --- a/crates/xrpl-wasm-vm/src/abi.rs +++ b/crates/xrpl-wasm-vm/src/abi.rs @@ -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); + } } diff --git a/src/test/app/HostFuncImpl_test.cpp b/src/test/app/HostFuncImpl_test.cpp index b4de4a0475..a844c136af 100644 --- a/src/test/app/HostFuncImpl_test.cpp +++ b/src/test/app/HostFuncImpl_test.cpp @@ -73,75 +73,7 @@ namespace xrpl::test { -static Bytes -toBytes(std::uint8_t value) -{ - return {value}; -} -static Bytes -toBytes(std::uint16_t value) -{ - auto const* b = reinterpret_cast(&value); - auto const* e = reinterpret_cast(&value + 1); - return Bytes{b, e}; -} - -static Bytes -toBytes(std::uint32_t value) -{ - auto const* b = reinterpret_cast(&value); - auto const* e = reinterpret_cast(&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()) - return toBytes(asset.get()); - - auto const& mptIssue = asset.get(); - 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( @@ -374,315 +306,7 @@ 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(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(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(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(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(HostFunctionError::SlotsFull)); - } - } - } void testGetTxField() @@ -947,111 +571,6 @@ struct HostFuncImpl_test : public beast::unit_test::Suite } } - 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(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(HostFunctionError::LedgerObjNotFound)); - } - } - } - void testGetLedgerObjField() { @@ -2314,55 +1833,6 @@ struct HostFuncImpl_test : public beast::unit_test::Suite 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() { @@ -2556,494 +2026,6 @@ struct HostFuncImpl_test : public beast::unit_test::Suite } } - 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) to a keylet - auto compareKeylet = [](std::vector 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(HostFunctionError::InvalidAccount)); - } - } - void testGetNFT() { diff --git a/src/tests/libxrpl/helpers/TestServiceRegistry.h b/src/tests/libxrpl/helpers/TestServiceRegistry.h index e763c8bde4..66bf520b73 100644 --- a/src/tests/libxrpl/helpers/TestServiceRegistry.h +++ b/src/tests/libxrpl/helpers/TestServiceRegistry.h @@ -1,13 +1,22 @@ #pragma once #include +#include #include #include #include #include #include #include +#include +#include #include +#include +#include +#include +#include +#include +#include #include #include @@ -15,11 +24,15 @@ #include #include +#include #include +#include #include #include +#include #include #include +#include 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 + 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 const&, majorityAmendments_t const&) override + { + throw std::logic_error("TestAmendmentTable::doValidatedLedger not implemented"); + } + void + trustChanged(hash_set const&) override + { + throw std::logic_error("TestAmendmentTable::trustChanged not implemented"); + } + std::map + doVoting( + Rules const&, + NetClock::time_point, + std::set const&, + majorityAmendments_t const&, + std::vector> const&) override + { + throw std::logic_error("TestAmendmentTable::doVoting not implemented"); + } + [[nodiscard]] std::vector + doValidation(std::set const&) const override + { + throw std::logic_error("TestAmendmentTable::doValidation not implemented"); + } + [[nodiscard]] std::vector + 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 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& diff --git a/src/tests/libxrpl/tx/wasm/RealHostFixture.h b/src/tests/libxrpl/tx/wasm/RealHostFixture.h new file mode 100644 index 0000000000..5259c540e6 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/RealHostFixture.h @@ -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 +#include // TapNone +#include +#include +#include // keylet::account +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace xrpl::test { + +static Bytes +toBytes(std::uint8_t value) +{ + return {value}; +} + +static Bytes +toBytes(std::uint16_t value) +{ + auto const* b = reinterpret_cast(&value); + auto const* e = reinterpret_cast(&value + 1); + return Bytes{b, e}; +} + +static Bytes +toBytes(std::uint32_t value) +{ + auto const* b = reinterpret_cast(&value); + auto const* e = reinterpret_cast(&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()) + return toBytes(asset.get()); + + auto const& mptIssue = asset.get(); + 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(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 finishTx_; + std::optional context_; + std::optional host_; +}; + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/tx/wasm/host_functions/AccountKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/AccountKeylet.cpp new file mode 100644 index 0000000000..ec9a95eda0 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/AccountKeylet.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/AmmKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/AmmKeylet.cpp new file mode 100644 index 0000000000..e5e9652ee7 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/AmmKeylet.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/BaseFee.cpp b/src/tests/libxrpl/tx/wasm/host_functions/BaseFee.cpp new file mode 100644 index 0000000000..dbe9c75143 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/BaseFee.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/CacheLedgerObj.cpp b/src/tests/libxrpl/tx/wasm/host_functions/CacheLedgerObj.cpp new file mode 100644 index 0000000000..d5ba55cbb0 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/CacheLedgerObj.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/CheckKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/CheckKeylet.cpp new file mode 100644 index 0000000000..85373e4bc7 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/CheckKeylet.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/CredentialKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/CredentialKeylet.cpp new file mode 100644 index 0000000000..74455297d4 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/CredentialKeylet.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/CurrentLedgerObjField.cpp b/src/tests/libxrpl/tx/wasm/host_functions/CurrentLedgerObjField.cpp new file mode 100644 index 0000000000..feef6c6dcb --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/CurrentLedgerObjField.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/DelegateKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/DelegateKeylet.cpp new file mode 100644 index 0000000000..3db51f1a67 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/DelegateKeylet.cpp @@ -0,0 +1,57 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/DepositPreauthKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/DepositPreauthKeylet.cpp new file mode 100644 index 0000000000..f59ff2f873 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/DepositPreauthKeylet.cpp @@ -0,0 +1,57 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/DidKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/DidKeylet.cpp new file mode 100644 index 0000000000..fd7dec9a37 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/DidKeylet.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/EscrowKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/EscrowKeylet.cpp new file mode 100644 index 0000000000..82a3ebc503 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/EscrowKeylet.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/IsAmendmentEnabled.cpp b/src/tests/libxrpl/tx/wasm/host_functions/IsAmendmentEnabled.cpp new file mode 100644 index 0000000000..64e6c86454 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/IsAmendmentEnabled.cpp @@ -0,0 +1,50 @@ +#include +#include + +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/LedgerSqn.cpp b/src/tests/libxrpl/tx/wasm/host_functions/LedgerSqn.cpp new file mode 100644 index 0000000000..a17c00b93c --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/LedgerSqn.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/MptokenIssuanceKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/MptokenIssuanceKeylet.cpp new file mode 100644 index 0000000000..7618cfd2ac --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/MptokenIssuanceKeylet.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/MptokenKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/MptokenKeylet.cpp new file mode 100644 index 0000000000..9ebb9695c2 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/MptokenKeylet.cpp @@ -0,0 +1,54 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/NFT.cpp b/src/tests/libxrpl/tx/wasm/host_functions/NFT.cpp new file mode 100644 index 0000000000..85b265a056 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/NFT.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/NftokenOfferKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/NftokenOfferKeylet.cpp new file mode 100644 index 0000000000..928d84de12 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/NftokenOfferKeylet.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/OfferKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/OfferKeylet.cpp new file mode 100644 index 0000000000..203fe4e183 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/OfferKeylet.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/OracleKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/OracleKeylet.cpp new file mode 100644 index 0000000000..b6bdc89d59 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/OracleKeylet.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/ParentLedgerHash.cpp b/src/tests/libxrpl/tx/wasm/host_functions/ParentLedgerHash.cpp new file mode 100644 index 0000000000..3d0114804f --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/ParentLedgerHash.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/ParentLedgerTime.cpp b/src/tests/libxrpl/tx/wasm/host_functions/ParentLedgerTime.cpp new file mode 100644 index 0000000000..aab394e1ac --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/ParentLedgerTime.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/PaychannelKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/PaychannelKeylet.cpp new file mode 100644 index 0000000000..20d17d3dc6 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/PaychannelKeylet.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/PermissionedDomainedKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/PermissionedDomainedKeylet.cpp new file mode 100644 index 0000000000..7a345768a4 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/PermissionedDomainedKeylet.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/SignerListKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/SignerListKeylet.cpp new file mode 100644 index 0000000000..a85eb614d0 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/SignerListKeylet.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/TicketKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/TicketKeylet.cpp new file mode 100644 index 0000000000..391c0ca8dc --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/TicketKeylet.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/TrustLineKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/TrustLineKeylet.cpp new file mode 100644 index 0000000000..71a8cd1f37 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/TrustLineKeylet.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/TxField.cpp b/src/tests/libxrpl/tx/wasm/host_functions/TxField.cpp new file mode 100644 index 0000000000..485abd6d51 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/TxField.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/UpdateData.cpp b/src/tests/libxrpl/tx/wasm/host_functions/UpdateData.cpp new file mode 100644 index 0000000000..6d2cb9bd18 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/UpdateData.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include +#include + +#include + +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 diff --git a/src/tests/libxrpl/tx/wasm/host_functions/VaultKeylet.cpp b/src/tests/libxrpl/tx/wasm/host_functions/VaultKeylet.cpp new file mode 100644 index 0000000000..131ef99079 --- /dev/null +++ b/src/tests/libxrpl/tx/wasm/host_functions/VaultKeylet.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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