diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index f300c253ca6..06a75e902dd 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -463,34 +463,10 @@ getAsfFlagMap() #pragma pop_macro("ACCOUNTSET_FLAG_TO_MAP") #pragma pop_macro("ACCOUNTSET_FLAGS") -#pragma push_macro("SPONSOR_FLAGS") -#pragma push_macro("SPONSOR_FLAG_TO_VALUE") -#pragma push_macro("SPONSOR_FLAG_TO_MAP") +// Sponsor flags (spf) -// Sponsor Flag values -#define SPONSOR_FLAGS(SPF_FLAG) \ - SPF_FLAG(spfSponsorFee, 1) \ - SPF_FLAG(spfSponsorReserve, 2) - -#define SPONSOR_FLAG_TO_VALUE(name, value) inline constexpr FlagValue name = value; -#define SPONSOR_FLAG_TO_MAP(name, value) {#name, value}, - -SPONSOR_FLAGS(SPONSOR_FLAG_TO_VALUE) - -inline std::map const& -getspfFlagMap() -{ - static std::map const flags = {SPONSOR_FLAGS(SPONSOR_FLAG_TO_MAP)}; - return flags; -} - -#undef SPONSOR_FLAG_TO_VALUE -#undef SPONSOR_FLAG_TO_MAP -#undef SPONSOR_FLAGS - -#pragma pop_macro("SPONSOR_FLAG_TO_VALUE") -#pragma pop_macro("SPONSOR_FLAG_TO_MAP") -#pragma pop_macro("SPONSOR_FLAGS") +inline constexpr FlagValue spfSponsorFee = 1; +inline constexpr FlagValue spfSponsorReserve = 2; } // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index c77f49c8649..79cf6eb0e4a 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -753,7 +753,7 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) { Throw( "NFTokenPage directory for " + to_string(owner) + - " cannot be repaired. std::unexpected link problem."); + " cannot be repaired. Unexpected link problem."); } newPrev->at(sfNextPageMin) = nextPage->key(); view.update(newPrev); diff --git a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp index 41d55e90195..55d87d1ea13 100644 --- a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp +++ b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp @@ -111,17 +111,17 @@ SponsorshipOwnerCountsMatch::finalize( return false; } - if (deltaSponsoredObjectOwnerCount_ != deltaSponsoredOwnerCount_) + if (invalidOwnerCountLessThanSponsoredOwnerCount_ > 0) { - JLOG(j.fatal()) << "Invariant failed: SponsoredObjectOwnerCount does not " - "equal SponsoredOwnerCount delta."; + JLOG(j.fatal()) + << "Invariant failed: OwnerCount must be greater than or equal to SponsoredOwnerCount."; return false; } - if (invalidOwnerCountLessThanSponsoredOwnerCount_ > 0) + if (deltaSponsoredObjectOwnerCount_ != deltaSponsoredOwnerCount_) { - JLOG(j.fatal()) - << "Invariant failed: OwnerCount must be greater than or equal to SponsoredOwnerCount."; + JLOG(j.fatal()) << "Invariant failed: SponsoredObjectOwnerCount does not " + "equal SponsoredOwnerCount delta."; return false; } diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index eef6af1fea2..c02075ad37d 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -5077,6 +5077,7 @@ class Invariants_test : public beast::unit_test::Suite testInvariantOverwrite(defaultAmendments() - fixCleanup3_1_3); testVaultComputeCoarsestScale(); testAMM(); + testSponsorship(); } }; diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index dd9eb1c119a..2de5fca57e5 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,8 @@ std::vector> gMappings{ {jss::oracle_document_id, FieldType::UInt32Field}, {jss::owner, FieldType::AccountField}, {jss::seq, FieldType::UInt32Field}, + {jss::sponsor, FieldType::AccountField}, + {jss::sponsee, FieldType::AccountField}, {jss::subject, FieldType::AccountField}, {jss::ticket_seq, FieldType::UInt32Field}, }; @@ -107,7 +110,7 @@ getFieldType(json::StaticString fieldName) return it->second; } - Throw("`mappings` is missing field " + std::string(fieldName.cStr())); + Throw("`gMappings` is missing field " + std::string(fieldName.cStr())); } std::string @@ -1886,6 +1889,58 @@ class LedgerEntry_test : public beast::unit_test::Suite runLedgerEntryTest(env, jss::signer_list); } + void + testSponsorship() + { + testcase("Sponsorship"); + + using namespace test::jtx; + + Env env{*this}; + Account const alice{"alice"}; + Account const bob{"bob"}; + env.fund(XRP(10000), alice, bob); + env.close(); + env(sponsor::set(alice, 0), sponsor::SponseeAcc(bob)); + env.close(); + std::string const ledgerHash{to_string(env.closed()->header().hash)}; + std::string sponsorshipIndex; + { + // Request by sponsor and sponsee. + json::Value jvParams; + jvParams[jss::sponsorship][jss::sponsor] = alice.human(); + jvParams[jss::sponsorship][jss::sponsee] = bob.human(); + jvParams[jss::ledger_hash] = ledgerHash; + json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Sponsorship); + BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == alice.human()); + BEAST_EXPECT(jrr[jss::node][sfSponsee.jsonName] == bob.human()); + sponsorshipIndex = jrr[jss::node][jss::index].asString(); + } + { + // Request by index. + json::Value jvParams; + jvParams[jss::sponsorship] = sponsorshipIndex; + jvParams[jss::ledger_hash] = ledgerHash; + json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Sponsorship); + BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == alice.human()); + BEAST_EXPECT(jrr[jss::node][sfSponsee.jsonName] == bob.human()); + } + { + // Check all malformed cases. + runLedgerEntryTest( + env, + jss::sponsorship, + { + {.fieldName = jss::sponsor, .malformedErrorMsg = "malformedSponsor"}, + {.fieldName = jss::sponsee, .malformedErrorMsg = "malformedSponsee"}, + }); + } + } + void testTicket() { @@ -2678,6 +2733,7 @@ class LedgerEntry_test : public beast::unit_test::Suite testPayChan(); testRippleState(); testSignerList(); + testSponsorship(); testTicket(); testDID(); testInvalidOracleLedgerEntry(); diff --git a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp index 7cccac8c3ed..e6e3e3c2914 100644 --- a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp @@ -720,7 +720,7 @@ parseSignerList( } static std::expected -parseTicket( +parseSponsorship( json::Value const& params, json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion) @@ -730,20 +730,21 @@ parseTicket( return parseObjectID(params, fieldName); } - auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress"); - if (!id) - return std::unexpected(id.error()); + auto const sponsorAccountID = + LedgerEntryHelpers::requiredAccountID(params, jss::sponsor, "malformedSponsor"); + if (!sponsorAccountID) + return std::unexpected(sponsorAccountID.error()); - auto const seq = - LedgerEntryHelpers::requiredUInt32(params, jss::ticket_seq, "malformedRequest"); - if (!seq) - return std::unexpected(seq.error()); + auto const sponseeAccountID = + LedgerEntryHelpers::requiredAccountID(params, jss::sponsee, "malformedSponsee"); + if (!sponseeAccountID) + return std::unexpected(sponseeAccountID.error()); - return getTicketIndex(*id, *seq); + return keylet::sponsor(*sponsorAccountID, *sponseeAccountID).key; } static std::expected -parseVault( +parseTicket( json::Value const& params, json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion) @@ -753,19 +754,20 @@ parseVault( return parseObjectID(params, fieldName); } - auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner"); + auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress"); if (!id) return std::unexpected(id.error()); - auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest"); + auto const seq = + LedgerEntryHelpers::requiredUInt32(params, jss::ticket_seq, "malformedRequest"); if (!seq) return std::unexpected(seq.error()); - return keylet::vault(*id, *seq).key; + return getTicketIndex(*id, *seq); } static std::expected -parseSponsorship( +parseVault( json::Value const& params, json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion) @@ -775,17 +777,15 @@ parseSponsorship( return parseObjectID(params, fieldName); } - auto const sponsorAccountID = - LedgerEntryHelpers::requiredAccountID(params, jss::sponsor, "malformedSponsor"); - if (!sponsorAccountID) - return std::unexpected(sponsorAccountID.error()); + auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner"); + if (!id) + return std::unexpected(id.error()); - auto const sponseeAccountID = - LedgerEntryHelpers::requiredAccountID(params, jss::sponsee, "malformedSponsee"); - if (!sponseeAccountID) - return std::unexpected(sponseeAccountID.error()); + auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest"); + if (!seq) + return std::unexpected(seq.error()); - return keylet::sponsor(*sponsorAccountID, *sponseeAccountID).key; + return keylet::vault(*id, *seq).key; } static std::expected