From cad305ac7ec0cf34bc070404d7d11bf4fa1ff856 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 13 Mar 2024 11:02:49 -0400 Subject: [PATCH 001/249] PoC for sponsored fees --- include/xrpl/protocol/STObject.h | 4 +- include/xrpl/protocol/STTx.h | 3 ++ include/xrpl/protocol/TxFlags.h | 4 ++ include/xrpl/protocol/detail/features.macro | 1 + include/xrpl/protocol/detail/sfields.macro | 1 + src/libxrpl/protocol/InnerObjectFormats.cpp | 9 ++++ src/libxrpl/protocol/STObject.cpp | 7 +++ src/libxrpl/protocol/STTx.cpp | 19 ++++++++ src/libxrpl/protocol/TxFormats.cpp | 1 + src/xrpld/app/tx/detail/Transactor.cpp | 52 +++++++++++++++++++-- 10 files changed, 95 insertions(+), 6 deletions(-) diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index 6cd083ef857..fbad0facca0 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -244,7 +244,9 @@ class STObject : public STBase, public CountedObject getFieldV256(SField const& field) const; STArray const& getFieldArray(SField const& field) const; - STCurrency const& + const STObject& + getFieldObject(SField const& field) const; + const STCurrency& getFieldCurrency(SField const& field) const; STNumber const& getFieldNumber(SField const& field) const; diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index f0d21572831..b6a0400358b 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -106,6 +106,9 @@ class STTx final : public STObject, public CountedObject std::uint32_t getSeqValue() const; + AccountID + getFeePayer() const; + boost::container::flat_set getMentionedAccounts() const; diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 2831933afb4..67d909c37c2 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -62,6 +62,10 @@ constexpr std::uint32_t tfInnerBatchTxn = 0x40000000; constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; constexpr std::uint32_t tfUniversalMask = ~tfUniversal; +// Sponsor flags: +constexpr std::uint32_t tfSponsorFee = 0x00000001; +constexpr std::uint32_t tfSponsorReserve = 0x00000002; + // AccountSet flags: constexpr std::uint32_t tfRequireDestTag = 0x00010000; constexpr std::uint32_t tfOptionalDestTag = 0x00020000; diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 1be0af5d013..550bd50126f 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -32,6 +32,7 @@ // If you add an amendment here, then do not forget to increment `numFeatures` // in include/xrpl/protocol/Feature.h. +XRPL_FEATURE(Sponsor, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo) diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 537fcae479b..303f7c33487 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -362,6 +362,7 @@ UNTYPED_SFIELD(sfCredential, OBJECT, 33) UNTYPED_SFIELD(sfRawTransaction, OBJECT, 34) UNTYPED_SFIELD(sfBatchSigner, OBJECT, 35) UNTYPED_SFIELD(sfBook, OBJECT, 36) +UNTYPED_SFIELD(sfSponsor, OBJECT, 37) // array of objects (common) // ARRAY/1 is reserved for end of array diff --git a/src/libxrpl/protocol/InnerObjectFormats.cpp b/src/libxrpl/protocol/InnerObjectFormats.cpp index 2de5e6624ef..c89a90025f1 100644 --- a/src/libxrpl/protocol/InnerObjectFormats.cpp +++ b/src/libxrpl/protocol/InnerObjectFormats.cpp @@ -172,6 +172,15 @@ InnerObjectFormats::InnerObjectFormats() {sfBookDirectory, soeREQUIRED}, {sfBookNode, soeREQUIRED}, }); + + add(sfSponsor.jsonName.c_str(), + sfSponsor.getCode(), + { + {sfAccount, soeREQUIRED}, + {sfFlags, soeREQUIRED}, + {sfSignature, soeOPTIONAL}, + {sfSigners, soeOPTIONAL}, + }); } InnerObjectFormats const& diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 9c23898a743..d2c76e4b0fd 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -689,6 +689,13 @@ STObject::getFieldArray(SField const& field) const return getFieldByConstRef(field, empty); } +const STObject& +STObject::getFieldObject(SField const& field) const +{ + static STObject const empty(field); + return getFieldByConstRef(field, empty); +} + STCurrency const& STObject::getFieldCurrency(SField const& field) const { diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index ee26dd69de9..c1d0f5a8777 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -233,6 +233,25 @@ STTx::getSeqValue() const return getSeqProxy().value(); } +AccountID +STTx::getFeePayer() const +{ + if (isFieldPresent(sfSponsor)) + { + if (getFieldObject(sfSponsor)[sfFlags] & tfSponsorFee) + { + return getFieldObject(sfSponsor)[sfAccount]; + } + } + + if (isFieldPresent(sfDelegate)) + { + return getAccountID(sfDelegate); + } + + return getAccountID(sfAccount); +} + void STTx::sign(PublicKey const& publicKey, SecretKey const& secretKey) { diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 5edffeb6660..a34136b6c23 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -47,6 +47,7 @@ TxFormats::TxFormats() {sfSigners, soeOPTIONAL}, // submit_multisigned {sfNetworkID, soeOPTIONAL}, {sfDelegate, soeOPTIONAL}, + {sfSponsor, soeOPTIONAL}, }; #pragma push_macro("UNWRAP") diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 0db04848426..62784784084 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,11 @@ preflight1(PreflightContext const& ctx) return temBAD_SIGNER; } + if (ctx.tx.isFieldPresent(sfSponsor) && !ctx.rules.enabled(featureSponsor)) + { + return temDISABLED; + } + auto const ret = preflight0(ctx); if (!isTesSuccess(ret)) return ret; @@ -152,6 +158,29 @@ preflight1(PreflightContext const& ctx) !ctx.rules.enabled(featureBatch), "Inner batch transaction must have a parent batch ID."); + // Sponsor checks + if (ctx.tx.isFieldPresent(sfSponsor)) + { + auto const sponsor = ctx.tx.getFieldObject(sfSponsor); + if (sponsor[sfAccount] == ctx.tx[sfAccount]) + { + JLOG(ctx.j.debug()) << "preflight1: invalid sponsor account"; + return temMALFORMED; + } + if (!(sponsor[sfFlags] & tfSponsorFee) && + !(sponsor[sfFlags] & tfSponsorReserve)) + { + JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; + return temMALFORMED; + } + if (!sponsor.isFieldPresent(sfSignature) && + !sponsor.isFieldPresent(sfSigners)) + { + JLOG(ctx.j.debug()) << "preflight1: no sfSignature or sfSigners"; + return temMALFORMED; + } + } + return tesSUCCESS; } @@ -292,9 +321,8 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (feePaid == beast::zero) return tesSUCCESS; - auto const id = ctx.tx.isFieldPresent(sfDelegate) - ? ctx.tx.getAccountID(sfDelegate) - : ctx.tx.getAccountID(sfAccount); + auto const id = ctx.tx.getFeePayer(); + JLOG(ctx.j.trace()) << "Fee payer: " + to_string(id); auto const sle = ctx.view.read(keylet::account(id)); if (!sle) return terNO_ACCOUNT; @@ -338,13 +366,22 @@ Transactor::payFee() } else { - auto const sle = view().peek(keylet::account(account_)); + auto const id = ctx_.tx.getFeePayer(); + auto const sle = view().peek(keylet::account(id)); + JLOG(j_.trace()) << "Fee payer: " + to_string(id); if (!sle) return tefINTERNAL; // LCOV_EXCL_LINE + if (id != account_) // sponsor + { + sle->setFieldAmount( + sfBalance, sle->getFieldAmount(sfBalance) - feePaid); + view().update(sle); + return tesSUCCESS; + } + // Deduct the fee, so it's not available during the transaction. // Will only write the account back if the transaction succeeds. - mSourceBalance -= feePaid; sle->setFieldAmount(sfBalance, mSourceBalance); @@ -608,6 +645,11 @@ Transactor::checkSign(PreclaimContext const& ctx) STArray const& txSigners(ctx.tx.getFieldArray(sfSigners)); return checkMultiSign(ctx.view, idAccount, txSigners, ctx.flags, ctx.j); } + + // if (ctx.tx.isFieldPresent(sfSponsor)) + // { + // // TODO: check the sponsor signature + // } // Check Single Sign XRPL_ASSERT( From 43c16f35f7137c060ec1686c6b55661dc75500ca Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 18 Mar 2024 15:50:04 -0400 Subject: [PATCH 002/249] PoC for sponsored reserve --- .../xrpl/protocol/detail/ledger_entries.macro | 2 ++ include/xrpl/protocol/detail/sfields.macro | 3 +++ src/libxrpl/protocol/LedgerFormats.cpp | 1 + src/xrpld/app/tx/detail/CreateTicket.cpp | 22 +++++++++++++++++++ src/xrpld/ledger/View.h | 11 ++++++++++ 5 files changed, 39 insertions(+) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 46c6e60db35..33db3e76918 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -167,6 +167,8 @@ LEDGER_ENTRY(ltACCOUNT_ROOT, 0x0061, AccountRoot, account, ({ {sfFirstNFTokenSequence, soeOPTIONAL}, {sfAMMID, soeOPTIONAL}, // pseudo-account designator {sfVaultID, soeOPTIONAL}, // pseudo-account designator + {sfSponsoredOwnerCount, soeOPTIONAL}, + {sfSponsoringOwnerCount, soeOPTIONAL}, })) /** A ledger object which contains a list of object identifiers. diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 303f7c33487..1d1637e2f88 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -114,6 +114,8 @@ TYPED_SFIELD(sfVoteWeight, UINT32, 48) TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50) TYPED_SFIELD(sfOracleDocumentID, UINT32, 51) TYPED_SFIELD(sfPermissionValue, UINT32, 52) +TYPED_SFIELD(sfSponsoredOwnerCount, UINT32, 53) +TYPED_SFIELD(sfSponsoringOwnerCount, UINT32, 54) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) @@ -289,6 +291,7 @@ TYPED_SFIELD(sfNFTokenMinter, ACCOUNT, 9) TYPED_SFIELD(sfEmitCallback, ACCOUNT, 10) TYPED_SFIELD(sfHolder, ACCOUNT, 11) TYPED_SFIELD(sfDelegate, ACCOUNT, 12) +TYPED_SFIELD(sfSponsorAccount, ACCOUNT, 13) // account (uncommon) TYPED_SFIELD(sfHookAccount, ACCOUNT, 16) diff --git a/src/libxrpl/protocol/LedgerFormats.cpp b/src/libxrpl/protocol/LedgerFormats.cpp index 94c6d65c880..bd69d3b95c5 100644 --- a/src/libxrpl/protocol/LedgerFormats.cpp +++ b/src/libxrpl/protocol/LedgerFormats.cpp @@ -33,6 +33,7 @@ LedgerFormats::LedgerFormats() {sfLedgerIndex, soeOPTIONAL}, {sfLedgerEntryType, soeREQUIRED}, {sfFlags, soeREQUIRED}, + {sfSponsorAccount, soeOPTIONAL}, }; #pragma push_macro("UNWRAP") diff --git a/src/xrpld/app/tx/detail/CreateTicket.cpp b/src/xrpld/app/tx/detail/CreateTicket.cpp index 594335f4897..1d718777e79 100644 --- a/src/xrpld/app/tx/detail/CreateTicket.cpp +++ b/src/xrpld/app/tx/detail/CreateTicket.cpp @@ -121,6 +121,13 @@ CreateTicket::doApply() sleTicket->setAccountID(sfAccount, account_); sleTicket->setFieldU32(sfTicketSequence, curTicketSeq); + + if (ctx_.tx.isFieldPresent(sfSponsor) && + (ctx_.tx.getFieldObject(sfSponsor)[sfFlags] & tfSponsorReserve)) + { + sleTicket->setAccountID( + sfSponsorAccount, ctx_.tx.getFieldObject(sfSponsor)[sfAccount]); + } view().insert(sleTicket); auto const page = view().dirInsert( @@ -146,6 +153,21 @@ CreateTicket::doApply() // Every added Ticket counts against the creator's reserve. adjustOwnerCount(view(), sleAccountRoot, ticketCount, viewJ); + // Check reserve sponsorship + if (ctx_.tx.isFieldPresent(sfSponsor) && + (ctx_.tx.getFieldObject(sfSponsor)[sfFlags] & tfSponsorReserve)) + { + AccountID const& sponsor = ctx_.tx.getFieldObject(sfSponsor)[sfAccount]; + auto const& sponsorSle = view().peek(keylet::account(sponsor)); + sponsorSle->setFieldU32( + sfSponsoringOwnerCount, + sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ticketCount); + sleAccountRoot->setFieldU32( + sfSponsoredOwnerCount, + sleAccountRoot->getFieldU32(sfSponsoredOwnerCount) + ticketCount); + view().update(sponsorSle); + } + // TicketCreate is the only transaction that can cause an account root's // Sequence field to increase by more than one. October 2018. sleAccountRoot->setFieldU32(sfSequence, firstTicketSeq + ticketCount); diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index 8c391499b6a..5d671dc9b53 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -443,6 +443,17 @@ adjustOwnerCount( std::int32_t amount, beast::Journal j); +inline void +adjustOwnerCount( + ApplyView& view, + AccountID const& account, + std::int32_t amount, + beast::Journal j) +{ + return adjustOwnerCount( + view, view.peek(keylet::account(account)), amount, j); +} + /** @{ */ /** Returns the first entry in the directory, advancing the index From d40f8cc23286e42e116311188eff69e276639408 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 30 Jul 2025 23:39:14 +0900 Subject: [PATCH 003/249] SponsorReserve,SponsorAccount - not fully tested - need to modify: Reserve check where using sfOwnerCount --- .../xrpl/protocol/detail/ledger_entries.macro | 1 + include/xrpl/protocol/detail/sfields.macro | 1 + src/libxrpl/protocol/InnerObjectFormats.cpp | 3 +- src/test/app/Sponsor_test.cpp | 423 ++++++++++++++++++ src/test/jtx/impl/owners.cpp | 21 + src/test/jtx/impl/sponsor.cpp | 123 +++++ src/test/jtx/owners.h | 51 +++ src/test/jtx/sponsor.h | 87 ++++ src/xrpld/app/misc/CredentialHelpers.cpp | 6 +- src/xrpld/app/tx/detail/CancelCheck.cpp | 3 +- src/xrpld/app/tx/detail/CashCheck.cpp | 3 +- src/xrpld/app/tx/detail/Change.cpp | 12 +- src/xrpld/app/tx/detail/CreateCheck.cpp | 4 +- src/xrpld/app/tx/detail/CreateOffer.cpp | 4 +- src/xrpld/app/tx/detail/CreateTicket.cpp | 25 +- src/xrpld/app/tx/detail/Credentials.cpp | 11 +- src/xrpld/app/tx/detail/DID.cpp | 7 +- src/xrpld/app/tx/detail/DelegateSet.cpp | 9 +- src/xrpld/app/tx/detail/DeleteOracle.cpp | 3 +- src/xrpld/app/tx/detail/DepositPreauth.cpp | 11 +- src/xrpld/app/tx/detail/Escrow.cpp | 19 +- src/xrpld/app/tx/detail/MPTokenAuthorize.cpp | 9 +- src/xrpld/app/tx/detail/MPTokenAuthorize.h | 1 + .../app/tx/detail/MPTokenIssuanceCreate.cpp | 8 +- .../app/tx/detail/MPTokenIssuanceCreate.h | 6 +- .../app/tx/detail/MPTokenIssuanceDestroy.cpp | 4 +- .../app/tx/detail/NFTokenCreateOffer.cpp | 1 + src/xrpld/app/tx/detail/NFTokenMint.cpp | 1 + src/xrpld/app/tx/detail/NFTokenUtils.cpp | 16 +- src/xrpld/app/tx/detail/NFTokenUtils.h | 1 + src/xrpld/app/tx/detail/PayChan.cpp | 8 +- src/xrpld/app/tx/detail/Payment.cpp | 8 + .../tx/detail/PermissionedDomainDelete.cpp | 3 +- .../app/tx/detail/PermissionedDomainSet.cpp | 4 +- src/xrpld/app/tx/detail/SetOracle.cpp | 3 +- src/xrpld/app/tx/detail/SetSignerList.cpp | 6 +- src/xrpld/app/tx/detail/SetTrust.cpp | 8 +- src/xrpld/app/tx/detail/Transactor.cpp | 13 +- src/xrpld/app/tx/detail/VaultCreate.cpp | 5 +- src/xrpld/app/tx/detail/VaultDelete.cpp | 9 +- src/xrpld/app/tx/detail/VaultDeposit.cpp | 9 +- src/xrpld/app/tx/detail/XChainBridge.cpp | 18 +- src/xrpld/ledger/View.h | 29 +- src/xrpld/ledger/detail/View.cpp | 101 ++++- 44 files changed, 1005 insertions(+), 93 deletions(-) create mode 100644 src/test/app/Sponsor_test.cpp create mode 100644 src/test/jtx/impl/sponsor.cpp create mode 100644 src/test/jtx/sponsor.h diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index e2c846d5aef..2aa49be96e4 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -169,6 +169,7 @@ LEDGER_ENTRY(ltACCOUNT_ROOT, 0x0061, AccountRoot, account, ({ {sfVaultID, soeOPTIONAL}, // pseudo-account designator {sfSponsoredOwnerCount, soeOPTIONAL}, {sfSponsoringOwnerCount, soeOPTIONAL}, + {sfSponsoringAccountCount,soeOPTIONAL}, })) /** A ledger object which contains a list of object identifiers. diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 1d1637e2f88..2bfe555043a 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -116,6 +116,7 @@ TYPED_SFIELD(sfOracleDocumentID, UINT32, 51) TYPED_SFIELD(sfPermissionValue, UINT32, 52) TYPED_SFIELD(sfSponsoredOwnerCount, UINT32, 53) TYPED_SFIELD(sfSponsoringOwnerCount, UINT32, 54) +TYPED_SFIELD(sfSponsoringAccountCount, UINT32, 55) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) diff --git a/src/libxrpl/protocol/InnerObjectFormats.cpp b/src/libxrpl/protocol/InnerObjectFormats.cpp index c89a90025f1..4b82b1acfdf 100644 --- a/src/libxrpl/protocol/InnerObjectFormats.cpp +++ b/src/libxrpl/protocol/InnerObjectFormats.cpp @@ -178,7 +178,8 @@ InnerObjectFormats::InnerObjectFormats() { {sfAccount, soeREQUIRED}, {sfFlags, soeREQUIRED}, - {sfSignature, soeOPTIONAL}, + {sfSigningPubKey, soeOPTIONAL}, + {sfTxnSignature, soeOPTIONAL}, {sfSigners, soeOPTIONAL}, }); } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp new file mode 100644 index 00000000000..e7d815dfdd4 --- /dev/null +++ b/src/test/app/Sponsor_test.cpp @@ -0,0 +1,423 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include + +#include + +namespace ripple { + +class Sponsor_test : public beast::unit_test::suite +{ +public: + void + testSponsorFee() + { + using namespace test::jtx; + + testcase("Sponsor Fee"); + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + + env(noop(alice), + fee(XRP(1)), + sponsor::as(sponsor, tfSponsorFee), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == XRP(10000)); + BEAST_EXPECT(env.balance(sponsor) == XRP(9999)); + } + + void + testSponsorAccount() + { + testcase("Sponsor Account"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + + Account const bob("bob"); + env(pay(alice, bob, STAmount(env.current()->fees().accountReserve(0))), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env.require(sponsored_owners(alice, 0)); + env.require(sponsoring_account_count(sponsor, 1)); + } + + void + testCheck() + { + testcase("Check"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + env.fund(XRP(10000), alice, bob, sponsor); + + // CheckCreate + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 1)); + env.require(sponsoring_count(sponsor, 1)); + + // CheckCancel + auto const checkId = keylet::check(alice, seq).key; + env(check::cancel(alice, checkId)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 0)); + env.require(sponsoring_count(sponsor, 0)); + + auto const seq2 = env.seq(alice); + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + // CheckCash + auto const checkId2 = keylet::check(alice, seq2).key; + env(check::cash(bob, checkId2, XRP(1))); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 0)); + env.require(sponsoring_count(sponsor, 0)); + + // printf( + // "meta: %s\n", + // env.meta()->getJson(JsonOptions::none).toStyledString().c_str()); + } + + void + testOfffer() + { + testcase("Offer"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const gw("gw"); + Account const sponsor("sponsor"); + + auto USD = gw["USD"]; + + env.fund(XRP(10000), alice, gw, sponsor); + + // OfferCreate + auto const seq = env.seq(alice); + env(offer(alice, USD(1), XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 1)); + env.require(sponsoring_count(sponsor, 1)); + + // OfferCancel + env(offer_cancel(alice, seq)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 0)); + env.require(sponsoring_count(sponsor, 0)); + + // TODO: test Execution + } + + void + testTicket() + { + testcase("Ticket"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("master"); + + env.fund(XRP(1000000), alice, sponsor); + + // TicketCreate + std::uint32_t const ticketSeq{env.seq(alice) + 1}; + env(ticket::create(alice, 250), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 250)); + env.require(sponsoring_count(sponsor, 250)); + + // use a Ticket + env(noop(alice), ticket::use(ticketSeq + 1)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 249)); + env.require(sponsoring_count(sponsor, 249)); + } + + void + testCredentials() + { + testcase("Credentials"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const issuer("issuer"); + Account const subject("subject"); + Account const sponsor("sponsor"); + + env.fund(XRP(1000000), issuer, subject, sponsor); + + // CredentialsCreate + env(credentials::create(subject, issuer, "credType"), + credentials::uri("uri"), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env.require(owners(issuer, 0)); + env.require(sponsored_owners(issuer, 1)); + env.require(sponsoring_count(sponsor, 1)); + + // CredentialsAccept + env(credentials::accept(subject, issuer, "credType"), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env.require(owners(issuer, 0)); + env.require(owners(subject, 0)); + env.require(sponsored_owners(issuer, 0)); + env.require(sponsored_owners(subject, 1)); + env.require(sponsoring_count(sponsor, 1)); + + // CredentialsDelete + env(credentials::deleteCred(subject, subject, issuer, "credType")); + env.close(); + + env.require(owners(issuer, 0)); + env.require(owners(subject, 0)); + env.require(sponsored_owners(issuer, 0)); + env.require(sponsored_owners(subject, 0)); + env.require(sponsoring_count(sponsor, 0)); + } + + void + testDelegate() + { + testcase("Delegate"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + env.fund(XRP(1000000), alice, bob, sponsor); + + // DelegateSet + env(delegate::set(alice, bob, {"Payment"}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 1)); + env.require(sponsoring_count(sponsor, 1)); + + // delete + env(delegate::set(alice, bob, {})); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 0)); + env.require(sponsoring_count(sponsor, 0)); + } + + void + testDepositPreauth() + { + } + + void + testDID() + { + testcase("DID"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + + env.fund(XRP(1000000), alice, sponsor); + + // DIDSet + env(did::set(alice), + did::uri("uri"), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 1)); + env.require(sponsoring_count(sponsor, 1)); + + // DIDDelete + env(did::del(alice)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 0)); + env.require(sponsoring_count(sponsor, 0)); + } + + void + testEscrow() + { + } + + void + testMPToken() + { + } + + void + testNFToken() + { + } + + void + testNFTokenOffer() + { + } + + void + testPayChan() + { + } + + void + testPermissionedDomain() + { + } + + void + testOracle() + { + } + + void + testSignerList() + { + testcase("SignerList"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + + env.fund(XRP(1000000), alice, sponsor); + + Account const bob("bob"); + + // SignerListSet + env(signers(alice, 1, {{bob, 1}}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 1)); + env.require(sponsoring_count(sponsor, 1)); + + // Delete + env(signers(alice, none)); + env.close(); + + env.require(owners(alice, 0)); + env.require(sponsored_owners(alice, 0)); + env.require(sponsoring_count(sponsor, 0)); + } + + void + testTrust() + { + } + + void + testVault() + { + } + + void + testXChain() + { + } + + void + testSponsorReserve() + { + testCheck(); + testOfffer(); + testTicket(); + testCredentials(); + testDelegate(); + // testDepositPreauth(); + testDID(); + // testEscrow(); + // testMPToken(); + // testNFToken(); + // testNFTokenOffer(); + // testPayChan(); + // testPermissionedDomain(); + // testOracle(); + testSignerList(); + // testTrust(); + // testVault(); + // testXChain(); + } + + void + run() override + { + testSponsorFee(); + testSponsorAccount(); + testSponsorReserve(); + } +}; + +BEAST_DEFINE_TESTSUITE(Sponsor, app, ripple); + +} // namespace ripple diff --git a/src/test/jtx/impl/owners.cpp b/src/test/jtx/impl/owners.cpp index 386ec29a37e..00e5a4039d2 100644 --- a/src/test/jtx/impl/owners.cpp +++ b/src/test/jtx/impl/owners.cpp @@ -55,6 +55,27 @@ owners::operator()(Env& env) const env.test.expect(env.le(account_)->getFieldU32(sfOwnerCount) == value_); } +void +sponsored_owners::operator()(Env& env) const +{ + env.test.expect( + env.le(account_)->getFieldU32(sfSponsoredOwnerCount) == value_); +} + +void +sponsoring_count::operator()(Env& env) const +{ + env.test.expect( + env.le(account_)->getFieldU32(sfSponsoringOwnerCount) == value_); +} + +void +sponsoring_account_count::operator()(Env& env) const +{ + env.test.expect( + env.le(account_)->getFieldU32(sfSponsoringAccountCount) == value_); +} + } // namespace jtx } // namespace test } // namespace ripple diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp new file mode 100644 index 00000000000..2db16a80db5 --- /dev/null +++ b/src/test/jtx/impl/sponsor.cpp @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include + +#include +#include + +namespace ripple { +namespace test { +namespace jtx { + +namespace sponsor { + +Json::Value +transferAccount(jtx::Account const& account) +{ + Json::Value jv; + jv[jss::TransactionType] = "SponsorTransfer"; + jv[sfSponsor.jsonName] = account.human(); + + return jv; +} + +Json::Value +transferObject(uint256 const& id) +{ + Json::Value jv; + jv[jss::TransactionType] = "SponsorTransfer"; + jv[sfLedgerIndex.jsonName] = to_string(id); + return jv; +} + +void +as::operator()(Env& env, JTx& jt) const +{ + jt.jv[sfSponsor.jsonName][sfAccount.jsonName] = sponsor_.human(); + jt.jv[sfSponsor.jsonName][sfFlags.jsonName] = flags; +} + +void +sig::operator()(Env& env, JTx& jt) const +{ + std::optional st; + try + { + // required to cast the STObject to STTx + jt.jv[jss::SigningPubKey] = ""; + st = parse(jt.jv); + } + catch (parse_error const&) + { + env.test.log << pretty(jt.jv) << std::endl; + Rethrow(); + } + + jt.jv[sfSponsor.jsonName][sfAccount.jsonName] = signer.acct.human(); + jt.jv[sfSponsor.jsonName][sfSigningPubKey.jsonName] = + strHex(signer.sig.pk().slice()); + + Serializer ss; + ss.add32(HashPrefix::txSign); + parse(jt.jv).addWithoutSigningFields(ss); + auto const sig = + ripple::sign(signer.acct.pk(), signer.acct.sk(), ss.slice()); + jt.jv[sfSponsor.jsonName][jss::TxnSignature] = + strHex(Slice{sig.data(), sig.size()}); +} + +void +msig::operator()(Env& env, JTx& jt) const +{ + auto const mySigners = signers; + jt.signer = [mySigners, &env](Env&, JTx& jtx) { + jtx[sfSponsor.getJsonName()][sfSigningPubKey.getJsonName()] = ""; + std::optional st; + try + { + st = parse(jtx.jv); + } + catch (parse_error const&) + { + env.test.log << pretty(jtx.jv) << std::endl; + Rethrow(); + } + auto& js = jtx[sfSponsor.getJsonName()][sfSigners.getJsonName()]; + for (std::size_t i = 0; i < mySigners.size(); ++i) + { + auto const& e = mySigners[i]; + auto& jo = js[i][sfSigner.getJsonName()]; + jo[jss::Account] = e.acct.human(); + jo[jss::SigningPubKey] = strHex(e.sig.pk().slice()); + + Serializer ss{buildMultiSigningData(*st, e.acct.id())}; + auto const sig = ripple::sign( + *publicKeyType(e.sig.pk().slice()), e.sig.sk(), ss.slice()); + jo[sfTxnSignature.getJsonName()] = + strHex(Slice{sig.data(), sig.size()}); + } + }; +} + +} // namespace sponsor +} // namespace jtx +} // namespace test +} // namespace ripple diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index fc904f9e870..5878ebcad9e 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -85,6 +85,57 @@ class owners operator()(Env& env) const; }; +/** Match the number of items in the account's owner directory */ +class sponsored_owners +{ +private: + Account account_; + std::uint32_t value_; + +public: + sponsored_owners(Account const& account, std::uint32_t value) + : account_(account), value_(value) + { + } + + void + operator()(Env& env) const; +}; + +/** Match the number of items in the account's owner directory */ +class sponsoring_count +{ +private: + Account account_; + std::uint32_t value_; + +public: + sponsoring_count(Account const& account, std::uint32_t value) + : account_(account), value_(value) + { + } + + void + operator()(Env& env) const; +}; + +/** Match the number of items in the account's owner directory */ +class sponsoring_account_count +{ +private: + Account account_; + std::uint32_t value_; + +public: + sponsoring_account_count(Account const& account, std::uint32_t value) + : account_(account), value_(value) + { + } + + void + operator()(Env& env) const; +}; + /** Match the number of trust lines in the account's owner directory */ using lines = owner_count; diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h new file mode 100644 index 00000000000..ec0c813158f --- /dev/null +++ b/src/test/jtx/sponsor.h @@ -0,0 +1,87 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include +#include + +#include "test/jtx/SignerUtils.h" + +namespace ripple { +namespace test { +namespace jtx { + +namespace sponsor { + +// Json::Value +// transferAccount(jtx::Account const& account); + +// Json::Value +// transferObject(uint256 const& id); + +struct as +{ +private: + jtx::Account sponsor_; + std::uint32_t flags; + +public: + as(jtx::Account const& account, std::uint32_t flags = 0) + : sponsor_(account), flags(flags) + { + } + + void + operator()(jtx::Env&, jtx::JTx& jtx) const; +}; + +struct sig +{ +private: + Reg signer; + +public: + sig(Reg signer_) : signer(std::move(signer_)) + { + } + + void + operator()(jtx::Env&, jtx::JTx& jtx) const; +}; + +struct msig +{ +private: + std::vector signers; + +public: + msig(std::vector signers_) : signers(std::move(signers_)) + { + sortSigners(signers); + } + + void + operator()(jtx::Env&, jtx::JTx& jtx) const; +}; + +} // namespace sponsor +} // namespace jtx +} // namespace test +} // namespace ripple diff --git a/src/xrpld/app/misc/CredentialHelpers.cpp b/src/xrpld/app/misc/CredentialHelpers.cpp index 6d1f9f78c56..caccefade49 100644 --- a/src/xrpld/app/misc/CredentialHelpers.cpp +++ b/src/xrpld/app/misc/CredentialHelpers.cpp @@ -93,7 +93,11 @@ deleteSLE( } // LCOV_EXCL_STOP if (isOwner) - adjustOwnerCount(view, sleAccount, -1, j); + { + auto const sponsor = + getLedgerEntryReserveSponsor(view, sleCredential); + adjustOwnerCount(view, sleAccount, sponsor, -1, j); + } return tesSUCCESS; }; diff --git a/src/xrpld/app/tx/detail/CancelCheck.cpp b/src/xrpld/app/tx/detail/CancelCheck.cpp index cfa3bd10e2d..30e0213d8bc 100644 --- a/src/xrpld/app/tx/detail/CancelCheck.cpp +++ b/src/xrpld/app/tx/detail/CancelCheck.cpp @@ -123,7 +123,8 @@ CancelCheck::doApply() // If we succeeded, update the check owner's reserve. auto const sleSrc = view().peek(keylet::account(srcId)); - adjustOwnerCount(view(), sleSrc, -1, viewJ); + auto const sponsor = getLedgerEntryReserveSponsor(view(), sleCheck); + adjustOwnerCount(view(), sleSrc, sponsor, -1, viewJ); // Remove check from ledger. view().erase(sleCheck); diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 0f1d08689c1..c2c3f372598 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -508,7 +508,8 @@ CashCheck::doApply() } // If we succeeded, update the check owner's reserve. - adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), -1, viewJ); + auto const sponsor = getLedgerEntryReserveSponsor(psb, sleCheck); + adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), sponsor, -1, viewJ); // Remove check from ledger. psb.erase(sleCheck); diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index 1392d84c081..28d2bcb340c 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -213,11 +213,19 @@ Change::activateTrustLinesToSelfFix() if (tl->getFlags() & lsfLowReserve) adjustOwnerCount( - sb, sb.peek(keylet::account(lo.getIssuer())), -1, j_); + sb, + sb.peek(keylet::account(lo.getIssuer())), + std::nullopt, + -1, + j_); if (tl->getFlags() & lsfHighReserve) adjustOwnerCount( - sb, sb.peek(keylet::account(hi.getIssuer())), -1, j_); + sb, + sb.peek(keylet::account(hi.getIssuer())), + std::nullopt, + -1, + j_); sb.erase(tl); diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/xrpld/app/tx/detail/CreateCheck.cpp index 9baceef9449..fbc084fd1c6 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/xrpld/app/tx/detail/CreateCheck.cpp @@ -243,7 +243,9 @@ CreateCheck::doApply() sleCheck->setFieldU64(sfOwnerNode, *page); } // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sle, 1, viewJ); + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + adjustOwnerCount(view(), sle, sponsor, 1, viewJ); + addSponsorToLedgerEntry(sleCheck, sponsor); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/CreateOffer.cpp b/src/xrpld/app/tx/detail/CreateOffer.cpp index 3cfae92cbde..fc1bcda26b7 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.cpp +++ b/src/xrpld/app/tx/detail/CreateOffer.cpp @@ -848,7 +848,8 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) } // Update owner count. - adjustOwnerCount(sb, sleCreator, 1, viewJ); + auto const sponsor = getTxReserveSponsor(sb, ctx_.tx); + adjustOwnerCount(sb, sleCreator, sponsor, 1, viewJ); JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue()) << " : " << to_string(saTakerGets.issue()) @@ -908,6 +909,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) sleOffer->setFlag(lsfSell); if (domainID) sleOffer->setFieldH256(sfDomainID, *domainID); + addSponsorToLedgerEntry(sleOffer, sponsor); // if it's a hybrid offer, set hybrid flag, and create an open dir if (bHybrid) diff --git a/src/xrpld/app/tx/detail/CreateTicket.cpp b/src/xrpld/app/tx/detail/CreateTicket.cpp index 1d718777e79..00c5a6c7b8e 100644 --- a/src/xrpld/app/tx/detail/CreateTicket.cpp +++ b/src/xrpld/app/tx/detail/CreateTicket.cpp @@ -113,6 +113,7 @@ CreateTicket::doApply() txSeq != 0 && txSeq != (firstTicketSeq - 1)) return tefINTERNAL; + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); for (std::uint32_t i = 0; i < ticketCount; ++i) { std::uint32_t const curTicketSeq = firstTicketSeq + i; @@ -122,12 +123,6 @@ CreateTicket::doApply() sleTicket->setAccountID(sfAccount, account_); sleTicket->setFieldU32(sfTicketSequence, curTicketSeq); - if (ctx_.tx.isFieldPresent(sfSponsor) && - (ctx_.tx.getFieldObject(sfSponsor)[sfFlags] & tfSponsorReserve)) - { - sleTicket->setAccountID( - sfSponsorAccount, ctx_.tx.getFieldObject(sfSponsor)[sfAccount]); - } view().insert(sleTicket); auto const page = view().dirInsert( @@ -142,6 +137,7 @@ CreateTicket::doApply() return tecDIR_FULL; sleTicket->setFieldU64(sfOwnerNode, *page); + addSponsorToLedgerEntry(sleTicket, sponsor); } // Update the record of the number of Tickets this account owns. @@ -151,22 +147,7 @@ CreateTicket::doApply() sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount); // Every added Ticket counts against the creator's reserve. - adjustOwnerCount(view(), sleAccountRoot, ticketCount, viewJ); - - // Check reserve sponsorship - if (ctx_.tx.isFieldPresent(sfSponsor) && - (ctx_.tx.getFieldObject(sfSponsor)[sfFlags] & tfSponsorReserve)) - { - AccountID const& sponsor = ctx_.tx.getFieldObject(sfSponsor)[sfAccount]; - auto const& sponsorSle = view().peek(keylet::account(sponsor)); - sponsorSle->setFieldU32( - sfSponsoringOwnerCount, - sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ticketCount); - sleAccountRoot->setFieldU32( - sfSponsoredOwnerCount, - sleAccountRoot->getFieldU32(sfSponsoredOwnerCount) + ticketCount); - view().update(sponsorSle); - } + adjustOwnerCount(view(), sleAccountRoot, sponsor, ticketCount, viewJ); // TicketCreate is the only transaction that can cause an account root's // Sequence field to increase by more than one. October 2018. diff --git a/src/xrpld/app/tx/detail/Credentials.cpp b/src/xrpld/app/tx/detail/Credentials.cpp index 73c397cf371..36e4ebbc730 100644 --- a/src/xrpld/app/tx/detail/Credentials.cpp +++ b/src/xrpld/app/tx/detail/Credentials.cpp @@ -174,7 +174,9 @@ CredentialCreate::doApply() return tecDIR_FULL; sleCred->setFieldU64(sfIssuerNode, *page); - adjustOwnerCount(view(), sleIssuer, 1, j_); + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + adjustOwnerCount(view(), sleIssuer, sponsor, 1, j_); + addSponsorToLedgerEntry(sleCred, sponsor); } if (subject == account_) @@ -392,8 +394,11 @@ CredentialAccept::doApply() sleCred->setFieldU32(sfFlags, lsfAccepted); view().update(sleCred); - adjustOwnerCount(view(), sleIssuer, -1, j_); - adjustOwnerCount(view(), sleSubject, 1, j_); + auto const currentSponsor = getTxReserveSponsor(view(), ctx_.tx); + adjustOwnerCount(view(), sleIssuer, currentSponsor, -1, j_); + auto const newSponsor = getTxReserveSponsor(view(), ctx_.tx); + adjustOwnerCount(view(), sleSubject, newSponsor, 1, j_); + addSponsorToLedgerEntry(sleCred, newSponsor); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/DID.cpp b/src/xrpld/app/tx/detail/DID.cpp index 31ce7c8770b..f8dd9619d8d 100644 --- a/src/xrpld/app/tx/detail/DID.cpp +++ b/src/xrpld/app/tx/detail/DID.cpp @@ -108,7 +108,9 @@ addSLE( return tecDIR_FULL; (*sle)[sfOwnerNode] = *page; } - adjustOwnerCount(ctx.view(), sleAccount, 1, ctx.journal); + auto const sponsor = getTxReserveSponsor(ctx.view(), ctx.tx); + adjustOwnerCount(ctx.view(), sleAccount, sponsor, 1, ctx.journal); + addSponsorToLedgerEntry(sle, sponsor); ctx.view().update(sleAccount); return tesSUCCESS; @@ -215,7 +217,8 @@ DIDDelete::deleteSLE( if (!sleOwner) return tecINTERNAL; - adjustOwnerCount(view, sleOwner, -1, j); + auto const sponsor = getLedgerEntryReserveSponsor(view, sle); + adjustOwnerCount(view, sleOwner, sponsor, -1, j); view.update(sleOwner); // Remove object from ledger diff --git a/src/xrpld/app/tx/detail/DelegateSet.cpp b/src/xrpld/app/tx/detail/DelegateSet.cpp index 34e1c3afd31..a98e3d5951a 100644 --- a/src/xrpld/app/tx/detail/DelegateSet.cpp +++ b/src/xrpld/app/tx/detail/DelegateSet.cpp @@ -123,7 +123,9 @@ DelegateSet::doApply() (*sle)[sfOwnerNode] = *page; ctx_.view().insert(sle); - adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal); + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + adjustOwnerCount(ctx_.view(), sleOwner, sponsor, 1, ctx_.journal); + addSponsorToLedgerEntry(sle, sponsor); } return tesSUCCESS; @@ -152,11 +154,12 @@ DelegateSet::deleteDelegate( if (!sleOwner) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleOwner, -1, j); + auto const sponsor = getLedgerEntryReserveSponsor(view, sle); + adjustOwnerCount(view, sleOwner, sponsor, -1, j); view.erase(sle); return tesSUCCESS; } -} // namespace ripple \ No newline at end of file +} // namespace ripple diff --git a/src/xrpld/app/tx/detail/DeleteOracle.cpp b/src/xrpld/app/tx/detail/DeleteOracle.cpp index 78e3d552303..206ed662f4f 100644 --- a/src/xrpld/app/tx/detail/DeleteOracle.cpp +++ b/src/xrpld/app/tx/detail/DeleteOracle.cpp @@ -94,7 +94,8 @@ DeleteOracle::deleteOracle( auto const count = sle->getFieldArray(sfPriceDataSeries).size() > 5 ? -2 : -1; - adjustOwnerCount(view, sleOwner, count, j); + auto const sponsor = getLedgerEntryReserveSponsor(view, sle); + adjustOwnerCount(view, sleOwner, sponsor, count, j); view.erase(sle); diff --git a/src/xrpld/app/tx/detail/DepositPreauth.cpp b/src/xrpld/app/tx/detail/DepositPreauth.cpp index f10f09b38fe..31fc1b866b4 100644 --- a/src/xrpld/app/tx/detail/DepositPreauth.cpp +++ b/src/xrpld/app/tx/detail/DepositPreauth.cpp @@ -206,7 +206,9 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sleOwner, 1, j_); + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); + addSponsorToLedgerEntry(slePreauth, sponsor); } else if (ctx_.tx.isFieldPresent(sfUnauthorize)) { @@ -270,7 +272,9 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sleOwner, 1, j_); + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); + addSponsorToLedgerEntry(slePreauth, sponsor); } else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials)) { @@ -311,7 +315,8 @@ DepositPreauth::removeFromLedger( if (!sleOwner) return tefINTERNAL; - adjustOwnerCount(view, sleOwner, -1, j); + auto const sponsor = getLedgerEntryReserveSponsor(view, slePreauth); + adjustOwnerCount(view, sleOwner, sponsor, -1, j); // Remove DepositPreauth from ledger. view.erase(slePreauth); diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index dd0ffac7787..633841acfdd 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -599,7 +599,9 @@ EscrowCreate::doApply() } // increment owner count - adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal); + addSponsorToLedgerEntry(slep, sponsor); ctx_.view().update(sle); return tesSUCCESS; } @@ -796,6 +798,7 @@ template static TER escrowUnlockApplyHelper( ApplyView& view, + STTx const& tx, Rate lockedRate, std::shared_ptr const& sleDest, STAmount const& xrpBalance, @@ -810,6 +813,7 @@ template <> TER escrowUnlockApplyHelper( ApplyView& view, + STTx const& tx, Rate lockedRate, std::shared_ptr const& sleDest, STAmount const& xrpBalance, @@ -945,6 +949,7 @@ template <> TER escrowUnlockApplyHelper( ApplyView& view, + STTx const& tx, Rate lockedRate, std::shared_ptr const& sleDest, STAmount const& xrpBalance, @@ -977,7 +982,9 @@ escrowUnlockApplyHelper( } // update owner count. - adjustOwnerCount(view, sleDest, 1, journal); + auto const sponsor = getTxReserveSponsor(view, tx); + adjustOwnerCount(view, sleDest, sponsor, 1, journal); + addSponsorToLedgerEntry(sleDest, sponsor); } if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && @@ -1157,6 +1164,7 @@ EscrowFinish::doApply() [&](T const&) { return escrowUnlockApplyHelper( ctx_.view(), + ctx_.tx, lockedRate, sled, mPriorBalance, @@ -1187,7 +1195,8 @@ EscrowFinish::doApply() // Adjust source owner count auto const sle = ctx_.view().peek(keylet::account(account)); - adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); + auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep); + adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); ctx_.view().update(sle); // Remove escrow from ledger @@ -1372,6 +1381,7 @@ EscrowCancel::doApply() [&](T const&) { return escrowUnlockApplyHelper( ctx_.view(), + ctx_.tx, parityRate, slep, mPriorBalance, @@ -1398,7 +1408,8 @@ EscrowCancel::doApply() } } - adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); + auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep); + adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); ctx_.view().update(sle); // Remove escrow from ledger diff --git a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp index 77b21b65f36..7ab27891b29 100644 --- a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp +++ b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp @@ -178,6 +178,7 @@ MPTokenAuthorize::createMPToken( TER MPTokenAuthorize::authorize( ApplyView& view, + STTx const& tx, beast::Journal journal, MPTAuthorizeArgs const& args) { @@ -208,7 +209,8 @@ MPTokenAuthorize::authorize( false)) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleAcct, -1, journal); + auto const sponsor = getLedgerEntryReserveSponsor(view, sleMpt); + adjustOwnerCount(view, sleAcct, sponsor, -1, journal); view.erase(sleMpt); return tesSUCCESS; @@ -243,7 +245,9 @@ MPTokenAuthorize::authorize( view.insert(mptoken); // Update owner count. - adjustOwnerCount(view, sleAcct, 1, journal); + auto const sponsor = getTxReserveSponsor(view, tx); + adjustOwnerCount(view, sleAcct, sponsor, 1, journal); + addSponsorToLedgerEntry(mptoken, sponsor); return tesSUCCESS; } @@ -289,6 +293,7 @@ MPTokenAuthorize::doApply() auto const& tx = ctx_.tx; return authorize( ctx_.view(), + tx, ctx_.journal, {.priorBalance = mPriorBalance, .mptIssuanceID = tx[sfMPTokenIssuanceID], diff --git a/src/xrpld/app/tx/detail/MPTokenAuthorize.h b/src/xrpld/app/tx/detail/MPTokenAuthorize.h index a81dc7dea27..8fc4e9a597d 100644 --- a/src/xrpld/app/tx/detail/MPTokenAuthorize.h +++ b/src/xrpld/app/tx/detail/MPTokenAuthorize.h @@ -51,6 +51,7 @@ class MPTokenAuthorize : public Transactor static TER authorize( ApplyView& view, + STTx const& tx, beast::Journal journal, MPTAuthorizeArgs const& args); diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp index da3b57c8feb..2db460076e0 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp @@ -85,6 +85,7 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) Expected MPTokenIssuanceCreate::create( ApplyView& view, + STTx const& tx, beast::Journal journal, MPTCreateArgs const& args) { @@ -100,6 +101,8 @@ MPTokenIssuanceCreate::create( auto const mptId = makeMptID(args.sequence, args.account); auto const mptIssuanceKeylet = keylet::mptIssuance(mptId); + auto const sponsor = getTxReserveSponsor(view, tx); + // create the MPTokenIssuance { auto const ownerNode = view.dirInsert( @@ -132,11 +135,13 @@ MPTokenIssuanceCreate::create( if (args.domainId) (*mptIssuance)[sfDomainID] = *args.domainId; + addSponsorToLedgerEntry(mptIssuance, sponsor); + view.insert(mptIssuance); } // Update owner count. - adjustOwnerCount(view, acct, 1, journal); + adjustOwnerCount(view, acct, sponsor, 1, journal); return mptId; } @@ -147,6 +152,7 @@ MPTokenIssuanceCreate::doApply() auto const& tx = ctx_.tx; auto const result = create( view(), + tx, j_, { .priorBalance = mPriorBalance, diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h index ea01908dffa..34cf8124ec3 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h @@ -56,7 +56,11 @@ class MPTokenIssuanceCreate : public Transactor doApply() override; static Expected - create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args); + create( + ApplyView& view, + STTx const& tx, + beast::Journal journal, + MPTCreateArgs const& args); }; } // namespace ripple diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp index e2b87dbd791..d46b869ccff 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp @@ -78,7 +78,9 @@ MPTokenIssuanceDestroy::doApply() view().erase(mpt); - adjustOwnerCount(view(), view().peek(keylet::account(account_)), -1, j_); + auto const sponsor = getLedgerEntryReserveSponsor(view(), mpt); + adjustOwnerCount( + view(), view().peek(keylet::account(account_)), sponsor, -1, j_); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp index 8e1a026415b..c258902490d 100644 --- a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp @@ -93,6 +93,7 @@ NFTokenCreateOffer::doApply() // Use implementation shared with NFTokenMint return nft::tokenOfferCreateApply( view(), + ctx_.tx, ctx_.tx[sfAccount], ctx_.tx[sfAmount], ctx_.tx[~sfDestination], diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/xrpld/app/tx/detail/NFTokenMint.cpp index 42b551b3a4d..f139c40aedd 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/xrpld/app/tx/detail/NFTokenMint.cpp @@ -331,6 +331,7 @@ NFTokenMint::doApply() // because a Mint is only allowed to create a sell offer. if (TER const ter = nft::tokenOfferCreateApply( view(), + ctx_.tx, ctx_.tx[sfAccount], ctx_.tx[sfAmount], ctx_.tx[~sfDestination], diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.cpp b/src/xrpld/app/tx/detail/NFTokenUtils.cpp index 4866a3b385c..f2b2e129400 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.cpp +++ b/src/xrpld/app/tx/detail/NFTokenUtils.cpp @@ -294,6 +294,7 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft) adjustOwnerCount( view, view.peek(keylet::account(owner)), + std::nullopt, 1, beast::Journal{beast::Journal::getNullSink()}); }); @@ -458,11 +459,15 @@ removeToken( cnt--; if (cnt != 0) + { + auto const sponsor = getLedgerEntryReserveSponsor(view, curr); adjustOwnerCount( view, view.peek(keylet::account(owner)), + sponsor, cnt, beast::Journal{beast::Journal::getNullSink()}); + } return tesSUCCESS; } @@ -496,9 +501,11 @@ removeToken( curr->makeFieldAbsent(sfPreviousPageMin); } + auto const sponsor = getLedgerEntryReserveSponsor(view, curr); adjustOwnerCount( view, view.peek(keylet::account(owner)), + sponsor, -1, beast::Journal{beast::Journal::getNullSink()}); @@ -550,6 +557,7 @@ removeToken( adjustOwnerCount( view, view.peek(keylet::account(owner)), + std::nullopt, -1 * cnt, beast::Journal{beast::Journal::getNullSink()}); @@ -703,9 +711,11 @@ deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer) false)) return false; + auto const sponsor = getLedgerEntryReserveSponsor(view, offer); adjustOwnerCount( view, view.peek(keylet::account(owner)), + sponsor, -1, beast::Journal{beast::Journal::getNullSink()}); @@ -1022,6 +1032,7 @@ tokenOfferCreatePreclaim( TER tokenOfferCreateApply( ApplyView& view, + STTx const& tx, AccountID const& acctID, STAmount const& amount, std::optional const& dest, @@ -1038,6 +1049,7 @@ tokenOfferCreateApply( return tecINSUFFICIENT_RESERVE; auto const offerID = keylet::nftoffer(acctID, seqProxy.value()); + auto const sponsor = getTxReserveSponsor(view, tx); // Create the offer: { @@ -1084,11 +1096,13 @@ tokenOfferCreateApply( if (dest) (*offer)[sfDestination] = *dest; + addSponsorToLedgerEntry(offer, sponsor); + view.insert(offer); } // Update owner count. - adjustOwnerCount(view, view.peek(acctKeylet), 1, j); + adjustOwnerCount(view, view.peek(acctKeylet), sponsor, 1, j); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.h b/src/xrpld/app/tx/detail/NFTokenUtils.h index 7ee0541984f..e0e276db27b 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.h +++ b/src/xrpld/app/tx/detail/NFTokenUtils.h @@ -142,6 +142,7 @@ tokenOfferCreatePreclaim( TER tokenOfferCreateApply( ApplyView& view, + STTx const& tx, AccountID const& acctID, STAmount const& amount, std::optional const& dest, diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index d9e53ac75c7..abfc15a11ed 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -155,7 +155,8 @@ closeChannel( "ripple::closeChannel : minimum channel amount"); (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; - adjustOwnerCount(view, sle, -1, j); + auto const sponsor = getLedgerEntryReserveSponsor(view, slep); + adjustOwnerCount(view, sle, sponsor, -1, j); view.update(sle); // Remove PayChan from ledger @@ -312,7 +313,10 @@ PayChanCreate::doApply() // Deduct owner's balance, increment owner count (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; - adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal); + addSponsorToLedgerEntry(slep, sponsor); + ctx_.view().update(sle); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 692e03109ee..9e5bc93a8d0 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -415,6 +415,14 @@ Payment::doApply() sleDst->setAccountID(sfAccount, dstAccountID); sleDst->setFieldU32(sfSequence, seqno); + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (sponsor.has_value()) + { + addSponsorToLedgerEntry(sleDst, sponsor); + sponsor.value()->setFieldU32(sfSponsoringAccountCount, 1); + view().update(sponsor.value()); + } + view().insert(sleDst); } else diff --git a/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp b/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp index 64c498b68b1..d5ca411792a 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp +++ b/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp @@ -87,7 +87,8 @@ PermissionedDomainDelete::doApply() XRPL_ASSERT( ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0, "ripple::PermissionedDomainDelete::doApply : nonzero owner count"); - adjustOwnerCount(view(), ownerSle, -1, ctx_.journal); + auto const sponsor = getLedgerEntryReserveSponsor(view(), slePd); + adjustOwnerCount(view(), ownerSle, sponsor, -1, ctx_.journal); view().erase(slePd); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp b/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp index 6e2df2a0829..b80cf88504f 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp +++ b/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp @@ -142,7 +142,9 @@ PermissionedDomainSet::doApply() slePd->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), ownerSle, 1, ctx_.journal); + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + adjustOwnerCount(view(), ownerSle, sponsor, 1, ctx_.journal); + addSponsorToLedgerEntry(slePd, sponsor); view().insert(slePd); } diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index 8559c3e7b9e..7e7cff6e051 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -188,7 +188,8 @@ adjustOwnerCount(ApplyContext& ctx, int count) if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) { - adjustOwnerCount(ctx.view(), sleAccount, count, ctx.journal); + adjustOwnerCount( + ctx.view(), sleAccount, std::nullopt, count, ctx.journal); return true; } diff --git a/src/xrpld/app/tx/detail/SetSignerList.cpp b/src/xrpld/app/tx/detail/SetSignerList.cpp index 4a1ee703a07..c3c7c2a3547 100644 --- a/src/xrpld/app/tx/detail/SetSignerList.cpp +++ b/src/xrpld/app/tx/detail/SetSignerList.cpp @@ -231,9 +231,11 @@ removeSignersFromLedger( return tefBAD_LEDGER; } + auto const sponsor = getLedgerEntryReserveSponsor(view, signers); adjustOwnerCount( view, view.peek(accountKeylet), + sponsor, removeFromOwnerCount, app.journal("View")); @@ -394,7 +396,9 @@ SetSignerList::replaceSignerList() // If we succeeded, the new entry counts against the // creator's reserve. - adjustOwnerCount(view(), sle, addedOwnerCount, viewJ); + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + adjustOwnerCount(view(), sle, sponsor, addedOwnerCount, viewJ); + addSponsorToLedgerEntry(signerList, sponsor); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index d3b39aaf113..ef7061bb2eb 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -636,7 +636,7 @@ SetTrust::doApply() if (bLowReserveSet && !bLowReserved) { // Set reserve for low account. - adjustOwnerCount(view(), sleLowAccount, 1, viewJ); + adjustOwnerCount(view(), sleLowAccount, std::nullopt, 1, viewJ); uFlagsOut |= lsfLowReserve; if (!bHigh) @@ -646,14 +646,14 @@ SetTrust::doApply() if (bLowReserveClear && bLowReserved) { // Clear reserve for low account. - adjustOwnerCount(view(), sleLowAccount, -1, viewJ); + adjustOwnerCount(view(), sleLowAccount, std::nullopt, -1, viewJ); uFlagsOut &= ~lsfLowReserve; } if (bHighReserveSet && !bHighReserved) { // Set reserve for high account. - adjustOwnerCount(view(), sleHighAccount, 1, viewJ); + adjustOwnerCount(view(), sleHighAccount, std::nullopt, 1, viewJ); uFlagsOut |= lsfHighReserve; if (bHigh) @@ -663,7 +663,7 @@ SetTrust::doApply() if (bHighReserveClear && bHighReserved) { // Clear reserve for high account. - adjustOwnerCount(view(), sleHighAccount, -1, viewJ); + adjustOwnerCount(view(), sleHighAccount, std::nullopt, -1, viewJ); uFlagsOut &= ~lsfHighReserve; } diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 62784784084..f9dee8e63c5 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -164,19 +164,19 @@ preflight1(PreflightContext const& ctx) auto const sponsor = ctx.tx.getFieldObject(sfSponsor); if (sponsor[sfAccount] == ctx.tx[sfAccount]) { - JLOG(ctx.j.debug()) << "preflight1: invalid sponsor account"; + JLOG(ctx.j.fatal()) << "preflight1: invalid sponsor account"; return temMALFORMED; } if (!(sponsor[sfFlags] & tfSponsorFee) && !(sponsor[sfFlags] & tfSponsorReserve)) { - JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; + JLOG(ctx.j.fatal()) << "preflight1: invalid sponsor flags"; return temMALFORMED; } - if (!sponsor.isFieldPresent(sfSignature) && + if (!sponsor.isFieldPresent(sfTxnSignature) && !sponsor.isFieldPresent(sfSigners)) { - JLOG(ctx.j.debug()) << "preflight1: no sfSignature or sfSigners"; + JLOG(ctx.j.fatal()) << "preflight1: no sfTxnSignature or sfSigners"; return temMALFORMED; } } @@ -558,7 +558,8 @@ Transactor::ticketDelete( } // Update the Ticket owner's reserve. - adjustOwnerCount(view, sleAccount, -1, j); + auto const sponsor = getLedgerEntryReserveSponsor(view, sleTicket); + adjustOwnerCount(view, sleAccount, sponsor, -1, j); // Remove Ticket from ledger. view.erase(sleTicket); @@ -645,7 +646,7 @@ Transactor::checkSign(PreclaimContext const& ctx) STArray const& txSigners(ctx.tx.getFieldArray(sfSigners)); return checkMultiSign(ctx.view, idAccount, txSigners, ctx.flags, ctx.j); } - + // if (ctx.tx.isFieldPresent(sfSponsor)) // { // // TODO: check the sponsor signature diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/xrpld/app/tx/detail/VaultCreate.cpp index cb6a994e7e5..09c9c9dd3e2 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/xrpld/app/tx/detail/VaultCreate.cpp @@ -174,7 +174,9 @@ VaultCreate::doApply() if (auto ter = dirLink(view(), account_, vault)) return ter; - adjustOwnerCount(view(), owner, 1, j_); + auto const sponsor = getTxReserveSponsor(view(), tx); + adjustOwnerCount(view(), owner, sponsor, 1, j_); + addSponsorToLedgerEntry(vault, sponsor); auto ownerCount = owner->at(sfOwnerCount); if (mPriorBalance < view().fees().accountReserve(ownerCount)) return tecINSUFFICIENT_RESERVE; @@ -203,6 +205,7 @@ VaultCreate::doApply() // in the vault auto maybeShare = MPTokenIssuanceCreate::create( view(), + tx, j_, { .priorBalance = std::nullopt, diff --git a/src/xrpld/app/tx/detail/VaultDelete.cpp b/src/xrpld/app/tx/detail/VaultDelete.cpp index 7861e9e9b64..f99c5f3b62a 100644 --- a/src/xrpld/app/tx/detail/VaultDelete.cpp +++ b/src/xrpld/app/tx/detail/VaultDelete.cpp @@ -112,7 +112,8 @@ VaultDelete::doApply() // Destroy the asset holding. auto asset = vault->at(sfAsset); - if (auto ter = removeEmptyHolding(view(), vault->at(sfAccount), asset, j_); + if (auto ter = removeEmptyHolding( + view(), ctx_.tx, vault->at(sfAccount), asset, j_); !isTesSuccess(ter)) return ter; @@ -145,7 +146,8 @@ VaultDelete::doApply() return tefBAD_LEDGER; // LCOV_EXCL_STOP } - adjustOwnerCount(view(), pseudoAcct, -1, j_); + auto const mptSponsor = getLedgerEntryReserveSponsor(view(), mpt); + adjustOwnerCount(view(), pseudoAcct, mptSponsor, -1, j_); view().erase(mpt); @@ -178,7 +180,8 @@ VaultDelete::doApply() return tefBAD_LEDGER; // LCOV_EXCL_STOP } - adjustOwnerCount(view(), owner, -1, j_); + auto const vaultSponsor = getLedgerEntryReserveSponsor(view(), vault); + adjustOwnerCount(view(), owner, vaultSponsor, -1, j_); // Destroy the vault. view().erase(vault); diff --git a/src/xrpld/app/tx/detail/VaultDeposit.cpp b/src/xrpld/app/tx/detail/VaultDeposit.cpp index 0efddb0ff7c..3aae47a32b4 100644 --- a/src/xrpld/app/tx/detail/VaultDeposit.cpp +++ b/src/xrpld/app/tx/detail/VaultDeposit.cpp @@ -200,7 +200,12 @@ VaultDeposit::doApply() if ((vault->getFlags() & tfVaultPrivate) && account_ != vault->at(sfOwner)) { if (auto const err = enforceMPTokenAuthorization( - ctx_.view(), mptIssuanceID, account_, mPriorBalance, j_); + ctx_.view(), + ctx_.tx, + mptIssuanceID, + account_, + mPriorBalance, + j_); !isTesSuccess(err)) return err; } @@ -212,6 +217,7 @@ VaultDeposit::doApply() { if (auto const err = MPTokenAuthorize::authorize( view(), + ctx_.tx, ctx_.journal, {.priorBalance = mPriorBalance, .mptIssuanceID = mptIssuanceID->value(), @@ -225,6 +231,7 @@ VaultDeposit::doApply() { if (auto const err = MPTokenAuthorize::authorize( view(), + ctx_.tx, ctx_.journal, { .priorBalance = mPriorBalance, diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index 6ca049ee66e..5c7a6dc0d65 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -753,7 +753,9 @@ finalizeClaimHelper( // Remove the claim id from the ledger outerSb.erase(sleClaimID); - adjustOwnerCount(outerSb, sleOwner, -1, j); + auto const sponsor = + getLedgerEntryReserveSponsor(outerSb, sleClaimID); + adjustOwnerCount(outerSb, sleOwner, sponsor, -1, j); } } @@ -981,6 +983,7 @@ TER applyCreateAccountAttestations( ApplyView& view, RawView& rawView, + STTx const& tx, TIter attBegin, TIter attEnd, AccountID const& doorAccount, @@ -1178,7 +1181,9 @@ applyCreateAccountAttestations( return tecINTERNAL; // Reserve was already checked - adjustOwnerCount(psb, sleDoor, 1, j); + auto const sponsor = getTxReserveSponsor(psb, tx); + adjustOwnerCount(psb, sleDoor, sponsor, 1, j); + addSponsorToLedgerEntry(createdSleClaimID, sponsor); psb.insert(createdSleClaimID); psb.update(sleDoor); } @@ -1360,6 +1365,7 @@ attestationDoApply(ApplyContext& ctx) return applyCreateAccountAttestations( ctx.view(), ctx.rawView(), + ctx.tx, &*att, &*att + 1, thisDoor, @@ -1547,7 +1553,9 @@ XChainCreateBridge::doApply() (*sleBridge)[sfOwnerNode] = *page; } - adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal); + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal); + addSponsorToLedgerEntry(sleBridge, sponsor); ctx_.view().insert(sleBridge); ctx_.view().update(sleAcct); @@ -2123,7 +2131,9 @@ XChainCreateClaimID::doApply() (*sleClaimID)[sfOwnerNode] = *page; } - adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal); + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal); + addSponsorToLedgerEntry(sleClaimID, sponsor); ctx_.view().insert(sleClaimID); ctx_.view().update(sleBridge); diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index c0cc2515763..78c4d263eb8 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -452,6 +452,17 @@ areCompatible( beast::Journal::Stream& s, char const* reason); +std::optional> +getTxReserveSponsor(ApplyView& view, STTx const& tx); + +std::optional> +getLedgerEntryReserveSponsor(ApplyView& view, SLE::ref sle); + +void +addSponsorToLedgerEntry( + std::shared_ptr const& sle, + std::optional> const& sponsorSle); + //------------------------------------------------------------------------------ // // Modifiers @@ -462,7 +473,8 @@ areCompatible( void adjustOwnerCount( ApplyView& view, - std::shared_ptr const& sle, + std::shared_ptr const& accountSle, + std::optional> const& sponsorSle, std::int32_t amount, beast::Journal j); @@ -470,11 +482,17 @@ inline void adjustOwnerCount( ApplyView& view, AccountID const& account, + std::optional const& sponsor, std::int32_t amount, beast::Journal j) { return adjustOwnerCount( - view, view.peek(keylet::account(account)), amount, j); + view, + view.peek(keylet::account(account)), + sponsor.has_value() ? view.peek(keylet::account(*sponsor)) + : std::optional>(), + amount, + j); } /** @{ */ @@ -590,6 +608,7 @@ addEmptyHolding( [[nodiscard]] TER addEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, XRPAmount priorBalance, MPTIssue const& mptIssue, @@ -641,6 +660,7 @@ trustCreate( [[nodiscard]] TER removeEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, Issue const& issue, beast::Journal journal); @@ -648,6 +668,7 @@ removeEmptyHolding( [[nodiscard]] TER removeEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, MPTIssue const& mptIssue, beast::Journal journal); @@ -655,13 +676,14 @@ removeEmptyHolding( [[nodiscard]] inline TER removeEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, Asset const& asset, beast::Journal journal) { return std::visit( [&](TIss const& issue) -> TER { - return removeEmptyHolding(view, accountID, issue, journal); + return removeEmptyHolding(view, tx, accountID, issue, journal); }, asset.value()); } @@ -863,6 +885,7 @@ requireAuth( [[nodiscard]] TER enforceMPTokenAuthorization( ApplyView& view, + STTx const& tx, MPTID const& mptIssuanceID, AccountID const& account, XRPAmount const& priorBalance, diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 1f616ed491d..36e647cca26 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1021,6 +1021,40 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) return std::nullopt; } +std::optional> +getTxReserveSponsor(ApplyView& view, STTx const& tx) +{ + if (tx.isFieldPresent(sfSponsor)) + { + auto const sponsorObj = tx.getFieldObject(sfSponsor); + auto const flags = sponsorObj.getFlags(); + auto const sponsorID = sponsorObj.getAccountID(sfAccount); + if (flags & tfSponsorReserve) + { + return view.peek(keylet::account(sponsorID)); + } + } + return std::nullopt; +} + +std::optional> +getLedgerEntryReserveSponsor(ApplyView& view, SLE::ref sle) +{ + if (sle->isFieldPresent(sfSponsorAccount)) + return view.peek(keylet::account(sle->getAccountID(sfSponsorAccount))); + return std::nullopt; +} + +void +addSponsorToLedgerEntry( + std::shared_ptr const& sle, + std::optional> const& sponsorSle) +{ + if (sponsorSle) + sle->setAccountID( + sfSponsorAccount, sponsorSle.value()->getAccountID(sfAccount)); +} + //------------------------------------------------------------------------------ // // Modifiers @@ -1030,19 +1064,48 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) void adjustOwnerCount( ApplyView& view, - std::shared_ptr const& sle, + std::shared_ptr const& accountSle, + std::optional> const& sponsorSle, std::int32_t amount, beast::Journal j) { - if (!sle) + if (!accountSle) return; XRPL_ASSERT(amount, "ripple::adjustOwnerCount : nonzero amount input"); - std::uint32_t const current{sle->getFieldU32(sfOwnerCount)}; - AccountID const id = (*sle)[sfAccount]; + + if (sponsorSle) + { + XRPL_ASSERT(sponsorSle, "ripple::adjustOwnerCount : sponsor not found"); + { + // modify sponsor's SponsoringOwnerCount + std::uint32_t const current{ + sponsorSle.value()->getFieldU32(sfSponsoringOwnerCount)}; + AccountID const id = sponsorSle.value()->getAccountID(sfAccount); + std::uint32_t const adjusted = + confineOwnerCount(current, amount, id, j); + view.adjustOwnerCountHook(id, current, adjusted); + sponsorSle.value()->setFieldU32(sfSponsoringOwnerCount, adjusted); + view.update(sponsorSle.value()); + } + { + // modify account's SponsoredOwnerCount + std::uint32_t const current{ + accountSle->getFieldU32(sfSponsoredOwnerCount)}; + AccountID const id = (*accountSle)[sfAccount]; + std::uint32_t const adjusted = + confineOwnerCount(current, amount, id, j); + view.adjustOwnerCountHook(id, current, adjusted); + accountSle->setFieldU32(sfSponsoredOwnerCount, adjusted); + view.update(accountSle); + } + return; + } + std::uint32_t const current{accountSle->getFieldU32(sfOwnerCount)}; + AccountID const id = (*accountSle)[sfAccount]; std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j); view.adjustOwnerCountHook(id, current, adjusted); - sle->setFieldU32(sfOwnerCount, adjusted); - view.update(sle); + accountSle->setFieldU32(sfOwnerCount, adjusted); + view.update(accountSle); } std::function @@ -1201,6 +1264,7 @@ addEmptyHolding( [[nodiscard]] TER addEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, XRPAmount priorBalance, MPTIssue const& mptIssue, @@ -1217,6 +1281,7 @@ addEmptyHolding( return MPTokenAuthorize::authorize( view, + tx, journal, {.priorBalance = priorBalance, .mptIssuanceID = mptID, @@ -1330,7 +1395,7 @@ trustCreate( } sleRippleState->setFieldU32(sfFlags, uFlags); - adjustOwnerCount(view, sleAccount, 1, j); + adjustOwnerCount(view, sleAccount, std::nullopt, 1, j); // ONLY: Create ripple balance. sleRippleState->setFieldAmount( @@ -1345,6 +1410,7 @@ trustCreate( [[nodiscard]] TER removeEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, Issue const& issue, beast::Journal journal) @@ -1375,7 +1441,7 @@ removeEmptyHolding( view.peek(keylet::account(line->at(sfLowLimit)->getIssuer())); if (!sleLowAccount) return tecINTERNAL; - adjustOwnerCount(view, sleLowAccount, -1, journal); + adjustOwnerCount(view, sleLowAccount, std::nullopt, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. @@ -1389,7 +1455,7 @@ removeEmptyHolding( view.peek(keylet::account(line->at(sfHighLimit)->getIssuer())); if (!sleHighAccount) return tecINTERNAL; - adjustOwnerCount(view, sleHighAccount, -1, journal); + adjustOwnerCount(view, sleHighAccount, std::nullopt, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. @@ -1407,6 +1473,7 @@ removeEmptyHolding( [[nodiscard]] TER removeEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, MPTIssue const& mptIssue, beast::Journal journal) @@ -1420,6 +1487,7 @@ removeEmptyHolding( return MPTokenAuthorize::authorize( view, + tx, journal, {.priorBalance = {}, .mptIssuanceID = mptID, @@ -1517,7 +1585,8 @@ offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) } } - adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j); + auto const sponsor = getLedgerEntryReserveSponsor(view, sle); + adjustOwnerCount(view, view.peek(keylet::account(owner)), sponsor, -1, j); view.erase(sle); @@ -1611,7 +1680,11 @@ rippleCreditIOU( { // Clear the reserve of the sender, possibly delete the line! adjustOwnerCount( - view, view.peek(keylet::account(uSenderID)), -1, j); + view, + view.peek(keylet::account(uSenderID)), + std::nullopt, + -1, + j); // Clear reserve flag. sleRippleState->setFieldU32( @@ -2067,7 +2140,7 @@ updateTrustLine( { // VFALCO Where is the line being deleted? // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(view, sle, -1, j); + adjustOwnerCount(view, sle, std::nullopt, -1, j); // Clear reserve flag. state->setFieldU32( @@ -2418,6 +2491,7 @@ requireAuth( [[nodiscard]] TER enforceMPTokenAuthorization( ApplyView& view, + STTx const& tx, MPTID const& mptIssuanceID, AccountID const& account, XRPAmount const& priorBalance, // for MPToken authorization @@ -2499,6 +2573,7 @@ enforceMPTokenAuthorization( "ripple::enforceMPTokenAuthorization : new MPToken for domain"); if (auto const err = MPTokenAuthorize::authorize( view, + tx, j, { .priorBalance = priorBalance, @@ -2660,7 +2735,7 @@ deleteAMMTrustLine( if (!(sleState->getFlags() & uFlags)) return tecINTERNAL; - adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j); + adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, std::nullopt, -1, j); return tesSUCCESS; } From 198ed72fe370909ce040d6dd8238e6e95c231e75 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 31 Jul 2025 00:12:09 +0900 Subject: [PATCH 004/249] fix OwnerCount --- src/test/app/Sponsor_test.cpp | 63 +++++++++++++++++++------------- src/test/jtx/impl/owners.cpp | 2 +- src/test/jtx/owners.h | 4 +- src/xrpld/ledger/detail/View.cpp | 2 +- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e7d815dfdd4..bfb335afbc3 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -91,9 +91,10 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 0)); + env.require(owners(alice, 1)); env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_count(sponsor, 1)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor, 1)); // CheckCancel auto const checkId = keylet::check(alice, seq).key; @@ -102,7 +103,7 @@ class Sponsor_test : public beast::unit_test::suite env.require(owners(alice, 0)); env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_count(sponsor, 0)); + env.require(sponsoring_owners(sponsor, 0)); auto const seq2 = env.seq(alice); env(check::create(alice, bob, XRP(1)), @@ -110,6 +111,11 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); + env.require(owners(alice, 1)); + env.require(sponsored_owners(alice, 1)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor, 1)); + // CheckCash auto const checkId2 = keylet::check(alice, seq2).key; env(check::cash(bob, checkId2, XRP(1))); @@ -117,7 +123,7 @@ class Sponsor_test : public beast::unit_test::suite env.require(owners(alice, 0)); env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_count(sponsor, 0)); + env.require(sponsoring_owners(sponsor, 0)); // printf( // "meta: %s\n", @@ -145,9 +151,10 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 0)); + env.require(owners(alice, 1)); env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_count(sponsor, 1)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor, 1)); // OfferCancel env(offer_cancel(alice, seq)); @@ -155,9 +162,10 @@ class Sponsor_test : public beast::unit_test::suite env.require(owners(alice, 0)); env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_count(sponsor, 0)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor, 0)); - // TODO: test Execution + // TODO: test Offer Execution } void @@ -178,17 +186,18 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 0)); + env.require(owners(alice, 250)); env.require(sponsored_owners(alice, 250)); - env.require(sponsoring_count(sponsor, 250)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor, 250)); // use a Ticket env(noop(alice), ticket::use(ticketSeq + 1)); env.close(); - env.require(owners(alice, 0)); + env.require(owners(alice, 249)); env.require(sponsored_owners(alice, 249)); - env.require(sponsoring_count(sponsor, 249)); + env.require(sponsoring_owners(sponsor, 249)); } void @@ -210,9 +219,11 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(issuer, 0)); + env.require(owners(issuer, 1)); + env.require(owners(subject, 0)); env.require(sponsored_owners(issuer, 1)); - env.require(sponsoring_count(sponsor, 1)); + env.require(sponsored_owners(subject, 0)); + env.require(sponsoring_owners(sponsor, 1)); // CredentialsAccept env(credentials::accept(subject, issuer, "credType"), @@ -221,10 +232,10 @@ class Sponsor_test : public beast::unit_test::suite env.close(); env.require(owners(issuer, 0)); - env.require(owners(subject, 0)); + env.require(owners(subject, 1)); env.require(sponsored_owners(issuer, 0)); env.require(sponsored_owners(subject, 1)); - env.require(sponsoring_count(sponsor, 1)); + env.require(sponsoring_owners(sponsor, 1)); // CredentialsDelete env(credentials::deleteCred(subject, subject, issuer, "credType")); @@ -234,7 +245,7 @@ class Sponsor_test : public beast::unit_test::suite env.require(owners(subject, 0)); env.require(sponsored_owners(issuer, 0)); env.require(sponsored_owners(subject, 0)); - env.require(sponsoring_count(sponsor, 0)); + env.require(sponsoring_owners(sponsor, 0)); } void @@ -255,9 +266,9 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 0)); + env.require(owners(alice, 1)); env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_count(sponsor, 1)); + env.require(sponsoring_owners(sponsor, 1)); // delete env(delegate::set(alice, bob, {})); @@ -265,7 +276,7 @@ class Sponsor_test : public beast::unit_test::suite env.require(owners(alice, 0)); env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_count(sponsor, 0)); + env.require(sponsoring_owners(sponsor, 0)); } void @@ -291,9 +302,9 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 0)); + env.require(owners(alice, 1)); env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_count(sponsor, 1)); + env.require(sponsoring_owners(sponsor, 1)); // DIDDelete env(did::del(alice)); @@ -301,7 +312,7 @@ class Sponsor_test : public beast::unit_test::suite env.require(owners(alice, 0)); env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_count(sponsor, 0)); + env.require(sponsoring_owners(sponsor, 0)); } void @@ -358,9 +369,9 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 0)); + env.require(owners(alice, 1)); env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_count(sponsor, 1)); + env.require(sponsoring_owners(sponsor, 1)); // Delete env(signers(alice, none)); @@ -368,7 +379,7 @@ class Sponsor_test : public beast::unit_test::suite env.require(owners(alice, 0)); env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_count(sponsor, 0)); + env.require(sponsoring_owners(sponsor, 0)); } void diff --git a/src/test/jtx/impl/owners.cpp b/src/test/jtx/impl/owners.cpp index 00e5a4039d2..165d8a577ee 100644 --- a/src/test/jtx/impl/owners.cpp +++ b/src/test/jtx/impl/owners.cpp @@ -63,7 +63,7 @@ sponsored_owners::operator()(Env& env) const } void -sponsoring_count::operator()(Env& env) const +sponsoring_owners::operator()(Env& env) const { env.test.expect( env.le(account_)->getFieldU32(sfSponsoringOwnerCount) == value_); diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index 5878ebcad9e..1a61b3c7a9f 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -103,14 +103,14 @@ class sponsored_owners }; /** Match the number of items in the account's owner directory */ -class sponsoring_count +class sponsoring_owners { private: Account account_; std::uint32_t value_; public: - sponsoring_count(Account const& account, std::uint32_t value) + sponsoring_owners(Account const& account, std::uint32_t value) : account_(account), value_(value) { } diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 36e647cca26..119f0a3a270 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1089,6 +1089,7 @@ adjustOwnerCount( } { // modify account's SponsoredOwnerCount + std::uint32_t const current{ accountSle->getFieldU32(sfSponsoredOwnerCount)}; AccountID const id = (*accountSle)[sfAccount]; @@ -1098,7 +1099,6 @@ adjustOwnerCount( accountSle->setFieldU32(sfSponsoredOwnerCount, adjusted); view.update(accountSle); } - return; } std::uint32_t const current{accountSle->getFieldU32(sfOwnerCount)}; AccountID const id = (*accountSle)[sfAccount]; From a798c60a084c7ef8df732f12846f7834ae32dec0 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 31 Jul 2025 00:12:23 +0900 Subject: [PATCH 005/249] test Disabled --- src/test/app/Sponsor_test.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index bfb335afbc3..36d5585010f 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -30,6 +30,23 @@ namespace ripple { class Sponsor_test : public beast::unit_test::suite { public: + void + testDisabled() + { + testcase("Disabled"); + using namespace test::jtx; + Env env{*this, testable_amendments() - featureSponsor}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + + env(noop(alice), + fee(XRP(1)), + sponsor::as(sponsor), + sponsor::sig(sponsor), + ter(temDISABLED)); + } + void testSponsorFee() { @@ -423,6 +440,7 @@ class Sponsor_test : public beast::unit_test::suite void run() override { + testDisabled(); testSponsorFee(); testSponsorAccount(); testSponsorReserve(); From f0addd81a1d39adea9db895b83db26043c375cb4 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 31 Jul 2025 17:43:54 +0900 Subject: [PATCH 006/249] fix addEmptyHolding --- src/xrpld/app/tx/detail/VaultCreate.cpp | 3 ++- src/xrpld/ledger/View.h | 4 +++- src/xrpld/ledger/detail/View.cpp | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/xrpld/app/tx/detail/VaultCreate.cpp index 09c9c9dd3e2..77ed8c8c76a 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/xrpld/app/tx/detail/VaultCreate.cpp @@ -188,7 +188,8 @@ VaultCreate::doApply() auto pseudoId = pseudo->at(sfAccount); auto asset = tx[sfAsset]; - if (auto ter = addEmptyHolding(view(), pseudoId, mPriorBalance, asset, j_); + if (auto ter = + addEmptyHolding(view(), tx, pseudoId, mPriorBalance, asset, j_); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index 78c4d263eb8..c7f45184ef2 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -600,6 +600,7 @@ isPseudoAccount(ReadView const& view, AccountID accountId) [[nodiscard]] TER addEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, XRPAmount priorBalance, Issue const& issue, @@ -617,6 +618,7 @@ addEmptyHolding( [[nodiscard]] inline TER addEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, XRPAmount priorBalance, Asset const& asset, @@ -625,7 +627,7 @@ addEmptyHolding( return std::visit( [&](TIss const& issue) -> TER { return addEmptyHolding( - view, accountID, priorBalance, issue, journal); + view, tx, accountID, priorBalance, issue, journal); }, asset.value()); } diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 119f0a3a270..afa6ce6a214 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1216,6 +1216,7 @@ isPseudoAccount(std::shared_ptr sleAcct) [[nodiscard]] TER addEmptyHolding( ApplyView& view, + STTx const& tx, AccountID const& accountID, XRPAmount priorBalance, Issue const& issue, From 67bdd099c8178359708938116bc7ee144d613468 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 Aug 2025 00:44:11 +0900 Subject: [PATCH 007/249] check reserve --- include/xrpl/protocol/Fees.h | 13 ++++- src/test/app/Sponsor_test.cpp | 16 +++++- src/xrpld/app/tx/detail/AMMClawback.cpp | 4 ++ src/xrpld/app/tx/detail/AMMWithdraw.cpp | 41 +++++++++++--- src/xrpld/app/tx/detail/AMMWithdraw.h | 8 +++ src/xrpld/app/tx/detail/CashCheck.cpp | 6 +- src/xrpld/app/tx/detail/CreateCheck.cpp | 13 ++--- src/xrpld/app/tx/detail/CreateOffer.cpp | 8 +-- src/xrpld/app/tx/detail/CreateTicket.cpp | 13 ++--- src/xrpld/app/tx/detail/Credentials.cpp | 26 ++++----- src/xrpld/app/tx/detail/DID.cpp | 15 ++--- src/xrpld/app/tx/detail/DelegateSet.cpp | 11 ++-- src/xrpld/app/tx/detail/DepositPreauth.cpp | 26 ++++----- src/xrpld/app/tx/detail/Escrow.cpp | 37 +++++++------ src/xrpld/app/tx/detail/MPTokenAuthorize.cpp | 16 +++--- .../app/tx/detail/MPTokenIssuanceCreate.cpp | 14 +++-- .../app/tx/detail/NFTokenAcceptOffer.cpp | 11 ++-- src/xrpld/app/tx/detail/NFTokenMint.cpp | 12 +++- src/xrpld/app/tx/detail/NFTokenUtils.cpp | 10 ++-- src/xrpld/app/tx/detail/PayChan.cpp | 36 +++++++----- .../app/tx/detail/PermissionedDomainSet.cpp | 10 ++-- src/xrpld/app/tx/detail/SetOracle.cpp | 10 ++-- src/xrpld/app/tx/detail/SetSignerList.cpp | 14 ++--- src/xrpld/app/tx/detail/SetTrust.cpp | 20 +++++-- src/xrpld/app/tx/detail/VaultCreate.cpp | 7 ++- src/xrpld/app/tx/detail/XChainBridge.cpp | 30 +++++----- src/xrpld/ledger/View.h | 13 ++++- src/xrpld/ledger/detail/View.cpp | 55 ++++++++++++++++++- 28 files changed, 317 insertions(+), 178 deletions(-) diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 4393f1a1d9c..937915c8f66 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -46,9 +46,18 @@ struct Fees the reserve increment times the number of increments. */ XRPAmount - accountReserve(std::size_t ownerCount) const + accountReserve( + std::size_t ownerCount, + std::size_t sponsoredOwnerCount = 0, + std::size_t sponsoringOwnerCount = 0, + bool isAccountSponsored = false, + std::size_t sponsoringAccountCount = 0) const { - return reserve + ownerCount * increment; + // return reserve + ownerCount * increment; + return (isAccountSponsored ? XRPAmount(0) : reserve) + + increment * + (ownerCount + sponsoringOwnerCount - sponsoredOwnerCount) + + reserve * sponsoringAccountCount; } }; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 36d5585010f..55d2a0137cf 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -99,7 +100,20 @@ class Sponsor_test : public beast::unit_test::suite Account const bob("bob"); Account const sponsor("sponsor"); - env.fund(XRP(10000), alice, bob, sponsor); + auto const reserve = env.current()->fees().reserve; + auto const increment = env.current()->fees().increment; + + env.fund(XRP(10000), alice, bob); + env.fund(drops(reserve) + drops(increment) - drops(1), sponsor); + + // check sponsor balance + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + + env(pay(alice, sponsor, drops(1))); + env.close(); // CheckCreate auto const seq = env.seq(alice); diff --git a/src/xrpld/app/tx/detail/AMMClawback.cpp b/src/xrpld/app/tx/detail/AMMClawback.cpp index 07c51517277..4d93b8c2a76 100644 --- a/src/xrpld/app/tx/detail/AMMClawback.cpp +++ b/src/xrpld/app/tx/detail/AMMClawback.cpp @@ -192,6 +192,7 @@ AMMClawback::applyGuts(Sandbox& sb) std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = AMMWithdraw::equalWithdrawTokens( sb, + ctx_.tx, *ammSle, holder, ammAccount, @@ -273,6 +274,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( // tfee is actually not used, so pass tfee as 0. return AMMWithdraw::equalWithdrawTokens( sb, + ctx_.tx, ammSle, holder, ammAccount, @@ -308,6 +310,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( return AMMWithdraw::withdraw( sb, + ctx_.tx, ammSle, ammAccount, holder, @@ -327,6 +330,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( // tfee is actually not used, so pass tfee as 0. return AMMWithdraw::withdraw( sb, + ctx_.tx, ammSle, ammAccount, holder, diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index 2ad1a19df56..abba36afee3 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -339,6 +339,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) if (subTxType & tfTwoAsset) return equalWithdrawLimit( sb, + ctx_.tx, *ammSle, ammAccountID, amountBalance, @@ -350,6 +351,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll) return singleWithdrawTokens( sb, + ctx_.tx, *ammSle, ammAccountID, amountBalance, @@ -360,6 +362,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) if (subTxType & tfLimitLPToken) return singleWithdrawEPrice( sb, + ctx_.tx, *ammSle, ammAccountID, amountBalance, @@ -370,6 +373,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) if (subTxType & tfSingleAsset) return singleWithdraw( sb, + ctx_.tx, *ammSle, ammAccountID, amountBalance, @@ -380,6 +384,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) { return equalWithdrawTokens( sb, + ctx_.tx, *ammSle, ammAccountID, amountBalance, @@ -435,6 +440,7 @@ AMMWithdraw::doApply() std::pair AMMWithdraw::withdraw( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -448,6 +454,7 @@ AMMWithdraw::withdraw( STAmount newLPTokenBalance; std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = withdraw( view, + tx, ammSle, ammAccount, account_, @@ -467,6 +474,7 @@ AMMWithdraw::withdraw( std::tuple> AMMWithdraw::withdraw( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, AccountID const& account, @@ -591,13 +599,18 @@ AMMWithdraw::withdraw( auto const balance = (*sleAccount)[sfBalance].xrp(); std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount); - // See also SetTrust::doApply() - XRPAmount const reserve( - (ownerCount < 2) ? XRPAmount(beast::zero) - : view.fees().accountReserve(ownerCount + 1)); - - if (std::max(priorBalance, balance) < reserve) - return tecINSUFFICIENT_RESERVE; + if (ownerCount >= 2) + { + auto const sponsor = getTxReserveSponsor(view, tx); + if (auto const ret = checkInsufficientReserve( + view, + sleAccount, + std::max(priorBalance, balance), + sponsor, + 1); + !isTesSuccess(ret)) + return ret; + } } return tesSUCCESS; }; @@ -685,6 +698,7 @@ adjustLPTokensIn( std::pair AMMWithdraw::equalWithdrawTokens( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -699,6 +713,7 @@ AMMWithdraw::equalWithdrawTokens( std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = equalWithdrawTokens( view, + tx, ammSle, account_, ammAccount, @@ -749,6 +764,7 @@ AMMWithdraw::deleteAMMAccountIfEmpty( std::tuple> AMMWithdraw::equalWithdrawTokens( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const account, AccountID const& ammAccount, @@ -770,6 +786,7 @@ AMMWithdraw::equalWithdrawTokens( { return withdraw( view, + tx, ammSle, ammAccount, account, @@ -805,6 +822,7 @@ AMMWithdraw::equalWithdrawTokens( return withdraw( view, + tx, ammSle, ammAccount, account, @@ -857,6 +875,7 @@ AMMWithdraw::equalWithdrawTokens( std::pair AMMWithdraw::equalWithdrawLimit( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -881,6 +900,7 @@ AMMWithdraw::equalWithdrawLimit( { return withdraw( view, + tx, ammSle, ammAccount, amountBalance, @@ -914,6 +934,7 @@ AMMWithdraw::equalWithdrawLimit( return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE return withdraw( view, + tx, ammSle, ammAccount, amountBalance, @@ -932,6 +953,7 @@ AMMWithdraw::equalWithdrawLimit( std::pair AMMWithdraw::singleWithdraw( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -958,6 +980,7 @@ AMMWithdraw::singleWithdraw( return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE return withdraw( view, + tx, ammSle, ammAccount, amountBalance, @@ -981,6 +1004,7 @@ AMMWithdraw::singleWithdraw( std::pair AMMWithdraw::singleWithdrawTokens( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -1000,6 +1024,7 @@ AMMWithdraw::singleWithdrawTokens( { return withdraw( view, + tx, ammSle, ammAccount, amountBalance, @@ -1035,6 +1060,7 @@ AMMWithdraw::singleWithdrawTokens( std::pair AMMWithdraw::singleWithdrawEPrice( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -1080,6 +1106,7 @@ AMMWithdraw::singleWithdrawEPrice( { return withdraw( view, + tx, ammSle, ammAccount, amountBalance, diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.h b/src/xrpld/app/tx/detail/AMMWithdraw.h index 1de91fd787f..6cc0811f14b 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.h +++ b/src/xrpld/app/tx/detail/AMMWithdraw.h @@ -102,6 +102,7 @@ class AMMWithdraw : public Transactor static std::tuple> equalWithdrawTokens( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const account, AccountID const& ammAccount, @@ -135,6 +136,7 @@ class AMMWithdraw : public Transactor static std::tuple> withdraw( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, AccountID const& account, @@ -177,6 +179,7 @@ class AMMWithdraw : public Transactor std::pair withdraw( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -202,6 +205,7 @@ class AMMWithdraw : public Transactor std::pair equalWithdrawTokens( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -227,6 +231,7 @@ class AMMWithdraw : public Transactor std::pair equalWithdrawLimit( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -249,6 +254,7 @@ class AMMWithdraw : public Transactor std::pair singleWithdraw( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -270,6 +276,7 @@ class AMMWithdraw : public Transactor std::pair singleWithdrawTokens( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, @@ -292,6 +299,7 @@ class AMMWithdraw : public Transactor std::pair singleWithdrawEPrice( Sandbox& view, + STTx const& tx, SLE const& ammSle, AccountID const& ammAccount, STAmount const& amountBalance, diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index c2c3f372598..c31591542b4 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -368,8 +368,10 @@ CashCheck::doApply() auto const sleDst = psb.peek(keylet::account(account_)); // Can the account cover the trust line's reserve? - if (std::uint32_t const ownerCount = {sleDst->at(sfOwnerCount)}; - mPriorBalance < psb.fees().accountReserve(ownerCount + 1)) + auto const sponsor = getTxReserveSponsor(psb, ctx_.tx); + if (auto const ret = checkInsufficientReserve( + psb, sleDst, mPriorBalance, sponsor, 1); + !isTesSuccess(ret)) { JLOG(j_.trace()) << "Trust line does not exist. " "Insufficent reserve to create line."; diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/xrpld/app/tx/detail/CreateCheck.cpp index fbc084fd1c6..a808698856c 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/xrpld/app/tx/detail/CreateCheck.cpp @@ -177,13 +177,11 @@ CreateCheck::doApply() // A check counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - { - STAmount const reserve{ - view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)}; - - if (mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; - } + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (auto const ret = + checkInsufficientReserve(view(), sle, mPriorBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; // Note that we use the value from the sequence or ticket as the // Check sequence. For more explanation see comments in SeqProxy.h. @@ -243,7 +241,6 @@ CreateCheck::doApply() sleCheck->setFieldU64(sfOwnerNode, *page); } // If we succeeded, the new entry counts against the creator's reserve. - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); adjustOwnerCount(view(), sle, sponsor, 1, viewJ); addSponsorToLedgerEntry(sleCheck, sponsor); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/CreateOffer.cpp b/src/xrpld/app/tx/detail/CreateOffer.cpp index fc1bcda26b7..ffbcdb0afe5 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.cpp +++ b/src/xrpld/app/tx/detail/CreateOffer.cpp @@ -813,10 +813,10 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) return {tefINTERNAL, false}; { - XRPAmount reserve = - sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1); - - if (mPriorBalance < reserve) + auto const sponsor = getTxReserveSponsor(sb, ctx_.tx); + if (auto const ret = checkInsufficientReserve( + sb, sleCreator, mPriorBalance, sponsor, 1); + !isTesSuccess(ret)) { // If we are here, the signing account had an insufficient reserve // *prior* to our processing. If something actually crossed, then diff --git a/src/xrpld/app/tx/detail/CreateTicket.cpp b/src/xrpld/app/tx/detail/CreateTicket.cpp index 00c5a6c7b8e..e2b97cecd49 100644 --- a/src/xrpld/app/tx/detail/CreateTicket.cpp +++ b/src/xrpld/app/tx/detail/CreateTicket.cpp @@ -91,13 +91,11 @@ CreateTicket::doApply() // check the starting balance because we want to allow dipping into the // reserve to pay fees. std::uint32_t const ticketCount = ctx_.tx[sfTicketCount]; - { - XRPAmount const reserve = view().fees().accountReserve( - sleAccountRoot->getFieldU32(sfOwnerCount) + ticketCount); - - if (mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; - } + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + view(), sleAccountRoot, mPriorBalance, sponsor, ticketCount); + !isTesSuccess(ret)) + return ret; beast::Journal viewJ{ctx_.app.journal("View")}; @@ -113,7 +111,6 @@ CreateTicket::doApply() txSeq != 0 && txSeq != (firstTicketSeq - 1)) return tefINTERNAL; - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); for (std::uint32_t i = 0; i < ticketCount; ++i) { std::uint32_t const curTicketSeq = firstTicketSeq + i; diff --git a/src/xrpld/app/tx/detail/Credentials.cpp b/src/xrpld/app/tx/detail/Credentials.cpp index 36e4ebbc730..48f6bff2a7c 100644 --- a/src/xrpld/app/tx/detail/Credentials.cpp +++ b/src/xrpld/app/tx/detail/Credentials.cpp @@ -148,12 +148,11 @@ CredentialCreate::doApply() if (!sleIssuer) return tefINTERNAL; - { - STAmount const reserve{view().fees().accountReserve( - sleIssuer->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; - } + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + view(), sleIssuer, mPriorBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; sleCred->setAccountID(sfSubject, subject); sleCred->setAccountID(sfIssuer, account_); @@ -174,7 +173,6 @@ CredentialCreate::doApply() return tecDIR_FULL; sleCred->setFieldU64(sfIssuerNode, *page); - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); adjustOwnerCount(view(), sleIssuer, sponsor, 1, j_); addSponsorToLedgerEntry(sleCred, sponsor); } @@ -372,12 +370,11 @@ CredentialAccept::doApply() if (!sleSubject || !sleIssuer) return tefINTERNAL; - { - STAmount const reserve{view().fees().accountReserve( - sleSubject->getFieldU32(sfOwnerCount) + 1)}; - if (mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; - } + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + view(), sleSubject, mPriorBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; auto const credType(ctx_.tx[sfCredentialType]); Keylet const credentialKey = keylet::credential(account_, issuer, credType); @@ -394,8 +391,7 @@ CredentialAccept::doApply() sleCred->setFieldU32(sfFlags, lsfAccepted); view().update(sleCred); - auto const currentSponsor = getTxReserveSponsor(view(), ctx_.tx); - adjustOwnerCount(view(), sleIssuer, currentSponsor, -1, j_); + adjustOwnerCount(view(), sleIssuer, sponsor, -1, j_); auto const newSponsor = getTxReserveSponsor(view(), ctx_.tx); adjustOwnerCount(view(), sleSubject, newSponsor, 1, j_); addSponsorToLedgerEntry(sleCred, newSponsor); diff --git a/src/xrpld/app/tx/detail/DID.cpp b/src/xrpld/app/tx/detail/DID.cpp index f8dd9619d8d..37d73327dd6 100644 --- a/src/xrpld/app/tx/detail/DID.cpp +++ b/src/xrpld/app/tx/detail/DID.cpp @@ -88,14 +88,12 @@ addSLE( return tefINTERNAL; // Check reserve availability for new object creation - { - auto const balance = STAmount((*sleAccount)[sfBalance]).xrp(); - auto const reserve = - ctx.view().fees().accountReserve((*sleAccount)[sfOwnerCount] + 1); - - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; - } + auto const sponsor = getTxReserveSponsor(ctx.view(), ctx.tx); + auto const balance = STAmount((*sleAccount)[sfBalance]).xrp(); + if (auto const ret = checkInsufficientReserve( + ctx.view(), sleAccount, balance, sponsor, 1); + !isTesSuccess(ret)) + return ret; // Add ledger object to ledger ctx.view().insert(sle); @@ -108,7 +106,6 @@ addSLE( return tecDIR_FULL; (*sle)[sfOwnerNode] = *page; } - auto const sponsor = getTxReserveSponsor(ctx.view(), ctx.tx); adjustOwnerCount(ctx.view(), sleAccount, sponsor, 1, ctx.journal); addSponsorToLedgerEntry(sle, sponsor); ctx.view().update(sleAccount); diff --git a/src/xrpld/app/tx/detail/DelegateSet.cpp b/src/xrpld/app/tx/detail/DelegateSet.cpp index a98e3d5951a..46b6425c232 100644 --- a/src/xrpld/app/tx/detail/DelegateSet.cpp +++ b/src/xrpld/app/tx/detail/DelegateSet.cpp @@ -99,11 +99,11 @@ DelegateSet::doApply() return tesSUCCESS; } - STAmount const reserve{ctx_.view().fees().accountReserve( - sleOwner->getFieldU32(sfOwnerCount) + 1)}; - - if (mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + view(), sleOwner, mPriorBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); if (!permissions.empty()) @@ -123,7 +123,6 @@ DelegateSet::doApply() (*sle)[sfOwnerNode] = *page; ctx_.view().insert(sle); - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); adjustOwnerCount(ctx_.view(), sleOwner, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(sle, sponsor); } diff --git a/src/xrpld/app/tx/detail/DepositPreauth.cpp b/src/xrpld/app/tx/detail/DepositPreauth.cpp index 31fc1b866b4..ad8db71e749 100644 --- a/src/xrpld/app/tx/detail/DepositPreauth.cpp +++ b/src/xrpld/app/tx/detail/DepositPreauth.cpp @@ -173,13 +173,11 @@ DepositPreauth::doApply() // A preauth counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - { - STAmount const reserve{view().fees().accountReserve( - sleOwner->getFieldU32(sfOwnerCount) + 1)}; - - if (mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; - } + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + view(), sleOwner, mPriorBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; // Preclaim already verified that the Preauth entry does not yet exist. // Create and populate the Preauth entry. @@ -206,7 +204,6 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); addSponsorToLedgerEntry(slePreauth, sponsor); } @@ -226,13 +223,11 @@ DepositPreauth::doApply() // A preauth counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - { - STAmount const reserve{view().fees().accountReserve( - sleOwner->getFieldU32(sfOwnerCount) + 1)}; - - if (mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; - } + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + view(), sleOwner, mPriorBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; // Preclaim already verified that the Preauth entry does not yet exist. // Create and populate the Preauth entry. @@ -272,7 +267,6 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); addSponsorToLedgerEntry(slePreauth, sponsor); } diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 633841acfdd..2ae070e7bee 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -495,16 +495,21 @@ EscrowCreate::doApply() // Check reserve and funds availability STAmount const amount{ctx_.tx[sfAmount]}; - auto const reserve = - ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); - - if (mSourceBalance < reserve) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + ctx_.view(), sle, mSourceBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; - // Check reserve and funds availability if (isXRP(amount)) { - if (mSourceBalance < reserve + STAmount(amount).xrp()) + if (auto const ret = checkInsufficientReserve( + ctx_.view(), + sle, + mSourceBalance - STAmount(amount).xrp(), + std::optional>(), + 1); + !isTesSuccess(ret)) return tecUNFUNDED; } @@ -599,7 +604,6 @@ EscrowCreate::doApply() } // increment owner count - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(slep, sponsor); ctx_.view().update(sle); @@ -841,8 +845,10 @@ escrowUnlockApplyHelper( if (!view.exists(trustLineKey) && createAsset && !receiverIssuer) { // Can the account cover the trust line's reserve? - if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; - xrpBalance < view.fees().accountReserve(ownerCount + 1)) + auto const sponsor = getTxReserveSponsor(view, tx); + if (auto const ret = + checkInsufficientReserve(view, sleDest, xrpBalance, sponsor, 1); + !isTesSuccess(ret)) { JLOG(journal.trace()) << "Trust line does not exist. " "Insufficent reserve to create line."; @@ -968,11 +974,11 @@ escrowUnlockApplyHelper( if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer) { - if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; - xrpBalance < view.fees().accountReserve(ownerCount + 1)) - { - return tecINSUFFICIENT_RESERVE; - } + auto const sponsor = getTxReserveSponsor(view, tx); + if (auto const ret = + checkInsufficientReserve(view, sleDest, xrpBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; if (auto const ter = MPTokenAuthorize::createMPToken(view, mptID, receiver, 0); @@ -982,7 +988,6 @@ escrowUnlockApplyHelper( } // update owner count. - auto const sponsor = getTxReserveSponsor(view, tx); adjustOwnerCount(view, sleDest, sponsor, 1, journal); addSponsorToLedgerEntry(sleDest, sponsor); } diff --git a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp index 7ab27891b29..c62db50ffc8 100644 --- a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp +++ b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp @@ -225,13 +225,14 @@ MPTokenAuthorize::authorize( // an account owns, in the case of MPTokens we only // *enforce* a reserve if the user owns more than two // items. This is similar to the reserve requirements of trust lines. - std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount); - XRPAmount const reserveCreate( - (uOwnerCount < 2) ? XRPAmount(beast::zero) - : view.fees().accountReserve(uOwnerCount + 1)); - - if (args.priorBalance < reserveCreate) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = getTxReserveSponsor(view, tx); + if (sleAcct->getFieldU32(sfOwnerCount) >= 2) + { + if (auto const ret = checkInsufficientReserve( + view, sleAcct, args.priorBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; + } auto const mptokenKey = keylet::mptoken(args.mptIssuanceID, args.account); @@ -245,7 +246,6 @@ MPTokenAuthorize::authorize( view.insert(mptoken); // Update owner count. - auto const sponsor = getTxReserveSponsor(view, tx); adjustOwnerCount(view, sleAcct, sponsor, 1, journal); addSponsorToLedgerEntry(mptoken, sponsor); diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp index 2db460076e0..af21c0d9905 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp @@ -93,16 +93,18 @@ MPTokenIssuanceCreate::create( if (!acct) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE - if (args.priorBalance && - *(args.priorBalance) < - view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) - return Unexpected(tecINSUFFICIENT_RESERVE); + auto const sponsor = getTxReserveSponsor(view, tx); + if (args.priorBalance) + { + if (auto const ret = checkInsufficientReserve( + view, acct, *(args.priorBalance), sponsor, 1); + !isTesSuccess(ret)) + return Unexpected(ret); // tecINSUFFICIENT_RESERVE + } auto const mptId = makeMptID(args.sequence, args.account); auto const mptIssuanceKeylet = keylet::mptIssuance(mptId); - auto const sponsor = getTxReserveSponsor(view, tx); - // create the MPTokenIssuance { auto const ownerNode = view.dirInsert( diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp index ab74e5ac395..d145f4b5be5 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp @@ -466,10 +466,13 @@ NFTokenAcceptOffer::transferNFToken( auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount); if (buyerOwnerCountAfter > buyerOwnerCountBefore) { - if (auto const reserve = - view().fees().accountReserve(buyerOwnerCountAfter); - buyerBalance < reserve) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = account_ == buyer + ? getTxReserveSponsor(ctx_.view(), ctx_.tx) + : std::optional>(); + if (auto const ret = checkInsufficientReserve( + ctx_.view(), sleBuyer, buyerBalance, sponsor, 0); + !isTesSuccess(ret)) + return ret; } } diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/xrpld/app/tx/detail/NFTokenMint.cpp index f139c40aedd..313111f08a9 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/xrpld/app/tx/detail/NFTokenMint.cpp @@ -352,9 +352,15 @@ NFTokenMint::doApply() view().read(keylet::account(account_))->getFieldU32(sfOwnerCount); ownerCountAfter > ownerCountBefore) { - if (auto const reserve = view().fees().accountReserve(ownerCountAfter); - mPriorBalance < reserve) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + ctx_.view(), + view().read(keylet::account(account_)), + mPriorBalance, + sponsor, + 0); + !isTesSuccess(ret)) + return ret; } return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.cpp b/src/xrpld/app/tx/detail/NFTokenUtils.cpp index f2b2e129400..6df2a053b69 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.cpp +++ b/src/xrpld/app/tx/detail/NFTokenUtils.cpp @@ -1044,12 +1044,14 @@ tokenOfferCreateApply( std::uint32_t txFlags) { Keylet const acctKeylet = keylet::account(acctID); - if (auto const acct = view.read(acctKeylet); - priorBalance < view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) - return tecINSUFFICIENT_RESERVE; + auto const acct = view.read(acctKeylet); + auto const sponsor = getTxReserveSponsor(view, tx); + if (auto const ret = + checkInsufficientReserve(view, acct, priorBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; auto const offerID = keylet::nftoffer(acctID, seqProxy.value()); - auto const sponsor = getTxReserveSponsor(view, tx); // Create the offer: { diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index abfc15a11ed..aaaee51f50a 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -204,13 +204,15 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) // Check reserve and funds availability { auto const balance = (*sle)[sfBalance]; - auto const reserve = - ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1); - - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; - - if (balance < reserve + ctx.tx[sfAmount]) + auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); + if (auto const ret = + checkInsufficientReserve(ctx.view, sle, balance, sponsor, 1); + !isTesSuccess(ret)) + return ret; + + if (auto const ret = checkInsufficientReserve( + ctx.view, sle, balance - ctx.tx[sfAmount], sponsor, 1); + !isTesSuccess(ret)) return tecUNFUNDED; } @@ -392,13 +394,19 @@ PayChanFund::doApply() { // Check reserve and funds availability auto const balance = (*sle)[sfBalance]; - auto const reserve = - ctx_.view().fees().accountReserve((*sle)[sfOwnerCount]); - - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; - - if (balance < reserve + ctx_.tx[sfAmount]) + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + if (auto const ret = + checkInsufficientReserve(ctx_.view(), sle, balance, sponsor, 0); + !isTesSuccess(ret)) + return ret; + + if (auto const ret = checkInsufficientReserve( + ctx_.view(), + sle, + balance - ctx_.tx[sfAmount], + std::optional>(), + 0); + !isTesSuccess(ret)) return tecUNFUNDED; } diff --git a/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp b/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp index b80cf88504f..17c5da3740d 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp +++ b/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp @@ -121,10 +121,11 @@ PermissionedDomainSet::doApply() // Create new permissioned domain. // Check reserve availability for new object creation auto const balance = STAmount((*ownerSle)[sfBalance]).xrp(); - auto const reserve = - ctx_.view().fees().accountReserve((*ownerSle)[sfOwnerCount] + 1); - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + ctx_.view(), ownerSle, balance, sponsor, 1); + !isTesSuccess(ret)) + return ret; Keylet const pdKeylet = keylet::permissionedDomain( account_, ctx_.tx.getFieldU32(sfSequence)); @@ -142,7 +143,6 @@ PermissionedDomainSet::doApply() slePd->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); adjustOwnerCount(view(), ownerSle, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(slePd, sponsor); view().insert(slePd); diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index 7e7cff6e051..6c5d99ae6f5 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -172,12 +172,12 @@ SetOracle::preclaim(PreclaimContext const& ctx) if (pairs.size() > maxOracleDataSeries) return tecARRAY_TOO_LARGE; - auto const reserve = ctx.view.fees().accountReserve( - sleSetter->getFieldU32(sfOwnerCount) + adjustReserve); auto const& balance = sleSetter->getFieldAmount(sfBalance); - - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); + if (auto const ret = checkInsufficientReserve( + ctx.view, sleSetter, balance, sponsor, adjustReserve); + !isTesSuccess(ret)) + return ret; return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/SetSignerList.cpp b/src/xrpld/app/tx/detail/SetSignerList.cpp index c3c7c2a3547..418d542e4c8 100644 --- a/src/xrpld/app/tx/detail/SetSignerList.cpp +++ b/src/xrpld/app/tx/detail/SetSignerList.cpp @@ -354,9 +354,6 @@ SetSignerList::replaceSignerList() if (!sle) return tefINTERNAL; - // Compute new reserve. Verify the account has funds to meet the reserve. - std::uint32_t const oldOwnerCount{(*sle)[sfOwnerCount]}; - // The required reserve changes based on featureMultiSignReserve... int addedOwnerCount{1}; std::uint32_t flags{lsfOneOwnerCount}; @@ -367,14 +364,14 @@ SetSignerList::replaceSignerList() flags = 0; } - XRPAmount const newReserve{ - view().fees().accountReserve(oldOwnerCount + addedOwnerCount)}; - // We check the reserve against the starting balance because we want to // allow dipping into the reserve to pay fees. This behavior is consistent // with CreateTicket. - if (mPriorBalance < newReserve) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + if (auto const ret = checkInsufficientReserve( + ctx_.view(), sle, mPriorBalance, sponsor, addedOwnerCount); + !isTesSuccess(ret)) + return ret; // Everything's ducky. Add the ltSIGNER_LIST to the ledger. auto signerList = std::make_shared(signerListKeylet); @@ -396,7 +393,6 @@ SetSignerList::replaceSignerList() // If we succeeded, the new entry counts against the // creator's reserve. - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); adjustOwnerCount(view(), sle, sponsor, addedOwnerCount, viewJ); addSponsorToLedgerEntry(signerList, sponsor); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index ef7061bb2eb..25b9eb33b6f 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -402,9 +402,7 @@ SetTrust::doApply() // well. A person with no intention of using the gateway // could use the extra XRP for their own purposes. - XRPAmount const reserveCreate( - (uOwnerCount < 2) ? XRPAmount(beast::zero) - : view().fees().accountReserve(uOwnerCount + 1)); + bool const freeTrustLine = uOwnerCount < 2; std::uint32_t uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0); std::uint32_t uQualityOut( @@ -455,6 +453,8 @@ SetTrust::doApply() SLE::pointer sleRippleState = view().peek(keylet::line(account_, uDstAccountID, currency)); + auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + if (sleRippleState) { STAmount saLowBalance; @@ -678,7 +678,9 @@ SetTrust::doApply() view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if (bReserveIncrease && mPriorBalance < reserveCreate) + else if (auto const ret = checkInsufficientReserve( + view(), sle, mPriorBalance, sponsor, 0); + !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -707,8 +709,14 @@ SetTrust::doApply() << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if (mPriorBalance < reserveCreate) // Reserve is not scaled by - // load. + else if (auto const ret = checkInsufficientReserve( + ctx_.view(), + sle, + mPriorBalance, + sponsor, + 1); + !freeTrustLine && + !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/xrpld/app/tx/detail/VaultCreate.cpp index 77ed8c8c76a..ce5b0d6c4ef 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/xrpld/app/tx/detail/VaultCreate.cpp @@ -177,9 +177,10 @@ VaultCreate::doApply() auto const sponsor = getTxReserveSponsor(view(), tx); adjustOwnerCount(view(), owner, sponsor, 1, j_); addSponsorToLedgerEntry(vault, sponsor); - auto ownerCount = owner->at(sfOwnerCount); - if (mPriorBalance < view().fees().accountReserve(ownerCount)) - return tecINSUFFICIENT_RESERVE; + if (auto const ret = + checkInsufficientReserve(view(), owner, mPriorBalance, sponsor, 0); + !isTesSuccess(ret)) + return ret; auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID); if (!maybePseudo) diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index 5c7a6dc0d65..56f493e003b 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -1066,11 +1066,11 @@ applyCreateAccountAttestations( // Check reserve auto const balance = (*sleDoor)[sfBalance]; - auto const reserve = - psb.fees().accountReserve((*sleDoor)[sfOwnerCount] + 1); - - if (balance < reserve) - return Unexpected(tecINSUFFICIENT_RESERVE); + auto const sponsor = std::optional>(); + if (auto const ret = + checkInsufficientReserve(psb, sleDoor, balance, sponsor, 1); + !isTesSuccess(ret)) + return Unexpected(ret); // tecINSUFFICIENT_RESERVE } std::vector atts; @@ -1507,11 +1507,11 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) return terNO_ACCOUNT; auto const balance = (*sleAcc)[sfBalance]; - auto const reserve = - ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1); - - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); + if (auto const ret = + checkInsufficientReserve(ctx.view, sleAcc, balance, sponsor, 1); + !isTesSuccess(ret)) + return ret; } return tesSUCCESS; @@ -2074,11 +2074,11 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) return terNO_ACCOUNT; auto const balance = (*sleAcc)[sfBalance]; - auto const reserve = - ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1); - - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; + auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); + if (auto const ret = + checkInsufficientReserve(ctx.view, sleAcc, balance, sponsor, 1); + !isTesSuccess(ret)) + return ret; } return tesSUCCESS; diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index c7f45184ef2..2c797a71201 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -452,11 +452,22 @@ areCompatible( beast::Journal::Stream& s, char const* reason); +TER +checkInsufficientReserve( + ReadView const& view, + std::shared_ptr accSle, + STAmount const& accBalance, + std::optional> const& _sponsorSle, + std::int32_t ownerCountDelta); + std::optional> getTxReserveSponsor(ApplyView& view, STTx const& tx); +std::optional> +getTxReserveSponsor(ReadView const& view, STTx const& tx); + std::optional> -getLedgerEntryReserveSponsor(ApplyView& view, SLE::ref sle); +getLedgerEntryReserveSponsor(ApplyView& view, std::shared_ptr sle); void addSponsorToLedgerEntry( diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index afa6ce6a214..3017ebb7291 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1021,6 +1021,43 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) return std::nullopt; } +TER +checkInsufficientReserve( + ReadView const& view, + std::shared_ptr accSle, + STAmount const& accBalance, + std::optional> const& _sponsorSle, + std::int32_t ownerCountDelta) +{ + if (_sponsorSle.has_value()) + { + auto const sponsorSle = _sponsorSle.value(); + auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance); + STAmount const sponsorReserve{view.fees().accountReserve( + sponsorSle->getFieldU32(sfOwnerCount), + sponsorSle->getFieldU32(sfSponsoredOwnerCount), + sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ownerCountDelta, + sponsorSle->isFieldPresent(sfSponsor), + sponsorSle->getFieldU32(sfSponsoringAccountCount))}; + + if (sponsorBalance < sponsorReserve) + return tecINSUFFICIENT_RESERVE; + } + else + { + STAmount const reserve{view.fees().accountReserve( + accSle->getFieldU32(sfOwnerCount) + ownerCountDelta, + accSle->getFieldU32(sfSponsoredOwnerCount), + accSle->getFieldU32(sfSponsoringOwnerCount), + accSle->isFieldPresent(sfSponsor), + accSle->getFieldU32(sfSponsoringAccountCount))}; + + if (accBalance < reserve) + return tecINSUFFICIENT_RESERVE; + } + return tesSUCCESS; +} + std::optional> getTxReserveSponsor(ApplyView& view, STTx const& tx) { @@ -1037,8 +1074,24 @@ getTxReserveSponsor(ApplyView& view, STTx const& tx) return std::nullopt; } +std::optional> +getTxReserveSponsor(ReadView const& view, STTx const& tx) +{ + if (tx.isFieldPresent(sfSponsor)) + { + auto const sponsorObj = tx.getFieldObject(sfSponsor); + auto const flags = sponsorObj.getFlags(); + auto const sponsorID = sponsorObj.getAccountID(sfAccount); + if (flags & tfSponsorReserve) + { + return view.read(keylet::account(sponsorID)); + } + } + return std::nullopt; +} + std::optional> -getLedgerEntryReserveSponsor(ApplyView& view, SLE::ref sle) +getLedgerEntryReserveSponsor(ApplyView& view, std::shared_ptr sle) { if (sle->isFieldPresent(sfSponsorAccount)) return view.peek(keylet::account(sle->getAccountID(sfSponsorAccount))); From 0bddc9544483ebd150308f9fd9c4f3ec29532b25 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 Aug 2025 01:03:14 +0900 Subject: [PATCH 008/249] clang-format --- include/xrpl/protocol/STObject.h | 4 ++-- src/libxrpl/protocol/STObject.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index fbad0facca0..8512e1d6240 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -244,9 +244,9 @@ class STObject : public STBase, public CountedObject getFieldV256(SField const& field) const; STArray const& getFieldArray(SField const& field) const; - const STObject& + STObject const& getFieldObject(SField const& field) const; - const STCurrency& + STCurrency const& getFieldCurrency(SField const& field) const; STNumber const& getFieldNumber(SField const& field) const; diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index d2c76e4b0fd..a5f1737418b 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -689,7 +689,7 @@ STObject::getFieldArray(SField const& field) const return getFieldByConstRef(field, empty); } -const STObject& +STObject const& STObject::getFieldObject(SField const& field) const { static STObject const empty(field); From 6e01f233e42c8e440b884ec9e50ca8f0f75a2948 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 Aug 2025 01:59:08 +0900 Subject: [PATCH 009/249] fix `fees().accountReserve(0)` to `fees().reserve` --- src/xrpld/app/misc/FeeVoteImpl.cpp | 6 +++--- src/xrpld/app/misc/NetworkOPs.cpp | 12 ++++-------- src/xrpld/app/misc/detail/TxQ.cpp | 2 +- src/xrpld/app/paths/PathRequest.cpp | 3 +-- src/xrpld/app/paths/Pathfinder.cpp | 2 +- src/xrpld/app/tx/detail/Payment.cpp | 5 +++-- src/xrpld/app/tx/detail/XChainBridge.cpp | 3 ++- src/xrpld/ledger/detail/View.cpp | 2 +- 8 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/xrpld/app/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index 85b5791d675..c688cd3b19e 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -142,7 +142,7 @@ FeeVoteImpl::doValidation( }; vote(lastFees.base, target_.reference_fee, "base fee", sfBaseFeeDrops); vote( - lastFees.accountReserve(0), + lastFees.reserve, target_.account_reserve, "base reserve", sfReserveBaseDrops); @@ -178,7 +178,7 @@ FeeVoteImpl::doValidation( vote(lastFees.base, target_.reference_fee, to64, "base fee", sfBaseFee); vote( - lastFees.accountReserve(0), + lastFees.reserve, target_.account_reserve, to32, "base reserve", @@ -207,7 +207,7 @@ FeeVoteImpl::doVoting( lastClosedLedger->fees().base, target_.reference_fee); detail::VotableValue baseReserveVote( - lastClosedLedger->fees().accountReserve(0), target_.account_reserve); + lastClosedLedger->fees().reserve, target_.account_reserve); detail::VotableValue incReserveVote( lastClosedLedger->fees().increment, target_.owner_reserve); diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index 3220ce99fcf..8a4574b18aa 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -2925,8 +2925,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) if (!human) { l[jss::base_fee] = baseFee.jsonClipped(); - l[jss::reserve_base] = - lpClosed->fees().accountReserve(0).jsonClipped(); + l[jss::reserve_base] = lpClosed->fees().reserve.jsonClipped(); l[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); l[jss::close_time] = Json::Value::UInt( lpClosed->info().closeTime.time_since_epoch().count()); @@ -2934,8 +2933,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) else { l[jss::base_fee_xrp] = baseFee.decimalXRP(); - l[jss::reserve_base_xrp] = - lpClosed->fees().accountReserve(0).decimalXRP(); + l[jss::reserve_base_xrp] = lpClosed->fees().reserve.decimalXRP(); l[jss::reserve_inc_xrp] = lpClosed->fees().increment.decimalXRP(); if (auto const closeOffset = app_.timeKeeper().closeOffset(); @@ -3125,8 +3123,7 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) if (!lpAccepted->rules().enabled(featureXRPFees)) jvObj[jss::fee_ref] = Config::FEE_UNITS_DEPRECATED; jvObj[jss::fee_base] = lpAccepted->fees().base.jsonClipped(); - jvObj[jss::reserve_base] = - lpAccepted->fees().accountReserve(0).jsonClipped(); + jvObj[jss::reserve_base] = lpAccepted->fees().reserve.jsonClipped(); jvObj[jss::reserve_inc] = lpAccepted->fees().increment.jsonClipped(); @@ -4177,8 +4174,7 @@ NetworkOPsImp::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) if (!lpClosed->rules().enabled(featureXRPFees)) jvResult[jss::fee_ref] = Config::FEE_UNITS_DEPRECATED; jvResult[jss::fee_base] = lpClosed->fees().base.jsonClipped(); - jvResult[jss::reserve_base] = - lpClosed->fees().accountReserve(0).jsonClipped(); + jvResult[jss::reserve_base] = lpClosed->fees().reserve.jsonClipped(); jvResult[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); jvResult[jss::network_id] = app_.config().NETWORK_ID; } diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 6924dae6c82..7c0a6f07e23 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -1113,7 +1113,7 @@ TxQ::apply( comparable scale to the base fee, ignore the reserve. Only check the account balance. */ - auto const reserve = view.fees().accountReserve(0); + auto const reserve = view.fees().reserve; auto const base = view.fees().base; if (totalFee >= balance || (reserve > 10 * base && totalFee >= reserve)) diff --git a/src/xrpld/app/paths/PathRequest.cpp b/src/xrpld/app/paths/PathRequest.cpp index 8a88e774d0f..a9db61d5b37 100644 --- a/src/xrpld/app/paths/PathRequest.cpp +++ b/src/xrpld/app/paths/PathRequest.cpp @@ -206,8 +206,7 @@ PathRequest::isValid(std::shared_ptr const& crCache) return false; } - if (!convert_all_ && - saDstAmount < STAmount(lrLedger->fees().accountReserve(0))) + if (!convert_all_ && saDstAmount < STAmount(lrLedger->fees().reserve)) { // Payment must meet reserve. jvStatus = rpcError(rpcDST_AMT_MALFORMED); diff --git a/src/xrpld/app/paths/Pathfinder.cpp b/src/xrpld/app/paths/Pathfinder.cpp index 74a33ec917c..d9698bf5e6f 100644 --- a/src/xrpld/app/paths/Pathfinder.cpp +++ b/src/xrpld/app/paths/Pathfinder.cpp @@ -278,7 +278,7 @@ Pathfinder::findPaths( return false; } - auto const reserve = STAmount(mLedger->fees().accountReserve(0)); + auto const reserve = STAmount(mLedger->fees().reserve); if (mDstAmount < reserve) { JLOG(j_.debug()) diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 9e5bc93a8d0..ff54f48fa57 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -316,7 +316,7 @@ Payment::preclaim(PreclaimContext const& ctx) // transaction would succeed. return telNO_DST_PARTIAL; } - else if (dstAmount < STAmount(ctx.view.fees().accountReserve(0))) + else if (dstAmount < STAmount(ctx.view.fees().reserve)) { // accountReserve is the minimum amount that an account can have. // Reserve is not scaled by load. @@ -608,6 +608,7 @@ Payment::doApply() auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount); // This is the total reserve in drops. + // TODO: TEQU auto const reserve = view().fees().accountReserve(ownerCount); // mPriorBalance is the balance on the sending account BEFORE the @@ -659,7 +660,7 @@ Payment::doApply() // to get the account un-wedged. // Get the base reserve. - XRPAmount const dstReserve{view().fees().accountReserve(0)}; + XRPAmount const dstReserve{view().fees().reserve}; if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve) diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index 56f493e003b..27683a16cf1 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -446,6 +446,7 @@ transferHelper( { auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount); + // TODO: TEQU auto const reserve = psb.fees().accountReserve(ownerCount); auto const availableBalance = [&]() -> STAmount { @@ -474,7 +475,7 @@ transferHelper( // Already checked, but OK to check again return tecNO_DST; } - if (amt < psb.fees().accountReserve(0)) + if (amt < psb.fees().reserve) { JLOG(j.trace()) << "Insufficient payment to create account."; return tecNO_DST_INSUF_XRP; diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 3017ebb7291..a6c0423e92a 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -630,7 +630,7 @@ xrpLiquid( // AMMs have no reserve requirement auto const reserve = sle->isFieldPresent(sfAMMID) - ? XRPAmount{0} + ? XRPAmount{0} // TODO: TEQU : view.fees().accountReserve(ownerCount); auto const fullBalance = sle->getFieldAmount(sfBalance); From abd8620c97ff304b3af6c089dd78405e4bdd835c Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 Aug 2025 13:23:41 +0900 Subject: [PATCH 010/249] Add SponsorTransfer --- .../xrpl/protocol/detail/transactions.macro | 5 + src/test/app/Sponsor_test.cpp | 137 +++++++++ src/test/jtx/impl/sponsor.cpp | 18 +- src/test/jtx/sponsor.h | 9 +- src/xrpld/app/tx/detail/SponsorTransfer.cpp | 285 ++++++++++++++++++ src/xrpld/app/tx/detail/SponsorTransfer.h | 48 +++ src/xrpld/app/tx/detail/applySteps.cpp | 1 + 7 files changed, 485 insertions(+), 18 deletions(-) create mode 100644 src/xrpld/app/tx/detail/SponsorTransfer.cpp create mode 100644 src/xrpld/app/tx/detail/SponsorTransfer.h diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 89e9a16df5e..fc3429e0ed5 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -525,6 +525,11 @@ TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, ({ {sfBatchSigners, soeOPTIONAL}, })) +/** This transaction transfer sponsor */ +TRANSACTION(ttSPONSOR_TRANSFER, 72, SponsorTransfer, Delegation::notDelegatable, ({ + {sfLedgerIndex, soeOPTIONAL}, +})) + /** This system-generated transaction type is used to update the status of the various amendments. For details, see: https://xrpl.org/amendments.html diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 55d2a0137cf..1578388b6e7 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -46,6 +47,141 @@ class Sponsor_test : public beast::unit_test::suite sponsor::as(sponsor), sponsor::sig(sponsor), ter(temDISABLED)); + + env(sponsor::transfer(alice), ter(temDISABLED)); + } + + void + testTransferSponsor() + { + testcase("Transfer Sponsor"); + using namespace test::jtx; + + { + // sponsor account + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, sponsor1, sponsor2); + + env(sponsor::transfer(alice), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1)); + env.close(); + + env.require(sponsored_owners(alice, 0)); + env.require(sponsored_owners(sponsor1, 0)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor1, 0)); + env.require(sponsoring_account_count(alice, 0)); + env.require(sponsoring_account_count(sponsor1, 1)); + auto const sle1 = env.le(keylet::account(alice)); + BEAST_EXPECT(sle1->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); + + // transfer sponsor + env(sponsor::transfer(alice), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + env.require(sponsored_owners(alice, 0)); + env.require(sponsored_owners(sponsor1, 0)); + env.require(sponsored_owners(sponsor2, 0)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor1, 0)); + env.require(sponsoring_owners(sponsor2, 0)); + env.require(sponsoring_account_count(alice, 0)); + env.require(sponsoring_account_count(sponsor1, 0)); + env.require(sponsoring_account_count(sponsor2, 1)); + auto const sle2 = env.le(keylet::account(alice)); + BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); + + // dissolve sponsor + env(sponsor::transfer(alice)); + env.close(); + + env.require(sponsored_owners(alice, 0)); + env.require(sponsored_owners(sponsor1, 0)); + env.require(sponsored_owners(sponsor2, 0)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor1, 0)); + env.require(sponsoring_owners(sponsor2, 0)); + env.require(sponsoring_account_count(alice, 0)); + env.require(sponsoring_account_count(sponsor1, 0)); + env.require(sponsoring_account_count(sponsor2, 0)); + auto const sle3 = env.le(keylet::account(alice)); + BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); + } + { + // sponsor object + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1)); + env.close(); + + env.require(owners(alice, 1)); + env.require(sponsored_owners(alice, 1)); + env.require(sponsored_owners(sponsor1, 0)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor1, 1)); + env.require(sponsoring_account_count(alice, 0)); + env.require(sponsoring_account_count(sponsor1, 0)); + auto const sle1 = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(sle1->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); + + // transfer sponsor + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + env.require(sponsored_owners(alice, 1)); + env.require(sponsored_owners(sponsor1, 0)); + env.require(sponsored_owners(sponsor2, 0)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor1, 0)); + env.require(sponsoring_owners(sponsor2, 1)); + env.require(sponsoring_account_count(alice, 0)); + env.require(sponsoring_account_count(sponsor1, 0)); + env.require(sponsoring_account_count(sponsor2, 0)); + auto const sle2 = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); + + // dissolve sponsor + env(sponsor::transfer(alice, checkId)); + env.close(); + + env.require(sponsored_owners(alice, 0)); + env.require(sponsored_owners(sponsor1, 0)); + env.require(sponsored_owners(sponsor2, 0)); + env.require(sponsoring_owners(alice, 0)); + env.require(sponsoring_owners(sponsor1, 0)); + env.require(sponsoring_owners(sponsor2, 0)); + env.require(sponsoring_account_count(alice, 0)); + env.require(sponsoring_account_count(sponsor1, 0)); + env.require(sponsoring_account_count(sponsor2, 0)); + auto const sle3 = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); + } } void @@ -455,6 +591,7 @@ class Sponsor_test : public beast::unit_test::suite run() override { testDisabled(); + testTransferSponsor(); testSponsorFee(); testSponsorAccount(); testSponsorReserve(); diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 2db16a80db5..385395f8e52 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -30,21 +30,13 @@ namespace jtx { namespace sponsor { Json::Value -transferAccount(jtx::Account const& account) +transfer(jtx::Account const& account, std::optional const& index) { Json::Value jv; - jv[jss::TransactionType] = "SponsorTransfer"; - jv[sfSponsor.jsonName] = account.human(); - - return jv; -} - -Json::Value -transferObject(uint256 const& id) -{ - Json::Value jv; - jv[jss::TransactionType] = "SponsorTransfer"; - jv[sfLedgerIndex.jsonName] = to_string(id); + jv[jss::TransactionType] = jss::SponsorTransfer; + jv[jss::Account] = account.human(); + if (index) + jv[sfLedgerIndex.jsonName] = to_string(*index); return jv; } diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index ec0c813158f..e09ccd26bd1 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -30,11 +30,10 @@ namespace jtx { namespace sponsor { -// Json::Value -// transferAccount(jtx::Account const& account); - -// Json::Value -// transferObject(uint256 const& id); +Json::Value +transfer( + jtx::Account const& account, + std::optional const& index = std::nullopt); struct as { diff --git a/src/xrpld/app/tx/detail/SponsorTransfer.cpp b/src/xrpld/app/tx/detail/SponsorTransfer.cpp new file mode 100644 index 00000000000..72c05eaef70 --- /dev/null +++ b/src/xrpld/app/tx/detail/SponsorTransfer.cpp @@ -0,0 +1,285 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +NotTEC +SponsorTransfer::preflight(PreflightContext const& ctx) +{ + if (!ctx.rules.enabled(featureSponsor)) + return temDISABLED; + + if (auto const ter = preflight1(ctx)) + return ter; + + if (ctx.tx.getFlags() & tfUniversalMask) + return temINVALID_FLAG; + + return preflight2(ctx); +} + +template +inline std::optional +getLedgerEntryOwner( + ReadView const& view, + T const& sle, + AccountID const& account) +{ + switch (sle->getType()) + { + case ltNFTOKEN_OFFER: + case ltORACLE: + case ltPERMISSIONED_DOMAIN: + case ltVAULT: + return sle->getAccountID(sfOwner); + case ltCHECK: + case ltDID: + case ltTICKET: + case ltOFFER: + case ltXCHAIN_OWNED_CLAIM_ID: + case ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID: + case ltESCROW: + case ltPAYCHAN: + case ltMPTOKEN: + case ltDELEGATE: + case ltBRIDGE: + case ltDEPOSIT_PREAUTH: + return sle->getAccountID(sfAccount); + case ltMPTOKEN_ISSUANCE: + return sle->getAccountID(sfIssuer); + case ltSIGNER_LIST: { + auto const signerList = view.read(keylet::signers(account)); + if (!signerList) + return std::nullopt; + if (signerList->getFieldH256(sfLedgerIndex) == + sle->getFieldH256(sfLedgerIndex)) + return account; + return std::nullopt; + } + case ltCREDENTIAL: { + if (sle->getFlags() & lsfAccepted) + return sle->getAccountID(sfSubject); + return sle->getAccountID(sfIssuer); + } + // case ltNFTOKEN_PAGE: + // case ltRIPPLE_STATE: + case ltACCOUNT_ROOT: { + // AccountRoot is not supported for object sponsorship + return std::nullopt; + } + case ltNEGATIVE_UNL: + case ltDIR_NODE: + case ltAMENDMENTS: + case ltLEDGER_HASHES: + case ltFEE_SETTINGS: + case ltAMM: + return std::nullopt; + default: + return std::nullopt; + }; +} + +TER +SponsorTransfer::preclaim(PreclaimContext const& ctx) +{ + auto const index = ctx.tx[~sfLedgerIndex]; + + if (index) + { + auto const sle = ctx.view.read(keylet::unchecked(*index)); + if (!sle) + return tecNO_ENTRY; + + auto const owner = + getLedgerEntryOwner(ctx.view, sle, ctx.tx[sfAccount]); + if (!owner) + return tecNO_PERMISSION; + + if (sle->isFieldPresent(sfSponsorAccount)) + { + auto const sponsor = sle->getAccountID(sfSponsorAccount); + if (sponsor == owner) + return tecNO_PERMISSION; + + // TODO: check reserve + } + else + { + // TODO: check reserve + } + } + else + { + auto const accSle = ctx.view.read(keylet::account(ctx.tx[sfAccount])); + if (!accSle) + return tecINTERNAL; + + if (accSle->isFieldPresent(sfSponsorAccount)) + { + // TODO: check reserve + } + else + { + // TODO: check reserve + } + } + + return tesSUCCESS; +} + +TER +SponsorTransfer::doApply() +{ + auto const& tx = ctx_.tx; + + auto const index = tx[~sfLedgerIndex]; + + auto const accSle = view().peek(keylet::account(account_)); + if (!accSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (index) + { + // transfer object sponsor + auto const objSle = view().peek(keylet::unchecked(*index)); + if (!objSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const owner = getLedgerEntryOwner(view(), objSle, account_); + if (!owner) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const ownerSle = view().peek(keylet::account(*owner)); + if (!ownerSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (tx.isFieldPresent(sfSponsor)) + { + auto const sponsorObj = tx.getFieldObject(sfSponsor); + auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); + auto const newSponsor = sponsorObj[sfAccount]; + // decrement old sponsoring count if exists + if (auto const oldSponsorSle = + view().peek(keylet::account(oldSponsor))) + { + oldSponsorSle->setFieldU32( + sfSponsoringOwnerCount, + oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - 1); + view().update(oldSponsorSle); + } + else + { + // update owner's sponsored count + ownerSle->setFieldU32( + sfSponsoredOwnerCount, + ownerSle->getFieldU32(sfSponsoredOwnerCount) + 1); + view().update(ownerSle); + } + // increment new sponsoring count + auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + newSponsorSle->setFieldU32( + sfSponsoringOwnerCount, + newSponsorSle->getFieldU32(sfSponsoringOwnerCount) + 1); + view().update(newSponsorSle); + + objSle->setAccountID(sfSponsorAccount, newSponsor); + view().update(objSle); + } + else + { + // dissolve object sponsor + auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); + // decrement sponsored count + accSle->setFieldU32( + sfSponsoredOwnerCount, + accSle->getFieldU32(sfSponsoredOwnerCount) - 1); + view().update(accSle); + // decrement old sponsoring count + if (auto const oldSponsorSle = + view().peek(keylet::account(oldSponsor))) + { + oldSponsorSle->setFieldU32( + sfSponsoringOwnerCount, + oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - 1); + view().update(oldSponsorSle); + } + + // remove sponsor from object + objSle->makeFieldAbsent(sfSponsorAccount); + view().update(objSle); + } + } + else + { + // Transfer Account sponsor + if (tx.isFieldPresent(sfSponsor)) + { + // transfer account sponsor + auto const sponsorObj = tx.getFieldObject(sfSponsor); + // increment new sponsoring count + auto const newSponsor = sponsorObj[sfAccount]; + auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + newSponsorSle->setFieldU32( + sfSponsoringAccountCount, + newSponsorSle->getFieldU32(sfSponsoringAccountCount) + 1); + view().update(newSponsorSle); + // decrement old sponsoring count + if (accSle->isFieldPresent(sfSponsorAccount)) + { + auto const oldSponsor = accSle->getAccountID(sfSponsorAccount); + auto const oldSponsorSle = + view().peek(keylet::account(oldSponsor)); + oldSponsorSle->setFieldU32( + sfSponsoringAccountCount, + oldSponsorSle->getFieldU32(sfSponsoringAccountCount) - 1); + view().update(oldSponsorSle); + } + accSle->setAccountID(sfSponsorAccount, newSponsor); + view().update(accSle); + } + else + { + // dissolve account sponsor + auto const oldSponsor = accSle->getAccountID(sfSponsorAccount); + accSle->makeFieldAbsent(sfSponsorAccount); + // decrement account sponsoring count + auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); + oldSponsorSle->setFieldU32( + sfSponsoringAccountCount, + oldSponsorSle->getFieldU32(sfSponsoringAccountCount) - 1); + view().update(oldSponsorSle); + } + } + + return tesSUCCESS; +} + +} // namespace ripple diff --git a/src/xrpld/app/tx/detail/SponsorTransfer.h b/src/xrpld/app/tx/detail/SponsorTransfer.h new file mode 100644 index 00000000000..68f596bcd08 --- /dev/null +++ b/src/xrpld/app/tx/detail/SponsorTransfer.h @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_SPONSORTRANSFER_H_INCLUDED +#define RIPPLE_TX_SPONSORTRANSFER_H_INCLUDED + +#include + +namespace ripple { + +class SponsorTransfer : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit SponsorTransfer(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace ripple + +#endif diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index 34259ebef0d..f47d47ebef3 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include From 5e65813a52b23f2d431248fa236dcb07dfc62b81 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 Aug 2025 18:21:44 +0900 Subject: [PATCH 011/249] add reserve checks for SponsorTransfer --- include/xrpl/protocol/Fees.h | 1 - src/test/app/Sponsor_test.cpp | 75 ++++++++++++++++++++- src/xrpld/app/tx/detail/SponsorTransfer.cpp | 71 ++++++++++++++----- src/xrpld/ledger/View.h | 3 +- src/xrpld/ledger/detail/View.cpp | 12 ++-- 5 files changed, 137 insertions(+), 25 deletions(-) diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 937915c8f66..f488c934285 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -53,7 +53,6 @@ struct Fees bool isAccountSponsored = false, std::size_t sponsoringAccountCount = 0) const { - // return reserve + ownerCount * increment; return (isAccountSponsored ? XRPAmount(0) : reserve) + increment * (ownerCount + sponsoringOwnerCount - sponsoredOwnerCount) + diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 1578388b6e7..8f789f5755e 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -63,7 +63,17 @@ class Sponsor_test : public beast::unit_test::suite Account const alice("alice"); Account const sponsor1("sponsor1"); Account const sponsor2("sponsor2"); - env.fund(XRP(10000), alice, sponsor1, sponsor2); + env.fund(XRP(10000), alice); + env.fund(env.current()->fees().reserve * 2 - 1, sponsor1, sponsor2); + env.close(); + + env(sponsor::transfer(alice), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1), + ter(tecINSUFFICIENT_RESERVE)); + + env(pay(alice, sponsor1, drops(1))); + env.close(); env(sponsor::transfer(alice), sponsor::as(sponsor1, tfSponsorReserve), @@ -81,6 +91,14 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); // transfer sponsor + env(sponsor::transfer(alice), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2), + ter(tecINSUFFICIENT_RESERVE)); + + env(pay(alice, sponsor2, drops(1))); + env.close(); + env(sponsor::transfer(alice), sponsor::as(sponsor2, tfSponsorReserve), sponsor::sig(sponsor2)); @@ -100,6 +118,21 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); // dissolve sponsor + env(pay(alice, + sponsor2, + (env.balance(alice).value() - + env.current()->fees().reserve - XRP(1) + drops(1))), + fee(XRP(1))); + env.close(); + + env.require( + balance(alice, env.current()->fees().reserve - drops(1))); + env(sponsor::transfer(alice), ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + env(pay(sponsor2, alice, XRP(1))); + env.close(); + env(sponsor::transfer(alice)); env.close(); @@ -122,7 +155,13 @@ class Sponsor_test : public beast::unit_test::suite Account const bob("bob"); Account const sponsor1("sponsor1"); Account const sponsor2("sponsor2"); - env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + env.fund(XRP(10000), alice, bob); + env.fund( + env.current()->fees().reserve + + env.current()->fees().increment - drops(1), + sponsor1, + sponsor2); + env.close(); auto const seq = env.seq(alice); env(check::create(alice, bob, XRP(1))); @@ -131,6 +170,15 @@ class Sponsor_test : public beast::unit_test::suite auto const checkId = keylet::check(alice, seq).key; BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + env(pay(alice, sponsor1, drops(1))); + env.close(); + env(sponsor::transfer(alice, checkId), sponsor::as(sponsor1, tfSponsorReserve), sponsor::sig(sponsor1)); @@ -148,6 +196,14 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); // transfer sponsor + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2), + ter(tecINSUFFICIENT_RESERVE)); + + env(pay(alice, sponsor2, drops(1))); + env.close(); + env(sponsor::transfer(alice, checkId), sponsor::as(sponsor2, tfSponsorReserve), sponsor::sig(sponsor2)); @@ -167,6 +223,21 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); // dissolve sponsor + env(pay(alice, + sponsor2, + (env.balance(alice).value() - + env.current()->fees().reserve - + env.current()->fees().increment - XRP(1) + drops(1))), + fee(XRP(1))); + env.close(); + + env(sponsor::transfer(alice, checkId), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + env(pay(sponsor2, alice, XRP(1))); + env.close(); + env(sponsor::transfer(alice, checkId)); env.close(); diff --git a/src/xrpld/app/tx/detail/SponsorTransfer.cpp b/src/xrpld/app/tx/detail/SponsorTransfer.cpp index 72c05eaef70..d58d0b511f3 100644 --- a/src/xrpld/app/tx/detail/SponsorTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorTransfer.cpp @@ -111,8 +111,15 @@ TER SponsorTransfer::preclaim(PreclaimContext const& ctx) { auto const index = ctx.tx[~sfLedgerIndex]; + auto const newSponsor = getTxReserveSponsor(ctx.view, ctx.tx); - if (index) + bool const isObjectSponsor = index != std::nullopt; + + auto const accSle = ctx.view.read(keylet::account(ctx.tx[sfAccount])); + if (!accSle) + return tecINTERNAL; + + if (isObjectSponsor) { auto const sle = ctx.view.read(keylet::unchecked(*index)); if (!sle) @@ -123,33 +130,64 @@ SponsorTransfer::preclaim(PreclaimContext const& ctx) if (!owner) return tecNO_PERMISSION; - if (sle->isFieldPresent(sfSponsorAccount)) + if (newSponsor) { - auto const sponsor = sle->getAccountID(sfSponsorAccount); - if (sponsor == owner) - return tecNO_PERMISSION; - - // TODO: check reserve + if (sle->isFieldPresent(sfSponsorAccount)) + { + // transfer sponsor + if ((*newSponsor)->getAccountID(sfAccount) == owner) + return tecNO_PERMISSION; + } } else { - // TODO: check reserve + // dissolve sponsor + // check object is sponsored + if (!sle->isFieldPresent(sfSponsorAccount)) + return tecNO_PERMISSION; } + + // check account have sufficient balance + if (auto const ter = checkInsufficientReserve( + ctx.view, + accSle, + accSle->getFieldAmount(sfBalance), + newSponsor, + // TODO: address variable ownerCount like PriceOracle + 1); + !isTesSuccess(ter)) + return ter; } else { - auto const accSle = ctx.view.read(keylet::account(ctx.tx[sfAccount])); - if (!accSle) - return tecINTERNAL; - - if (accSle->isFieldPresent(sfSponsorAccount)) + if (newSponsor) { - // TODO: check reserve + if (accSle->isFieldPresent(sfSponsorAccount)) + { + // check not same account + if ((*newSponsor)->getAccountID(sfAccount) == + accSle->getAccountID(sfAccount)) + return tecNO_PERMISSION; + } } else { - // TODO: check reserve + // dissolve sponsor + // check account is sponsored + if (!accSle->isFieldPresent(sfSponsorAccount)) + return tecNO_PERMISSION; } + + // check account have sufficient balance + if (auto const ter = checkInsufficientReserve( + ctx.view, + accSle, + accSle->getFieldAmount(sfBalance), + newSponsor, + 0, + 1); + !isTesSuccess(ter)) + return ter; } return tesSUCCESS; @@ -161,12 +199,13 @@ SponsorTransfer::doApply() auto const& tx = ctx_.tx; auto const index = tx[~sfLedgerIndex]; + bool const isObjectSponsor = index != std::nullopt; auto const accSle = view().peek(keylet::account(account_)); if (!accSle) return tefINTERNAL; // LCOV_EXCL_LINE - if (index) + if (isObjectSponsor) { // transfer object sponsor auto const objSle = view().peek(keylet::unchecked(*index)); diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index 2c797a71201..2ae31a55d3f 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -458,7 +458,8 @@ checkInsufficientReserve( std::shared_ptr accSle, STAmount const& accBalance, std::optional> const& _sponsorSle, - std::int32_t ownerCountDelta); + std::int32_t ownerCountDelta, + std::int32_t accountCountDelta = 0); std::optional> getTxReserveSponsor(ApplyView& view, STTx const& tx); diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index a6c0423e92a..fe08e3a16c4 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1027,7 +1027,8 @@ checkInsufficientReserve( std::shared_ptr accSle, STAmount const& accBalance, std::optional> const& _sponsorSle, - std::int32_t ownerCountDelta) + std::int32_t ownerCountDelta, + std::int32_t accountCountDelta) { if (_sponsorSle.has_value()) { @@ -1037,8 +1038,9 @@ checkInsufficientReserve( sponsorSle->getFieldU32(sfOwnerCount), sponsorSle->getFieldU32(sfSponsoredOwnerCount), sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ownerCountDelta, - sponsorSle->isFieldPresent(sfSponsor), - sponsorSle->getFieldU32(sfSponsoringAccountCount))}; + sponsorSle->isFieldPresent(sfSponsorAccount), + sponsorSle->getFieldU32(sfSponsoringAccountCount) + + accountCountDelta)}; if (sponsorBalance < sponsorReserve) return tecINSUFFICIENT_RESERVE; @@ -1049,8 +1051,8 @@ checkInsufficientReserve( accSle->getFieldU32(sfOwnerCount) + ownerCountDelta, accSle->getFieldU32(sfSponsoredOwnerCount), accSle->getFieldU32(sfSponsoringOwnerCount), - accSle->isFieldPresent(sfSponsor), - accSle->getFieldU32(sfSponsoringAccountCount))}; + accSle->isFieldPresent(sfSponsorAccount), + accSle->getFieldU32(sfSponsoringAccountCount) + accountCountDelta)}; if (accBalance < reserve) return tecINSUFFICIENT_RESERVE; From 2071b39d6a024cb677f2989a6aa1d65ae740c61e Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 Aug 2025 18:33:19 +0900 Subject: [PATCH 012/249] use helper --- src/test/app/Sponsor_test.cpp | 234 +++++++++++++++--------------- src/test/jtx/Env.h | 18 +++ src/test/jtx/TestHelpers.h | 18 +++ src/test/jtx/impl/Env.cpp | 27 ++++ src/test/jtx/impl/TestHelpers.cpp | 18 +++ 5 files changed, 196 insertions(+), 119 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 8f789f5755e..263c8ce5089 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -80,12 +80,12 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor1)); env.close(); - env.require(sponsored_owners(alice, 0)); - env.require(sponsored_owners(sponsor1, 0)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor1, 0)); - env.require(sponsoring_account_count(alice, 0)); - env.require(sponsoring_account_count(sponsor1, 1)); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 1); auto const sle1 = env.le(keylet::account(alice)); BEAST_EXPECT(sle1->isFieldPresent(sfSponsorAccount)); BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); @@ -104,15 +104,15 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor2)); env.close(); - env.require(sponsored_owners(alice, 0)); - env.require(sponsored_owners(sponsor1, 0)); - env.require(sponsored_owners(sponsor2, 0)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor1, 0)); - env.require(sponsoring_owners(sponsor2, 0)); - env.require(sponsoring_account_count(alice, 0)); - env.require(sponsoring_account_count(sponsor1, 0)); - env.require(sponsoring_account_count(sponsor2, 1)); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); auto const sle2 = env.le(keylet::account(alice)); BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); @@ -125,8 +125,8 @@ class Sponsor_test : public beast::unit_test::suite fee(XRP(1))); env.close(); - env.require( - balance(alice, env.current()->fees().reserve - drops(1))); + BEAST_EXPECT( + env.balance(alice) == env.current()->fees().reserve - drops(1)); env(sponsor::transfer(alice), ter(tecINSUFFICIENT_RESERVE)); env.close(); @@ -136,15 +136,15 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer(alice)); env.close(); - env.require(sponsored_owners(alice, 0)); - env.require(sponsored_owners(sponsor1, 0)); - env.require(sponsored_owners(sponsor2, 0)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor1, 0)); - env.require(sponsoring_owners(sponsor2, 0)); - env.require(sponsoring_account_count(alice, 0)); - env.require(sponsoring_account_count(sponsor1, 0)); - env.require(sponsoring_account_count(sponsor2, 0)); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); auto const sle3 = env.le(keylet::account(alice)); BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); } @@ -184,13 +184,13 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor1)); env.close(); - env.require(owners(alice, 1)); - env.require(sponsored_owners(alice, 1)); - env.require(sponsored_owners(sponsor1, 0)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor1, 1)); - env.require(sponsoring_account_count(alice, 0)); - env.require(sponsoring_account_count(sponsor1, 0)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); auto const sle1 = env.le(keylet::unchecked(checkId)); BEAST_EXPECT(sle1->isFieldPresent(sfSponsorAccount)); BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); @@ -209,15 +209,15 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor2)); env.close(); - env.require(sponsored_owners(alice, 1)); - env.require(sponsored_owners(sponsor1, 0)); - env.require(sponsored_owners(sponsor2, 0)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor1, 0)); - env.require(sponsoring_owners(sponsor2, 1)); - env.require(sponsoring_account_count(alice, 0)); - env.require(sponsoring_account_count(sponsor1, 0)); - env.require(sponsoring_account_count(sponsor2, 0)); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); auto const sle2 = env.le(keylet::unchecked(checkId)); BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); @@ -241,15 +241,15 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer(alice, checkId)); env.close(); - env.require(sponsored_owners(alice, 0)); - env.require(sponsored_owners(sponsor1, 0)); - env.require(sponsored_owners(sponsor2, 0)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor1, 0)); - env.require(sponsoring_owners(sponsor2, 0)); - env.require(sponsoring_account_count(alice, 0)); - env.require(sponsoring_account_count(sponsor1, 0)); - env.require(sponsoring_account_count(sponsor2, 0)); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); auto const sle3 = env.le(keylet::unchecked(checkId)); BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); } @@ -293,8 +293,8 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_account_count(sponsor, 1)); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1); } void @@ -329,19 +329,19 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 1)); - env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 1)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // CheckCancel auto const checkId = keylet::check(alice, seq).key; env(check::cancel(alice, checkId)); env.close(); - env.require(owners(alice, 0)); - env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 0)); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); auto const seq2 = env.seq(alice); env(check::create(alice, bob, XRP(1)), @@ -349,23 +349,19 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 1)); - env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 1)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // CheckCash auto const checkId2 = keylet::check(alice, seq2).key; env(check::cash(bob, checkId2, XRP(1))); env.close(); - env.require(owners(alice, 0)); - env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 0)); - - // printf( - // "meta: %s\n", - // env.meta()->getJson(JsonOptions::none).toStyledString().c_str()); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } void @@ -389,19 +385,19 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 1)); - env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 1)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // OfferCancel env(offer_cancel(alice, seq)); env.close(); - env.require(owners(alice, 0)); - env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 0)); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); // TODO: test Offer Execution } @@ -424,18 +420,18 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 250)); - env.require(sponsored_owners(alice, 250)); - env.require(sponsoring_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 250)); + BEAST_EXPECT(ownerCount(env, alice) == 250); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 250); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 250); // use a Ticket env(noop(alice), ticket::use(ticketSeq + 1)); env.close(); - env.require(owners(alice, 249)); - env.require(sponsored_owners(alice, 249)); - env.require(sponsoring_owners(sponsor, 249)); + BEAST_EXPECT(ownerCount(env, alice) == 249); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 249); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 249); } void @@ -457,11 +453,11 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(issuer, 1)); - env.require(owners(subject, 0)); - env.require(sponsored_owners(issuer, 1)); - env.require(sponsored_owners(subject, 0)); - env.require(sponsoring_owners(sponsor, 1)); + BEAST_EXPECT(ownerCount(env, issuer) == 1); + BEAST_EXPECT(ownerCount(env, subject) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // CredentialsAccept env(credentials::accept(subject, issuer, "credType"), @@ -469,21 +465,21 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(issuer, 0)); - env.require(owners(subject, 1)); - env.require(sponsored_owners(issuer, 0)); - env.require(sponsored_owners(subject, 1)); - env.require(sponsoring_owners(sponsor, 1)); + BEAST_EXPECT(ownerCount(env, issuer) == 0); + BEAST_EXPECT(ownerCount(env, subject) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // CredentialsDelete env(credentials::deleteCred(subject, subject, issuer, "credType")); env.close(); - env.require(owners(issuer, 0)); - env.require(owners(subject, 0)); - env.require(sponsored_owners(issuer, 0)); - env.require(sponsored_owners(subject, 0)); - env.require(sponsoring_owners(sponsor, 0)); + BEAST_EXPECT(ownerCount(env, issuer) == 0); + BEAST_EXPECT(ownerCount(env, subject) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } void @@ -504,17 +500,17 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 1)); - env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_owners(sponsor, 1)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // delete env(delegate::set(alice, bob, {})); env.close(); - env.require(owners(alice, 0)); - env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 0)); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } void @@ -540,17 +536,17 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 1)); - env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_owners(sponsor, 1)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // DIDDelete env(did::del(alice)); env.close(); - env.require(owners(alice, 0)); - env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 0)); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } void @@ -607,17 +603,17 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - env.require(owners(alice, 1)); - env.require(sponsored_owners(alice, 1)); - env.require(sponsoring_owners(sponsor, 1)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // Delete env(signers(alice, none)); env.close(); - env.require(owners(alice, 0)); - env.require(sponsored_owners(alice, 0)); - env.require(sponsoring_owners(sponsor, 0)); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } void diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index 21a239e3d76..9b26ae1ad5d 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -487,6 +487,24 @@ class Env std::uint32_t ownerCount(Account const& account) const; + /** Return the number of sponsored objects owned by an account. + * Returns 0 if the account does not exist. + */ + std::uint32_t + sponsoredOwnerCount(Account const& account) const; + + /** Return the number of sponsoring objects owned by an account. + * Returns 0 if the account does not exist. + */ + std::uint32_t + sponsoringOwnerCount(Account const& account) const; + + /** Return the number of sponsoring accounts owned by an account. + * Returns 0 if the account does not exist. + */ + std::uint32_t + sponsoringAccountCount(Account const& account) const; + /** Return an account root. @return empty if the account does not exist. */ diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index d4a39b64985..d535e172c8e 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -111,6 +111,24 @@ checkArraySize(Json::Value const& val, unsigned int size); std::uint32_t ownerCount(test::jtx::Env const& env, test::jtx::Account const& account); +// Helper function that returns the sponsored owner count on an account. +std::uint32_t +sponsoredOwnerCount( + test::jtx::Env const& env, + test::jtx::Account const& account); + +// Helper function that returns the sponsoring owner count on an account. +std::uint32_t +sponsoringOwnerCount( + test::jtx::Env const& env, + test::jtx::Account const& account); + +// Helper function that returns the sponsoring account count on an account. +std::uint32_t +sponsoringAccountCount( + test::jtx::Env const& env, + test::jtx::Account const& account); + /* Path finding */ /******************************************************************************/ void diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 7c17687eee9..06df1d86c44 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -250,6 +250,33 @@ Env::ownerCount(Account const& account) const return sle->getFieldU32(sfOwnerCount); } +std::uint32_t +Env::sponsoredOwnerCount(Account const& account) const +{ + auto const sle = le(account); + if (!sle) + Throw("missing account root"); + return sle->getFieldU32(sfSponsoredOwnerCount); +} + +std::uint32_t +Env::sponsoringOwnerCount(Account const& account) const +{ + auto const sle = le(account); + if (!sle) + Throw("missing account root"); + return sle->getFieldU32(sfSponsoringOwnerCount); +} + +std::uint32_t +Env::sponsoringAccountCount(Account const& account) const +{ + auto const sle = le(account); + if (!sle) + Throw("missing account root"); + return sle->getFieldU32(sfSponsoringAccountCount); +} + std::uint32_t Env::seq(Account const& account) const { diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index 5f8c53877ac..6549c09239c 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -56,6 +56,24 @@ ownerCount(Env const& env, Account const& account) return env.ownerCount(account); } +std::uint32_t +sponsoredOwnerCount(Env const& env, Account const& account) +{ + return env.sponsoredOwnerCount(account); +} + +std::uint32_t +sponsoringOwnerCount(Env const& env, Account const& account) +{ + return env.sponsoringOwnerCount(account); +} + +std::uint32_t +sponsoringAccountCount(Env const& env, Account const& account) +{ + return env.sponsoringAccountCount(account); +} + /* Path finding */ /******************************************************************************/ void From 02d8f9fbeff5eddaa831d4a72692884fc09f07a7 Mon Sep 17 00:00:00 2001 From: tequ Date: Sun, 7 Sep 2025 22:11:15 +0900 Subject: [PATCH 013/249] Sponsor signing --- include/xrpl/protocol/HashPrefix.h | 3 + include/xrpl/protocol/STTx.h | 62 +++++++ include/xrpl/protocol/Sign.h | 25 --- include/xrpl/protocol/Sponsor.h | 35 ++++ include/xrpl/protocol/detail/sfields.macro | 2 +- src/libxrpl/protocol/STTx.cpp | 181 ++++++++++++++++++++- src/libxrpl/protocol/Sign.cpp | 48 ------ src/test/app/Sponsor_test.cpp | 108 ++++++++++++ src/test/jtx/JTx.h | 2 + src/test/jtx/impl/Env.cpp | 3 + src/test/jtx/impl/sponsor.cpp | 64 +++++--- src/test/jtx/impl/utility.cpp | 6 +- src/test/jtx/sponsor.h | 4 +- src/xrpld/app/tx/detail/Transactor.cpp | 54 +++++- src/xrpld/app/tx/detail/Transactor.h | 3 + src/xrpld/app/tx/detail/apply.cpp | 11 ++ 16 files changed, 495 insertions(+), 116 deletions(-) create mode 100644 include/xrpl/protocol/Sponsor.h diff --git a/include/xrpl/protocol/HashPrefix.h b/include/xrpl/protocol/HashPrefix.h index 7e486af4c01..f54db8abe0b 100644 --- a/include/xrpl/protocol/HashPrefix.h +++ b/include/xrpl/protocol/HashPrefix.h @@ -91,6 +91,9 @@ enum class HashPrefix : std::uint32_t { /** Batch */ batch = detail::make_hash_prefix('B', 'C', 'H'), + + /** Sponsor */ + sponsor = detail::make_hash_prefix('S', 'P', 'N'), }; template diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index b6a0400358b..a06bf7fb457 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -112,6 +112,12 @@ class STTx final : public STObject, public CountedObject boost::container::flat_set getMentionedAccounts() const; + static Blob + getSigningData(STTx const& that); + + static Blob + getSponsorSigningData(STTx const& that); + uint256 getTransactionID() const; @@ -138,6 +144,11 @@ class STTx final : public STObject, public CountedObject RequireFullyCanonicalSig requireCanonicalSig, Rules const& rules) const; + Expected + checkSponsorSign( + RequireFullyCanonicalSig requireCanonicalSig, + Rules const& rules) const; + // SQL Functions with metadata. static std::string const& getMetaSQLInsertReplaceHeader(); @@ -170,12 +181,23 @@ class STTx final : public STObject, public CountedObject STObject const& batchSigner, RequireFullyCanonicalSig requireCanonicalSig) const; + Expected + checkSponsorSingleSign( + STObject const& signer, + RequireFullyCanonicalSig requireCanonicalSig) const; + Expected checkBatchMultiSign( STObject const& batchSigner, RequireFullyCanonicalSig requireCanonicalSig, Rules const& rules) const; + Expected + checkSponsorMultiSign( + STObject const& signer, + RequireFullyCanonicalSig requireCanonicalSig, + Rules const& rules) const; + STBase* copy(std::size_t n, void* buf) const override; STBase* @@ -224,6 +246,46 @@ STTx::getTransactionID() const return tid_; } +/** Return a Serializer suitable for computing a multisigning TxnSignature. */ +Serializer +buildMultiSigningData(STObject const& obj, AccountID const& signingID); + +/** Break the multi-signing hash computation into 2 parts for optimization. + + We can optimize verifying multiple multisignatures by splitting the + data building into two parts; + o A large part that is shared by all of the computations. + o A small part that is unique to each signer in the multisignature. + + The following methods support that optimization: + 1. startMultiSigningData provides the large part which can be shared. + 2. finishMultiSigningData caps the passed in serializer with each + signer's unique data. +*/ +Serializer +startMultiSigningData(STObject const& obj); + +inline void +finishMultiSigningData(AccountID const& signingID, Serializer& s) +{ + s.addBitString(signingID); +} + +Serializer +buildSponsorMultiSigningData( + STObject const& obj, + AccountID const& signingID, + uint32_t flags); + +Serializer +startSponsorSigningData(STObject const& obj); + +inline void +finishSponsorSigningData(AccountID const& signerID, Serializer& s) +{ + s.addBitString(signerID); +} + } // namespace ripple #endif diff --git a/include/xrpl/protocol/Sign.h b/include/xrpl/protocol/Sign.h index 5aa9fabddc3..512d82cb43c 100644 --- a/include/xrpl/protocol/Sign.h +++ b/include/xrpl/protocol/Sign.h @@ -61,31 +61,6 @@ verify( PublicKey const& pk, SF_VL const& sigField = sfSignature); -/** Return a Serializer suitable for computing a multisigning TxnSignature. */ -Serializer -buildMultiSigningData(STObject const& obj, AccountID const& signingID); - -/** Break the multi-signing hash computation into 2 parts for optimization. - - We can optimize verifying multiple multisignatures by splitting the - data building into two parts; - o A large part that is shared by all of the computations. - o A small part that is unique to each signer in the multisignature. - - The following methods support that optimization: - 1. startMultiSigningData provides the large part which can be shared. - 2. finishMultiSigningData caps the passed in serializer with each - signer's unique data. -*/ -Serializer -startMultiSigningData(STObject const& obj); - -inline void -finishMultiSigningData(AccountID const& signingID, Serializer& s) -{ - s.addBitString(signingID); -} - } // namespace ripple #endif diff --git a/include/xrpl/protocol/Sponsor.h b/include/xrpl/protocol/Sponsor.h new file mode 100644 index 00000000000..0533ed496ad --- /dev/null +++ b/include/xrpl/protocol/Sponsor.h @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include + +namespace ripple { + +inline void +addSerializeSponsorData( + Serializer& msg, + AccountID const& sponsorID, + std::uint32_t const& flags) +{ + msg.addBitString(sponsorID); + msg.add32(flags); +} + +} // namespace ripple diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 2bfe555043a..f6b6417bdce 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -366,7 +366,7 @@ UNTYPED_SFIELD(sfCredential, OBJECT, 33) UNTYPED_SFIELD(sfRawTransaction, OBJECT, 34) UNTYPED_SFIELD(sfBatchSigner, OBJECT, 35) UNTYPED_SFIELD(sfBook, OBJECT, 36) -UNTYPED_SFIELD(sfSponsor, OBJECT, 37) +UNTYPED_SFIELD(sfSponsor, OBJECT, 37, SField::sMD_Default, SField::notSigning) // array of objects (common) // ARRAY/1 is reserved for end of array diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 5d56995e86b..3129c8635ef 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -184,15 +185,26 @@ STTx::getMentionedAccounts() const return list; } -static Blob -getSigningData(STTx const& that) +Blob +STTx::getSigningData(STTx const& that) { Serializer s; s.add32(HashPrefix::txSign); that.addWithoutSigningFields(s); + if (that.isFieldPresent(sfSponsor)) + { + auto const sst = that.getFieldObject(sfSponsor); + addSerializeSponsorData(s, sst.getAccountID(sfAccount), sst.getFlags()); + } return s.getData(); } +Blob +STTx::getSponsorSigningData(STTx const& that) +{ + return startSponsorSigningData(that).getData(); +} + uint256 STTx::getSigningHash() const { @@ -238,7 +250,7 @@ STTx::getFeePayer() const { if (isFieldPresent(sfSponsor)) { - if (getFieldObject(sfSponsor)[sfFlags] & tfSponsorFee) + if (getFieldObject(sfSponsor).isFlag(tfSponsorFee)) { return getFieldObject(sfSponsor)[sfAccount]; } @@ -320,6 +332,42 @@ STTx::checkBatchSign( return Unexpected("Internal batch signature check failure."); } +Expected +STTx::checkSponsorSign( + RequireFullyCanonicalSig requireCanonicalSig, + Rules const& rules) const +{ + try + { + XRPL_ASSERT( + isFieldPresent(sfSponsor), + "STTx::checkSponsorSign: not a sponsored transaction"); + if (!isFieldPresent(sfSponsor)) + { + JLOG(debugLog().fatal()) << "not a sponsored transaction"; + return Unexpected("Not a sponsored transaction."); + } + STObject const& sponsorObj{getFieldObject(sfSponsor)}; + + Blob const& signingPubKey = sponsorObj.getFieldVL(sfSigningPubKey); + + auto const result = signingPubKey.empty() + ? checkSponsorMultiSign(sponsorObj, requireCanonicalSig, rules) + : checkSponsorSingleSign(sponsorObj, requireCanonicalSig); + + if (!result) + return result; + + return {}; + } + catch (std::exception const& e) + { + JLOG(debugLog().error()) + << "Sponsor signature check failed: " << e.what(); + } + return Unexpected("Sponsor signature check failure."); +} + Json::Value STTx::getJson(JsonOptions options) const { @@ -457,6 +505,17 @@ STTx::checkBatchSingleSign( return singleSignHelper(batchSigner, msg.slice(), fullyCanonical); } +Expected +STTx::checkSponsorSingleSign( + STObject const& signer, + RequireFullyCanonicalSig requireCanonicalSig) const +{ + auto const data = getSponsorSigningData(*this); + bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig) || + (requireCanonicalSig == STTx::RequireFullyCanonicalSig::yes); + return singleSignHelper(signer, makeSlice(data), fullyCanonical); +} + Expected multiSignHelper( STObject const& signerObj, @@ -465,9 +524,9 @@ multiSignHelper( Rules const& rules) { // Make sure the MultiSigners are present. Otherwise they are not - // attempting multi-signing and we just have a bad SigningPubKey. + // attempting multi-signing and we just have a bad Signers. if (!signerObj.isFieldPresent(sfSigners)) - return Unexpected("Empty SigningPubKey."); + return Unexpected("Empty Signers."); // We don't allow both an sfSigners and an sfTxnSignature. Both fields // being present would indicate that the transaction is signed both ways. @@ -560,6 +619,32 @@ STTx::checkBatchMultiSign( rules); } +Expected +STTx::checkSponsorMultiSign( + STObject const& sponsorObj, + RequireFullyCanonicalSig requireCanonicalSig, + Rules const& rules) const +{ + bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig) || + (requireCanonicalSig == RequireFullyCanonicalSig::yes); + + // We can ease the computational load inside the loop a bit by + // pre-constructing part of the data that we hash. Fill a Serializer + // with the stuff that stays constant from signature to signature. + auto const data = startSponsorSigningData(*this); + Serializer dataStart = Serializer(data.data(), data.size()); + + return multiSignHelper( + sponsorObj, + fullyCanonical, + [&dataStart](AccountID const& accountID) mutable -> Serializer { + Serializer s = dataStart; + finishSponsorSigningData(accountID, s); + return s; + }, + rules); +} + Expected STTx::checkMultiSign( RequireFullyCanonicalSig requireCanonicalSig, @@ -848,4 +933,90 @@ isPseudoTx(STObject const& tx) return tt == ttAMENDMENT || tt == ttFEE || tt == ttUNL_MODIFY; } +// Questions regarding buildMultiSigningData: +// +// Why do we include the Signer.Account in the blob to be signed? +// +// Unless you include the Account which is signing in the signing blob, +// you could swap out any Signer.Account for any other, which may also +// be on the SignerList and have a RegularKey matching the +// Signer.SigningPubKey. +// +// That RegularKey may be set to allow some 3rd party to sign transactions +// on the account's behalf, and that RegularKey could be common amongst all +// users of the 3rd party. That's just one example of sharing the same +// RegularKey amongst various accounts and just one vulnerability. +// +// "When you have something that's easy to do that makes entire classes of +// attacks clearly and obviously impossible, you need a damn good reason +// not to do it." -- David Schwartz +// +// Why would we include the signingFor account in the blob to be signed? +// +// In the current signing scheme, the account that a signer is `signing +// for/on behalf of` is the tx_json.Account. +// +// Later we might support more levels of signing. Suppose Bob is a signer +// for Alice, and Carol is a signer for Bob, so Carol can sign for Bob who +// signs for Alice. But suppose Alice has two signers: Bob and Dave. If +// Carol is a signer for both Bob and Dave, then the signature needs to +// distinguish between Carol signing for Bob and Carol signing for Dave. +// +// So, if we support multiple levels of signing, then we'll need to +// incorporate the "signing for" accounts into the signing data as well. +Serializer +buildMultiSigningData(STObject const& obj, AccountID const& signingID) +{ + Serializer s{startMultiSigningData(obj)}; + finishMultiSigningData(signingID, s); + return s; +} + +Serializer +startMultiSigningData(STObject const& obj) +{ + Serializer s; + s.add32(HashPrefix::txMultiSign); + obj.addWithoutSigningFields(s); + // if (obj.isFieldPresent(sfSponsor)) + // { + // auto const sst = obj.getFieldObject(sfSponsor); + // addSerializeSponsorData(s, sst.getAccountID(sfAccount), + // sst.getFlags()); + // } + return s; +} + +Serializer +buildSponsorMultiSigningData( + STObject const& obj, + AccountID const& signingID, + uint32_t flags) +{ + Serializer s{startSponsorSigningData(obj)}; + finishSponsorSigningData(signingID, s); + return s; +} + +Serializer +startSponsorSigningData(STObject const& obj) +{ + Serializer s; + s.add32(HashPrefix::sponsor); + STObject tmp = obj; + tmp.setFieldVL(sfSigningPubKey, Blob{}); + tmp.addWithoutSigningFields(s); + + XRPL_ASSERT( + tmp.isFieldPresent(sfSponsor), + "STTx::getSponsorSigningData : sponsor is not set"); + + if (tmp.isFieldPresent(sfSponsor)) + { + auto const sst = tmp.getFieldObject(sfSponsor); + addSerializeSponsorData(s, sst.getAccountID(sfAccount), sst.getFlags()); + } + return s; +} + } // namespace ripple diff --git a/src/libxrpl/protocol/Sign.cpp b/src/libxrpl/protocol/Sign.cpp index 27c2b0435c4..b72742336d1 100644 --- a/src/libxrpl/protocol/Sign.cpp +++ b/src/libxrpl/protocol/Sign.cpp @@ -61,52 +61,4 @@ verify( pk, Slice(ss.data(), ss.size()), Slice(sig->data(), sig->size())); } -// Questions regarding buildMultiSigningData: -// -// Why do we include the Signer.Account in the blob to be signed? -// -// Unless you include the Account which is signing in the signing blob, -// you could swap out any Signer.Account for any other, which may also -// be on the SignerList and have a RegularKey matching the -// Signer.SigningPubKey. -// -// That RegularKey may be set to allow some 3rd party to sign transactions -// on the account's behalf, and that RegularKey could be common amongst all -// users of the 3rd party. That's just one example of sharing the same -// RegularKey amongst various accounts and just one vulnerability. -// -// "When you have something that's easy to do that makes entire classes of -// attacks clearly and obviously impossible, you need a damn good reason -// not to do it." -- David Schwartz -// -// Why would we include the signingFor account in the blob to be signed? -// -// In the current signing scheme, the account that a signer is `signing -// for/on behalf of` is the tx_json.Account. -// -// Later we might support more levels of signing. Suppose Bob is a signer -// for Alice, and Carol is a signer for Bob, so Carol can sign for Bob who -// signs for Alice. But suppose Alice has two signers: Bob and Dave. If -// Carol is a signer for both Bob and Dave, then the signature needs to -// distinguish between Carol signing for Bob and Carol signing for Dave. -// -// So, if we support multiple levels of signing, then we'll need to -// incorporate the "signing for" accounts into the signing data as well. -Serializer -buildMultiSigningData(STObject const& obj, AccountID const& signingID) -{ - Serializer s{startMultiSigningData(obj)}; - finishMultiSigningData(signingID, s); - return s; -} - -Serializer -startMultiSigningData(STObject const& obj) -{ - Serializer s; - s.add32(HashPrefix::txMultiSign); - obj.addWithoutSigningFields(s); - return s; -} - } // namespace ripple diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 263c8ce5089..bb0c64edbd3 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -51,6 +51,112 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer(alice), ter(temDISABLED)); } + void + testSingleSigning() + { + testcase("Single signing"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const invalid("invalid"); + + env.fund(XRP(10000), alice, sponsor); + env.close(); + + // Signature doesn't exist + auto tx = noop(alice); + tx[sfSponsor.jsonName][sfAccount.jsonName] = sponsor.human(); + tx[sfSponsor.jsonName][sfSigningPubKey.jsonName] = + strHex(sponsor.pk().slice()); + + env(tx, + fee(XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + ter(telENV_RPC_FAILED)); + + // Invalid signature + tx[sfSponsor.jsonName][sfTxnSignature.jsonName] = "DEADBEEF"; + env(tx, + fee(XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + ter(telENV_RPC_FAILED)); + + // Signer account doesn't exist + env(noop(alice), + fee(XRP(1)), + sponsor::as(invalid, tfSponsorReserve), + sponsor::sig(invalid), + ter(tefBAD_AUTH)); + + // Success + env(noop(alice), + fee(XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tesSUCCESS)); + } + + void + testMultiSigning() + { + testcase("Multi signing"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const invalid("invalid"); + + Account const signer1("signer1"); + Account const signer2("signer2"); + + env.fund(XRP(10000), alice, sponsor); + env.close(); + + env(signers(sponsor, 1, {{signer1, 1}, {signer2, 1}})); + env.close(); + + // Signature doesn't exist + env(noop(alice), + fee(XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + ter(telENV_RPC_FAILED)); + + // Invalid signature + auto tx = noop(alice); + auto& signers1 = + tx[sfSponsor.jsonName][sfSigners.jsonName][0U][sfSigner.jsonName]; + signers1[sfAccount.jsonName] = signer1.human(); + signers1[sfSigningPubKey.jsonName] = strHex(signer1.pk().slice()); + signers1[sfTxnSignature.jsonName] = "DEADBEEF"; + env(tx, + fee(XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + ter(telENV_RPC_FAILED)); + + // Signer account doesn't exist + env(noop(alice), + fee(XRP(1)), + sponsor::as(invalid, tfSponsorReserve), + sponsor::msig({signer1}), + ter(tefBAD_AUTH)); + + env(noop(alice), + fee(XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::msig({signer1}), + ter(tesSUCCESS)); + + env(signers(sponsor, 2, {{signer1, 1}, {signer2, 1}})); + env.close(); + + env(noop(alice), + fee(XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::msig({signer1, signer2}), + ter(tesSUCCESS)); + } + void testTransferSponsor() { @@ -658,6 +764,8 @@ class Sponsor_test : public beast::unit_test::suite run() override { testDisabled(); + testSingleSigning(); + testMultiSigning(); testTransferSponsor(); testSponsorFee(); testSponsorAccount(); diff --git a/src/test/jtx/JTx.h b/src/test/jtx/JTx.h index 198839dd28b..a074adb6000 100644 --- a/src/test/jtx/JTx.h +++ b/src/test/jtx/JTx.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_TEST_JTX_JTX_H_INCLUDED #define RIPPLE_TEST_JTX_JTX_H_INCLUDED +#include #include #include @@ -55,6 +56,7 @@ struct JTx bool fill_netid = true; std::shared_ptr stx; std::function signer; + std::function sponsorSigner; JTx() = default; JTx(JTx const&) = default; diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index b44479d3cad..b37340129fb 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -567,6 +567,9 @@ Env::autofill_sig(JTx& jt) jtx::sign(jv, lookup(ar->getAccountID(sfRegularKey))); else jtx::sign(jv, account); + + if (jt.sponsorSigner) + jt.sponsorSigner(*this, jt); } void diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 385395f8e52..4d8340db964 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -21,6 +21,7 @@ #include #include +#include #include namespace ripple { @@ -50,61 +51,70 @@ as::operator()(Env& env, JTx& jt) const void sig::operator()(Env& env, JTx& jt) const { - std::optional st; - try - { - // required to cast the STObject to STTx - jt.jv[jss::SigningPubKey] = ""; - st = parse(jt.jv); - } - catch (parse_error const&) - { - env.test.log << pretty(jt.jv) << std::endl; - Rethrow(); - } + auto const signer = signer_; jt.jv[sfSponsor.jsonName][sfAccount.jsonName] = signer.acct.human(); jt.jv[sfSponsor.jsonName][sfSigningPubKey.jsonName] = strHex(signer.sig.pk().slice()); - Serializer ss; - ss.add32(HashPrefix::txSign); - parse(jt.jv).addWithoutSigningFields(ss); - auto const sig = - ripple::sign(signer.acct.pk(), signer.acct.sk(), ss.slice()); - jt.jv[sfSponsor.jsonName][jss::TxnSignature] = - strHex(Slice{sig.data(), sig.size()}); + jt.sponsorSigner = [signer, &env](Env&, JTx& jtx) { + std::optional st; + try + { + Json::Value jv = jtx.jv; + st = parse(jv); + } + catch (parse_error const&) + { + env.test.log << pretty(jtx.jv) << std::endl; + Rethrow(); + } + + auto const sst = st->getFieldObject(sfSponsor); + + auto const signingData = + STTx::getSponsorSigningData(STTx{std::move(*st)}); + + auto const sig = ripple::sign( + signer.acct.pk(), signer.acct.sk(), makeSlice(signingData)); + jtx.jv[sfSponsor.jsonName][jss::TxnSignature] = + strHex(Slice{sig.data(), sig.size()}); + }; } void msig::operator()(Env& env, JTx& jt) const { + jt.jv[sfSponsor.jsonName][sfSigningPubKey.jsonName] = ""; auto const mySigners = signers; - jt.signer = [mySigners, &env](Env&, JTx& jtx) { - jtx[sfSponsor.getJsonName()][sfSigningPubKey.getJsonName()] = ""; + jt.sponsorSigner = [mySigners, &env](Env&, JTx& jtx) { std::optional st; try { - st = parse(jtx.jv); + Json::Value jv = jtx.jv; + jv[jss::SigningPubKey] = ""; + st = parse(jv); } catch (parse_error const&) { env.test.log << pretty(jtx.jv) << std::endl; Rethrow(); } - auto& js = jtx[sfSponsor.getJsonName()][sfSigners.getJsonName()]; + auto const sst = st->getFieldObject(sfSponsor); + auto& js = jtx[sfSponsor.jsonName][sfSigners.jsonName]; for (std::size_t i = 0; i < mySigners.size(); ++i) { auto const& e = mySigners[i]; - auto& jo = js[i][sfSigner.getJsonName()]; + auto& jo = js[i][sfSigner.jsonName]; jo[jss::Account] = e.acct.human(); jo[jss::SigningPubKey] = strHex(e.sig.pk().slice()); - Serializer ss{buildMultiSigningData(*st, e.acct.id())}; + Serializer ss{ + buildSponsorMultiSigningData(*st, e.acct.id(), sst.getFlags())}; + auto const sig = ripple::sign( *publicKeyType(e.sig.pk().slice()), e.sig.sk(), ss.slice()); - jo[sfTxnSignature.getJsonName()] = - strHex(Slice{sig.data(), sig.size()}); + jo[sfTxnSignature.jsonName] = strHex(Slice{sig.data(), sig.size()}); } }; } diff --git a/src/test/jtx/impl/utility.cpp b/src/test/jtx/impl/utility.cpp index 27b45a32cb8..fed7d10a4dc 100644 --- a/src/test/jtx/impl/utility.cpp +++ b/src/test/jtx/impl/utility.cpp @@ -47,10 +47,8 @@ void sign(Json::Value& jv, Account const& account) { jv[jss::SigningPubKey] = strHex(account.pk().slice()); - Serializer ss; - ss.add32(HashPrefix::txSign); - parse(jv).addWithoutSigningFields(ss); - auto const sig = ripple::sign(account.pk(), account.sk(), ss.slice()); + auto const blob = STTx::getSigningData(STTx{parse(jv)}); + auto const sig = ripple::sign(account.pk(), account.sk(), makeSlice(blob)); jv[jss::TxnSignature] = strHex(Slice{sig.data(), sig.size()}); } diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index e09ccd26bd1..683c2e0ae49 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -54,10 +54,10 @@ struct as struct sig { private: - Reg signer; + Reg signer_; public: - sig(Reg signer_) : signer(std::move(signer_)) + sig(Reg signer) : signer_(std::move(signer)) { } diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index f9dee8e63c5..4ad45d13ac2 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -647,10 +647,11 @@ Transactor::checkSign(PreclaimContext const& ctx) return checkMultiSign(ctx.view, idAccount, txSigners, ctx.flags, ctx.j); } - // if (ctx.tx.isFieldPresent(sfSponsor)) - // { - // // TODO: check the sponsor signature - // } + if (ctx.tx.isFieldPresent(sfSponsor)) + { + if (auto const ret = checkSponsorSign(ctx); !isTesSuccess(ret)) + return ret; + } // Check Single Sign XRPL_ASSERT( @@ -721,6 +722,51 @@ Transactor::checkBatchSign(PreclaimContext const& ctx) return ret; } +NotTEC +Transactor::checkSponsorSign(PreclaimContext const& ctx) +{ + NotTEC ret = tesSUCCESS; + + if (!ctx.tx.isFieldPresent(sfSponsor)) + return tesSUCCESS; + + auto const sponsorObj = ctx.tx.getFieldObject(sfSponsor); + + auto const sponsorAcc = sponsorObj.getAccountID(sfAccount); + Blob const& pkSigner = sponsorObj.getFieldVL(sfSigningPubKey); + + auto const sleAccount = ctx.view.read(keylet::account(sponsorAcc)); + if (!sleAccount) + return tefBAD_AUTH; + + if (pkSigner.empty()) + { + STArray const& txSigners(sponsorObj.getFieldArray(sfSigners)); + if (ret = checkMultiSign( + ctx.view, sponsorAcc, txSigners, ctx.flags, ctx.j); + !isTesSuccess(ret)) + return ret; + } + else + { + // LCOV_EXCL_START + if (!publicKeyType(makeSlice(pkSigner))) + return tefBAD_AUTH; + // LCOV_EXCL_STOP + + auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner))); + + if (ret = checkSingleSign( + idSigner, sponsorAcc, sleAccount, ctx.view.rules(), ctx.j); + !isTesSuccess(ret)) + { + return ret; + } + } + + return ret; +} + NotTEC Transactor::checkSingleSign( AccountID const& idSigner, diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index 42d4861a63a..08a52cf226d 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -192,6 +192,9 @@ class Transactor static NotTEC checkBatchSign(PreclaimContext const& ctx); + static NotTEC + checkSponsorSign(PreclaimContext const& ctx); + // Returns the fee in fee units, not scaled for load. static XRPAmount calculateBaseFee(ReadView const& view, STTx const& tx); diff --git a/src/xrpld/app/tx/detail/apply.cpp b/src/xrpld/app/tx/detail/apply.cpp index e2e0adae45c..e455675bb8b 100644 --- a/src/xrpld/app/tx/detail/apply.cpp +++ b/src/xrpld/app/tx/detail/apply.cpp @@ -83,6 +83,17 @@ checkValidity( ? STTx::RequireFullyCanonicalSig::yes : STTx::RequireFullyCanonicalSig::no; + if (tx.isFieldPresent(sfSponsor) && rules.enabled(featureSponsor)) + { + auto const sigVerify = + tx.checkSponsorSign(requireCanonicalSig, rules); + if (!sigVerify) + { + router.setFlags(id, SF_SIGBAD); + return {Validity::SigBad, sigVerify.error()}; + } + } + auto const sigVerify = tx.checkSign(requireCanonicalSig, rules); if (!sigVerify) { From e589b71ee0183d42e6b09c74f3cef7615d3ca44a Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 13 Sep 2025 09:36:38 +0900 Subject: [PATCH 014/249] v2. SponsorSet --- include/xrpl/protocol/Indexes.h | 4 + include/xrpl/protocol/LedgerFormats.h | 6 + include/xrpl/protocol/TER.h | 1 + include/xrpl/protocol/TxFlags.h | 12 +- .../xrpl/protocol/detail/ledger_entries.macro | 12 + include/xrpl/protocol/detail/sfields.macro | 6 + .../xrpl/protocol/detail/transactions.macro | 14 +- include/xrpl/protocol/jss.h | 3 +- src/libxrpl/protocol/Indexes.cpp | 9 + src/libxrpl/protocol/TER.cpp | 1 + src/test/app/Sponsor_test.cpp | 251 ++++++++++++++-- src/test/jtx/impl/sponsor.cpp | 40 ++- src/test/jtx/sponsor.h | 26 +- src/test/rpc/AccountSet_test.cpp | 3 +- src/xrpld/app/tx/detail/DeleteAccount.cpp | 15 + src/xrpld/app/tx/detail/InvariantCheck.cpp | 9 + src/xrpld/app/tx/detail/SetAccount.cpp | 8 + src/xrpld/app/tx/detail/SponsorSet.cpp | 273 ++++++++++++++++++ src/xrpld/app/tx/detail/SponsorSet.h | 48 +++ src/xrpld/app/tx/detail/SponsorTransfer.cpp | 8 +- src/xrpld/app/tx/detail/Transactor.cpp | 91 +++++- src/xrpld/app/tx/detail/Transactor.h | 3 + src/xrpld/app/tx/detail/apply.cpp | 17 +- src/xrpld/app/tx/detail/applySteps.cpp | 6 + src/xrpld/rpc/handlers/LedgerEntry.cpp | 21 ++ 25 files changed, 840 insertions(+), 47 deletions(-) create mode 100644 src/xrpld/app/tx/detail/SponsorSet.cpp create mode 100644 src/xrpld/app/tx/detail/SponsorSet.h diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 3e3f2843c15..5baaaf5b86a 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -174,6 +174,10 @@ static ticket_t const ticket{}; Keylet signers(AccountID const& account) noexcept; +/** A Sponsor */ +Keylet +sponsor(AccountID const& sponsor, AccountID const& sponsee) noexcept; + /** A Check */ /** @{ */ Keylet diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index e3efe8fec2a..dcca9544b3f 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -149,6 +149,8 @@ enum LedgerSpecificFlags { 0x40000000, // True, enable trustline locking lsfAllowTrustLineClawback = 0x80000000, // True, enable clawback + lsfDisallowIncomingSponsor = + 0x00004000, // True, reject new sponsor // ltOFFER lsfPassive = 0x00010000, @@ -196,6 +198,10 @@ enum LedgerSpecificFlags { // ltVAULT lsfVaultPrivate = 0x00010000, + + // ltSPONSORSHIP + lsfSponsorshipRequireSignForFee = 0x00010000, + lsfSponsorshipRequireSignForReserve = 0x00020000, }; //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index 9ace6b80f8b..95e9c27816e 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -362,6 +362,7 @@ enum TECcodes : TERUnderlyingType { tecPSEUDO_ACCOUNT = 196, tecPRECISION_LOSS = 197, tecNO_DELEGATE_PERMISSION = 198, + tecNO_SPONSOR_PERMISSION = 199, }; //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index b43283f22a3..1216bfe7918 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -62,9 +62,10 @@ constexpr std::uint32_t tfInnerBatchTxn = 0x40000000; constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; constexpr std::uint32_t tfUniversalMask = ~tfUniversal; -// Sponsor flags: +// Sponsor flags (Global): constexpr std::uint32_t tfSponsorFee = 0x00000001; constexpr std::uint32_t tfSponsorReserve = 0x00000002; +constexpr std::uint32_t tfSponsorMask = tfSponsorFee | tfSponsorReserve; // AccountSet flags: constexpr std::uint32_t tfRequireDestTag = 0x00010000; @@ -97,6 +98,7 @@ constexpr std::uint32_t asfDisallowIncomingPayChan = 14; constexpr std::uint32_t asfDisallowIncomingTrustline = 15; constexpr std::uint32_t asfAllowTrustLineClawback = 16; constexpr std::uint32_t asfAllowTrustLineLocking = 17; +constexpr std::uint32_t asfDisallowIncomingSponsor = 19; // OfferCreate flags: constexpr std::uint32_t tfPassive = 0x00010000; @@ -253,6 +255,14 @@ constexpr std::uint32_t tfIndependent = 0x00080000; constexpr std::uint32_t const tfBatchMask = ~(tfUniversal | tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent) | tfInnerBatchTxn; +// SponsorSet flags: +constexpr std::uint32_t tfSponsorshipSetRequireSignForFee = 0x00010000; +constexpr std::uint32_t tfSponsorshipClearRequireSignForFee = 0x00020000; +constexpr std::uint32_t tfSponsorshipSetRequireSignForReserve = 0x00040000; +constexpr std::uint32_t tfSponsorshipClearRequireSignForReserve = 0x00080000; +constexpr std::uint32_t tfDeleteObject = 0x00100000; +constexpr std::uint32_t tfSponsorSetMask = ~(tfUniversal | tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee | tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve | tfDeleteObject); + // clang-format on } // namespace ripple diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 66914d92a1d..f59b18d9108 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -507,5 +507,17 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({ // no PermissionedDomainID ever (use MPTIssuance.sfDomainID) })) +/** A ledger object representing a sponsorship. + \sa keylet::sponsor + */ +LEDGER_ENTRY(ltSPONSORSHIP, 0x0085, Sponsorship, sponsorship, ({ + {sfAccount, soeREQUIRED}, + {sfSponsee, soeREQUIRED}, + {sfSponsorNode, soeREQUIRED}, + {sfSponseeNode, soeREQUIRED}, + {sfFeeAmount, soeOPTIONAL}, + {sfReserveCount, soeOPTIONAL}, +})) + #undef EXPAND #undef LEDGER_ENTRY_DUPLICATE diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index f6b6417bdce..5fe30502c1d 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -117,6 +117,7 @@ TYPED_SFIELD(sfPermissionValue, UINT32, 52) TYPED_SFIELD(sfSponsoredOwnerCount, UINT32, 53) TYPED_SFIELD(sfSponsoringOwnerCount, UINT32, 54) TYPED_SFIELD(sfSponsoringAccountCount, UINT32, 55) +TYPED_SFIELD(sfReserveCount, UINT32, 56) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) @@ -148,6 +149,8 @@ TYPED_SFIELD(sfMPTAmount, UINT64, 26, SField::sMD_BaseTen|SFie TYPED_SFIELD(sfIssuerNode, UINT64, 27) TYPED_SFIELD(sfSubjectNode, UINT64, 28) TYPED_SFIELD(sfLockedAmount, UINT64, 29, SField::sMD_BaseTen|SField::sMD_Default) +TYPED_SFIELD(sfSponsorNode, UINT64, 30) +TYPED_SFIELD(sfSponseeNode, UINT64, 31) // 128-bit TYPED_SFIELD(sfEmailHash, UINT128, 1) @@ -200,6 +203,7 @@ TYPED_SFIELD(sfHookSetTxnID, UINT256, 33) TYPED_SFIELD(sfDomainID, UINT256, 34) TYPED_SFIELD(sfVaultID, UINT256, 35) TYPED_SFIELD(sfParentBatchID, UINT256, 36) +TYPED_SFIELD(sfObjectID, UINT256, 37) // number (common) TYPED_SFIELD(sfNumber, NUMBER, 1) @@ -244,6 +248,7 @@ TYPED_SFIELD(sfPrice, AMOUNT, 28) TYPED_SFIELD(sfSignatureReward, AMOUNT, 29) TYPED_SFIELD(sfMinAccountCreateAmount, AMOUNT, 30) TYPED_SFIELD(sfLPTokenBalance, AMOUNT, 31) +TYPED_SFIELD(sfFeeAmount, AMOUNT, 32) // variable length (common) TYPED_SFIELD(sfPublicKey, VL, 1) @@ -293,6 +298,7 @@ TYPED_SFIELD(sfEmitCallback, ACCOUNT, 10) TYPED_SFIELD(sfHolder, ACCOUNT, 11) TYPED_SFIELD(sfDelegate, ACCOUNT, 12) TYPED_SFIELD(sfSponsorAccount, ACCOUNT, 13) +TYPED_SFIELD(sfSponsee, ACCOUNT, 14) // account (uncommon) TYPED_SFIELD(sfHookAccount, ACCOUNT, 16) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index d281a5ab4b1..997e70013ff 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -526,9 +526,17 @@ TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, ({ {sfBatchSigners, soeOPTIONAL}, })) -/** This transaction transfer sponsor */ -TRANSACTION(ttSPONSOR_TRANSFER, 72, SponsorTransfer, Delegation::notDelegatable, ({ - {sfLedgerIndex, soeOPTIONAL}, +/** This transaction transfer sponsorship */ +TRANSACTION(ttSPONSORSHIP_TRANSFER, 72, SponsorTransfer, Delegation::notDelegatable, ({ + {sfObjectID, soeOPTIONAL}, +})) + +/** This transaction create sponsorship object */ +TRANSACTION(ttSPONSORSHIP_SET, 73, SponsorSet, Delegation::notDelegatable, ({ + {sfSponsorAccount, soeOPTIONAL}, + {sfSponsee, soeREQUIRED}, + {sfFeeAmount, soeOPTIONAL}, + {sfReserveCount, soeOPTIONAL}, })) /** This system-generated transaction type is used to update the status of the various amendments. diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 68d2497aca8..ecad01fa48a 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -540,7 +540,7 @@ JSS(reserve_inc_xrp); // out: NetworkOPs JSS(response); // websocket JSS(result); // RPC JSS(ripple_lines); // out: NetworkOPs -JSS(ripple_state); // in: LedgerEntr +JSS(ripple_state); // in: LedgerEntry JSS(ripplerpc); // ripple RPC version JSS(role); // out: Ping.cpp JSS(rpc); @@ -580,6 +580,7 @@ JSS(source_account); // in: PathRequest, RipplePathFind JSS(source_amount); // in: PathRequest, RipplePathFind JSS(source_currencies); // in: PathRequest, RipplePathFind JSS(source_tag); // out: AccountChannels +JSS(sponsee); // in: LedgerEntry JSS(stand_alone); // out: NetworkOPs JSS(standard_deviation); // out: get_aggregate_price JSS(start); // in: TxHistory diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 486945992ab..969b737c0c5 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -96,6 +96,7 @@ enum class LedgerNameSpace : std::uint16_t { PERMISSIONED_DOMAIN = 'm', DELEGATE = 'E', VAULT = 'V', + SPONSORSHIP = 'N', // No longer used or supported. Left here to reserve the space // to avoid accidental reuse. @@ -332,6 +333,14 @@ signers(AccountID const& account) noexcept return signers(account, 0); } +Keylet +sponsor(AccountID const& sponsor, AccountID const& sponsee) noexcept +{ + return { + ltSPONSORSHIP, + indexHash(LedgerNameSpace::SPONSORSHIP, sponsor, sponsee)}; +} + Keylet check(AccountID const& id, std::uint32_t seq) noexcept { diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index a396949afe2..1093d8fc6db 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -128,6 +128,7 @@ transResults() MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."), MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."), MAKE_ERROR(tecNO_DELEGATE_PERMISSION, "Delegated account lacks permission to perform this transaction."), + MAKE_ERROR(tecNO_SPONSOR_PERMISSION, "Does not have permission to sponsored this transaction."), MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."), MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."), diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index bb0c64edbd3..68fb63ee88c 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -49,6 +49,110 @@ class Sponsor_test : public beast::unit_test::suite ter(temDISABLED)); env(sponsor::transfer(alice), ter(temDISABLED)); + env(sponsor::set(sponsor, alice, 0), ter(temDISABLED)); + } + + void + testInvalidSponsorSet() + { + testcase("Invalid SponsorSet"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const noFunded("noFunded"); + Account const gw("gw"); + + auto const USD = gw["USD"]; + env.fund(XRP(10000), alice, sponsor, gw); + env.close(); + + // + // preflight + // + + // Invalid flags + { + env(sponsor::set( + sponsor, alice, ~tfSponsorSetMask - tfInnerBatchTxn), + ter(temINVALID_FLAG)); + + env(sponsor::set( + sponsor, + alice, + tfSponsorshipSetRequireSignForFee | + tfSponsorshipClearRequireSignForFee), + ter(temINVALID_FLAG)); + + env(sponsor::set( + sponsor, + alice, + tfSponsorshipSetRequireSignForReserve | + tfSponsorshipClearRequireSignForReserve), + ter(temINVALID_FLAG)); + + for (auto flag : + {tfSponsorshipSetRequireSignForFee, + tfSponsorshipClearRequireSignForFee, + tfSponsorshipSetRequireSignForReserve, + tfSponsorshipClearRequireSignForReserve}) + { + env(sponsor::set(sponsor, alice, tfDeleteObject | flag), + ter(temINVALID_FLAG)); + } + } + + // invalid SponsorAccount + env(sponsor::set(alice, sponsor, tfDeleteObject), + sponsor::sponsorAcc(alice), + ter(temMALFORMED)); + env(sponsor::set(alice, sponsor, tfDeleteObject), + sponsor::sponsorAcc(bob), + ter(temMALFORMED)); + env(sponsor::set(alice, alice, 0), + sponsor::sponsorAcc(sponsor), + ter(temMALFORMED)); + + // Invalid Sponsee + env(sponsor::set(sponsor, sponsor, 0), ter(temMALFORMED)); + + // Invalid feeAmount + env(sponsor::set( + sponsor, alice, tfSponsorshipClearRequireSignForFee, 0, XRP(1)), + ter(temMALFORMED)); + + for (auto amt : {XRP(-1), XRP(0), USD(1)}) + { + env(sponsor::set(sponsor, alice, 0, 1, amt), ter(temBAD_AMOUNT)); + } + + // Invalid reserveCount + env(sponsor::set( + sponsor, alice, tfSponsorshipClearRequireSignForReserve, 1), + ter(temMALFORMED)); + env(sponsor::set(sponsor, alice, 0, 0), ter(temMALFORMED)); + + // Invalid Delete operation + env(sponsor::set(sponsor, alice, tfDeleteObject, 1), ter(temMALFORMED)); + env(sponsor::set(sponsor, alice, tfDeleteObject, std::nullopt, XRP(1)), + ter(temMALFORMED)); + + // + // preclaim + // + + // Invalid Sponsee + env(sponsor::set(sponsor, noFunded, 0), ter(tecNO_DST)); + + // Invalid Delete operation (not found) + env(sponsor::set(sponsor, alice, tfDeleteObject), ter(tecNO_ENTRY)); + + // DisallowIncomingSponsor: tested in other testcase + + // create sponsor to use above tests + env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); + env.close(); } void @@ -116,12 +220,6 @@ class Sponsor_test : public beast::unit_test::suite env(signers(sponsor, 1, {{signer1, 1}, {signer2, 1}})); env.close(); - // Signature doesn't exist - env(noop(alice), - fee(XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - ter(telENV_RPC_FAILED)); - // Invalid signature auto tx = noop(alice); auto& signers1 = @@ -367,19 +465,57 @@ class Sponsor_test : public beast::unit_test::suite using namespace test::jtx; testcase("Sponsor Fee"); - Env env{*this, testable_amendments()}; - Account const alice("alice"); - Account const sponsor("sponsor"); - env.fund(XRP(10000), alice, sponsor); - env(noop(alice), - fee(XRP(1)), - sponsor::as(sponsor, tfSponsorFee), - sponsor::sig(sponsor)); - env.close(); + { + // co-signing + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + env.close(); + + env(noop(alice), + fee(XRP(1)), + sponsor::as(sponsor, tfSponsorFee), + sponsor::sig(sponsor), + ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == XRP(10000)); + BEAST_EXPECT(env.balance(sponsor) == XRP(9999)); + } + { + // pre funded + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + env.close(); + + env(sponsor::set(sponsor, alice, 0, std::nullopt, XRP(1)), + ter(tesSUCCESS)); + env.close(); - BEAST_EXPECT(env.balance(alice) == XRP(10000)); - BEAST_EXPECT(env.balance(sponsor) == XRP(9999)); + auto const sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle->getFieldAmount(sfFeeAmount) == XRP(1)); + BEAST_EXPECT(!sle->isFieldPresent(sfReserveCount)); + + auto const sponsorBalanceBefore = env.balance(sponsor); + auto const aliceBalanceBefore = env.balance(alice); + + env(noop(alice), + fee(drops(500)), + sponsor::as(sponsor, tfSponsorFee), + ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalanceBefore); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalanceBefore); + + auto const sle2 = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT( + sle2->getFieldAmount(sfFeeAmount) == XRP(1) - drops(500)); + } } void @@ -418,6 +554,7 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob); env.fund(drops(reserve) + drops(increment) - drops(1), sponsor); + env.close(); // check sponsor balance env(check::create(alice, bob, XRP(1)), @@ -483,6 +620,7 @@ class Sponsor_test : public beast::unit_test::suite auto USD = gw["USD"]; env.fund(XRP(10000), alice, gw, sponsor); + env.close(); // OfferCreate auto const seq = env.seq(alice); @@ -518,6 +656,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("master"); env.fund(XRP(1000000), alice, sponsor); + env.close(); // TicketCreate std::uint32_t const ticketSeq{env.seq(alice) + 1}; @@ -551,6 +690,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); env.fund(XRP(1000000), issuer, subject, sponsor); + env.close(); // CredentialsCreate env(credentials::create(subject, issuer, "credType"), @@ -599,6 +739,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); // DelegateSet env(delegate::set(alice, bob, {"Payment"}), @@ -634,6 +775,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); env.fund(XRP(1000000), alice, sponsor); + env.close(); // DIDSet env(did::set(alice), @@ -700,6 +842,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); env.fund(XRP(1000000), alice, sponsor); + env.close(); Account const bob("bob"); @@ -737,6 +880,73 @@ class Sponsor_test : public beast::unit_test::suite { } + void + testDisallowIncoming() + { + testcase("DisallowIncoming"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + + env.fund(XRP(1000000), alice, sponsor); + env.close(); + + // set DisallowIncomingSponsor + env(fset(alice, asfDisallowIncomingSponsor)); + env.close(); + + // Create sponsor should fail + env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), + ter(tecNO_PERMISSION)); + env.close(); + + // clear flag + env(fclear(alice, asfDisallowIncomingSponsor)); + env.close(); + + // Create sponsor + env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); + env.close(); + + // set flag + env(fset(alice, asfDisallowIncomingSponsor)); + env.close(); + + // Update sponsor should success + env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); + env.close(); + + // Delete sponsor shoud success + env(sponsor::set(sponsor, alice, tfDeleteObject), ter(tesSUCCESS)); + env.close(); + } + void + testAccountDelete() + { + testcase("AccountDelete"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // set sponsor + env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); + env.close(); + + // AccountDelete + env(acctdelete(alice, bob)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + void testSponsorReserve() { @@ -764,12 +974,19 @@ class Sponsor_test : public beast::unit_test::suite run() override { testDisabled(); + testInvalidSponsorSet(); + testSingleSigning(); testMultiSigning(); + // testInvalidSigninig(); // borh TxnSignature and Signers are present + // -> error testTransferSponsor(); testSponsorFee(); testSponsorAccount(); testSponsorReserve(); + testDisallowIncoming(); + + // testAccountDelete(); } }; diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 4d8340db964..f28c643b46a 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -22,6 +22,7 @@ #include #include +#include #include namespace ripple { @@ -30,6 +31,36 @@ namespace jtx { namespace sponsor { +Json::Value +set(jtx::Account const& account, + jtx::Account const& sponsee, + uint32_t flags, + std::optional reserveCount, + std::optional feeAmount) +{ + Json::Value jv; + jv[jss::TransactionType] = jss::SponsorSet; + jv[jss::Account] = account.human(); + jv[sfSponsee.jsonName] = sponsee.human(); + jv[sfFlags.jsonName] = flags; + if (reserveCount) + jv[sfReserveCount.jsonName] = *reserveCount; + if (feeAmount) + jv[sfFeeAmount.jsonName] = feeAmount->getJson(JsonOptions::none); + return jv; +} + +Json::Value +del(jtx::Account const& account, jtx::Account const& sponsee) +{ + Json::Value jv; + jv[jss::TransactionType] = jss::SponsorSet; + jv[jss::Account] = account.human(); + jv[sfSponsee.jsonName] = sponsee.human(); + jv[sfFlags.jsonName] = tfDeleteObject; + return jv; +} + Json::Value transfer(jtx::Account const& account, std::optional const& index) { @@ -37,10 +68,16 @@ transfer(jtx::Account const& account, std::optional const& index) jv[jss::TransactionType] = jss::SponsorTransfer; jv[jss::Account] = account.human(); if (index) - jv[sfLedgerIndex.jsonName] = to_string(*index); + jv[sfObjectID.jsonName] = to_string(*index); return jv; } +void +sponsorAcc::operator()(Env& env, JTx& jt) const +{ + jt.jv[sfSponsorAccount.jsonName] = sponsor_.human(); +} + void as::operator()(Env& env, JTx& jt) const { @@ -85,7 +122,6 @@ sig::operator()(Env& env, JTx& jt) const void msig::operator()(Env& env, JTx& jt) const { - jt.jv[sfSponsor.jsonName][sfSigningPubKey.jsonName] = ""; auto const mySigners = signers; jt.sponsorSigner = [mySigners, &env](Env&, JTx& jtx) { std::optional st; diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index 683c2e0ae49..bd603c0d412 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -21,8 +21,7 @@ #include #include - -#include "test/jtx/SignerUtils.h" +#include namespace ripple { namespace test { @@ -30,11 +29,34 @@ namespace jtx { namespace sponsor { +Json::Value +set(jtx::Account const& account, + jtx::Account const& sponsee, + std::uint32_t flags, + std::optional reserveCount = std::nullopt, + std::optional feeAmount = std::nullopt); + +Json::Value +del(jtx::Account const& account, jtx::Account const& sponsee); + Json::Value transfer( jtx::Account const& account, std::optional const& index = std::nullopt); +struct sponsorAcc +{ +private: + jtx::Account sponsor_; + +public: + sponsorAcc(jtx::Account const& account) : sponsor_(account) + { + } + + void + operator()(jtx::Env&, jtx::JTx& jtx) const; +}; struct as { private: diff --git a/src/test/rpc/AccountSet_test.cpp b/src/test/rpc/AccountSet_test.cpp index 3615a715cd4..2383434b9d5 100644 --- a/src/test/rpc/AccountSet_test.cpp +++ b/src/test/rpc/AccountSet_test.cpp @@ -87,7 +87,8 @@ class AccountSet_test : public beast::unit_test::suite if (flag == asfDisallowIncomingCheck || flag == asfDisallowIncomingPayChan || flag == asfDisallowIncomingNFTokenOffer || - flag == asfDisallowIncomingTrustline) + flag == asfDisallowIncomingTrustline || + flag == asfDisallowIncomingSponsor) { // These flags are part of the DisallowIncoming amendment // and are tested elsewhere diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index 4311aa79a8d..d7398e3faef 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -194,6 +195,18 @@ removeDelegateFromLedger( return DelegateSet::deleteDelegate(view, sleDel, account, j); } +TER +removeSponsorshipFromLedger( + Application& app, + ApplyView& view, + AccountID const&, + uint256 const& delIndex, + std::shared_ptr const& sleDel, + beast::Journal j) +{ + return SponsorSet::deleteSponsorship(view, sleDel, j); +} + // Return nullptr if the LedgerEntryType represents an obligation that can't // be deleted. Otherwise return the pointer to the function that can delete // the non-obligation @@ -220,6 +233,8 @@ nonObligationDeleter(LedgerEntryType t) return removeCredentialFromLedger; case ltDELEGATE: return removeDelegateFromLedger; + case ltSPONSORSHIP: + return removeSponsorshipFromLedger; default: return nullptr; } diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index da0dfc117fc..fd1ab52f003 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -112,6 +112,10 @@ XRPNotCreated::visitEntry( if (isXRP((*before)[sfAmount])) drops_ -= (*before)[sfAmount].xrp().drops(); break; + case ltSPONSORSHIP: + if (before->isFieldPresent(sfFeeAmount)) + drops_ -= (*before)[sfFeeAmount].xrp().drops(); + break; default: break; } @@ -134,6 +138,10 @@ XRPNotCreated::visitEntry( if (!isDelete && isXRP((*after)[sfAmount])) drops_ += (*after)[sfAmount].xrp().drops(); break; + case ltSPONSORSHIP: + if (!isDelete && after->isFieldPresent(sfFeeAmount)) + drops_ += (*after)[sfFeeAmount].xrp().drops(); + break; default: break; } @@ -543,6 +551,7 @@ LedgerEntryTypesMatch::visitEntry( case ltCREDENTIAL: case ltPERMISSIONED_DOMAIN: case ltVAULT: + case ltSPONSORSHIP: break; default: invalidTypeAdded_ = true; diff --git a/src/xrpld/app/tx/detail/SetAccount.cpp b/src/xrpld/app/tx/detail/SetAccount.cpp index ec618981c17..55154b1aa62 100644 --- a/src/xrpld/app/tx/detail/SetAccount.cpp +++ b/src/xrpld/app/tx/detail/SetAccount.cpp @@ -648,6 +648,14 @@ SetAccount::doApply() uFlagsOut |= lsfDisallowIncomingTrustline; else if (uClearFlag == asfDisallowIncomingTrustline) uFlagsOut &= ~lsfDisallowIncomingTrustline; + + if (ctx_.view().rules().enabled(featureSponsor)) + { + if (uSetFlag == asfDisallowIncomingSponsor) + uFlagsOut |= lsfDisallowIncomingSponsor; + else if (uClearFlag == asfDisallowIncomingSponsor) + uFlagsOut &= ~lsfDisallowIncomingSponsor; + } } // Set or clear flags for disallowing escrow diff --git a/src/xrpld/app/tx/detail/SponsorSet.cpp b/src/xrpld/app/tx/detail/SponsorSet.cpp new file mode 100644 index 00000000000..4281dae7c9d --- /dev/null +++ b/src/xrpld/app/tx/detail/SponsorSet.cpp @@ -0,0 +1,273 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include + +#include + +namespace ripple { + +NotTEC +SponsorSet::preflight(PreflightContext const& ctx) +{ + if (!ctx.rules.enabled(featureSponsor)) + return temDISABLED; + + if (auto const ter = preflight1(ctx)) + return ter; + + // check Flags + { + if (ctx.tx.getFlags() & tfSponsorSetMask) + return temINVALID_FLAG; + + if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForFee) && + ctx.tx.isFlag(tfSponsorshipClearRequireSignForFee)) + return temINVALID_FLAG; + + if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForReserve) && + ctx.tx.isFlag(tfSponsorshipClearRequireSignForReserve)) + return temINVALID_FLAG; + + if (ctx.tx.isFlag(tfDeleteObject)) + { + // check Flags + if (ctx.tx.getFlags() & + (tfSponsorshipSetRequireSignForFee | + tfSponsorshipSetRequireSignForReserve | + tfSponsorshipClearRequireSignForFee | + tfSponsorshipClearRequireSignForReserve)) + return temINVALID_FLAG; + } + } + + if (ctx.tx.isFieldPresent(sfSponsorAccount)) + { + // SponsorAccount is used when sponsee Deleting ltSponsorship + // Account => Sponsee of Sponsorshop + // SponsorAccount => Sponsor of Sponsorshop + // Sponsee => Sponsee of Sponsorshop + if (ctx.tx.getAccountID(sfAccount) == + ctx.tx.getAccountID(sfSponsorAccount) || + ctx.tx.getAccountID(sfSponsee) != + ctx.tx.getAccountID(sfSponsorAccount) || + !ctx.tx.isFlag(tfDeleteObject)) + return temMALFORMED; + } + + auto const sponsor = ctx.tx.isFieldPresent(sfSponsorAccount) + ? ctx.tx.getAccountID(sfSponsorAccount) + : ctx.tx.getAccountID(sfAccount); + auto const sponsee = ctx.tx.getAccountID(sfSponsee); + + if (sponsee == sponsor) + return temMALFORMED; + + if (ctx.tx.isFieldPresent(sfFeeAmount)) + { + if (ctx.tx.getFlags() & tfSponsorshipClearRequireSignForFee) + return temMALFORMED; + + auto const feeAmount = ctx.tx.getFieldAmount(sfFeeAmount); + + if (!isXRP(feeAmount)) + return temBAD_AMOUNT; + + if (feeAmount.xrp().drops() <= 0) + return temBAD_AMOUNT; + } + + if (ctx.tx.isFieldPresent(sfReserveCount)) + { + if (ctx.tx.getFlags() & tfSponsorshipClearRequireSignForReserve) + return temMALFORMED; + + auto const reserveCount = ctx.tx.getFieldU32(sfReserveCount); + // TODO: max reserveCount? + if (reserveCount < 1) + return temMALFORMED; + } + + if (ctx.tx.isFlag(tfDeleteObject)) + { + if (ctx.tx.isFieldPresent(sfFeeAmount) || + ctx.tx.isFieldPresent(sfReserveCount)) + return temMALFORMED; + } + + return preflight2(ctx); +} + +TER +SponsorSet::preclaim(PreclaimContext const& ctx) +{ + auto const sponsor = ctx.tx.isFieldPresent(sfSponsorAccount) + ? ctx.tx.getAccountID(sfSponsorAccount) + : ctx.tx.getAccountID(sfAccount); + auto const sponsee = ctx.tx[sfSponsee]; + + // check Sponsee + auto const sponseeSle = ctx.view.read(keylet::account(sponsee)); + if (!sponseeSle) + return tecNO_DST; + + // check if object exists + auto const sponsorObjSle = ctx.view.read(keylet::sponsor(sponsor, sponsee)); + + if (ctx.tx.isFlag(tfDeleteObject) && !sponsorObjSle) + return tecNO_ENTRY; + + if (sponseeSle->isFlag(lsfDisallowIncomingSponsor) && !sponsorObjSle) + // new sponsor creation is not allowed by disallowIncomingSponsor flag + return tecNO_PERMISSION; + + return tesSUCCESS; +} + +TER +SponsorSet::doApply() +{ + auto const sponseeAcc = ctx_.tx[sfSponsee]; + auto const keylet = keylet::sponsor(account_, sponseeAcc); + + auto const sponsorAcc = ctx_.tx.isFieldPresent(sfSponsorAccount) + ? ctx_.tx.getAccountID(sfSponsorAccount) + : account_; + + auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorAcc)); + if (!sponsorAccSle) + return tecINTERNAL; + + auto const sponsorObjSle = ctx_.view().peek(keylet); + + if (ctx_.tx.isFlag(tfDeleteObject)) + { + // Delete + if (!sponsorObjSle) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const sponsor = + getLedgerEntryReserveSponsor(ctx_.view(), sponsorObjSle); + adjustOwnerCount(ctx_.view(), sponsorAccSle, sponsor, -1, ctx_.journal); + + ctx_.view().dirRemove( + keylet::ownerDir(sponsorAcc), + (*sponsorObjSle)[sfSponsorNode], + sponsorObjSle->key(), + false); + ctx_.view().dirRemove( + keylet::ownerDir(sponseeAcc), + (*sponsorObjSle)[sfSponseeNode], + sponsorObjSle->key(), + false); + + // transfer feeAmount from ledger entry + auto const feeAmount = sponsorObjSle->getFieldAmount(sfFeeAmount); + (*sponsorAccSle)[sfBalance] += feeAmount; + + ctx_.view().erase(sponsorObjSle); + + return tesSUCCESS; + } + + auto const feeAmount = ctx_.tx[~sfFeeAmount]; + auto const reserveCount = ctx_.tx[~sfReserveCount]; + + auto reserveSponsorAccSle = getTxReserveSponsor(view(), ctx_.tx); + + if (!sponsorObjSle) + { + // Create + auto newSle = std::make_shared(keylet); + + if (auto const ret = checkInsufficientReserve( + ctx_.view(), + sponsorAccSle, + mPriorBalance, + reserveSponsorAccSle, + 1); + !isTesSuccess(ret)) + return tecUNFUNDED; + + (*newSle)[sfAccount] = sponsorAcc; + (*newSle)[sfSponsee] = sponseeAcc; + (*newSle)[sfFlags] = ctx_.tx.getFlags(); + if (feeAmount) + { + (*sponsorAccSle)[sfBalance] -= *feeAmount; + (*newSle)[sfFeeAmount] = *feeAmount; + } + if (reserveCount) + { + (*newSle)[sfReserveCount] = *reserveCount; + } + + auto const sponsorPage = view().dirInsert( + keylet::ownerDir(sponsorAcc), keylet, describeOwnerDir(sponsorAcc)); + (*newSle)[sfSponsorNode] = *sponsorPage; + + auto const sponseePage = view().dirInsert( + keylet::ownerDir(sponseeAcc), keylet, describeOwnerDir(sponseeAcc)); + (*newSle)[sfSponseeNode] = *sponseePage; + + auto viewJ = ctx_.app.journal("View"); + + adjustOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, viewJ); + addSponsorToLedgerEntry(newSle, reserveSponsorAccSle); + + ctx_.view().insert(newSle); + return tesSUCCESS; + } + + // Update + if (feeAmount) + { + // TODO: transfer feeAmount to ledger entry + (*sponsorAccSle)[sfBalance] -= *feeAmount; + (*sponsorObjSle)[sfFeeAmount] += *feeAmount; + } + + if (reserveCount) + (*sponsorObjSle)[sfReserveCount] = + (*sponsorObjSle)[sfReserveCount] + *reserveCount; + + // TODO: update Flags? + auto flags = sponsorObjSle->getFieldU32(sfFlags); + if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForFee)) + flags |= lsfSponsorshipRequireSignForFee; + + if (ctx_.tx.isFlag(tfSponsorshipClearRequireSignForFee)) + flags &= ~lsfSponsorshipRequireSignForFee; + + if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForReserve)) + flags |= lsfSponsorshipRequireSignForReserve; + + if (ctx_.tx.isFlag(tfSponsorshipClearRequireSignForReserve)) + flags &= ~lsfSponsorshipRequireSignForReserve; + + if (flags != (*sponsorObjSle)[sfFlags]) + (*sponsorObjSle)[sfFlags] = flags; + + ctx_.view().update(sponsorObjSle); + + return tesSUCCESS; +} + +} // namespace ripple diff --git a/src/xrpld/app/tx/detail/SponsorSet.h b/src/xrpld/app/tx/detail/SponsorSet.h new file mode 100644 index 00000000000..f2ed220bb00 --- /dev/null +++ b/src/xrpld/app/tx/detail/SponsorSet.h @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_SPONSORSET_H_INCLUDED +#define RIPPLE_TX_SPONSORSET_H_INCLUDED + +#include + +namespace ripple { + +class SponsorSet : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit SponsorSet(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; +}; + +} // namespace ripple + +#endif diff --git a/src/xrpld/app/tx/detail/SponsorTransfer.cpp b/src/xrpld/app/tx/detail/SponsorTransfer.cpp index d58d0b511f3..7dc0909a889 100644 --- a/src/xrpld/app/tx/detail/SponsorTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorTransfer.cpp @@ -79,8 +79,8 @@ getLedgerEntryOwner( auto const signerList = view.read(keylet::signers(account)); if (!signerList) return std::nullopt; - if (signerList->getFieldH256(sfLedgerIndex) == - sle->getFieldH256(sfLedgerIndex)) + if (signerList->getFieldH256(sfObjectID) == + sle->getFieldH256(sfObjectID)) return account; return std::nullopt; } @@ -110,7 +110,7 @@ getLedgerEntryOwner( TER SponsorTransfer::preclaim(PreclaimContext const& ctx) { - auto const index = ctx.tx[~sfLedgerIndex]; + auto const index = ctx.tx[~sfObjectID]; auto const newSponsor = getTxReserveSponsor(ctx.view, ctx.tx); bool const isObjectSponsor = index != std::nullopt; @@ -198,7 +198,7 @@ SponsorTransfer::doApply() { auto const& tx = ctx_.tx; - auto const index = tx[~sfLedgerIndex]; + auto const index = tx[~sfObjectID]; bool const isObjectSponsor = index != std::nullopt; auto const accSle = view().peek(keylet::account(account_)); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 4ad45d13ac2..c6c42cc57aa 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -167,18 +167,11 @@ preflight1(PreflightContext const& ctx) JLOG(ctx.j.fatal()) << "preflight1: invalid sponsor account"; return temMALFORMED; } - if (!(sponsor[sfFlags] & tfSponsorFee) && - !(sponsor[sfFlags] & tfSponsorReserve)) + if (!(sponsor.getFlags() & tfSponsorMask)) { JLOG(ctx.j.fatal()) << "preflight1: invalid sponsor flags"; return temMALFORMED; } - if (!sponsor.isFieldPresent(sfTxnSignature) && - !sponsor.isFieldPresent(sfSigners)) - { - JLOG(ctx.j.fatal()) << "preflight1: no sfTxnSignature or sfSigners"; - return temMALFORMED; - } } return tesSUCCESS; @@ -255,6 +248,41 @@ Transactor::checkPermission(ReadView const& view, STTx const& tx) return checkTxPermission(sle, tx); } +TER +Transactor::checkSponsor(ReadView const& view, STTx const& tx) +{ + if (!tx.isFieldPresent(sfSponsor)) + return tesSUCCESS; + + auto const txSponsor = tx.getFieldObject(sfSponsor); + + auto const sponsorAcc = txSponsor.getAccountID(sfAccount); + auto const sponseeAcc = tx.getAccountID(sfAccount); + + auto const sponsorSle = view.read(keylet::sponsor(sponsorAcc, sponseeAcc)); + if (!sponsorSle) + return tesSUCCESS; + + auto const hasSignature = txSponsor.isFieldPresent(sfTxnSignature) || + !txSponsor.getFieldVL(sfSigningPubKey).empty() || + txSponsor.isFieldPresent(sfSigners); + + if (txSponsor.isFlag(tfSponsorFee) && + sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) + { + if (!hasSignature) + return tecNO_SPONSOR_PERMISSION; + } + if (txSponsor.isFlag(tfSponsorReserve) && + sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) + { + if (!hasSignature) + return tecNO_SPONSOR_PERMISSION; + } + + return tesSUCCESS; +} + XRPAmount Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) { @@ -263,6 +291,7 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) // The computation has two parts: // * The base fee, which is the same for most transactions. // * The additional cost of each multisignature on the transaction. + // * The additional cost of each multisignature on the sponsor. XRPAmount const baseFee = view.fees().base; // Each signer adds one more baseFee to the minimum required fee @@ -270,7 +299,16 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) std::size_t const signerCount = tx.isFieldPresent(sfSigners) ? tx.getFieldArray(sfSigners).size() : 0; - return baseFee + (signerCount * baseFee); + std::size_t sponsorSignerCount = 0; + if (tx.isFieldPresent(sfSponsor)) + { + auto const sponsorObj = tx.getFieldObject(sfSponsor); + sponsorSignerCount += sponsorObj.isFieldPresent(sfSigners) + ? sponsorObj.getFieldArray(sfSigners).size() + : 0; + } + + return baseFee + ((signerCount + sponsorSignerCount) * baseFee); } XRPAmount @@ -352,6 +390,17 @@ Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); + auto const isFeeSponsorObj = [&]() -> bool { + if (ctx_.tx.isFieldPresent(sfSponsor)) + { + auto const sponsor = ctx_.tx.getFieldObject(sfSponsor); + if (sponsor.getFieldVL(sfSigningPubKey).empty() && + !sponsor.isFieldPresent(sfSigners)) + return sponsor.getFlags() & tfSponsorFee; + } + return false; + }; + if (ctx_.tx.isFieldPresent(sfDelegate)) { // Delegated transactions are paid by the delegated account. @@ -364,6 +413,19 @@ Transactor::payFee() sfBalance, delegatedSle->getFieldAmount(sfBalance) - feePaid); view().update(delegatedSle); } + else if (isFeeSponsorObj()) + { + auto const sponsor = ctx_.tx.getFieldObject(sfSponsor); + auto const sponsorAcc = sponsor.getAccountID(sfAccount); + auto const sponsorSle = + view().peek(keylet::sponsor(sponsorAcc, account_)); + if (!sponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + sponsorSle->setFieldAmount( + sfFeeAmount, sponsorSle->getFieldAmount(sfFeeAmount) - feePaid); + view().update(sponsorSle); + } else { auto const id = ctx_.tx.getFeePayer(); @@ -649,8 +711,15 @@ Transactor::checkSign(PreclaimContext const& ctx) if (ctx.tx.isFieldPresent(sfSponsor)) { - if (auto const ret = checkSponsorSign(ctx); !isTesSuccess(ret)) - return ret; + auto const sponsorObj = ctx.tx.getFieldObject(sfSponsor); + auto const isCoSigned = sponsorObj.isFieldPresent(sfTxnSignature) || + !sponsorObj.getFieldVL(sfSigningPubKey).empty() || + sponsorObj.isFieldPresent(sfSigners); + if (isCoSigned) + { + if (auto const ret = checkSponsorSign(ctx); !isTesSuccess(ret)) + return ret; + } } // Check Single Sign diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index 08a52cf226d..d5beac58054 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -209,6 +209,9 @@ class Transactor static TER checkPermission(ReadView const& view, STTx const& tx); + + static TER + checkSponsor(ReadView const& view, STTx const& tx); ///////////////////////////////////////////////////// // Interface used by DeleteAccount diff --git a/src/xrpld/app/tx/detail/apply.cpp b/src/xrpld/app/tx/detail/apply.cpp index e455675bb8b..6b91314ad07 100644 --- a/src/xrpld/app/tx/detail/apply.cpp +++ b/src/xrpld/app/tx/detail/apply.cpp @@ -85,12 +85,19 @@ checkValidity( if (tx.isFieldPresent(sfSponsor) && rules.enabled(featureSponsor)) { - auto const sigVerify = - tx.checkSponsorSign(requireCanonicalSig, rules); - if (!sigVerify) + auto const sponsorObj = tx.getFieldObject(sfSponsor); + auto const isCoSigned = sponsorObj.isFieldPresent(sfTxnSignature) || + !sponsorObj.getFieldVL(sfSigningPubKey).empty() || + sponsorObj.isFieldPresent(sfSigners); + if (isCoSigned) { - router.setFlags(id, SF_SIGBAD); - return {Validity::SigBad, sigVerify.error()}; + auto const sigVerify = + tx.checkSponsorSign(requireCanonicalSig, rules); + if (!sigVerify) + { + router.setFlags(id, SF_SIGBAD); + return {Validity::SigBad, sigVerify.error()}; + } } } diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index f47d47ebef3..8f25145aa56 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -206,6 +207,11 @@ invoke_preclaim(PreclaimContext const& ctx) result = T::checkPermission(ctx.view, ctx.tx); + if (result != tesSUCCESS) + return result; + + result = T::checkSponsor(ctx.view, ctx.tx); + if (result != tesSUCCESS) return result; diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 61a7e2fb2c4..930c54d49fe 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -629,6 +629,27 @@ parseVault(Json::Value const& params, Json::StaticString const fieldName) return keylet::vault(*id, *seq).key; } +static Expected +parseSponsorship(Json::Value const& params, Json::StaticString const fieldName) +{ + if (!params.isObject()) + { + return parseObjectID(params, fieldName); + } + + auto const id = LedgerEntryHelpers::requiredAccountID( + params, jss::owner, "malformedOwner"); + if (!id) + return Unexpected(id.error()); + + auto const sponsee = LedgerEntryHelpers::requiredAccountID( + params, jss::sponsee, "malformedAddress"); + if (!sponsee) + return Unexpected(sponsee.error()); + + return keylet::sponsor(*id, *sponsee).key; +} + static Expected parseXChainOwnedClaimID( Json::Value const& claim_id, From 8e895a3e7dd785868c17fe0eac6c587ebb357a20 Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 13 Sep 2025 09:49:30 +0900 Subject: [PATCH 015/249] fullly rename --- include/xrpl/protocol/TxFlags.h | 4 +- .../xrpl/protocol/detail/transactions.macro | 4 +- src/test/app/Sponsor_test.cpp | 8 ++-- src/test/jtx/impl/sponsor.cpp | 6 +-- src/xrpld/app/tx/detail/DeleteAccount.cpp | 4 +- .../{SponsorSet.cpp => SponsorshipSet.cpp} | 40 ++++++++++++++++--- .../{SponsorTransfer.h => SponsorshipSet.h} | 15 +++++-- ...orTransfer.cpp => SponsorshipTransfer.cpp} | 8 ++-- .../{SponsorSet.h => SponsorshipTransfer.h} | 8 ++-- src/xrpld/app/tx/detail/applySteps.cpp | 4 +- 10 files changed, 69 insertions(+), 32 deletions(-) rename src/xrpld/app/tx/detail/{SponsorSet.cpp => SponsorshipSet.cpp} (89%) rename src/xrpld/app/tx/detail/{SponsorTransfer.h => SponsorshipSet.h} (78%) rename src/xrpld/app/tx/detail/{SponsorTransfer.cpp => SponsorshipTransfer.cpp} (98%) rename src/xrpld/app/tx/detail/{SponsorSet.h => SponsorshipTransfer.h} (86%) diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 1216bfe7918..bcc869830c2 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -255,13 +255,13 @@ constexpr std::uint32_t tfIndependent = 0x00080000; constexpr std::uint32_t const tfBatchMask = ~(tfUniversal | tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent) | tfInnerBatchTxn; -// SponsorSet flags: +// SponsorshipSet flags: constexpr std::uint32_t tfSponsorshipSetRequireSignForFee = 0x00010000; constexpr std::uint32_t tfSponsorshipClearRequireSignForFee = 0x00020000; constexpr std::uint32_t tfSponsorshipSetRequireSignForReserve = 0x00040000; constexpr std::uint32_t tfSponsorshipClearRequireSignForReserve = 0x00080000; constexpr std::uint32_t tfDeleteObject = 0x00100000; -constexpr std::uint32_t tfSponsorSetMask = ~(tfUniversal | tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee | tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve | tfDeleteObject); +constexpr std::uint32_t tfSponsorshipSetMask = ~(tfUniversal | tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee | tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve | tfDeleteObject); // clang-format on diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 997e70013ff..b6c114f5a20 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -527,12 +527,12 @@ TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, ({ })) /** This transaction transfer sponsorship */ -TRANSACTION(ttSPONSORSHIP_TRANSFER, 72, SponsorTransfer, Delegation::notDelegatable, ({ +TRANSACTION(ttSPONSORSHIP_TRANSFER, 72, SponsorshipTransfer, Delegation::notDelegatable, ({ {sfObjectID, soeOPTIONAL}, })) /** This transaction create sponsorship object */ -TRANSACTION(ttSPONSORSHIP_SET, 73, SponsorSet, Delegation::notDelegatable, ({ +TRANSACTION(ttSPONSORSHIP_SET, 73, SponsorshipSet, Delegation::notDelegatable, ({ {sfSponsorAccount, soeOPTIONAL}, {sfSponsee, soeREQUIRED}, {sfFeeAmount, soeOPTIONAL}, diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 68fb63ee88c..319f9f6ba25 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -53,9 +53,9 @@ class Sponsor_test : public beast::unit_test::suite } void - testInvalidSponsorSet() + testInvalidSponsorshipSet() { - testcase("Invalid SponsorSet"); + testcase("Invalid SponsorshipSet"); using namespace test::jtx; Env env{*this, testable_amendments()}; Account const alice("alice"); @@ -75,7 +75,7 @@ class Sponsor_test : public beast::unit_test::suite // Invalid flags { env(sponsor::set( - sponsor, alice, ~tfSponsorSetMask - tfInnerBatchTxn), + sponsor, alice, ~tfSponsorshipSetMask - tfInnerBatchTxn), ter(temINVALID_FLAG)); env(sponsor::set( @@ -974,7 +974,7 @@ class Sponsor_test : public beast::unit_test::suite run() override { testDisabled(); - testInvalidSponsorSet(); + testInvalidSponsorshipSet(); testSingleSigning(); testMultiSigning(); diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index f28c643b46a..6bfe074c166 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -39,7 +39,7 @@ set(jtx::Account const& account, std::optional feeAmount) { Json::Value jv; - jv[jss::TransactionType] = jss::SponsorSet; + jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); jv[sfSponsee.jsonName] = sponsee.human(); jv[sfFlags.jsonName] = flags; @@ -54,7 +54,7 @@ Json::Value del(jtx::Account const& account, jtx::Account const& sponsee) { Json::Value jv; - jv[jss::TransactionType] = jss::SponsorSet; + jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); jv[sfSponsee.jsonName] = sponsee.human(); jv[sfFlags.jsonName] = tfDeleteObject; @@ -65,7 +65,7 @@ Json::Value transfer(jtx::Account const& account, std::optional const& index) { Json::Value jv; - jv[jss::TransactionType] = jss::SponsorTransfer; + jv[jss::TransactionType] = jss::SponsorshipTransfer; jv[jss::Account] = account.human(); if (index) jv[sfObjectID.jsonName] = to_string(*index); diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index d7398e3faef..33f7a108f9e 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -204,7 +204,7 @@ removeSponsorshipFromLedger( std::shared_ptr const& sleDel, beast::Journal j) { - return SponsorSet::deleteSponsorship(view, sleDel, j); + return SponsorshipSet::deleteSponsorship(view, sleDel, j); } // Return nullptr if the LedgerEntryType represents an obligation that can't diff --git a/src/xrpld/app/tx/detail/SponsorSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp similarity index 89% rename from src/xrpld/app/tx/detail/SponsorSet.cpp rename to src/xrpld/app/tx/detail/SponsorshipSet.cpp index 4281dae7c9d..923c421a346 100644 --- a/src/xrpld/app/tx/detail/SponsorSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include +#include #include #include @@ -25,7 +25,7 @@ namespace ripple { NotTEC -SponsorSet::preflight(PreflightContext const& ctx) +SponsorshipSet::preflight(PreflightContext const& ctx) { if (!ctx.rules.enabled(featureSponsor)) return temDISABLED; @@ -35,7 +35,7 @@ SponsorSet::preflight(PreflightContext const& ctx) // check Flags { - if (ctx.tx.getFlags() & tfSponsorSetMask) + if (ctx.tx.getFlags() & tfSponsorshipSetMask) return temINVALID_FLAG; if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForFee) && @@ -116,7 +116,7 @@ SponsorSet::preflight(PreflightContext const& ctx) } TER -SponsorSet::preclaim(PreclaimContext const& ctx) +SponsorshipSet::preclaim(PreclaimContext const& ctx) { auto const sponsor = ctx.tx.isFieldPresent(sfSponsorAccount) ? ctx.tx.getAccountID(sfSponsorAccount) @@ -142,7 +142,7 @@ SponsorSet::preclaim(PreclaimContext const& ctx) } TER -SponsorSet::doApply() +SponsorshipSet::doApply() { auto const sponseeAcc = ctx_.tx[sfSponsee]; auto const keylet = keylet::sponsor(account_, sponseeAcc); @@ -270,4 +270,34 @@ SponsorSet::doApply() return tesSUCCESS; } +TER +SponsorshipSet::deleteSponsorship( + ApplyView& view, + std::shared_ptr const& sle, + beast::Journal j) +{ + auto const sponsor = sle->getAccountID(sfSponsorAccount); + auto const sponsee = sle->getAccountID(sfSponsee); + + // adjust balance + auto const sponsorAccSle = view.peek(keylet::account(sponsor)); + if (!sponsorAccSle) + return tecINTERNAL; + + auto const feeAmount = sle->getFieldAmount(sfFeeAmount); + + (*sponsorAccSle)[sfBalance] += feeAmount; + + // delete sponsor node + view.dirRemove( + keylet::ownerDir(sponsor), (*sle)[sfSponsorNode], sle->key(), false); + // delete sponsee node + view.dirRemove( + keylet::ownerDir(sponsee), (*sle)[sfSponseeNode], sle->key(), false); + + view.erase(sle); + + return tesSUCCESS; +} + } // namespace ripple diff --git a/src/xrpld/app/tx/detail/SponsorTransfer.h b/src/xrpld/app/tx/detail/SponsorshipSet.h similarity index 78% rename from src/xrpld/app/tx/detail/SponsorTransfer.h rename to src/xrpld/app/tx/detail/SponsorshipSet.h index 68f596bcd08..ec22cca67e5 100644 --- a/src/xrpld/app/tx/detail/SponsorTransfer.h +++ b/src/xrpld/app/tx/detail/SponsorshipSet.h @@ -17,19 +17,19 @@ */ //============================================================================== -#ifndef RIPPLE_TX_SPONSORTRANSFER_H_INCLUDED -#define RIPPLE_TX_SPONSORTRANSFER_H_INCLUDED +#ifndef RIPPLE_TX_SPONSORSHIPSET_H_INCLUDED +#define RIPPLE_TX_SPONSORSHIPSET_H_INCLUDED #include namespace ripple { -class SponsorTransfer : public Transactor +class SponsorshipSet : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit SponsorTransfer(ApplyContext& ctx) : Transactor(ctx) + explicit SponsorshipSet(ApplyContext& ctx) : Transactor(ctx) { } @@ -41,6 +41,13 @@ class SponsorTransfer : public Transactor TER doApply() override; + + // Interface used by DeleteAccount + static TER + deleteSponsorship( + ApplyView& view, + std::shared_ptr const& sle, + beast::Journal j); }; } // namespace ripple diff --git a/src/xrpld/app/tx/detail/SponsorTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp similarity index 98% rename from src/xrpld/app/tx/detail/SponsorTransfer.cpp rename to src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 7dc0909a889..9eae02ac828 100644 --- a/src/xrpld/app/tx/detail/SponsorTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include +#include #include #include @@ -32,7 +32,7 @@ namespace ripple { NotTEC -SponsorTransfer::preflight(PreflightContext const& ctx) +SponsorshipTransfer::preflight(PreflightContext const& ctx) { if (!ctx.rules.enabled(featureSponsor)) return temDISABLED; @@ -108,7 +108,7 @@ getLedgerEntryOwner( } TER -SponsorTransfer::preclaim(PreclaimContext const& ctx) +SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { auto const index = ctx.tx[~sfObjectID]; auto const newSponsor = getTxReserveSponsor(ctx.view, ctx.tx); @@ -194,7 +194,7 @@ SponsorTransfer::preclaim(PreclaimContext const& ctx) } TER -SponsorTransfer::doApply() +SponsorshipTransfer::doApply() { auto const& tx = ctx_.tx; diff --git a/src/xrpld/app/tx/detail/SponsorSet.h b/src/xrpld/app/tx/detail/SponsorshipTransfer.h similarity index 86% rename from src/xrpld/app/tx/detail/SponsorSet.h rename to src/xrpld/app/tx/detail/SponsorshipTransfer.h index f2ed220bb00..452c4b06b2c 100644 --- a/src/xrpld/app/tx/detail/SponsorSet.h +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.h @@ -17,19 +17,19 @@ */ //============================================================================== -#ifndef RIPPLE_TX_SPONSORSET_H_INCLUDED -#define RIPPLE_TX_SPONSORSET_H_INCLUDED +#ifndef RIPPLE_TX_SponsorshipTransfer_H_INCLUDED +#define RIPPLE_TX_SponsorshipTransfer_H_INCLUDED #include namespace ripple { -class SponsorSet : public Transactor +class SponsorshipTransfer : public Transactor { public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - explicit SponsorSet(ApplyContext& ctx) : Transactor(ctx) + explicit SponsorshipTransfer(ApplyContext& ctx) : Transactor(ctx) { } diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index 8f25145aa56..cb6371891da 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -62,8 +62,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include From 4eea76ca92bc3fac8cf2f9fce3e79b6ca2ac6502 Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 13 Sep 2025 22:34:54 +0900 Subject: [PATCH 016/249] Create Sponsored Account / AccountDelete with ltSponsorship, Sponsored Account --- include/xrpl/protocol/TxFlags.h | 3 +- src/test/app/Sponsor_test.cpp | 113 +++++++++++++++++---- src/xrpld/app/tx/detail/DeleteAccount.cpp | 25 +++++ src/xrpld/app/tx/detail/Payment.cpp | 49 ++++++--- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 8 +- 5 files changed, 161 insertions(+), 37 deletions(-) diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index bcc869830c2..3c77c79885f 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -113,8 +113,9 @@ constexpr std::uint32_t tfOfferCreateMask = constexpr std::uint32_t tfNoRippleDirect = 0x00010000; constexpr std::uint32_t tfPartialPayment = 0x00020000; constexpr std::uint32_t tfLimitQuality = 0x00040000; +constexpr std::uint32_t tfSponsorCreatedAccount = 0x00080000; constexpr std::uint32_t tfPaymentMask = - ~(tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect); + ~(tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect | tfSponsorCreatedAccount); constexpr std::uint32_t tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment); // TrustSet flags: diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 319f9f6ba25..f0b2626b229 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -527,16 +527,48 @@ class Sponsor_test : public beast::unit_test::suite Account const alice("alice"); Account const sponsor("sponsor"); + Account const bob("bob"); + Account const charlie("charlie"); env.fund(XRP(10000), alice, sponsor); - Account const bob("bob"); - env(pay(alice, bob, STAmount(env.current()->fees().accountReserve(0))), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // Account is not sponsored by normal Sponsor specification + { + env(pay(alice, + bob, + STAmount(env.current()->fees().accountReserve(0))), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1); + auto const bobSle = env.le(keylet::account(bob)); + BEAST_EXPECT(!bobSle->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 0); + } + + // Use tfSponsorCreatedAccount to sponsor an account + { + // to funded accoutn + env(pay(sponsor, + bob, + STAmount(env.current()->fees().accountReserve(0))), + txflags(tfSponsorCreatedAccount), + ter(tecNO_SPONSOR_PERMISSION)); + + // to non-funded account + env(pay(sponsor, + charlie, + STAmount(env.current()->fees().accountReserve(0))), + txflags(tfSponsorCreatedAccount)); + env.close(); + + auto const charlieSle = env.le(keylet::account(charlie)); + BEAST_EXPECT(charlieSle->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT( + charlieSle->getAccountID(sfSponsorAccount) == sponsor.id()); + BEAST_EXPECT(sponsoredOwnerCount(env, charlie) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1); + } } void @@ -926,25 +958,66 @@ class Sponsor_test : public beast::unit_test::suite { testcase("AccountDelete"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); - env.fund(XRP(1000000), alice, bob, sponsor); - env.close(); + { + // Delete Account with ltSponsorship + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); - // set sponsor - env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); - env.close(); + // set sponsor + env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), + ter(tesSUCCESS)); + env.close(); - // AccountDelete - env(acctdelete(alice, bob)); - env.close(); + incLgrSeqForAccDel(env, alice); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + auto const keylet = keylet::sponsor(sponsor, alice); + auto const sponsorObj = env.le(keylet); + BEAST_EXPECT(sponsorObj); + + // AccountDelete + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(alice, bob), fee(requiredFee), ter(tesSUCCESS)); + env.close(); + } + + { + // Delete SponsoredAccount + Env env{*this, testable_amendments()}; + env.memoize(alice); + env.fund(XRP(1000000), bob, sponsor); + env.close(); + + // create SponsoredAccount + env(pay(sponsor, alice, XRP(10000)), + txflags(tfSponsorCreatedAccount)); + env.close(); + + incLgrSeqForAccDel(env, alice); + + // AccountDelete: destination = non-sponsor + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(alice, bob), + fee(requiredFee), + ter(tecNO_SPONSOR_PERMISSION)); + + auto const sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT( + sponsorSle->getFieldU32(sfSponsoringAccountCount) == 1); + + incLgrSeqForAccDel(env, alice); + + // AccountDelete: destination = sponsor + env(acctdelete(alice, sponsor), fee(requiredFee), ter(tesSUCCESS)); + + auto const sponsorSle2 = env.le(keylet::account(sponsor)); + BEAST_EXPECT( + !sponsorSle2->isFieldPresent(sfSponsoringAccountCount)); + } } void @@ -986,7 +1059,7 @@ class Sponsor_test : public beast::unit_test::suite testSponsorReserve(); testDisallowIncoming(); - // testAccountDelete(); + testAccountDelete(); } }; diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index 33f7a108f9e..45c496730ab 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -299,6 +299,15 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) return tecHAS_OBLIGATIONS; } + if (sleAccount->isFieldPresent(sfSponsorAccount)) + { + if (dst != sleAccount->getAccountID(sfSponsorAccount)) + return tecNO_SPONSOR_PERMISSION; + } + if (sleAccount->isFieldPresent(sfSponsoringOwnerCount) || + sleAccount->isFieldPresent(sfSponsoringAccountCount)) + return tecHAS_OBLIGATIONS; + // We don't allow an account to be deleted if its sequence number // is within 256 of the current ledger. This prevents replay of old // transactions if this account is resurrected after it is deleted. @@ -430,6 +439,22 @@ DeleteAccount::doApply() (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance; ctx_.deliver(mSourceBalance); + if (src->isFieldPresent(sfSponsorAccount)) + { + auto const sponsorAcc = src->getAccountID(sfSponsorAccount); + auto sponsorSle = view().peek(keylet::account(sponsorAcc)); + + auto const sponsoringAccountCount = + sponsorSle->getFieldU32(sfSponsoringAccountCount); + + if (sponsoringAccountCount == 1) + sponsorSle->makeFieldAbsent(sfSponsoringAccountCount); + else + sponsorSle->setFieldU32( + sfSponsoringAccountCount, sponsoringAccountCount - 1); + view().update(sponsorSle); + } + XRPL_ASSERT( (*src)[sfBalance] == XRPAmount(0), "ripple::DeleteAccount::doApply : source balance is zero"); diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 78ace009c95..e8a1c7f9645 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -98,6 +98,15 @@ Payment::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } + if (txFlags & tfSponsorCreatedAccount) + { + if (!ctx.rules.enabled(featureSponsor)) + return temINVALID_FLAG; + + if (!dstAmount.native()) + return temMALFORMED; + } + if (mptDirect && ctx.tx.isFieldPresent(sfPaths)) return temMALFORMED; @@ -330,19 +339,24 @@ Payment::preclaim(PreclaimContext const& ctx) return tecNO_DST_INSUF_XRP; } } - else if ( - (sleDst->getFlags() & lsfRequireDestTag) && - !ctx.tx.isFieldPresent(sfDestinationTag)) + else { - // The tag is basically account-specific information we don't - // understand, but we can require someone to fill it in. + if (txFlags & tfSponsorCreatedAccount) + return tecNO_SPONSOR_PERMISSION; - // We didn't make this test for a newly-formed account because there's - // no way for this field to be set. - JLOG(ctx.j.trace()) - << "Malformed transaction: DestinationTag required."; + if ((sleDst->getFlags() & lsfRequireDestTag) && + !ctx.tx.isFieldPresent(sfDestinationTag)) + { + // The tag is basically account-specific information we don't + // understand, but we can require someone to fill it in. + + // We didn't make this test for a newly-formed account because + // there's no way for this field to be set. + JLOG(ctx.j.trace()) + << "Malformed transaction: DestinationTag required."; - return tecDST_TAG_NEEDED; + return tecDST_TAG_NEEDED; + } } // Payment with at least one intermediate step and uses transitive balances. @@ -415,12 +429,19 @@ Payment::doApply() sleDst->setAccountID(sfAccount, dstAccountID); sleDst->setFieldU32(sfSequence, seqno); - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); - if (sponsor.has_value()) + if (txFlags & tfSponsorCreatedAccount) { + auto const sponsor = view().peek(keylet::account(account_)); + if (!sponsor) + return tecINTERNAL; // LCOV_EXCL_LINE + auto const currentSponsoringAccountCount = + sponsor->getFieldU32(sfSponsoringAccountCount); + sponsor->setFieldU32( + sfSponsoringAccountCount, currentSponsoringAccountCount + 1); + view().update(sponsor); + addSponsorToLedgerEntry(sleDst, sponsor); - sponsor.value()->setFieldU32(sfSponsoringAccountCount, 1); - view().update(sponsor.value()); + view().update(sponsor); } view().insert(sleDst); diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 923c421a346..b30209bb7a2 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -276,7 +276,7 @@ SponsorshipSet::deleteSponsorship( std::shared_ptr const& sle, beast::Journal j) { - auto const sponsor = sle->getAccountID(sfSponsorAccount); + auto const sponsor = sle->getAccountID(sfAccount); auto const sponsee = sle->getAccountID(sfSponsee); // adjust balance @@ -285,9 +285,13 @@ SponsorshipSet::deleteSponsorship( return tecINTERNAL; auto const feeAmount = sle->getFieldAmount(sfFeeAmount); - (*sponsorAccSle)[sfBalance] += feeAmount; + auto const reserveSponsor = getLedgerEntryReserveSponsor(view, sle); + adjustOwnerCount(view, sponsorAccSle, reserveSponsor, -1, j); + + view.update(sponsorAccSle); + // delete sponsor node view.dirRemove( keylet::ownerDir(sponsor), (*sle)[sfSponsorNode], sle->key(), false); From 6aa0331ffebd89e0ca12f72b881fe7fec694150d Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 13 Sep 2025 22:40:19 +0900 Subject: [PATCH 017/249] sfAccount to sfOwner --- include/xrpl/protocol/detail/ledger_entries.macro | 4 ++-- include/xrpl/protocol/detail/sfields.macro | 3 +-- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 12 ++++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index f59b18d9108..2300bcf1e4d 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -511,9 +511,9 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({ \sa keylet::sponsor */ LEDGER_ENTRY(ltSPONSORSHIP, 0x0085, Sponsorship, sponsorship, ({ - {sfAccount, soeREQUIRED}, + {sfOwner, soeREQUIRED}, {sfSponsee, soeREQUIRED}, - {sfSponsorNode, soeREQUIRED}, + {sfOwnerNode, soeREQUIRED}, {sfSponseeNode, soeREQUIRED}, {sfFeeAmount, soeOPTIONAL}, {sfReserveCount, soeOPTIONAL}, diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 5fe30502c1d..8dbc1ad28cc 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -149,8 +149,7 @@ TYPED_SFIELD(sfMPTAmount, UINT64, 26, SField::sMD_BaseTen|SFie TYPED_SFIELD(sfIssuerNode, UINT64, 27) TYPED_SFIELD(sfSubjectNode, UINT64, 28) TYPED_SFIELD(sfLockedAmount, UINT64, 29, SField::sMD_BaseTen|SField::sMD_Default) -TYPED_SFIELD(sfSponsorNode, UINT64, 30) -TYPED_SFIELD(sfSponseeNode, UINT64, 31) +TYPED_SFIELD(sfSponseeNode, UINT64, 30) // 128-bit TYPED_SFIELD(sfEmailHash, UINT128, 1) diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index b30209bb7a2..f709cbf2a1b 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -169,7 +169,7 @@ SponsorshipSet::doApply() ctx_.view().dirRemove( keylet::ownerDir(sponsorAcc), - (*sponsorObjSle)[sfSponsorNode], + (*sponsorObjSle)[sfOwnerNode], sponsorObjSle->key(), false); ctx_.view().dirRemove( @@ -206,7 +206,7 @@ SponsorshipSet::doApply() !isTesSuccess(ret)) return tecUNFUNDED; - (*newSle)[sfAccount] = sponsorAcc; + (*newSle)[sfOwner] = sponsorAcc; (*newSle)[sfSponsee] = sponseeAcc; (*newSle)[sfFlags] = ctx_.tx.getFlags(); if (feeAmount) @@ -221,7 +221,7 @@ SponsorshipSet::doApply() auto const sponsorPage = view().dirInsert( keylet::ownerDir(sponsorAcc), keylet, describeOwnerDir(sponsorAcc)); - (*newSle)[sfSponsorNode] = *sponsorPage; + (*newSle)[sfOwnerNode] = *sponsorPage; auto const sponseePage = view().dirInsert( keylet::ownerDir(sponseeAcc), keylet, describeOwnerDir(sponseeAcc)); @@ -239,7 +239,7 @@ SponsorshipSet::doApply() // Update if (feeAmount) { - // TODO: transfer feeAmount to ledger entry + // transfer feeAmount to ledger entry (*sponsorAccSle)[sfBalance] -= *feeAmount; (*sponsorObjSle)[sfFeeAmount] += *feeAmount; } @@ -276,7 +276,7 @@ SponsorshipSet::deleteSponsorship( std::shared_ptr const& sle, beast::Journal j) { - auto const sponsor = sle->getAccountID(sfAccount); + auto const sponsor = sle->getAccountID(sfOwner); auto const sponsee = sle->getAccountID(sfSponsee); // adjust balance @@ -294,7 +294,7 @@ SponsorshipSet::deleteSponsorship( // delete sponsor node view.dirRemove( - keylet::ownerDir(sponsor), (*sle)[sfSponsorNode], sle->key(), false); + keylet::ownerDir(sponsor), (*sle)[sfOwnerNode], sle->key(), false); // delete sponsee node view.dirRemove( keylet::ownerDir(sponsee), (*sle)[sfSponseeNode], sle->key(), false); From 4abd94e7acd206199b86f43f5256297fda92be6d Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 15 Sep 2025 11:04:47 +0900 Subject: [PATCH 018/249] MaxFee --- .../xrpl/protocol/detail/ledger_entries.macro | 5 +- include/xrpl/protocol/detail/sfields.macro | 1 + .../xrpl/protocol/detail/transactions.macro | 1 + src/test/app/Sponsor_test.cpp | 8 ++ src/xrpld/app/tx/detail/SponsorshipSet.cpp | 27 ++++- src/xrpld/app/tx/detail/Transactor.cpp | 99 +++++++++++++++---- src/xrpld/app/tx/detail/applySteps.cpp | 6 +- 7 files changed, 119 insertions(+), 28 deletions(-) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 2300bcf1e4d..1457d0f5a58 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -513,10 +513,11 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({ LEDGER_ENTRY(ltSPONSORSHIP, 0x0085, Sponsorship, sponsorship, ({ {sfOwner, soeREQUIRED}, {sfSponsee, soeREQUIRED}, - {sfOwnerNode, soeREQUIRED}, - {sfSponseeNode, soeREQUIRED}, {sfFeeAmount, soeOPTIONAL}, + {sfMaxFee, soeOPTIONAL}, {sfReserveCount, soeOPTIONAL}, + {sfOwnerNode, soeREQUIRED}, + {sfSponseeNode, soeREQUIRED}, })) #undef EXPAND diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 8dbc1ad28cc..274d5e6d9bf 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -248,6 +248,7 @@ TYPED_SFIELD(sfSignatureReward, AMOUNT, 29) TYPED_SFIELD(sfMinAccountCreateAmount, AMOUNT, 30) TYPED_SFIELD(sfLPTokenBalance, AMOUNT, 31) TYPED_SFIELD(sfFeeAmount, AMOUNT, 32) +TYPED_SFIELD(sfMaxFee, AMOUNT, 33) // variable length (common) TYPED_SFIELD(sfPublicKey, VL, 1) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index b6c114f5a20..b66f09f6847 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -536,6 +536,7 @@ TRANSACTION(ttSPONSORSHIP_SET, 73, SponsorshipSet, Delegation::notDelegatable, ( {sfSponsorAccount, soeOPTIONAL}, {sfSponsee, soeREQUIRED}, {sfFeeAmount, soeOPTIONAL}, + {sfMaxFee, soeOPTIONAL}, {sfReserveCount, soeOPTIONAL}, })) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index f0b2626b229..ff96b236e56 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -137,6 +137,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set(sponsor, alice, tfDeleteObject, 1), ter(temMALFORMED)); env(sponsor::set(sponsor, alice, tfDeleteObject, std::nullopt, XRP(1)), ter(temMALFORMED)); + // TODO: test MaxFee with tfDeleteObject // // preclaim @@ -492,6 +493,13 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, sponsor); env.close(); + // not yet funded + env(noop(alice), + fee(drops(500)), + sponsor::as(sponsor, tfSponsorFee), + ter(tecNO_SPONSOR_PERMISSION)); + + // set sponsorship env(sponsor::set(sponsor, alice, 0, std::nullopt, XRP(1)), ter(tesSUCCESS)); env.close(); diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index f709cbf2a1b..27255789849 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -94,6 +94,18 @@ SponsorshipSet::preflight(PreflightContext const& ctx) return temBAD_AMOUNT; } + if (ctx.tx.isFieldPresent(sfMaxFee)) + { + auto const maxFee = ctx.tx.getFieldAmount(sfMaxFee); + if (!isXRP(maxFee)) + return temBAD_AMOUNT; + + if (maxFee.xrp().drops() <= 0) + return temBAD_AMOUNT; + + // TODO: check maxFee > basefee + } + if (ctx.tx.isFieldPresent(sfReserveCount)) { if (ctx.tx.getFlags() & tfSponsorshipClearRequireSignForReserve) @@ -108,7 +120,8 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (ctx.tx.isFlag(tfDeleteObject)) { if (ctx.tx.isFieldPresent(sfFeeAmount) || - ctx.tx.isFieldPresent(sfReserveCount)) + ctx.tx.isFieldPresent(sfReserveCount) || + ctx.tx.isFieldPresent(sfMaxFee)) return temMALFORMED; } @@ -188,6 +201,7 @@ SponsorshipSet::doApply() } auto const feeAmount = ctx_.tx[~sfFeeAmount]; + auto const maxFee = ctx_.tx[~sfMaxFee]; auto const reserveCount = ctx_.tx[~sfReserveCount]; auto reserveSponsorAccSle = getTxReserveSponsor(view(), ctx_.tx); @@ -214,6 +228,10 @@ SponsorshipSet::doApply() (*sponsorAccSle)[sfBalance] -= *feeAmount; (*newSle)[sfFeeAmount] = *feeAmount; } + if (maxFee) + { + (*newSle)[sfMaxFee] = *maxFee; + } if (reserveCount) { (*newSle)[sfReserveCount] = *reserveCount; @@ -244,11 +262,16 @@ SponsorshipSet::doApply() (*sponsorObjSle)[sfFeeAmount] += *feeAmount; } + if (maxFee) + { + (*sponsorObjSle)[sfMaxFee] = *maxFee; + } + if (reserveCount) (*sponsorObjSle)[sfReserveCount] = (*sponsorObjSle)[sfReserveCount] + *reserveCount; - // TODO: update Flags? + // update Flags auto flags = sponsorObjSle->getFieldU32(sfFlags); if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForFee)) flags |= lsfSponsorshipRequireSignForFee; diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index c6c42cc57aa..d98209686db 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -259,25 +259,35 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) auto const sponsorAcc = txSponsor.getAccountID(sfAccount); auto const sponseeAcc = tx.getAccountID(sfAccount); - auto const sponsorSle = view.read(keylet::sponsor(sponsorAcc, sponseeAcc)); - if (!sponsorSle) - return tesSUCCESS; - auto const hasSignature = txSponsor.isFieldPresent(sfTxnSignature) || !txSponsor.getFieldVL(sfSigningPubKey).empty() || txSponsor.isFieldPresent(sfSigners); - if (txSponsor.isFlag(tfSponsorFee) && - sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) + auto const sponsorSle = view.read(keylet::sponsor(sponsorAcc, sponseeAcc)); + if (!hasSignature) { - if (!hasSignature) + // pre funded + if (!sponsorSle) return tecNO_SPONSOR_PERMISSION; } - if (txSponsor.isFlag(tfSponsorReserve) && - sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) + else { - if (!hasSignature) - return tecNO_SPONSOR_PERMISSION; + // co-signed + if (!sponsorSle) + return tesSUCCESS; + + if (txSponsor.isFlag(tfSponsorFee) && + sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) + { + if (!hasSignature) + return tecNO_SPONSOR_PERMISSION; + } + if (txSponsor.isFlag(tfSponsorReserve) && + sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) + { + if (!hasSignature) + return tecNO_SPONSOR_PERMISSION; + } } return tesSUCCESS; @@ -359,21 +369,68 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (feePaid == beast::zero) return tesSUCCESS; - auto const id = ctx.tx.getFeePayer(); - JLOG(ctx.j.trace()) << "Fee payer: " + to_string(id); - auto const sle = ctx.view.read(keylet::account(id)); - if (!sle) - return terNO_ACCOUNT; + std::optional availableBalance; + + if (ctx.tx.isFieldPresent(sfSponsor)) + { + auto const txSponsor = ctx.tx.getFieldObject(sfSponsor); + if (txSponsor.isFlag(tfSponsorFee) && + (!txSponsor.isFieldPresent(sfTxnSignature) && + !txSponsor.isFieldPresent(sfSigners))) + { + // use prefunded fee sponsor + auto const keylet = keylet::sponsor( + txSponsor.getAccountID(sfAccount), ctx.tx[sfAccount]); + auto const sponsorSle = ctx.view.read(keylet); + if (!sponsorSle) + return tecNO_SPONSOR_PERMISSION; + + XRPAmount const maxFee = sponsorSle->isFieldPresent(sfMaxFee) + ? sponsorSle->getFieldAmount(sfMaxFee).xrp() + : INITIAL_XRP; + + XRPAmount const feeAmount = sponsorSle->isFieldPresent(sfFeeAmount) + ? sponsorSle->getFieldAmount(sfFeeAmount).xrp() + : XRPAmount(0); + + // feePaid should <= maxFee + if (feePaid > maxFee) + return tecNO_SPONSOR_PERMISSION; + + // feePaid should <= feeAmount + if (feePaid > feeAmount) + return tecNO_SPONSOR_PERMISSION; + + availableBalance = feeAmount; + } + else + { + // proceed to use fee payer + } + } + + if (!availableBalance) + { + auto const id = ctx.tx.getFeePayer(); + JLOG(ctx.j.trace()) << "Fee payer: " + to_string(id); + auto const sle = ctx.view.read(keylet::account(id)); + if (!sle) + return terNO_ACCOUNT; + + availableBalance = (*sle)[sfBalance].xrp(); + } - auto const balance = (*sle)[sfBalance].xrp(); + XRPL_ASSERT( + availableBalance, + "ripple::Transactor::checkFee : could not get balance for fee"); - if (balance < feePaid) + if (*availableBalance < feePaid) { JLOG(ctx.j.trace()) - << "Insufficient balance:" << " balance=" << to_string(balance) - << " paid=" << to_string(feePaid); + << "Insufficient balance:" << " balance=" + << to_string(*availableBalance) << " paid=" << to_string(feePaid); - if ((balance > beast::zero) && !ctx.view.open()) + if ((*availableBalance > beast::zero) && !ctx.view.open()) { // Closed ledger, non-zero balance, less than fee return tecINSUFF_FEE; diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index cb6371891da..9b315462b25 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -200,17 +200,17 @@ invoke_preclaim(PreclaimContext const& ctx) if (result != tesSUCCESS) return result; - result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); + result = T::checkSponsor(ctx.view, ctx.tx); if (result != tesSUCCESS) return result; - result = T::checkPermission(ctx.view, ctx.tx); + result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); if (result != tesSUCCESS) return result; - result = T::checkSponsor(ctx.view, ctx.tx); + result = T::checkPermission(ctx.view, ctx.tx); if (result != tesSUCCESS) return result; From 1cdf7bb745ab5e822ecba8c9b3f1eab27db6ad96 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 15 Sep 2025 16:25:16 +0900 Subject: [PATCH 019/249] feePayer --- include/xrpl/protocol/STTx.h | 3 - include/xrpl/protocol/TER.h | 1 + src/libxrpl/protocol/STTx.cpp | 19 --- src/libxrpl/protocol/TER.cpp | 1 + src/test/app/Sponsor_test.cpp | 201 ++++++++++++++++++++----- src/xrpld/app/tx/detail/Transactor.cpp | 170 ++++++++++----------- src/xrpld/app/tx/detail/Transactor.h | 17 +++ src/xrpld/app/tx/detail/applySteps.cpp | 4 +- 8 files changed, 266 insertions(+), 150 deletions(-) diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index a06bf7fb457..a1e529fe183 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -106,9 +106,6 @@ class STTx final : public STObject, public CountedObject std::uint32_t getSeqValue() const; - AccountID - getFeePayer() const; - boost::container::flat_set getMentionedAccounts() const; diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index 95e9c27816e..39ba667ead4 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -227,6 +227,7 @@ enum TERcodes : TERUnderlyingType { terNO_AMM, // AMM doesn't exist for the asset pair terADDRESS_COLLISION, // Failed to allocate AccountID when trying to // create a pseudo-account + terNO_SPONSORSHIP, // No sponsorship found }; //------------------------------------------------------------------------------ diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 3129c8635ef..134319c7d32 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -245,25 +245,6 @@ STTx::getSeqValue() const return getSeqProxy().value(); } -AccountID -STTx::getFeePayer() const -{ - if (isFieldPresent(sfSponsor)) - { - if (getFieldObject(sfSponsor).isFlag(tfSponsorFee)) - { - return getFieldObject(sfSponsor)[sfAccount]; - } - } - - if (isFieldPresent(sfDelegate)) - { - return getAccountID(sfDelegate); - } - - return getAccountID(sfAccount); -} - void STTx::sign(PublicKey const& publicKey, SecretKey const& secretKey) { diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index 1093d8fc6db..3ab4fe3f206 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -236,6 +236,7 @@ transResults() MAKE_ERROR(terPRE_TICKET, "Ticket is not yet in ledger."), MAKE_ERROR(terNO_AMM, "AMM doesn't exist for the asset pair."), MAKE_ERROR(terADDRESS_COLLISION, "Failed to allocate an unique account address."), + MAKE_ERROR(terNO_SPONSORSHIP, "No sponsorship found."), MAKE_ERROR(tesSUCCESS, "The transaction was applied. Only final in a validated ledger."), }; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index ff96b236e56..dd3607cc17d 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -471,58 +471,189 @@ class Sponsor_test : public beast::unit_test::suite // co-signing Env env{*this, testable_amendments()}; Account const alice("alice"); + Account const bob("bob"); Account const sponsor("sponsor"); - env.fund(XRP(10000), alice, sponsor); + env.fund(XRP(10000), alice, bob); env.close(); - env(noop(alice), - fee(XRP(1)), - sponsor::as(sponsor, tfSponsorFee), - sponsor::sig(sponsor), - ter(tesSUCCESS)); + { + // Fee should be checked before permission check, + // otherwise tecNO_SPONSOR_PERMISSION returned when permission + // check fails could cause context reset to pay fee because it + // is tec error + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + env(pay(alice, bob, XRP(100)), + fee(XRP(2000)), + sponsor::as(sponsor, tfSponsorFee), + sponsor::sig(sponsor), + ter(terNO_ACCOUNT)); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + } + + env.fund(XRP(1000), sponsor); env.close(); - BEAST_EXPECT(env.balance(alice) == XRP(10000)); - BEAST_EXPECT(env.balance(sponsor) == XRP(9999)); + { + // Sponsor pays the fee + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + auto const sendAmt = XRP(100); + auto const feeAmt = XRP(10); + env(pay(alice, bob, sendAmt), + fee(feeAmt), + sponsor::as(sponsor, tfSponsorFee), + sponsor::sig(sponsor)); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt); + BEAST_EXPECT(env.balance(bob) == bobBalance + sendAmt); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance - feeAmt); + } + + { + // insufficient balance to pay fee + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + env(pay(alice, bob, XRP(100)), + fee(XRP(2000)), + sponsor::as(sponsor, tfSponsorFee), + sponsor::sig(sponsor), + ter(terINSUF_FEE_B)); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + } + + { + // fee is paid by Sponsor + // on context reset (tec error) + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto const feeAmt = XRP(10); + + env(pay(alice, bob, XRP(20000)), + fee(feeAmt), + sponsor::as(sponsor, tfSponsorFee), + sponsor::sig(sponsor), + ter(tecUNFUNDED_PAYMENT)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance - feeAmt); + } } + { // pre funded Env env{*this, testable_amendments()}; Account const alice("alice"); + Account const bob("bob"); Account const sponsor("sponsor"); - env.fund(XRP(10000), alice, sponsor); + env.fund(XRP(10000), alice, bob, sponsor); env.close(); - // not yet funded - env(noop(alice), - fee(drops(500)), - sponsor::as(sponsor, tfSponsorFee), - ter(tecNO_SPONSOR_PERMISSION)); - - // set sponsorship - env(sponsor::set(sponsor, alice, 0, std::nullopt, XRP(1)), - ter(tesSUCCESS)); - env.close(); + auto const sponsorFeeBalance = [&](Account const& sponsor, + Account const& alice) { + return env.le(keylet::sponsor(sponsor, alice)) + ->getFieldAmount(sfFeeAmount) + .xrp(); + }; - auto const sle = env.le(keylet::sponsor(sponsor, alice)); - BEAST_EXPECT(sle->getFieldAmount(sfFeeAmount) == XRP(1)); - BEAST_EXPECT(!sle->isFieldPresent(sfReserveCount)); - - auto const sponsorBalanceBefore = env.balance(sponsor); - auto const aliceBalanceBefore = env.balance(alice); + { + // Fee should be checked before permission check, + // otherwise tecNO_SPONSOR_PERMISSION returned when permission + // check fails could cause context reset to pay fee because it + // is tec error + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + env(pay(alice, bob, XRP(100)), + fee(XRP(2000)), + sponsor::as(sponsor, tfSponsorFee), + ter(terNO_SPONSORSHIP)); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + } - env(noop(alice), - fee(drops(500)), - sponsor::as(sponsor, tfSponsorFee), - ter(tesSUCCESS)); + env(sponsor::set(sponsor, alice, 0, std::nullopt, XRP(100))); env.close(); - BEAST_EXPECT(env.balance(alice) == aliceBalanceBefore); - BEAST_EXPECT(env.balance(sponsor) == sponsorBalanceBefore); + { + // Sponsor pays the fee + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto sponsorFee = sponsorFeeBalance(sponsor, alice); + + auto const sendAmt = XRP(100); + auto const feeAmt = XRP(10); + env(pay(alice, bob, sendAmt), + fee(feeAmt), + sponsor::as(sponsor, tfSponsorFee)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt); + BEAST_EXPECT(env.balance(bob) == bobBalance + sendAmt); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT( + sponsorFeeBalance(sponsor, alice) == sponsorFee - feeAmt); + } - auto const sle2 = env.le(keylet::sponsor(sponsor, alice)); - BEAST_EXPECT( - sle2->getFieldAmount(sfFeeAmount) == XRP(1) - drops(500)); + { + // insufficient balance to pay fee + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto sponsorFee = sponsorFeeBalance(sponsor, alice); + + env(pay(alice, bob, XRP(100)), + fee(XRP(2000)), + sponsor::as(sponsor, tfSponsorFee), + ter(terINSUF_FEE_B)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT(sponsorFeeBalance(sponsor, alice) == sponsorFee); + } + + { + // fee is paid by Sponsor + // on context reset (tec error) + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto sponsorFee = sponsorFeeBalance(sponsor, alice); + auto const feeAmt = XRP(10); + + env(pay(alice, bob, XRP(20000)), + fee(feeAmt), + sponsor::as(sponsor, tfSponsorFee), + ter(tecUNFUNDED_PAYMENT)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT( + sponsorFeeBalance(sponsor, alice) == sponsorFee - feeAmt); + } } } @@ -1067,7 +1198,7 @@ class Sponsor_test : public beast::unit_test::suite testSponsorReserve(); testDisallowIncoming(); - testAccountDelete(); + // testAccountDelete(); } }; diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index d98209686db..dd0f6ea9f8a 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -371,53 +371,44 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) std::optional availableBalance; - if (ctx.tx.isFieldPresent(sfSponsor)) + auto const result = getFeePayer(ctx.tx); + + if (result.type == FeePayerType::SponsorPreFunded) { - auto const txSponsor = ctx.tx.getFieldObject(sfSponsor); - if (txSponsor.isFlag(tfSponsorFee) && - (!txSponsor.isFieldPresent(sfTxnSignature) && - !txSponsor.isFieldPresent(sfSigners))) + // use prefunded fee sponsor + auto const sponsorSle = ctx.view.read(result.keylet); + if (!sponsorSle) + return terNO_SPONSORSHIP; + else { - // use prefunded fee sponsor - auto const keylet = keylet::sponsor( - txSponsor.getAccountID(sfAccount), ctx.tx[sfAccount]); - auto const sponsorSle = ctx.view.read(keylet); - if (!sponsorSle) - return tecNO_SPONSOR_PERMISSION; + XRPL_ASSERT( + sponsorSle->getType() == ltSPONSORSHIP, + "ripple::Transactor::checkFee : could not get sponsorship"); - XRPAmount const maxFee = sponsorSle->isFieldPresent(sfMaxFee) - ? sponsorSle->getFieldAmount(sfMaxFee).xrp() - : INITIAL_XRP; - - XRPAmount const feeAmount = sponsorSle->isFieldPresent(sfFeeAmount) - ? sponsorSle->getFieldAmount(sfFeeAmount).xrp() + XRPAmount feeAmount = sponsorSle->isFieldPresent(result.field) + ? sponsorSle->getFieldAmount(result.field).xrp() : XRPAmount(0); - // feePaid should <= maxFee - if (feePaid > maxFee) - return tecNO_SPONSOR_PERMISSION; - - // feePaid should <= feeAmount - if (feePaid > feeAmount) - return tecNO_SPONSOR_PERMISSION; - + if (sponsorSle->isFieldPresent(sfMaxFee)) + feeAmount = std::min( + feeAmount, sponsorSle->getFieldAmount(sfMaxFee).xrp()); availableBalance = feeAmount; } - else - { - // proceed to use fee payer - } } - - if (!availableBalance) + else { - auto const id = ctx.tx.getFeePayer(); - JLOG(ctx.j.trace()) << "Fee payer: " + to_string(id); - auto const sle = ctx.view.read(keylet::account(id)); + auto const keylet = result.keylet; + JLOG(ctx.j.trace()) << "Fee payer: " + to_string(keylet.key); + auto const sle = ctx.view.read(keylet); + if (!sle) return terNO_ACCOUNT; - availableBalance = (*sle)[sfBalance].xrp(); + XRPL_ASSERT( + sle->getType() == ltACCOUNT_ROOT, + "ripple::Transactor::checkFee : could not get account"); + + availableBalance = (*sle)[result.field].xrp(); } XRPL_ASSERT( @@ -447,65 +438,26 @@ Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); - auto const isFeeSponsorObj = [&]() -> bool { - if (ctx_.tx.isFieldPresent(sfSponsor)) - { - auto const sponsor = ctx_.tx.getFieldObject(sfSponsor); - if (sponsor.getFieldVL(sfSigningPubKey).empty() && - !sponsor.isFieldPresent(sfSigners)) - return sponsor.getFlags() & tfSponsorFee; - } - return false; - }; + auto const result = getFeePayer(ctx_.tx); - if (ctx_.tx.isFieldPresent(sfDelegate)) - { - // Delegated transactions are paid by the delegated account. - auto const delegate = ctx_.tx.getAccountID(sfDelegate); - auto const delegatedSle = view().peek(keylet::account(delegate)); - if (!delegatedSle) - return tefINTERNAL; // LCOV_EXCL_LINE - - delegatedSle->setFieldAmount( - sfBalance, delegatedSle->getFieldAmount(sfBalance) - feePaid); - view().update(delegatedSle); - } - else if (isFeeSponsorObj()) - { - auto const sponsor = ctx_.tx.getFieldObject(sfSponsor); - auto const sponsorAcc = sponsor.getAccountID(sfAccount); - auto const sponsorSle = - view().peek(keylet::sponsor(sponsorAcc, account_)); - if (!sponsorSle) - return tefINTERNAL; // LCOV_EXCL_LINE + auto const sle = view().peek(result.keylet); - sponsorSle->setFieldAmount( - sfFeeAmount, sponsorSle->getFieldAmount(sfFeeAmount) - feePaid); - view().update(sponsorSle); - } - else - { - auto const id = ctx_.tx.getFeePayer(); - auto const sle = view().peek(keylet::account(id)); - JLOG(j_.trace()) << "Fee payer: " + to_string(id); - if (!sle) - return tefINTERNAL; // LCOV_EXCL_LINE + JLOG(j_.trace()) << "Fee payer: " + to_string(result.keylet.key); - if (id != account_) // sponsor - { - sle->setFieldAmount( - sfBalance, sle->getFieldAmount(sfBalance) - feePaid); - view().update(sle); - return tesSUCCESS; - } + if (!sle) + return tefINTERNAL; // LCOV_EXCL_LINE + sle->setFieldAmount( + result.field, sle->getFieldAmount(result.field) - feePaid); + + view().update(sle); + + if (result.type == FeePayerType::Account) // Deduct the fee, so it's not available during the transaction. // Will only write the account back if the transaction succeeds. mSourceBalance -= feePaid; - sle->setFieldAmount(sfBalance, mSourceBalance); - // VFALCO Should we call view().rawDestroyXRP() here as well? - } + // VFALCO Should we call view().rawDestroyXRP() here as well? return tesSUCCESS; } @@ -1223,13 +1175,14 @@ Transactor::reset(XRPAmount fee) if (!txnAcct) return {tefINTERNAL, beast::zero}; - auto const payerSle = ctx_.tx.isFieldPresent(sfDelegate) - ? view().peek(keylet::account(ctx_.tx.getAccountID(sfDelegate))) - : txnAcct; + auto const result = getFeePayer(ctx_.tx); + + auto const payerSle = view().peek(result.keylet); + if (!payerSle) return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE - auto const balance = payerSle->getFieldAmount(sfBalance).xrp(); + auto const balance = payerSle->getFieldAmount(result.field).xrp(); // balance should have already been checked in checkFee / preFlight. XRPL_ASSERT( @@ -1248,7 +1201,8 @@ Transactor::reset(XRPAmount fee) // If for some reason we are unable to consume the ticket or sequence // then the ledger is corrupted. Rather than make things worse we // reject the transaction. - payerSle->setFieldAmount(sfBalance, balance - fee); + payerSle->setFieldAmount(result.field, balance - fee); + TER const ter{consumeSeqProxy(txnAcct)}; XRPL_ASSERT( isTesSuccess(ter), "ripple::Transactor::reset : result is tesSUCCESS"); @@ -1263,6 +1217,40 @@ Transactor::reset(XRPAmount fee) return {ter, fee}; } +FeePayer +Transactor::getFeePayer(STTx const& tx) +{ + if (tx.isFieldPresent(sfSponsor) && + tx.getFieldObject(sfSponsor).isFlag(tfSponsorFee)) + { + auto const sponsor = tx.getFieldObject(sfSponsor); + auto const hasSignature = sponsor.isFieldPresent(sfTxnSignature) || + !sponsor.getFieldVL(sfSigningPubKey).empty() || + sponsor.isFieldPresent(sfSigners); + + if (!hasSignature) + { + // pre funded + auto const keylet = keylet::sponsor( + sponsor.getAccountID(sfAccount), tx.getAccountID(sfAccount)); + return FeePayer{ + keylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; + } + // co-signed + auto const keylet = keylet::account(sponsor.getAccountID(sfAccount)); + return FeePayer{keylet, sfBalance, FeePayerType::SponsorCoSigned}; + } + + if (tx.isFieldPresent(sfDelegate)) + { + auto const keylet = keylet::account(tx.getAccountID(sfDelegate)); + return FeePayer{keylet, sfBalance, FeePayerType::Delegate}; + } + + auto const keylet = keylet::account(tx.getAccountID(sfAccount)); + return FeePayer{keylet, sfBalance, FeePayerType::Account}; +} + // The sole purpose of this function is to provide a convenient, named // location to set a breakpoint, to be used when replaying transactions. void diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index d5beac58054..0877d163525 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -134,6 +134,20 @@ struct PreclaimContext class TxConsequences; struct PreflightResult; +enum class FeePayerType { + Account, + Delegate, + SponsorCoSigned, + SponsorPreFunded, +}; + +struct FeePayer +{ + Keylet keylet; + TypedField const& field; + FeePayerType type; +}; + class Transactor { protected: @@ -254,6 +268,9 @@ class Transactor std::pair reset(XRPAmount fee); + static FeePayer + getFeePayer(STTx const& tx); + TER consumeSeqProxy(SLE::pointer const& sleAccount); TER diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index 9b315462b25..79dd7134518 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -200,12 +200,12 @@ invoke_preclaim(PreclaimContext const& ctx) if (result != tesSUCCESS) return result; - result = T::checkSponsor(ctx.view, ctx.tx); + result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); if (result != tesSUCCESS) return result; - result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); + result = T::checkSponsor(ctx.view, ctx.tx); if (result != tesSUCCESS) return result; From 110b2225795d82a82cfb0eca53f868e762f47f07 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 15 Sep 2025 18:52:42 +0900 Subject: [PATCH 020/249] add MaxFee test --- src/test/app/Sponsor_test.cpp | 75 +++++++++++++++++++++++++---------- src/test/jtx/impl/sponsor.cpp | 40 ++++++++++++++++++- src/test/jtx/sponsor.h | 18 ++++++++- 3 files changed, 109 insertions(+), 24 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index dd3607cc17d..2d13b3a0fb5 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -118,24 +118,25 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set(sponsor, sponsor, 0), ter(temMALFORMED)); // Invalid feeAmount - env(sponsor::set( - sponsor, alice, tfSponsorshipClearRequireSignForFee, 0, XRP(1)), + env(sponsor::set_fee( + sponsor, alice, tfSponsorshipClearRequireSignForFee, XRP(1)), ter(temMALFORMED)); for (auto amt : {XRP(-1), XRP(0), USD(1)}) { - env(sponsor::set(sponsor, alice, 0, 1, amt), ter(temBAD_AMOUNT)); + env(sponsor::set_fee(sponsor, alice, 0, amt), ter(temBAD_AMOUNT)); } // Invalid reserveCount - env(sponsor::set( + env(sponsor::set_reserve( sponsor, alice, tfSponsorshipClearRequireSignForReserve, 1), ter(temMALFORMED)); - env(sponsor::set(sponsor, alice, 0, 0), ter(temMALFORMED)); + env(sponsor::set_reserve(sponsor, alice, 0, 0), ter(temMALFORMED)); // Invalid Delete operation - env(sponsor::set(sponsor, alice, tfDeleteObject, 1), ter(temMALFORMED)); - env(sponsor::set(sponsor, alice, tfDeleteObject, std::nullopt, XRP(1)), + env(sponsor::set_reserve(sponsor, alice, tfDeleteObject, 1), + ter(temMALFORMED)); + env(sponsor::set_fee(sponsor, alice, tfDeleteObject, XRP(1)), ter(temMALFORMED)); // TODO: test MaxFee with tfDeleteObject @@ -590,7 +591,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); } - env(sponsor::set(sponsor, alice, 0, std::nullopt, XRP(100))); + env(sponsor::set_fee(sponsor, alice, 0, XRP(100))); env.close(); { @@ -616,21 +617,51 @@ class Sponsor_test : public beast::unit_test::suite { // insufficient balance to pay fee - auto aliceBalance = env.balance(alice); - auto bobBalance = env.balance(bob); - auto sponsorBalance = env.balance(sponsor); - auto sponsorFee = sponsorFeeBalance(sponsor, alice); - - env(pay(alice, bob, XRP(100)), - fee(XRP(2000)), - sponsor::as(sponsor, tfSponsorFee), - ter(terINSUF_FEE_B)); + { + // > FeeAmount + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto sponsorFee = sponsorFeeBalance(sponsor, alice); + + env(pay(alice, bob, XRP(100)), + fee(XRP(100) + drops(1)), + sponsor::as(sponsor, tfSponsorFee), + ter(terINSUF_FEE_B)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT( + sponsorFeeBalance(sponsor, alice) == sponsorFee); + } + + // reset FeeAmount and MaxFee + env(sponsor::del(sponsor, alice)); + env.close(); + env(sponsor::set_fee(sponsor, alice, 0, XRP(10), XRP(1))); env.close(); - BEAST_EXPECT(env.balance(alice) == aliceBalance); - BEAST_EXPECT(env.balance(bob) == bobBalance); - BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); - BEAST_EXPECT(sponsorFeeBalance(sponsor, alice) == sponsorFee); + { + // > MaxFee + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto sponsorFee = sponsorFeeBalance(sponsor, alice); + + env(pay(alice, bob, XRP(100)), + fee(XRP(1) + drops(1)), + sponsor::as(sponsor, tfSponsorFee), + ter(terINSUF_FEE_B)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT( + sponsorFeeBalance(sponsor, alice) == sponsorFee); + } } { @@ -640,7 +671,7 @@ class Sponsor_test : public beast::unit_test::suite auto bobBalance = env.balance(bob); auto sponsorBalance = env.balance(sponsor); auto sponsorFee = sponsorFeeBalance(sponsor, alice); - auto const feeAmt = XRP(10); + auto const feeAmt = XRP(1); env(pay(alice, bob, XRP(20000)), fee(feeAmt), diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 6bfe074c166..8286b52cc88 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -36,7 +36,8 @@ set(jtx::Account const& account, jtx::Account const& sponsee, uint32_t flags, std::optional reserveCount, - std::optional feeAmount) + std::optional feeAmount, + std::optional maxFee) { Json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; @@ -47,6 +48,43 @@ set(jtx::Account const& account, jv[sfReserveCount.jsonName] = *reserveCount; if (feeAmount) jv[sfFeeAmount.jsonName] = feeAmount->getJson(JsonOptions::none); + if (maxFee) + jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::none); + return jv; +} + +Json::Value +set_fee( + jtx::Account const& account, + jtx::Account const& sponsee, + uint32_t flags, + STAmount feeAmount, + std::optional maxFee) +{ + Json::Value jv; + jv[jss::TransactionType] = jss::SponsorshipSet; + jv[jss::Account] = account.human(); + jv[sfSponsee.jsonName] = sponsee.human(); + jv[sfFlags.jsonName] = flags; + jv[sfFeeAmount.jsonName] = feeAmount.getJson(JsonOptions::none); + if (maxFee) + jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::none); + return jv; +} + +Json::Value +set_reserve( + jtx::Account const& account, + jtx::Account const& sponsee, + uint32_t flags, + uint32_t reserveCount) +{ + Json::Value jv; + jv[jss::TransactionType] = jss::SponsorshipSet; + jv[jss::Account] = account.human(); + jv[sfSponsee.jsonName] = sponsee.human(); + jv[sfFlags.jsonName] = flags; + jv[sfReserveCount.jsonName] = reserveCount; return jv; } diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index bd603c0d412..a5dcc88872d 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -34,7 +34,23 @@ set(jtx::Account const& account, jtx::Account const& sponsee, std::uint32_t flags, std::optional reserveCount = std::nullopt, - std::optional feeAmount = std::nullopt); + std::optional feeAmount = std::nullopt, + std::optional maxFee = std::nullopt); + +Json::Value +set_fee( + jtx::Account const& account, + jtx::Account const& sponsee, + std::uint32_t flags, + STAmount feeAmount, + std::optional maxFee = std::nullopt); + +Json::Value +set_reserve( + jtx::Account const& account, + jtx::Account const& sponsee, + std::uint32_t flags, + std::uint32_t reserveCount); Json::Value del(jtx::Account const& account, jtx::Account const& sponsee); From 20bce6494638cc42c4ede51bfadce7328efdaace Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 15 Sep 2025 19:09:57 +0900 Subject: [PATCH 021/249] test AccountDelete for sponsorship --- include/xrpl/protocol/jss.h | 1 + src/test/app/Sponsor_test.cpp | 9 ++++++++- src/test/jtx/impl/sponsor.cpp | 13 +++++++++++++ src/test/jtx/sponsor.h | 6 ++++++ src/xrpld/rpc/handlers/LedgerEntry.cpp | 12 ++++++------ 5 files changed, 34 insertions(+), 7 deletions(-) diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index ecad01fa48a..07b9e589275 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -581,6 +581,7 @@ JSS(source_amount); // in: PathRequest, RipplePathFind JSS(source_currencies); // in: PathRequest, RipplePathFind JSS(source_tag); // out: AccountChannels JSS(sponsee); // in: LedgerEntry +JSS(sponsor); // in: LedgerEntry JSS(stand_alone); // out: NetworkOPs JSS(standard_deviation); // out: get_aggregate_price JSS(start); // in: TxHistory diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 2d13b3a0fb5..cdf8562ffe7 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1153,6 +1153,13 @@ class Sponsor_test : public beast::unit_test::suite auto const requiredFee = drops(env.current()->fees().increment); env(acctdelete(alice, bob), fee(requiredFee), ter(tesSUCCESS)); env.close(); + + BEAST_EXPECT(!env.le(keylet)); + auto const jv = sponsor::ledgerEntry(env, sponsor, alice); + BEAST_EXPECT( + jv.isObject() && jv.isMember(jss::result) && + jv[jss::result].isMember(jss::error) && + jv[jss::result][jss::error] == "entryNotFound"); } { @@ -1229,7 +1236,7 @@ class Sponsor_test : public beast::unit_test::suite testSponsorReserve(); testDisallowIncoming(); - // testAccountDelete(); + testAccountDelete(); } }; diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 8286b52cc88..551a408ae10 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -193,6 +193,19 @@ msig::operator()(Env& env, JTx& jt) const }; } +Json::Value +ledgerEntry( + jtx::Env& env, + jtx::Account const& sponsor, + jtx::Account const& sponsee) +{ + Json::Value jvParams; + jvParams[jss::ledger_index] = jss::validated; + jvParams[jss::sponsorship][jss::sponsor] = sponsor.human(); + jvParams[jss::sponsorship][jss::sponsee] = sponsee.human(); + return env.rpc("json", "ledger_entry", to_string(jvParams)); +} + } // namespace sponsor } // namespace jtx } // namespace test diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index a5dcc88872d..faab0bfb8c2 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -118,6 +118,12 @@ struct msig operator()(jtx::Env&, jtx::JTx& jtx) const; }; +Json::Value +ledgerEntry( + jtx::Env& env, + jtx::Account const& sponsor, + jtx::Account const& sponsee); + } // namespace sponsor } // namespace jtx } // namespace test diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 930c54d49fe..b7a47ffab32 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -637,17 +637,17 @@ parseSponsorship(Json::Value const& params, Json::StaticString const fieldName) return parseObjectID(params, fieldName); } - auto const id = LedgerEntryHelpers::requiredAccountID( - params, jss::owner, "malformedOwner"); - if (!id) - return Unexpected(id.error()); + auto const sponsor = LedgerEntryHelpers::requiredAccountID( + params, jss::sponsor, "malformedSponsor"); + if (!sponsor) + return Unexpected(sponsor.error()); auto const sponsee = LedgerEntryHelpers::requiredAccountID( - params, jss::sponsee, "malformedAddress"); + params, jss::sponsee, "malformedSponsee"); if (!sponsee) return Unexpected(sponsee.error()); - return keylet::sponsor(*id, *sponsee).key; + return keylet::sponsor(*sponsor, *sponsee).key; } static Expected From 6c0732ccbe625e53bb19bd7558aa1045200e3283 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 15 Sep 2025 20:29:58 +0900 Subject: [PATCH 022/249] test checks --- src/test/app/Sponsor_test.cpp | 147 +++++++++++++++++++++++-------- src/xrpld/ledger/detail/View.cpp | 16 ++-- 2 files changed, 119 insertions(+), 44 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index cdf8562ffe7..6815f266f44 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -749,64 +749,133 @@ class Sponsor_test : public beast::unit_test::suite Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); + Account const gw("gw"); Account const sponsor("sponsor"); + auto const USD = gw["USD"]; + auto const reserve = env.current()->fees().reserve; auto const increment = env.current()->fees().increment; - env.fund(XRP(10000), alice, bob); + env.fund(XRP(10000), alice, bob, gw); env.fund(drops(reserve) + drops(increment) - drops(1), sponsor); env.close(); - // check sponsor balance - env(check::create(alice, bob, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), - ter(tecINSUFFICIENT_RESERVE)); - - env(pay(alice, sponsor, drops(1))); + env.trust(USD(100), alice); env.close(); - - // CheckCreate - auto const seq = env.seq(alice); - env(check::create(alice, bob, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + env(pay(gw, alice, USD(100))); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + { + BEAST_EXPECT( + env.balance(sponsor) < drops(reserve) + drops(increment)); - // CheckCancel - auto const checkId = keylet::check(alice, seq).key; - env(check::cancel(alice, checkId)); + // check sponsor balance + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } + + env(pay(env.master, sponsor, drops(1))); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + { + BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - auto const seq2 = env.seq(alice); - env(check::create(alice, bob, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // CheckCreate -> CheckCancel + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + auto const keylet = keylet::check(alice, seq); + BEAST_EXPECT( + env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); - // CheckCash - auto const checkId2 = keylet::check(alice, seq2).key; - env(check::cash(bob, checkId2, XRP(1))); + env(check::cancel(alice, keylet.key), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + { + // CheckCreate -> CheckCash + auto const seq2 = env.seq(alice); + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // CheckCash + auto const checkId2 = keylet::check(alice, seq2).key; + env(check::cash(bob, checkId2, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + env(pay(env.master, sponsor, drops(env.current()->fees().increment))); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + // TODO: RippleState sponsor + // { + // // CheckCreate -> CheckCash(CheckCashMakesTrustLine) + // auto const seq2 = env.seq(alice); + // env(check::create(alice, bob, USD(1)), + // sponsor::as(sponsor, tfSponsorReserve), + // sponsor::sig(sponsor)); + // env.close(); + + // BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + + // Check BEAST_EXPECT(ownerCount(env, bob) == 0); + // BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + // BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // auto const keylet = keylet::check(alice, seq2); + // BEAST_EXPECT( + // env.le(keylet)->getAccountID(sfSponsorAccount) == + // sponsor.id()); + + // // CheckCash + // env(check::cash(bob, keylet.key, USD(1)), + // sponsor::as(sponsor, tfSponsorReserve), + // sponsor::sig(sponsor)); + // env.close(); + + // BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + // BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState + // BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + // BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // } } void diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index c1933cc15de..d207739a486 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1133,13 +1133,16 @@ adjustOwnerCount( { // modify sponsor's SponsoringOwnerCount std::uint32_t const current{ - sponsorSle.value()->getFieldU32(sfSponsoringOwnerCount)}; - AccountID const id = sponsorSle.value()->getAccountID(sfAccount); + (*sponsorSle)->getFieldU32(sfSponsoringOwnerCount)}; + AccountID const id = (*sponsorSle)->getAccountID(sfAccount); std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j); view.adjustOwnerCountHook(id, current, adjusted); - sponsorSle.value()->setFieldU32(sfSponsoringOwnerCount, adjusted); - view.update(sponsorSle.value()); + if (adjusted == 0) + (*sponsorSle)->makeFieldAbsent(sfSponsoringOwnerCount); + else + (*sponsorSle)->setFieldU32(sfSponsoringOwnerCount, adjusted); + view.update(*sponsorSle); } { // modify account's SponsoredOwnerCount @@ -1150,7 +1153,10 @@ adjustOwnerCount( std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j); view.adjustOwnerCountHook(id, current, adjusted); - accountSle->setFieldU32(sfSponsoredOwnerCount, adjusted); + if (adjusted == 0) + accountSle->makeFieldAbsent(sfSponsoredOwnerCount); + else + accountSle->setFieldU32(sfSponsoredOwnerCount, adjusted); view.update(accountSle); } } From 2a27280f1a948f78755d73199cc3007e71a49461 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 15 Sep 2025 22:07:43 +0900 Subject: [PATCH 023/249] fix test error --- include/xrpl/protocol/STTx.h | 2 +- src/libxrpl/protocol/STTx.cpp | 4 ++-- src/test/app/MPToken_test.cpp | 9 +++++++++ src/test/jtx/impl/utility.cpp | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index a1e529fe183..bee7d10c16e 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -110,7 +110,7 @@ class STTx final : public STObject, public CountedObject getMentionedAccounts() const; static Blob - getSigningData(STTx const& that); + getSigningData(STObject const& that); static Blob getSponsorSigningData(STTx const& that); diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 134319c7d32..549fe3d7512 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -186,7 +186,7 @@ STTx::getMentionedAccounts() const } Blob -STTx::getSigningData(STTx const& that) +STTx::getSigningData(STObject const& that) { Serializer s; s.add32(HashPrefix::txSign); @@ -507,7 +507,7 @@ multiSignHelper( // Make sure the MultiSigners are present. Otherwise they are not // attempting multi-signing and we just have a bad Signers. if (!signerObj.isFieldPresent(sfSigners)) - return Unexpected("Empty Signers."); + return Unexpected("Empty SigningPubKey."); // We don't allow both an sfSigners and an sfTxnSignature. Both fields // being present would indicate that the transaction is signed both ways. diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 6470962f2f0..d5410b27d01 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -2313,6 +2313,15 @@ class MPToken_test : public beast::unit_test::suite reward = STAmount{sfSignatureReward, USD(10)}; minAmount = STAmount{sfMinAccountCreateAmount, mpt}; } + // SponsorshipSet + { + Json::Value jv; + jv[jss::TransactionType] = jss::SponsorshipSet; + jv[jss::Account] = alice.human(); + jv[sfSponsee.fieldName] = carol.human(); + jv[sfFeeAmount.fieldName] = mpt.getJson(JsonOptions::none); + test(jv, sfFeeAmount.fieldName.c_str()); + } } BEAST_EXPECT(txWithAmounts.empty()); } diff --git a/src/test/jtx/impl/utility.cpp b/src/test/jtx/impl/utility.cpp index fed7d10a4dc..0df9ac2cb0f 100644 --- a/src/test/jtx/impl/utility.cpp +++ b/src/test/jtx/impl/utility.cpp @@ -47,7 +47,7 @@ void sign(Json::Value& jv, Account const& account) { jv[jss::SigningPubKey] = strHex(account.pk().slice()); - auto const blob = STTx::getSigningData(STTx{parse(jv)}); + auto const blob = STTx::getSigningData(parse(jv)); auto const sig = ripple::sign(account.pk(), account.sk(), makeSlice(blob)); jv[jss::TxnSignature] = strHex(Slice{sig.data(), sig.size()}); } From 5ac7bbf51b864148dd1bdb7667002b38663fc3c5 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 15 Sep 2025 22:48:40 +0900 Subject: [PATCH 024/249] test SponsorshipRequire flags --- src/test/app/Sponsor_test.cpp | 76 ++++++++++++++++++++-- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 19 +++--- src/xrpld/app/tx/detail/Transactor.cpp | 27 +++----- 3 files changed, 89 insertions(+), 33 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 6815f266f44..e0c095b2635 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -118,19 +118,12 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set(sponsor, sponsor, 0), ter(temMALFORMED)); // Invalid feeAmount - env(sponsor::set_fee( - sponsor, alice, tfSponsorshipClearRequireSignForFee, XRP(1)), - ter(temMALFORMED)); - for (auto amt : {XRP(-1), XRP(0), USD(1)}) { env(sponsor::set_fee(sponsor, alice, 0, amt), ter(temBAD_AMOUNT)); } // Invalid reserveCount - env(sponsor::set_reserve( - sponsor, alice, tfSponsorshipClearRequireSignForReserve, 1), - ter(temMALFORMED)); env(sponsor::set_reserve(sponsor, alice, 0, 0), ter(temMALFORMED)); // Invalid Delete operation @@ -686,6 +679,38 @@ class Sponsor_test : public beast::unit_test::suite sponsorFeeBalance(sponsor, alice) == sponsorFee - feeAmt); } } + + // test lsfSponsorshipRequireSignForFee + { + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // set flag + env(sponsor::set_fee( + sponsor, alice, tfSponsorshipSetRequireSignForFee, XRP(10))); + env.close(); + + env(pay(alice, bob, XRP(100)), + fee(XRP(10)), + sponsor::as(sponsor, tfSponsorFee), + ter(terNO_SPONSORSHIP)); + env.close(); + + // clear flag + env(sponsor::set_fee( + sponsor, alice, tfSponsorshipClearRequireSignForFee, XRP(10))); + env.close(); + + env(pay(alice, bob, XRP(100)), + fee(XRP(10)), + sponsor::as(sponsor, tfSponsorFee), + ter(tesSUCCESS)); + env.close(); + } } void @@ -741,6 +766,42 @@ class Sponsor_test : public beast::unit_test::suite } } + void + testRequireFlag() + { + testcase("SponsorshipRequireSignForReserve"); + using namespace test::jtx; + + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // set flag + env(sponsor::set_reserve( + sponsor, alice, tfSponsorshipSetRequireSignForReserve, 10)); + env.close(); + + env(check::create(alice, bob, XRP(100)), + fee(XRP(10)), + sponsor::as(sponsor, tfSponsorReserve), + ter(terNO_SPONSORSHIP)); + env.close(); + + // clear flag + env(sponsor::set_reserve( + sponsor, alice, tfSponsorshipClearRequireSignForReserve, 1)); + env.close(); + + env(check::create(alice, bob, XRP(100)), + fee(XRP(10)), + sponsor::as(sponsor, tfSponsorReserve), + ter(tesSUCCESS)); + env.close(); + } + void testCheck() { @@ -1269,6 +1330,7 @@ class Sponsor_test : public beast::unit_test::suite void testSponsorReserve() { + testRequireFlag(); testCheck(); testOfffer(); testTicket(); diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 27255789849..5724d380f1f 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -82,9 +82,6 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (ctx.tx.isFieldPresent(sfFeeAmount)) { - if (ctx.tx.getFlags() & tfSponsorshipClearRequireSignForFee) - return temMALFORMED; - auto const feeAmount = ctx.tx.getFieldAmount(sfFeeAmount); if (!isXRP(feeAmount)) @@ -108,9 +105,6 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (ctx.tx.isFieldPresent(sfReserveCount)) { - if (ctx.tx.getFlags() & tfSponsorshipClearRequireSignForReserve) - return temMALFORMED; - auto const reserveCount = ctx.tx.getFieldU32(sfReserveCount); // TODO: max reserveCount? if (reserveCount < 1) @@ -158,12 +152,13 @@ TER SponsorshipSet::doApply() { auto const sponseeAcc = ctx_.tx[sfSponsee]; - auto const keylet = keylet::sponsor(account_, sponseeAcc); auto const sponsorAcc = ctx_.tx.isFieldPresent(sfSponsorAccount) ? ctx_.tx.getAccountID(sfSponsorAccount) : account_; + auto const keylet = keylet::sponsor(sponsorAcc, sponseeAcc); + auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorAcc)); if (!sponsorAccSle) return tecINTERNAL; @@ -222,7 +217,6 @@ SponsorshipSet::doApply() (*newSle)[sfOwner] = sponsorAcc; (*newSle)[sfSponsee] = sponseeAcc; - (*newSle)[sfFlags] = ctx_.tx.getFlags(); if (feeAmount) { (*sponsorAccSle)[sfBalance] -= *feeAmount; @@ -237,6 +231,15 @@ SponsorshipSet::doApply() (*newSle)[sfReserveCount] = *reserveCount; } + auto flags = 0; + if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForFee)) + flags |= lsfSponsorshipRequireSignForFee; + + if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForReserve)) + flags |= lsfSponsorshipRequireSignForReserve; + + (*newSle)[sfFlags] = flags; + auto const sponsorPage = view().dirInsert( keylet::ownerDir(sponsorAcc), keylet, describeOwnerDir(sponsorAcc)); (*newSle)[sfOwnerNode] = *sponsorPage; diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index dd0f6ea9f8a..ac6ce1c32bf 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -259,35 +259,26 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) auto const sponsorAcc = txSponsor.getAccountID(sfAccount); auto const sponseeAcc = tx.getAccountID(sfAccount); - auto const hasSignature = txSponsor.isFieldPresent(sfTxnSignature) || + auto const hasSponsorSignature = txSponsor.isFieldPresent(sfTxnSignature) || !txSponsor.getFieldVL(sfSigningPubKey).empty() || txSponsor.isFieldPresent(sfSigners); - auto const sponsorSle = view.read(keylet::sponsor(sponsorAcc, sponseeAcc)); - if (!hasSignature) + if (!hasSponsorSignature) { + auto const sponsorSle = + view.read(keylet::sponsor(sponsorAcc, sponseeAcc)); + // pre funded if (!sponsorSle) - return tecNO_SPONSOR_PERMISSION; - } - else - { - // co-signed - if (!sponsorSle) - return tesSUCCESS; + return terNO_SPONSORSHIP; if (txSponsor.isFlag(tfSponsorFee) && sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) - { - if (!hasSignature) - return tecNO_SPONSOR_PERMISSION; - } + return terNO_SPONSORSHIP; + if (txSponsor.isFlag(tfSponsorReserve) && sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) - { - if (!hasSignature) - return tecNO_SPONSOR_PERMISSION; - } + return terNO_SPONSORSHIP; } return tesSUCCESS; From 759f8430202c5dbd31d5d744bb77278e5926a7b2 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 16 Sep 2025 23:11:19 +0900 Subject: [PATCH 025/249] Sponsor permissions --- .../xrpl/protocol/detail/permissions.macro | 6 ++ src/test/app/Sponsor_test.cpp | 89 +++++++++++++++++++ src/xrpld/app/tx/detail/SponsorshipSet.cpp | 37 ++++++++ src/xrpld/app/tx/detail/SponsorshipSet.h | 3 + 4 files changed, 135 insertions(+) diff --git a/include/xrpl/protocol/detail/permissions.macro b/include/xrpl/protocol/detail/permissions.macro index ec19c5767f3..5ed18312763 100644 --- a/include/xrpl/protocol/detail/permissions.macro +++ b/include/xrpl/protocol/detail/permissions.macro @@ -66,3 +66,9 @@ PERMISSION(MPTokenIssuanceLock, ttMPTOKEN_ISSUANCE_SET, 65547) /** This permission grants the delegated account the ability to unlock MPToken. */ PERMISSION(MPTokenIssuanceUnlock, ttMPTOKEN_ISSUANCE_SET, 65548) + +/** This permission grants the delegated account the ability to set SponsorFee. */ +PERMISSION(SponsorFee, ttSPONSORSHIP_SET, 65549) + +/** This permission grants the delegated account the ability to set SponsorReserve. */ +PERMISSION(SponsorReserve, ttSPONSORSHIP_SET, 65550) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e0c095b2635..df5f7c31edf 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1327,6 +1327,93 @@ class Sponsor_test : public beast::unit_test::suite } } + void + testDelegatePermission() + { + testcase("DelegatePermission"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const carol("carol"); + + // + // Permission SponsorFee + // + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, carol); + env.close(); + auto const testFeePermission = [&](TER result) { + // FeeAmount + env(sponsor::set(alice, bob, 0, std::nullopt, XRP(100)), + delegate::as(carol), + ter(result)); + // MaxFee + env(sponsor::set( + alice, bob, 0, std::nullopt, std::nullopt, XRP(100)), + delegate::as(carol), + ter(result)); + // SetRequireSignForFee flag + env(sponsor::set(alice, bob, tfSponsorshipSetRequireSignForFee), + delegate::as(carol), + ter(result)); + env.close(); + }; + + // no delegated + testFeePermission(tecNO_DELEGATE_PERMISSION); + + // set non-SponsorFee Permission + env(delegate::set(alice, carol, {"SponsorReserve"})); + env.close(); + + testFeePermission(tecNO_DELEGATE_PERMISSION); + + // set SponsorFee Permission + env(delegate::set(alice, carol, {"SponsorFee"})); + env.close(); + + testFeePermission(tesSUCCESS); + } + + // + // Permission SponsorReserve + // + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, carol); + env.close(); + + auto const testReservePermission = [&](TER result) { + // ReserveCount + env(sponsor::set(alice, bob, 0, 100), + delegate::as(carol), + ter(result)); + // SetRequireSignForReserve flag + env(sponsor::set( + alice, bob, tfSponsorshipSetRequireSignForReserve), + delegate::as(carol), + ter(result)); + env.close(); + }; + + // no delegated + testReservePermission(tecNO_DELEGATE_PERMISSION); + + // set non-SponsorReserve Permission + env(delegate::set(alice, carol, {"SponsorFee"})); + env.close(); + + testReservePermission(tecNO_DELEGATE_PERMISSION); + + // set SponsorReserve Permission + env(delegate::set(alice, carol, {"SponsorReserve"})); + env.close(); + + testReservePermission(tesSUCCESS); + } + } + void testSponsorReserve() { @@ -1368,6 +1455,8 @@ class Sponsor_test : public beast::unit_test::suite testDisallowIncoming(); testAccountDelete(); + + testDelegatePermission(); } }; diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 5724d380f1f..352d25cc2a9 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -17,6 +17,7 @@ */ //============================================================================== +#include #include #include @@ -122,6 +123,42 @@ SponsorshipSet::preflight(PreflightContext const& ctx) return preflight2(ctx); } +TER +SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) +{ + auto const delegate = tx[~sfDelegate]; + if (!delegate) + return tesSUCCESS; + + auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate); + auto const sle = view.read(delegateKey); + + if (!sle) + return tecNO_DELEGATE_PERMISSION; + + if (checkTxPermission(sle, tx) == tesSUCCESS) + return tesSUCCESS; + + std::unordered_set granularPermissions; + loadGranularPermission(sle, ttSPONSORSHIP_SET, granularPermissions); + + auto const sponsoringFee = tx.isFieldPresent(sfFeeAmount) || + tx.isFieldPresent(sfMaxFee) || + tx.isFlag(tfSponsorshipSetRequireSignForFee); + auto const sponsoringReserve = tx.isFieldPresent(sfReserveCount) || + tx.isFlag(tfSponsorshipSetRequireSignForReserve); + + if (granularPermissions.contains(SponsorFee) && sponsoringFee) + return tesSUCCESS; + + if (granularPermissions.contains(SponsorReserve) && sponsoringReserve) + return tesSUCCESS; + + // TODO: needs to check permission to delete sponsorship? + + return tecNO_DELEGATE_PERMISSION; +} + TER SponsorshipSet::preclaim(PreclaimContext const& ctx) { diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.h b/src/xrpld/app/tx/detail/SponsorshipSet.h index ec22cca67e5..ea037a686c3 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.h +++ b/src/xrpld/app/tx/detail/SponsorshipSet.h @@ -36,6 +36,9 @@ class SponsorshipSet : public Transactor static NotTEC preflight(PreflightContext const& ctx); + static TER + checkPermission(ReadView const& view, STTx const& tx); + static TER preclaim(PreclaimContext const& ctx); From 2afe5707ab2abda6e87b11569235040015b9c5e3 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 18 Sep 2025 16:32:50 +0900 Subject: [PATCH 026/249] High/Low SponsorAccount --- .../xrpl/protocol/detail/ledger_entries.macro | 2 + include/xrpl/protocol/detail/sfields.macro | 6 +- src/test/app/Sponsor_test.cpp | 328 ++++++++++++++---- src/test/jtx.h | 1 + src/xrpld/app/tx/detail/CashCheck.cpp | 13 +- src/xrpld/app/tx/detail/Escrow.cpp | 10 +- src/xrpld/app/tx/detail/Payment.cpp | 15 +- src/xrpld/app/tx/detail/SetTrust.cpp | 35 +- src/xrpld/app/tx/detail/Transactor.h | 2 +- src/xrpld/app/tx/detail/XChainBridge.cpp | 15 +- src/xrpld/ledger/View.h | 21 +- src/xrpld/ledger/detail/View.cpp | 132 ++++--- 12 files changed, 449 insertions(+), 131 deletions(-) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 1457d0f5a58..090e3eadfd6 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -304,6 +304,8 @@ LEDGER_ENTRY(ltRIPPLE_STATE, 0x0072, RippleState, state, ({ {sfHighNode, soeOPTIONAL}, {sfHighQualityIn, soeOPTIONAL}, {sfHighQualityOut, soeOPTIONAL}, + {sfHighSponsorAccount, soeOPTIONAL}, + {sfLowSponsorAccount, soeOPTIONAL}, })) /** The ledger object which lists the network's fee settings. diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 274d5e6d9bf..c6f6fa98ca9 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -297,8 +297,6 @@ TYPED_SFIELD(sfNFTokenMinter, ACCOUNT, 9) TYPED_SFIELD(sfEmitCallback, ACCOUNT, 10) TYPED_SFIELD(sfHolder, ACCOUNT, 11) TYPED_SFIELD(sfDelegate, ACCOUNT, 12) -TYPED_SFIELD(sfSponsorAccount, ACCOUNT, 13) -TYPED_SFIELD(sfSponsee, ACCOUNT, 14) // account (uncommon) TYPED_SFIELD(sfHookAccount, ACCOUNT, 16) @@ -309,6 +307,10 @@ TYPED_SFIELD(sfAttestationRewardAccount, ACCOUNT, 21) TYPED_SFIELD(sfLockingChainDoor, ACCOUNT, 22) TYPED_SFIELD(sfIssuingChainDoor, ACCOUNT, 23) TYPED_SFIELD(sfSubject, ACCOUNT, 24) +TYPED_SFIELD(sfSponsorAccount, ACCOUNT, 25) +TYPED_SFIELD(sfHighSponsorAccount, ACCOUNT, 26) +TYPED_SFIELD(sfLowSponsorAccount, ACCOUNT, 27) +TYPED_SFIELD(sfSponsee, ACCOUNT, 28) // vector of 256-bit TYPED_SFIELD(sfIndexes, VECTOR256, 1, SField::sMD_Never) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index df5f7c31edf..a1fa54b8890 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -18,16 +18,11 @@ //============================================================================== #include -#include -#include -#include -#include -#include -#include #include namespace ripple { +namespace test { class Sponsor_test : public beast::unit_test::suite { @@ -905,77 +900,131 @@ class Sponsor_test : public beast::unit_test::suite env(pay(env.master, sponsor, drops(env.current()->fees().increment))); env.close(); - // TODO: RippleState sponsor - // { - // // CheckCreate -> CheckCash(CheckCashMakesTrustLine) - // auto const seq2 = env.seq(alice); - // env(check::create(alice, bob, USD(1)), - // sponsor::as(sponsor, tfSponsorReserve), - // sponsor::sig(sponsor)); - // env.close(); - - // BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + - // Check BEAST_EXPECT(ownerCount(env, bob) == 0); - // BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - // BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); - // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - - // auto const keylet = keylet::check(alice, seq2); - // BEAST_EXPECT( - // env.le(keylet)->getAccountID(sfSponsorAccount) == - // sponsor.id()); - - // // CheckCash - // env(check::cash(bob, keylet.key, USD(1)), - // sponsor::as(sponsor, tfSponsorReserve), - // sponsor::sig(sponsor)); - // env.close(); - - // BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState - // BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState - // BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - // BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); - // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // } + // RippleState sponsor + { + // CheckCreate -> CheckCash(CheckCashMakesTrustLine) + auto const seq2 = env.seq(alice); + env(check::create(alice, bob, USD(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + auto const keylet = keylet::check(alice, seq2); + BEAST_EXPECT( + env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); + + // CheckCash + env(check::cash(bob, keylet.key, USD(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } } void - testOfffer() + testOffer() { testcase("Offer"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); + Account const bob("bob"); Account const gw("gw"); - Account const sponsor("sponsor"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); auto USD = gw["USD"]; + auto EUR = gw["EUR"]; - env.fund(XRP(10000), alice, gw, sponsor); - env.close(); + { + Env env{*this, testable_amendments()}; - // OfferCreate - auto const seq = env.seq(alice); - env(offer(alice, USD(1), XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + env.fund(XRP(10000), alice, gw, sponsor1); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // OfferCreate + auto const seq = env.seq(alice); + env(offer(alice, USD(1), XRP(1)), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1)); + env.close(); - // OfferCancel - env(offer_cancel(alice, seq)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + // OfferCancel + env(offer_cancel(alice, seq)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + } + + // test Offer Execution doesn't sponsor new trustline + { + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor1, sponsor2); + env.close(); + + env(trust(alice, USD(100))); + env(trust(bob, EUR(100))); + env.close(); + + env(pay(gw, alice, USD(100))); + env(pay(gw, bob, EUR(100))); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); - // TODO: test Offer Execution + // OfferCreate + env(offer(alice, EUR(1), USD(1)), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + + // OfferCreate (cross offer) + env(offer(bob, USD(1), EUR(1)), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + + // does not sponsor new trustline by cross offer + BEAST_EXPECT(ownerCount(env, bob) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } } void @@ -1132,6 +1181,109 @@ class Sponsor_test : public beast::unit_test::suite void testEscrow() { + testcase("Escrow"); + using namespace test::jtx; + using namespace std::chrono_literals; + + Account const alice("alice"); + Account const bob("bob"); + Account const gw("gw"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + auto const USD = gw["USD"]; + { + // Native Escrow + Env env{*this, testable_amendments()}; + auto const baseFee = env.current()->fees().base; + + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // EscrowCreate + auto const seq = env.seq(alice); + env(escrow::create(alice, bob, XRP(100)), + escrow::condition(escrow::cb1), + escrow::cancel_time(env.now() + 10s), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq)) + ->getAccountID(sfSponsorAccount) == sponsor.id()); + + // EscrowFinish + env(escrow::finish(bob, alice, seq), + escrow::condition(escrow::cb1), + escrow::fulfillment(escrow::fb1), + fee(baseFee * 150)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + { + // IOU Escrow + Env env{*this, testable_amendments()}; + auto const baseFee = env.current()->fees().base; + + env.fund(XRP(1000000), alice, bob, gw, sponsor, sponsor2); + env.close(); + + env(fset(gw, asfAllowTrustLineLocking)); + env.close(); + + env.trust(USD(1000000), alice); + env.close(); + env(pay(gw, alice, USD(10000))); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // EscrowCreate + auto const seq = env.seq(alice); + env(escrow::create(alice, bob, USD(100)), + escrow::condition(escrow::cb1), + escrow::cancel_time(env.now() + 10s), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq)) + ->getAccountID(sfSponsorAccount) == sponsor.id()); + + // EscrowFinish + env(escrow::finish(bob, alice, seq), + escrow::condition(escrow::cb1), + escrow::fulfillment(escrow::fb1), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2), + fee(baseFee * 150)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + BEAST_EXPECT( + env.le(keylet::line(bob, gw, USD.currency)) + ->getAccountID(sfHighSponsorAccount) == sponsor2.id()); + } } void @@ -1198,8 +1350,53 @@ class Sponsor_test : public beast::unit_test::suite } void - testTrust() + testTrustSet() { + testcase("TrustSet"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + auto const& highAcc = alice > bob ? alice : bob; + auto const& lowAcc = alice > bob ? bob : alice; + for (bool isIssuerHigh : {false, true}) + { + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; + + auto const USD = issuer["USD"]; + + // create TrustLine + env(trust(user, USD(100)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, user) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + BEAST_EXPECT( + env.le(keylet::line(user, issuer, USD.currency)) + ->getAccountID( + isIssuerHigh ? sfLowSponsorAccount + : sfHighSponsorAccount) == sponsor.id()); + + // delete TrustLine + env(trust(user, USD(0))); + env.close(); + + BEAST_EXPECT(ownerCount(env, user) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + BEAST_EXPECT(!env.le(keylet::line(user, issuer, USD.currency))); + } } void @@ -1419,13 +1616,13 @@ class Sponsor_test : public beast::unit_test::suite { testRequireFlag(); testCheck(); - testOfffer(); + testOffer(); testTicket(); testCredentials(); testDelegate(); // testDepositPreauth(); testDID(); - // testEscrow(); + testEscrow(); // testMPToken(); // testNFToken(); // testNFTokenOffer(); @@ -1433,7 +1630,7 @@ class Sponsor_test : public beast::unit_test::suite // testPermissionedDomain(); // testOracle(); testSignerList(); - // testTrust(); + testTrustSet(); // testVault(); // testXChain(); } @@ -1462,4 +1659,5 @@ class Sponsor_test : public beast::unit_test::suite BEAST_DEFINE_TESTSUITE(Sponsor, app, ripple); +} // namespace test } // namespace ripple diff --git a/src/test/jtx.h b/src/test/jtx.h index 6347b9dcf98..79d92d03dab 100644 --- a/src/test/jtx.h +++ b/src/test/jtx.h @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index c31591542b4..573d8b97fa7 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -272,6 +272,11 @@ CashCheck::doApply() return tecFAILED_PROCESSING; } + auto const sponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); + std::optional> sponsorSle = std::nullopt; + if (sponsorAcc) + sponsorSle = psb.peek(keylet::account(*sponsorAcc)); + // Preclaim already checked that source has at least the requested // funds. // @@ -368,9 +373,8 @@ CashCheck::doApply() auto const sleDst = psb.peek(keylet::account(account_)); // Can the account cover the trust line's reserve? - auto const sponsor = getTxReserveSponsor(psb, ctx_.tx); if (auto const ret = checkInsufficientReserve( - psb, sleDst, mPriorBalance, sponsor, 1); + psb, sleDst, mPriorBalance, sponsorSle, 1); !isTesSuccess(ret)) { JLOG(j_.trace()) << "Trust line does not exist. " @@ -399,6 +403,7 @@ CashCheck::doApply() Issue(currency, account_), // limit of zero 0, // quality in 0, // quality out + sponsorAcc, // sponsor viewJ); // journal !isTesSuccess(ter)) { @@ -510,8 +515,8 @@ CashCheck::doApply() } // If we succeeded, update the check owner's reserve. - auto const sponsor = getLedgerEntryReserveSponsor(psb, sleCheck); - adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), sponsor, -1, viewJ); + adjustOwnerCount( + psb, psb.peek(keylet::account(srcId)), sponsorSle, -1, viewJ); // Remove check from ledger. psb.erase(sleCheck); diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 2ae070e7bee..1b4cbe65a05 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -845,9 +845,12 @@ escrowUnlockApplyHelper( if (!view.exists(trustLineKey) && createAsset && !receiverIssuer) { // Can the account cover the trust line's reserve? - auto const sponsor = getTxReserveSponsor(view, tx); - if (auto const ret = - checkInsufficientReserve(view, sleDest, xrpBalance, sponsor, 1); + auto const sponeorAcc = getTxReserveSponsorAccountID(tx); + std::optional> sponsorSle = std::nullopt; + if (sponeorAcc) + sponsorSle = view.peek(keylet::account(*sponeorAcc)); + if (auto const ret = checkInsufficientReserve( + view, sleDest, xrpBalance, sponsorSle, 1); !isTesSuccess(ret)) { JLOG(journal.trace()) << "Trust line does not exist. " @@ -876,6 +879,7 @@ escrowUnlockApplyHelper( Issue(currency, receiver), // limit of zero 0, // quality in 0, // quality out + sponeorAcc, // sponsor journal); // journal !isTesSuccess(ter)) { diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index e8a1c7f9645..d77b98c1891 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -638,8 +638,19 @@ Payment::doApply() auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount); // This is the total reserve in drops. - // TODO: TEQU - auto const reserve = view().fees().accountReserve(ownerCount); + std::size_t sponsoredOwnerCount = + sleSrc->getFieldU32(sfSponsoredOwnerCount); + std::size_t sponsoringOwnerCount = + sleSrc->getFieldU32(sfSponsoringOwnerCount); + bool isAccountSponsored = sleSrc->isFieldPresent(sfSponsorAccount); + std::size_t sponsoringAccountCount = + sleSrc->getFieldU32(sfSponsoringAccountCount); + auto const reserve = view().fees().accountReserve( + ownerCount, + sponsoredOwnerCount, + sponsoringOwnerCount, + isAccountSponsored, + sponsoringAccountCount); // mPriorBalance is the balance on the sending account BEFORE the // fees were charged. We want to make sure we have enough reserve diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 25b9eb33b6f..3dc6afd1e65 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -453,7 +453,10 @@ SetTrust::doApply() SLE::pointer sleRippleState = view().peek(keylet::line(account_, uDstAccountID, currency)); - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const txSponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); + std::optional> txSponsorSle = std::nullopt; + if (txSponsorAcc) + txSponsorSle = view().peek(keylet::account(*txSponsorAcc)); if (sleRippleState) { @@ -628,6 +631,11 @@ SetTrust::doApply() bool bReserveIncrease = false; + auto const currentHighSponsor = getLedgerEntryReserveSponsor( + view(), sleRippleState, sfHighSponsorAccount); + auto const currentLowSponsor = getLedgerEntryReserveSponsor( + view(), sleRippleState, sfLowSponsorAccount); + if (bSetAuth) { uFlagsOut |= (bHigh ? lsfHighAuth : lsfLowAuth); @@ -636,9 +644,12 @@ SetTrust::doApply() if (bLowReserveSet && !bLowReserved) { // Set reserve for low account. - adjustOwnerCount(view(), sleLowAccount, std::nullopt, 1, viewJ); + adjustOwnerCount(view(), sleLowAccount, txSponsorSle, 1, viewJ); uFlagsOut |= lsfLowReserve; + addSponsorToLedgerEntry( + sleRippleState, txSponsorSle, sfLowSponsorAccount); + if (!bHigh) bReserveIncrease = true; } @@ -646,16 +657,22 @@ SetTrust::doApply() if (bLowReserveClear && bLowReserved) { // Clear reserve for low account. - adjustOwnerCount(view(), sleLowAccount, std::nullopt, -1, viewJ); + adjustOwnerCount( + view(), sleLowAccount, currentLowSponsor, -1, viewJ); uFlagsOut &= ~lsfLowReserve; + + removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsorAccount); } if (bHighReserveSet && !bHighReserved) { // Set reserve for high account. - adjustOwnerCount(view(), sleHighAccount, std::nullopt, 1, viewJ); + adjustOwnerCount(view(), sleHighAccount, txSponsorSle, 1, viewJ); uFlagsOut |= lsfHighReserve; + addSponsorToLedgerEntry( + sleRippleState, txSponsorSle, sfHighSponsorAccount); + if (bHigh) bReserveIncrease = true; } @@ -663,8 +680,11 @@ SetTrust::doApply() if (bHighReserveClear && bHighReserved) { // Clear reserve for high account. - adjustOwnerCount(view(), sleHighAccount, std::nullopt, -1, viewJ); + adjustOwnerCount( + view(), sleHighAccount, currentHighSponsor, -1, viewJ); uFlagsOut &= ~lsfHighReserve; + + removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsorAccount); } if (uFlagsIn != uFlagsOut) @@ -679,7 +699,7 @@ SetTrust::doApply() } // Reserve is not scaled by load. else if (auto const ret = checkInsufficientReserve( - view(), sle, mPriorBalance, sponsor, 0); + view(), sle, mPriorBalance, txSponsorSle, 0); !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " @@ -713,7 +733,7 @@ SetTrust::doApply() ctx_.view(), sle, mPriorBalance, - sponsor, + txSponsorSle, 1); !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. @@ -751,6 +771,7 @@ SetTrust::doApply() saLimitAllow, // Limit for who is being charged. uQualityIn, uQualityOut, + txSponsorAcc, viewJ); } diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index 0877d163525..db811b6f7b4 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -144,7 +144,7 @@ enum class FeePayerType { struct FeePayer { Keylet keylet; - TypedField const& field; + SF_AMOUNT const& field; FeePayerType type; }; diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index 27683a16cf1..8fbfe370f12 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -446,8 +446,19 @@ transferHelper( { auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount); - // TODO: TEQU - auto const reserve = psb.fees().accountReserve(ownerCount); + std::size_t sponsoredOwnerCount = + sleSrc->getFieldU32(sfSponsoredOwnerCount); + std::size_t sponsoringOwnerCount = + sleSrc->getFieldU32(sfSponsoringOwnerCount); + bool isAccountSponsored = sleSrc->isFieldPresent(sfSponsorAccount); + std::size_t sponsoringAccountCount = + sleSrc->getFieldU32(sfSponsoringAccountCount); + auto const reserve = psb.fees().accountReserve( + ownerCount, + sponsoredOwnerCount, + sponsoringOwnerCount, + isAccountSponsored, + sponsoringAccountCount); auto const availableBalance = [&]() -> STAmount { STAmount const curBal = (*sleSrc)[sfBalance]; diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index ab3a599976b..a7fb33fafa9 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -461,6 +461,9 @@ checkInsufficientReserve( std::int32_t ownerCountDelta, std::int32_t accountCountDelta = 0); +std::optional +getTxReserveSponsorAccountID(STTx const& tx); + std::optional> getTxReserveSponsor(ApplyView& view, STTx const& tx); @@ -468,12 +471,21 @@ std::optional> getTxReserveSponsor(ReadView const& view, STTx const& tx); std::optional> -getLedgerEntryReserveSponsor(ApplyView& view, std::shared_ptr sle); +getLedgerEntryReserveSponsor( + ApplyView& view, + std::shared_ptr sle, + SF_ACCOUNT const& field = sfSponsorAccount); void addSponsorToLedgerEntry( std::shared_ptr const& sle, - std::optional> const& sponsorSle); + std::optional> const& sponsorSle, + SF_ACCOUNT const& field = sfSponsorAccount); + +void +removeSponsorFromLedgerEntry( + std::shared_ptr const& sle, + SF_ACCOUNT const& field = sfSponsorAccount); //------------------------------------------------------------------------------ // @@ -501,8 +513,8 @@ adjustOwnerCount( return adjustOwnerCount( view, view.peek(keylet::account(account)), - sponsor.has_value() ? view.peek(keylet::account(*sponsor)) - : std::optional>(), + sponsor ? view.peek(keylet::account(*sponsor)) + : std::optional>(), amount, j); } @@ -680,6 +692,7 @@ trustCreate( // Issuer should be the account being set. std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, + std::optional const& sponsorAccountID, beast::Journal j); [[nodiscard]] TER diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index d207739a486..61956c1c284 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -627,10 +627,21 @@ xrpLiquid( std::uint32_t const ownerCount = confineOwnerCount( view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); - // AMMs have no reserve requirement + std::size_t sponsoredOwnerCount = sle->getFieldU32(sfSponsoredOwnerCount); + std::size_t sponsoringOwnerCount = sle->getFieldU32(sfSponsoringOwnerCount); + bool isAccountSponsored = sle->isFieldPresent(sfSponsorAccount); + std::size_t sponsoringAccountCount = + sle->getFieldU32(sfSponsoringAccountCount); + auto const reserve = sle->isFieldPresent(sfAMMID) - ? XRPAmount{0} // TODO: TEQU - : view.fees().accountReserve(ownerCount); + // AMMs have no reserve requirement + ? XRPAmount{0} + : view.fees().accountReserve( + ownerCount, + sponsoredOwnerCount, + sponsoringOwnerCount, + isAccountSponsored, + sponsoringAccountCount); auto const fullBalance = sle->getFieldAmount(sfBalance); @@ -1025,20 +1036,20 @@ checkInsufficientReserve( ReadView const& view, std::shared_ptr accSle, STAmount const& accBalance, - std::optional> const& _sponsorSle, + std::optional> const& sponsorSle, std::int32_t ownerCountDelta, std::int32_t accountCountDelta) { - if (_sponsorSle.has_value()) + if (sponsorSle) { - auto const sponsorSle = _sponsorSle.value(); - auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance); + auto const sponsorBalance = (*sponsorSle)->getFieldAmount(sfBalance); STAmount const sponsorReserve{view.fees().accountReserve( - sponsorSle->getFieldU32(sfOwnerCount), - sponsorSle->getFieldU32(sfSponsoredOwnerCount), - sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ownerCountDelta, - sponsorSle->isFieldPresent(sfSponsorAccount), - sponsorSle->getFieldU32(sfSponsoringAccountCount) + + (*sponsorSle)->getFieldU32(sfOwnerCount), + (*sponsorSle)->getFieldU32(sfSponsoredOwnerCount), + (*sponsorSle)->getFieldU32(sfSponsoringOwnerCount) + + ownerCountDelta, + (*sponsorSle)->isFieldPresent(sfSponsorAccount), + (*sponsorSle)->getFieldU32(sfSponsoringAccountCount) + accountCountDelta)}; if (sponsorBalance < sponsorReserve) @@ -1059,54 +1070,64 @@ checkInsufficientReserve( return tesSUCCESS; } -std::optional> -getTxReserveSponsor(ApplyView& view, STTx const& tx) +std::optional +getTxReserveSponsorAccountID(STTx const& tx) { if (tx.isFieldPresent(sfSponsor)) { auto const sponsorObj = tx.getFieldObject(sfSponsor); - auto const flags = sponsorObj.getFlags(); - auto const sponsorID = sponsorObj.getAccountID(sfAccount); - if (flags & tfSponsorReserve) - { - return view.peek(keylet::account(sponsorID)); - } + if (sponsorObj.isFlag(tfSponsorReserve)) + return sponsorObj.getAccountID(sfAccount); } return std::nullopt; } +std::optional> +getTxReserveSponsor(ApplyView& view, STTx const& tx) +{ + auto const sponsorID = getTxReserveSponsorAccountID(tx); + if (sponsorID) + return view.peek(keylet::account(*sponsorID)); + return std::nullopt; +} + std::optional> getTxReserveSponsor(ReadView const& view, STTx const& tx) { - if (tx.isFieldPresent(sfSponsor)) - { - auto const sponsorObj = tx.getFieldObject(sfSponsor); - auto const flags = sponsorObj.getFlags(); - auto const sponsorID = sponsorObj.getAccountID(sfAccount); - if (flags & tfSponsorReserve) - { - return view.read(keylet::account(sponsorID)); - } - } + auto const sponsorID = getTxReserveSponsorAccountID(tx); + if (sponsorID) + return view.read(keylet::account(*sponsorID)); return std::nullopt; } std::optional> -getLedgerEntryReserveSponsor(ApplyView& view, std::shared_ptr sle) +getLedgerEntryReserveSponsor( + ApplyView& view, + std::shared_ptr sle, + SF_ACCOUNT const& field) { - if (sle->isFieldPresent(sfSponsorAccount)) - return view.peek(keylet::account(sle->getAccountID(sfSponsorAccount))); + if (sle->isFieldPresent(field)) + return view.peek(keylet::account(sle->getAccountID(field))); return std::nullopt; } void addSponsorToLedgerEntry( std::shared_ptr const& sle, - std::optional> const& sponsorSle) + std::optional> const& sponsorSle, + SF_ACCOUNT const& field) { if (sponsorSle) - sle->setAccountID( - sfSponsorAccount, sponsorSle.value()->getAccountID(sfAccount)); + sle->setAccountID(field, (*sponsorSle)->getAccountID(sfAccount)); +} + +void +removeSponsorFromLedgerEntry( + std::shared_ptr const& sle, + SF_ACCOUNT const& field) +{ + if (sle->isFieldPresent(field)) + sle->makeFieldAbsent(field); } //------------------------------------------------------------------------------ @@ -1146,7 +1167,6 @@ adjustOwnerCount( } { // modify account's SponsoredOwnerCount - std::uint32_t const current{ accountSle->getFieldU32(sfSponsoredOwnerCount)}; AccountID const id = (*accountSle)[sfAccount]; @@ -1304,6 +1324,7 @@ addEmptyHolding( // If the line already exists, don't create it again. if (view.read(index)) return tecDUPLICATE; + auto const& sponsorAccountID = getTxReserveSponsorAccountID(tx); return trustCreate( view, high, @@ -1319,6 +1340,7 @@ addEmptyHolding( /*limit=*/STAmount{Issue{currency, dstId}}, /*qualityIn=*/0, /*qualityOut=*/0, + sponsorAccountID, journal); } @@ -1391,12 +1413,13 @@ authorizeMPToken( // - add the new mptokenKey to the owner directory // - create the MPToken object for the holder + auto const sponsor = getTxReserveSponsor(view, tx); + // The reserve that is required to create the MPToken. Note // that although the reserve increases with every item // an account owns, in the case of MPTokens we only // *enforce* a reserve if the user owns more than two // items. This is similar to the reserve requirements of trust lines. - auto const sponsor = getTxReserveSponsor(view, tx); if (sleAcct->getFieldU32(sfOwnerCount) >= 2) { if (auto const ret = checkInsufficientReserve( @@ -1473,6 +1496,7 @@ trustCreate( // Issuer should be the account being set. std::uint32_t uQualityIn, std::uint32_t uQualityOut, + std::optional const& sponsorAccountID, beast::Journal j) { JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", " @@ -1561,8 +1585,17 @@ trustCreate( uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); } + std::optional> sponsorSle = std::nullopt; + if (sponsorAccountID) + sponsorSle = view.peek(keylet::account(*sponsorAccountID)); + sleRippleState->setFieldU32(sfFlags, uFlags); - adjustOwnerCount(view, sleAccount, std::nullopt, 1, j); + adjustOwnerCount(view, sleAccount, sponsorSle, 1, j); + + addSponsorToLedgerEntry( + sleRippleState, + sponsorSle, + bSetHigh ? sfHighSponsorAccount : sfLowSponsorAccount); // ONLY: Create ripple balance. sleRippleState->setFieldAmount( @@ -1847,13 +1880,21 @@ rippleCreditIOU( // Sender quality out is 0. { // Clear the reserve of the sender, possibly delete the line! + auto const currentSponsor = getLedgerEntryReserveSponsor( + view, + sleRippleState, + !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); adjustOwnerCount( view, view.peek(keylet::account(uSenderID)), - std::nullopt, + currentSponsor, -1, j); + removeSponsorFromLedgerEntry( + sleRippleState, + !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); + // Clear reserve flag. sleRippleState->setFieldU32( sfFlags, @@ -1917,6 +1958,7 @@ rippleCreditIOU( saReceiverLimit, 0, 0, + std::nullopt, j); } @@ -2308,12 +2350,19 @@ updateTrustLine( { // VFALCO Where is the line being deleted? // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(view, sle, std::nullopt, -1, j); + auto const currentSponsor = getLedgerEntryReserveSponsor( + view, + sle, + !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); + adjustOwnerCount(view, sle, currentSponsor, -1, j); // Clear reserve flag. state->setFieldU32( sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); + removeSponsorFromLedgerEntry( + sle, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); + // Balance is zero, receiver reserve is clear. if (!after // Balance is zero. && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve))) @@ -2419,6 +2468,7 @@ issueIOU( limit, 0, 0, + std::nullopt, j); } From a43ae9a3ce7c6530fbaf8a8efb8df872c14b3051 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 18 Sep 2025 18:49:53 +0900 Subject: [PATCH 027/249] add InvariantCheck for sponsor count --- src/test/app/Invariants_test.cpp | 69 ++++++++++++++++ src/xrpld/app/tx/detail/DeleteAccount.cpp | 2 + src/xrpld/app/tx/detail/InvariantCheck.cpp | 94 ++++++++++++++++++++++ src/xrpld/app/tx/detail/InvariantCheck.h | 59 ++++++++++++++ 4 files changed, 224 insertions(+) diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index ae2a1c45df1..dc9c71373d6 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -112,6 +112,11 @@ class Invariants_test : public beast::unit_test::suite { terActual = ac.checkInvariants(terActual, fee); BEAST_EXPECT(terExpect == terActual); + if (terExpect != terActual) + { + printf("terActual: %s\n", transHuman(terActual).c_str()); + printf("terExpect: %s\n", transHuman(terExpect).c_str()); + } BEAST_EXPECT( sink.messages().str().starts_with("Invariant failed:") || sink.messages().str().starts_with( @@ -1604,6 +1609,69 @@ class Invariants_test : public beast::unit_test::suite {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); } + void + testSponsorship() + { + using namespace test::jtx; + using namespace std::string_literals; + testcase << "Sponsorship"; + { + auto const expect_message = + "SponsoredOwnerCount does not equal " + "SponsoringOwnerCount delta."; + + doInvariantCheck( + {{expect_message}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(A1.id())); + if (!sle) + return false; + sle->setFieldU32(sfSponsoredOwnerCount, 1); + ac.view().update(sle); + return true; + }); + + doInvariantCheck( + {{expect_message}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(A1.id())); + if (!sle) + return false; + sle->setFieldU32(sfSponsoringOwnerCount, 1); + ac.view().update(sle); + return true; + }); + } + + { + auto const expect_message = + "Invariant failed: Net delta of SponsoringAccountCount does " + "not match net delta of sfSponsorAccount presence."; + + doInvariantCheck( + {{expect_message}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(A1.id())); + if (!sle) + return false; + sle->setFieldU32(sfSponsoringAccountCount, 1); + ac.view().update(sle); + return true; + }); + + doInvariantCheck( + {{expect_message}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(A1.id())); + if (!sle) + return false; + sle->setAccountID(sfSponsorAccount, A2.id()); + ac.view().update(sle); + return true; + }); + } + } + public: void run() override @@ -1623,6 +1691,7 @@ class Invariants_test : public beast::unit_test::suite testNFTokenPageInvariants(); testPermissionedDomainInvariants(); testPermissionedDEX(); + testSponsorship(); } }; diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index 45c496730ab..9de07e5a517 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -453,6 +453,8 @@ DeleteAccount::doApply() sponsorSle->setFieldU32( sfSponsoringAccountCount, sponsoringAccountCount - 1); view().update(sponsorSle); + + (*src).makeFieldAbsent(sfSponsorAccount); } XRPL_ASSERT( diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index fd1ab52f003..cafc1c92d06 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -2037,4 +2037,98 @@ ValidAMM::finalize( return true; } +// Add new sponsorship-related invariants implementations + +void +SponsorshipOwnerCountsMatch::visitEntry( + bool, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + auto getSponsored = + [](std::shared_ptr const& sle) -> std::uint32_t { + if (sle && sle->getType() == ltACCOUNT_ROOT) + return sle->getFieldU32(sfSponsoredOwnerCount); + return 0; + }; + auto getSponsoring = + [](std::shared_ptr const& sle) -> std::uint32_t { + if (sle && sle->getType() == ltACCOUNT_ROOT) + return sle->getFieldU32(sfSponsoringOwnerCount); + return 0; + }; + + std::int64_t const beforeSponsored = getSponsored(before); + std::int64_t const afterSponsored = getSponsored(after); + std::int64_t const beforeSponsoring = getSponsoring(before); + std::int64_t const afterSponsoring = getSponsoring(after); + + deltaSponsoredOwnerCount_ += (afterSponsored - beforeSponsored); + deltaSponsoringOwnerCount_ += (afterSponsoring - beforeSponsoring); +} + +bool +SponsorshipOwnerCountsMatch::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const& j) +{ + if (deltaSponsoredOwnerCount_ != deltaSponsoringOwnerCount_) + { + JLOG(j.fatal()) << "Invariant failed: SponsoredOwnerCount does not " + "equal SponsoringOwnerCount delta."; + return false; + } + + return true; +} + +void +SponsorshipAccountCountMatchesField::visitEntry( + bool, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + auto getSponsoringAccountCount = + [](std::shared_ptr const& sle) -> std::uint32_t { + if (sle && sle->getType() == ltACCOUNT_ROOT) + return sle->getFieldU32(sfSponsoringAccountCount); + return 0; + }; + + auto hasSponsorField = [](std::shared_ptr const& sle) -> bool { + return sle && sle->getType() == ltACCOUNT_ROOT && + sle->isFieldPresent(sfSponsorAccount); + }; + + std::int64_t const beforeCount = getSponsoringAccountCount(before); + std::int64_t const afterCount = getSponsoringAccountCount(after); + deltaSponsoringAccountCount_ += (afterCount - beforeCount); + + int const beforePresent = hasSponsorField(before) ? 1 : 0; + int const afterPresent = hasSponsorField(after) ? 1 : 0; + deltaSponsorFieldPresence_ += (afterPresent - beforePresent); +} + +bool +SponsorshipAccountCountMatchesField::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const& j) +{ + if (deltaSponsoringAccountCount_ != deltaSponsorFieldPresence_) + { + JLOG(j.fatal()) + << "Invariant failed: Net delta of SponsoringAccountCount does not " + "match net delta of sfSponsorAccount presence."; + return false; + } + + return true; +} + } // namespace ripple diff --git a/src/xrpld/app/tx/detail/InvariantCheck.h b/src/xrpld/app/tx/detail/InvariantCheck.h index 529c05ce0e9..745be3b365a 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.h +++ b/src/xrpld/app/tx/detail/InvariantCheck.h @@ -531,6 +531,63 @@ class NFTokenCountTracking beast::Journal const&); }; +/** + * @brief Invariant: Sponsored owner counts are balanced. + * + * The following check is made for every transaction: + * - The sum of all per-account deltas of `sfSponsoredOwnerCount` equals + * the sum of all per-account deltas of `sfSponsoringOwnerCount`. + */ +class SponsorshipOwnerCountsMatch +{ + std::int64_t deltaSponsoredOwnerCount_ = 0; + std::int64_t deltaSponsoringOwnerCount_ = 0; + +public: + void + visitEntry( + bool, + std::shared_ptr const&, + std::shared_ptr const&); + + bool + finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const&); +}; + +/** + * @brief Invariant: Sponsoring account relationships tracked consistently. + * + * The following check is made for every transaction: + * - The net delta of `sfSponsoringAccountCount` across all accounts equals + * the net delta of the count of ltACCOUNT_ROOT entries having + * `sfSponsorAccount` present (presence transitions only: add/remove). + */ +class SponsorshipAccountCountMatchesField +{ + std::int64_t deltaSponsoringAccountCount_ = 0; + std::int64_t deltaSponsorFieldPresence_ = 0; + +public: + void + visitEntry( + bool, + std::shared_ptr const&, + std::shared_ptr const&); + + bool + finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const&); +}; + /** * @brief Invariant: Token holder's trustline balance cannot be negative after * Clawback. @@ -716,6 +773,8 @@ using InvariantChecks = std::tuple< NoXRPTrustLines, NoDeepFreezeTrustLinesWithoutFreeze, TransfersNotFrozen, + SponsorshipOwnerCountsMatch, + SponsorshipAccountCountMatchesField, NoBadOffers, NoZeroEscrow, ValidNewAccountRoot, From 89af81745bd599b07591e2a4610629b6f31c0f59 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 19 Sep 2025 01:36:45 +0900 Subject: [PATCH 028/249] add tests --- src/test/app/AMM_test.cpp | 2 +- src/test/app/PayChan_test.cpp | 2 + src/test/app/Sponsor_test.cpp | 607 +++++++++++++++++- src/test/jtx/TestHelpers.h | 4 + src/test/jtx/impl/TestHelpers.cpp | 4 + src/xrpld/app/tx/detail/Credentials.cpp | 9 +- .../app/tx/detail/SponsorshipTransfer.cpp | 34 +- 7 files changed, 624 insertions(+), 38 deletions(-) diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index c89aebf813c..bb90edcb0ff 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -3665,7 +3665,7 @@ struct AMM_test : public jtx::AMMTest auto const settleDelay = 100s; NetClock::time_point const cancelAfter = env.current()->info().parentCloseTime + 200s; - env(create( + env(paychan::create( carol, ammAlice.ammAccount(), XRP(1'000), diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index 3a5d3d6ff5e..38db51565d8 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -31,6 +31,8 @@ namespace ripple { namespace test { +using namespace jtx::paychan; + struct PayChan_test : public beast::unit_test::suite { FeatureBitset const disallowIncoming{featureDisallowIncoming}; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index a1fa54b8890..e4a20ff0c9c 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -807,13 +807,14 @@ class Sponsor_test : public beast::unit_test::suite Account const bob("bob"); Account const gw("gw"); Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); auto const USD = gw["USD"]; auto const reserve = env.current()->fees().reserve; auto const increment = env.current()->fees().increment; - env.fund(XRP(10000), alice, bob, gw); + env.fund(XRP(10000), alice, bob, gw, sponsor2); env.fund(drops(reserve) + drops(increment) - drops(1), sponsor); env.close(); @@ -859,14 +860,30 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT( env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); - env(check::cancel(alice, keylet.key), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + // transfer sponsor + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + BEAST_EXPECT( + env.le(keylet)->getAccountID(sfSponsorAccount) == + sponsor2.id()); + + // CheckCancel + env(check::cancel(alice, keylet.key)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } { @@ -900,9 +917,9 @@ class Sponsor_test : public beast::unit_test::suite env(pay(env.master, sponsor, drops(env.current()->fees().increment))); env.close(); - // RippleState sponsor + // RippleState sponsor (CheckCashMakesTrustLine) { - // CheckCreate -> CheckCash(CheckCashMakesTrustLine) + // CheckCreate -> CheckCash auto const seq2 = env.seq(alice); env(check::create(alice, bob, USD(1)), sponsor::as(sponsor, tfSponsorReserve), @@ -950,7 +967,7 @@ class Sponsor_test : public beast::unit_test::suite { Env env{*this, testable_amendments()}; - env.fund(XRP(10000), alice, gw, sponsor1); + env.fund(XRP(10000), alice, gw, sponsor1, sponsor2); env.close(); // OfferCreate @@ -965,6 +982,22 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + // transfer sponsor + auto const keylet = keylet::offer(alice, seq); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + BEAST_EXPECT( + env.le(keylet)->getAccountID(sfSponsorAccount) == + sponsor2.id()); + // OfferCancel env(offer_cancel(alice, seq)); env.close(); @@ -973,6 +1006,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } // test Offer Execution doesn't sponsor new trustline @@ -1035,8 +1069,9 @@ class Sponsor_test : public beast::unit_test::suite Env env{*this, testable_amendments()}; Account const alice("alice"); Account const sponsor("master"); + Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, sponsor); + env.fund(XRP(1000000), alice, sponsor, sponsor2); env.close(); // TicketCreate @@ -1051,13 +1086,32 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 250); + auto const keylet = keylet::ticket(alice, ticketSeq); + BEAST_EXPECT( + env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); + + // transfer sponsor + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 250); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 250); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 249); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + BEAST_EXPECT( + env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor2.id()); + // use a Ticket - env(noop(alice), ticket::use(ticketSeq + 1)); + env(noop(alice), ticket::use(ticketSeq)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 249); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 249); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 249); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } void @@ -1069,12 +1123,15 @@ class Sponsor_test : public beast::unit_test::suite Account const issuer("issuer"); Account const subject("subject"); Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), issuer, subject, sponsor); + env.fund(XRP(1000000), issuer, subject, sponsor, sponsor2); env.close(); + auto const credType = std::string("credType"); + // CredentialsCreate - env(credentials::create(subject, issuer, "credType"), + env(credentials::create(subject, issuer, credType), credentials::uri("uri"), sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); @@ -1086,8 +1143,23 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // transfer sponsor + auto const keylet = keylet::credential( + subject, issuer, Slice(credType.data(), credType.size())); + env(sponsor::transfer(issuer, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, issuer) == 1); + BEAST_EXPECT(ownerCount(env, subject) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // CredentialsAccept - env(credentials::accept(subject, issuer, "credType"), + env(credentials::accept(subject, issuer, credType), sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); env.close(); @@ -1097,9 +1169,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); // CredentialsDelete - env(credentials::deleteCred(subject, subject, issuer, "credType")); + env(credentials::deleteCred(subject, subject, issuer, credType)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 0); @@ -1107,6 +1180,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + + // TODO: Accept Sponsored Credentials without sponsoring + // TODO: Self Accept Sponsored Credentials } void @@ -1118,8 +1195,9 @@ class Sponsor_test : public beast::unit_test::suite Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, bob, sponsor); + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); // DelegateSet @@ -1132,6 +1210,18 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // transfer sponsor + auto const keylet = keylet::delegate(alice, bob); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // delete env(delegate::set(alice, bob, {})); env.close(); @@ -1144,6 +1234,46 @@ class Sponsor_test : public beast::unit_test::suite void testDepositPreauth() { + testcase("DepositPreauth"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); + + // DepositPreauthSet + env(deposit::auth(alice, sponsor), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // transfer sponsor + auto const keylet = keylet::depositPreauth(alice, sponsor); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // DepositPreauthDelete + env(deposit::unauth(alice, sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } void @@ -1154,8 +1284,9 @@ class Sponsor_test : public beast::unit_test::suite Env env{*this, testable_amendments()}; Account const alice("alice"); Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, sponsor); + env.fund(XRP(1000000), alice, sponsor, sponsor2); env.close(); // DIDSet @@ -1169,6 +1300,18 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // transfer sponsor + auto const keylet = keylet::did(alice); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // DIDDelete env(did::del(alice)); env.close(); @@ -1176,6 +1319,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } void @@ -1196,14 +1340,14 @@ class Sponsor_test : public beast::unit_test::suite Env env{*this, testable_amendments()}; auto const baseFee = env.current()->fees().base; - env.fund(XRP(1000000), alice, bob, sponsor); + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); // EscrowCreate auto const seq = env.seq(alice); env(escrow::create(alice, bob, XRP(100)), escrow::condition(escrow::cb1), - escrow::cancel_time(env.now() + 10s), + escrow::cancel_time(env.now() + 100s), sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); env.close(); @@ -1216,6 +1360,21 @@ class Sponsor_test : public beast::unit_test::suite env.le(keylet::escrow(alice, seq)) ->getAccountID(sfSponsorAccount) == sponsor.id()); + // transfer sponsor + env(sponsor::transfer(alice, keylet::escrow(alice, seq).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq)) + ->getAccountID(sfSponsorAccount) == sponsor2.id()); + // EscrowFinish env(escrow::finish(bob, alice, seq), escrow::condition(escrow::cb1), @@ -1226,6 +1385,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } { @@ -1289,26 +1449,413 @@ class Sponsor_test : public beast::unit_test::suite void testMPToken() { + testcase("MPToken"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + // MPTokenIssuanceCreate + Json::Value jv = {}; + jv[sfAccount] = alice.human(); + jv[sfTransactionType] = jss::MPTokenIssuanceCreate; + auto const mptid = makeMptID(env.seq(alice), alice.id()); + env(jv, sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // transfer sponsor + auto const mptIssuanceKeylet = keylet::mptIssuance(mptid); + env(sponsor::transfer(alice, mptIssuanceKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // MPTokenAuthorize + jv = {}; + jv[sfTransactionType] = jss::MPTokenAuthorize; + jv[sfAccount] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + env(jv, sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // transfer sponsor + auto const mptTokenKeylet = keylet::mptoken(mptid, bob); + env(sponsor::transfer(alice, mptTokenKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); + + // MPTokenAuthorize Unauthorize + jv = {}; + jv[sfTransactionType] = jss::MPTokenAuthorize; + jv[sfAccount] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + jv[sfFlags] = tfMPTUnauthorize; + env(jv); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // MPTokenIssuanceDestroy + jv = {}; + jv[sfTransactionType] = jss::MPTokenIssuanceDestroy; + jv[sfAccount] = alice.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + env(jv); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } void testNFToken() { + // testcase("NFToken"); + // using namespace test::jtx; + // Env env{*this, testable_amendments()}; + // Account const alice("alice"); + // Account const bob("bob"); + // Account const sponsor("sponsor"); + // Account const sponsor2("sponsor2"); + + // env.fund(XRP(1000000), alice, bob, sponsor); + // env.close(); + + // // NFTokenMint + // env(token::mint(alice), + // sponsor::as(sponsor, tfSponsorReserve), + // sponsor::sig(sponsor)); + // env.close(); + + // BEAST_EXPECT(ownerCount(env, alice) == 1); + // BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); } void testNFTokenOffer() { + testcase("NFTokenOffer"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const broker("broker"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + auto const taxon = 0u; + + { + // Mint + CreateOffer + CancelOffer + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + // Mint + uint256 const nftId{ + token::getNextID(env, alice, taxon, tfTransferable)}; + env(token::mint(alice, taxon), txflags(tfTransferable)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // NFTokenOfferCreate + uint256 const offerIndex = + keylet::nftoffer(alice, env.seq(alice)).key; + env(token::createOffer(alice, nftId, XRP(1)), + token::destination(bob), + txflags(tfSellNFToken), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // transfer sponsor + env(sponsor::transfer(alice, offerIndex), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // NFTokenOfferCancel + env(token::cancelOffer(alice, {offerIndex})); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + { + // Mint + CreateSellOffer + AcceptSellOffer + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // Mint + uint256 const nftId{ + token::getNextID(env, alice, taxon, tfTransferable)}; + env(token::mint(alice, taxon), txflags(tfTransferable)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // NFTokenOfferCreate + uint256 const offerIndex = + keylet::nftoffer(alice, env.seq(alice)).key; + env(token::createOffer(alice, nftId, XRP(1)), + token::destination(bob), + txflags(tfSellNFToken), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // NFTokenOfferAccept + env(token::acceptSellOffer(bob, offerIndex)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + { + // Mint + CreateBuyOffer + AcceptBuyOffer + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // Mint + uint256 const nftId{ + token::getNextID(env, alice, taxon, tfTransferable)}; + env(token::mint(alice, taxon), txflags(tfTransferable)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // NFTokenOfferCreate + uint256 const offerIndex = keylet::nftoffer(bob, env.seq(bob)).key; + env(token::createOffer(bob, nftId, XRP(1)), + token::owner(alice), + token::destination(alice), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // NFTokenOfferAccept + env(token::acceptBuyOffer(alice, offerIndex)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + { + // Broker + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, broker, sponsor, sponsor2); + env.close(); + + // Mint + uint256 const nftId{ + token::getNextID(env, alice, taxon, tfTransferable)}; + env(token::mint(alice, taxon), txflags(tfTransferable)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // NFTokenOfferCreate (BuyOffer) + uint256 const buyOfferIndex = + keylet::nftoffer(bob, env.seq(bob)).key; + env(token::createOffer(bob, nftId, XRP(1)), + token::owner(alice), + token::destination(broker), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // NFTokenOfferCreate (SellOffer) + uint256 const sellOfferIndex = + keylet::nftoffer(alice, env.seq(alice)).key; + env(token::createOffer(alice, nftId, XRP(1)), + txflags(tfSellNFToken), + token::destination(broker), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // NFTokenOfferAccept + env(token::brokerOffers(broker, buyOfferIndex, sellOfferIndex)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } } void testPayChan() { + testcase("PayChan"); + using namespace test::jtx; + using namespace std::literals::chrono_literals; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + // PayChanCreate + auto const pk = alice.pk(); + auto const settleDelay = 10s; + auto const chan = paychan::channel(alice, bob, env.seq(alice)); + env(paychan::create(alice, bob, XRP(100), settleDelay, pk), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // transfer sponsor + env(sponsor::transfer(alice, chan), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + env.close(env.now() + settleDelay); + // PayChanClaim (delete PayChan) + env(paychan::claim(bob, chan), txflags(tfClose)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } void testPermissionedDomain() { + testcase("PermissionedDomain"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); + + // PermissionedDomainSet + auto const seq = env.seq(alice); + pdomain::Credentials credentials{{alice, "first credential"}}; + env(pdomain::setTx(alice, credentials), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // transfer sponsor + env(sponsor::transfer( + alice, keylet::permissionedDomain(alice, seq).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // PermissionedDomainDelete + auto objects = pdomain::getObjects(alice, env); + auto const domain = objects.begin()->first; + env(pdomain::deleteTx(alice, domain)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } void @@ -1324,8 +1871,9 @@ class Sponsor_test : public beast::unit_test::suite Env env{*this, testable_amendments()}; Account const alice("alice"); Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, sponsor); + env.fund(XRP(1000000), alice, sponsor, sponsor2); env.close(); Account const bob("bob"); @@ -1340,6 +1888,17 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // // transfer sponsor + // env(sponsor::transfer(alice, keylet::signers(alice).key), + // sponsor::as(sponsor2, tfSponsorReserve), + // sponsor::sig(sponsor2)); + // env.close(); + + // BEAST_EXPECT(ownerCount(env, alice) == 1); + // BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // Delete env(signers(alice, none)); env.close(); @@ -1347,6 +1906,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } void @@ -1397,6 +1957,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::line(user, issuer, USD.currency))); } + // TODO: transfer sponsor } void @@ -1620,14 +2181,14 @@ class Sponsor_test : public beast::unit_test::suite testTicket(); testCredentials(); testDelegate(); - // testDepositPreauth(); + testDepositPreauth(); testDID(); testEscrow(); - // testMPToken(); + testMPToken(); // testNFToken(); - // testNFTokenOffer(); - // testPayChan(); - // testPermissionedDomain(); + testNFTokenOffer(); + testPayChan(); + testPermissionedDomain(); // testOracle(); testSignerList(); testTrustSet(); diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index d535e172c8e..3b20c71776b 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -254,6 +254,8 @@ expectLedgerEntryRoot( /* Payment Channel */ /******************************************************************************/ +namespace paychan { + Json::Value create( AccountID const& account, @@ -312,6 +314,8 @@ channelBalance(ReadView const& view, uint256 const& chan); bool channelExists(ReadView const& view, uint256 const& chan); +} // namespace paychan + /* Crossing Limits */ /******************************************************************************/ diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index 6549c09239c..1c72683e08e 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -231,6 +231,8 @@ expectLedgerEntryRoot( /* Payment Channel */ /******************************************************************************/ +namespace paychan { + Json::Value create( AccountID const& account, @@ -322,6 +324,8 @@ channelExists(ReadView const& view, uint256 const& chan) return bool(slep); } +} // namespace paychan + /* Crossing Limits */ /******************************************************************************/ diff --git a/src/xrpld/app/tx/detail/Credentials.cpp b/src/xrpld/app/tx/detail/Credentials.cpp index 48f6bff2a7c..523480a3848 100644 --- a/src/xrpld/app/tx/detail/Credentials.cpp +++ b/src/xrpld/app/tx/detail/Credentials.cpp @@ -370,15 +370,16 @@ CredentialAccept::doApply() if (!sleSubject || !sleIssuer) return tefINTERNAL; - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const newSponsor = getTxReserveSponsor(view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - view(), sleSubject, mPriorBalance, sponsor, 1); + view(), sleSubject, mPriorBalance, newSponsor, 1); !isTesSuccess(ret)) return ret; auto const credType(ctx_.tx[sfCredentialType]); Keylet const credentialKey = keylet::credential(account_, issuer, credType); auto const sleCred = view().peek(credentialKey); // Checked in preclaim() + auto const currentSponsor = getLedgerEntryReserveSponsor(view(), sleCred); if (checkExpired(sleCred, view().info().parentCloseTime)) { @@ -391,8 +392,8 @@ CredentialAccept::doApply() sleCred->setFieldU32(sfFlags, lsfAccepted); view().update(sleCred); - adjustOwnerCount(view(), sleIssuer, sponsor, -1, j_); - auto const newSponsor = getTxReserveSponsor(view(), ctx_.tx); + adjustOwnerCount(view(), sleIssuer, currentSponsor, -1, j_); + removeSponsorFromLedgerEntry(sleCred); adjustOwnerCount(view(), sleSubject, newSponsor, 1, j_); addSponsorToLedgerEntry(sleCred, newSponsor); diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 9eae02ac828..330f145851d 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -79,17 +79,22 @@ getLedgerEntryOwner( auto const signerList = view.read(keylet::signers(account)); if (!signerList) return std::nullopt; - if (signerList->getFieldH256(sfObjectID) == - sle->getFieldH256(sfObjectID)) + if (signerList->getFieldH256(sfLedgerIndex) == + sle->getFieldH256(sfLedgerIndex)) return account; return std::nullopt; } case ltCREDENTIAL: { - if (sle->getFlags() & lsfAccepted) + if (sle->isFlag(lsfAccepted)) return sle->getAccountID(sfSubject); return sle->getAccountID(sfIssuer); } - // case ltNFTOKEN_PAGE: + case ltNFTOKEN_PAGE: { + // the upper 20 bytes of the index of ltNFTokenPage are the Owner's + // AccountID + uint256 const& key = sle->key(); + return AccountID::fromVoid(key.data()); + } // case ltRIPPLE_STATE: case ltACCOUNT_ROOT: { // AccountRoot is not supported for object sponsorship @@ -229,9 +234,14 @@ SponsorshipTransfer::doApply() if (auto const oldSponsorSle = view().peek(keylet::account(oldSponsor))) { - oldSponsorSle->setFieldU32( - sfSponsoringOwnerCount, - oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - 1); + auto const newCount = + oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - 1; + if (newCount == 0) + oldSponsorSle->makeFieldAbsent(sfSponsoringOwnerCount); + else + oldSponsorSle->setFieldU32( + sfSponsoringOwnerCount, newCount); + view().update(oldSponsorSle); } else @@ -257,9 +267,13 @@ SponsorshipTransfer::doApply() // dissolve object sponsor auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); // decrement sponsored count - accSle->setFieldU32( - sfSponsoredOwnerCount, - accSle->getFieldU32(sfSponsoredOwnerCount) - 1); + auto const newCount = + accSle->getFieldU32(sfSponsoredOwnerCount) - 1; + if (newCount == 0) + accSle->makeFieldAbsent(sfSponsoredOwnerCount); + else + accSle->setFieldU32(sfSponsoredOwnerCount, newCount); + view().update(accSle); // decrement old sponsoring count if (auto const oldSponsorSle = From 453fd23512b5673b64abf9966ea5333065468813 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 19 Sep 2025 21:06:34 +0900 Subject: [PATCH 029/249] add Sponsor Oracle --- src/test/app/Sponsor_test.cpp | 252 +++++++++++++++++- src/test/jtx.h | 1 + src/xrpld/app/tx/detail/SetOracle.cpp | 43 ++- .../app/tx/detail/SponsorshipTransfer.cpp | 30 ++- 4 files changed, 314 insertions(+), 12 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e4a20ff0c9c..b852d91b070 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -19,6 +19,7 @@ #include +#include #include namespace ripple { @@ -1861,6 +1862,255 @@ class Sponsor_test : public beast::unit_test::suite void testOracle() { + testcase("Oracle"); + using namespace test::jtx; + using namespace std::chrono; + using DataSeries = std::vector< + std::tuple>; + + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); + + auto const oracleSet = + [&env](Account const& account, uint8_t dataSeriesSize) { + auto const now = env.timeKeeper().now(); + env.close(now + oracle::testStartTime - epoch_offset); + Json::Value jv; + jv[jss::TransactionType] = jss::OracleSet; + jv[jss::Account] = to_string(account); + jv[jss::OracleDocumentID] = 1; + jv[jss::LastUpdateTime] = to_string( + duration_cast( + env.current()->info().closeTime.time_since_epoch()) + .count() + + epoch_offset.count() + 100); + jv[jss::PriceDataSeries] = Json::arrayValue; + jv[jss::Provider] = strHex(std::string{"provider"}); + jv[jss::AssetClass] = strHex(std::string{"currency"}); + + DataSeries const series = { + {"XRP", "US1", 740, 1}, + {"XRP", "US2", 750, 1}, + {"XRP", "US3", 740, 1}, + {"XRP", "US4", 750, 1}, + {"XRP", "US5", 740, 1}, + {"XRP", "US6", 750, 1}, + {"XRP", "US7", 740, 1}, + {"XRP", "US8", 750, 1}, + {"XRP", "US9", 740, 1}, + {"XRP", "U10", 750, 1}, + }; + + DataSeries actualSeries( + series.begin(), series.begin() + dataSeriesSize); + + Json::Value dataSeries(Json::arrayValue); + for (auto const& data : actualSeries) + { + Json::Value priceData; + Json::Value price; + price[jss::BaseAsset] = std::get<0>(data); + price[jss::QuoteAsset] = std::get<1>(data); + price[jss::AssetPrice] = std::get<2>(data); + price[jss::Scale] = std::get<3>(data); + priceData[jss::PriceData] = price; + dataSeries.append(priceData); + } + jv[jss::PriceDataSeries] = dataSeries; + return jv; + }; + + auto const oracleDelete = [&](Account const& account) { + Json::Value jv; + jv[jss::TransactionType] = jss::OracleDelete; + jv[jss::Account] = to_string(account); + jv[jss::OracleDocumentID] = 1; + return jv; + }; + + { + // OracleSet (reserve 1) + env(oracleSet(alice, 5), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // transfer sponsor + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // OracleDelete + env(oracleDelete(alice)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + { + // OracleSet (reserve 2) + env(oracleSet(alice, 6), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + + // transfer sponsor + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); + + // OracleDelete + env(oracleDelete(alice)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + { + // OracleSet (reserve 1->2, sponsor1 -> no-sponsor) + env(oracleSet(alice, 5), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // reserve 1->2 + env(oracleSet(alice, 6)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // OracleDelete + env(oracleDelete(alice)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + { + // OracleSet (reserve 1->2, sponsor1 -> sponsor2) + env(oracleSet(alice, 5), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // reserve 1->2 + env(oracleSet(alice, 6), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); + + // OracleDelete + env(oracleDelete(alice)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + { + // OracleSet (reserve 1->2, non-sponsor -> sponsor1) + env(oracleSet(alice, 5)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // reserve 1->2 + env(oracleSet(alice, 6), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + + // OracleDelete + env(oracleDelete(alice)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + for (bool isTwoOwnerCount : {false, true}) + { + // test sponsor transfer + auto const dataSeriesSize = isTwoOwnerCount ? 6 : 5; + auto const ocount = isTwoOwnerCount ? 2 : 1; + env(oracleSet(alice, dataSeriesSize), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == ocount); + + // transfer sponsor + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == ocount); + + // disolve sponsor + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } } void @@ -2189,7 +2439,7 @@ class Sponsor_test : public beast::unit_test::suite testNFTokenOffer(); testPayChan(); testPermissionedDomain(); - // testOracle(); + testOracle(); testSignerList(); testTrustSet(); // testVault(); diff --git a/src/test/jtx.h b/src/test/jtx.h index 79d92d03dab..62cb5829c4e 100644 --- a/src/test/jtx.h +++ b/src/test/jtx.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index d2e1cfa1ae8..2c52d28766d 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -183,13 +183,15 @@ SetOracle::preclaim(PreclaimContext const& ctx) } static bool -adjustOwnerCount(ApplyContext& ctx, int count) +adjustOwnerCount( + ApplyContext& ctx, + std::optional> const& sponsor, + int count) { if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) { - adjustOwnerCount( - ctx.view(), sleAccount, std::nullopt, count, ctx.journal); + adjustOwnerCount(ctx.view(), sleAccount, sponsor, count, ctx.journal); return true; } @@ -275,8 +277,34 @@ SetOracle::doApply() auto const newCount = pairs.size() > 5 ? 2 : 1; auto const adjust = newCount - oldCount; - if (adjust != 0 && !adjustOwnerCount(ctx_, adjust)) - return tefINTERNAL; // LCOV_EXCL_LINE + + if (adjust > 0) + { + // To continue receiving sponsorship from the same account after the + // OwnerCount increases from 1 to 2, it is necessary to sign with + // the sponsor decrease current sponsored owner count. + // Otherwise, the sponsorship will be deleted. + + auto const currentsponsor = + getLedgerEntryReserveSponsor(ctx_.view(), sle); + auto const newSponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + + // decrease current sponsored owner count + if (!adjustOwnerCount(ctx_, currentsponsor, -oldCount)) + return tefINTERNAL; // LCOV_EXCL_LINE + removeSponsorFromLedgerEntry(sle); + // increase new owner count + if (!adjustOwnerCount(ctx_, newSponsor, newCount)) + return tefINTERNAL; // LCOV_EXCL_LINE + addSponsorToLedgerEntry(sle, newSponsor); + } + else if (adjust < 0) + { + // decrease owner count + auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), sle); + if (!adjustOwnerCount(ctx_, sponsor, adjust)) + return tefINTERNAL; // LCOV_EXCL_LINE + } ctx_.view().update(sle); } @@ -321,9 +349,12 @@ SetOracle::doApply() (*sle)[sfOwnerNode] = *page; auto const count = series.size() > 5 ? 2 : 1; - if (!adjustOwnerCount(ctx_, count)) + auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + if (!adjustOwnerCount(ctx_, sponsor, count)) return tefINTERNAL; // LCOV_EXCL_LINE + addSponsorToLedgerEntry(sle, sponsor); + ctx_.view().insert(sle); } diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 330f145851d..03c20668f0e 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -112,6 +112,20 @@ getLedgerEntryOwner( }; } +template +inline std::uint32_t +getLedgerEntryOwnerCount(T const& sle) +{ + switch (sle->getType()) + { + case ltORACLE: { + return sle->getFieldArray(sfPriceDataSeries).size() > 5 ? 2 : 1; + } + default: + return 1; + } +}; + TER SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { @@ -225,6 +239,8 @@ SponsorshipTransfer::doApply() if (!ownerSle) return tefINTERNAL; // LCOV_EXCL_LINE + auto const ownerCountDelta = getLedgerEntryOwnerCount(objSle); + if (tx.isFieldPresent(sfSponsor)) { auto const sponsorObj = tx.getFieldObject(sfSponsor); @@ -235,7 +251,8 @@ SponsorshipTransfer::doApply() view().peek(keylet::account(oldSponsor))) { auto const newCount = - oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - 1; + oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - + ownerCountDelta; if (newCount == 0) oldSponsorSle->makeFieldAbsent(sfSponsoringOwnerCount); else @@ -249,14 +266,16 @@ SponsorshipTransfer::doApply() // update owner's sponsored count ownerSle->setFieldU32( sfSponsoredOwnerCount, - ownerSle->getFieldU32(sfSponsoredOwnerCount) + 1); + ownerSle->getFieldU32(sfSponsoredOwnerCount) + + ownerCountDelta); view().update(ownerSle); } // increment new sponsoring count auto const newSponsorSle = view().peek(keylet::account(newSponsor)); newSponsorSle->setFieldU32( sfSponsoringOwnerCount, - newSponsorSle->getFieldU32(sfSponsoringOwnerCount) + 1); + newSponsorSle->getFieldU32(sfSponsoringOwnerCount) + + ownerCountDelta); view().update(newSponsorSle); objSle->setAccountID(sfSponsorAccount, newSponsor); @@ -268,7 +287,7 @@ SponsorshipTransfer::doApply() auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); // decrement sponsored count auto const newCount = - accSle->getFieldU32(sfSponsoredOwnerCount) - 1; + accSle->getFieldU32(sfSponsoredOwnerCount) - ownerCountDelta; if (newCount == 0) accSle->makeFieldAbsent(sfSponsoredOwnerCount); else @@ -281,7 +300,8 @@ SponsorshipTransfer::doApply() { oldSponsorSle->setFieldU32( sfSponsoringOwnerCount, - oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - 1); + oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - + ownerCountDelta); view().update(oldSponsorSle); } From cc5e0634384d6d43d34ec4dfc3fe4dfc661d6f8c Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 19 Sep 2025 21:25:46 +0900 Subject: [PATCH 030/249] add Credential sponsor test --- src/test/app/Sponsor_test.cpp | 118 +++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 45 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index b852d91b070..05c52ece0c2 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1130,61 +1130,89 @@ class Sponsor_test : public beast::unit_test::suite env.close(); auto const credType = std::string("credType"); + auto const credTypeSlice = Slice(credType.data(), credType.size()); // CredentialsCreate - env(credentials::create(subject, issuer, credType), - credentials::uri("uri"), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + { + env(credentials::create(subject, issuer, credType), + credentials::uri("uri"), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, issuer) == 1); - BEAST_EXPECT(ownerCount(env, subject) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, issuer) == 1); + BEAST_EXPECT(ownerCount(env, subject) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // transfer sponsor - auto const keylet = keylet::credential( - subject, issuer, Slice(credType.data(), credType.size())); - env(sponsor::transfer(issuer, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + // transfer sponsor + auto const keylet = + keylet::credential(subject, issuer, credTypeSlice); + env(sponsor::transfer(issuer, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - BEAST_EXPECT(ownerCount(env, issuer) == 1); - BEAST_EXPECT(ownerCount(env, subject) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(ownerCount(env, issuer) == 1); + BEAST_EXPECT(ownerCount(env, subject) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - // CredentialsAccept - env(credentials::accept(subject, issuer, credType), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // CredentialsAccept + env(credentials::accept(subject, issuer, credType), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, issuer) == 0); - BEAST_EXPECT(ownerCount(env, subject) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(ownerCount(env, issuer) == 0); + BEAST_EXPECT(ownerCount(env, subject) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); - // CredentialsDelete - env(credentials::deleteCred(subject, subject, issuer, credType)); - env.close(); + // CredentialsDelete + env(credentials::deleteCred(subject, subject, issuer, credType)); + env.close(); - BEAST_EXPECT(ownerCount(env, issuer) == 0); - BEAST_EXPECT(ownerCount(env, subject) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(ownerCount(env, issuer) == 0); + BEAST_EXPECT(ownerCount(env, subject) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + { + // Accept Sponsored Credentials without sponsoring + env(credentials::create(subject, issuer, credType), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - // TODO: Accept Sponsored Credentials without sponsoring - // TODO: Self Accept Sponsored Credentials + BEAST_EXPECT(ownerCount(env, issuer) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + env(credentials::accept(subject, issuer, credType)); + env.close(); + + // sponsorship is removed + BEAST_EXPECT(ownerCount(env, issuer) == 0); + BEAST_EXPECT(ownerCount(env, subject) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT( + !env.le(keylet::credential(subject, issuer, credTypeSlice)) + ->isFieldPresent(sfSponsorAccount)); + + env(credentials::deleteCred(subject, subject, issuer, credType)); + env.close(); + } } void From 5dc63b64ebaf253a686b656460010c36e71ab3ac Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 20 Sep 2025 10:34:46 +0900 Subject: [PATCH 031/249] fix SponsorshipTransfer --- src/test/app/Sponsor_test.cpp | 10 +++++++++- src/xrpld/app/tx/detail/SponsorshipTransfer.cpp | 9 +++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 05c52ece0c2..04478744e0a 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -374,6 +374,14 @@ class Sponsor_test : public beast::unit_test::suite env(pay(alice, sponsor1, drops(1))); env.close(); + // Invalid Owner + env(sponsor::transfer(bob, checkId), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1), + ter(tecNO_PERMISSION)); + env.close(); + + // Valid Owner env(sponsor::transfer(alice, checkId), sponsor::as(sponsor1, tfSponsorReserve), sponsor::sig(sponsor1)); @@ -1530,7 +1538,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor auto const mptTokenKeylet = keylet::mptoken(mptid, bob); - env(sponsor::transfer(alice, mptTokenKeylet.key), + env(sponsor::transfer(bob, mptTokenKeylet.key), sponsor::as(sponsor2, tfSponsorReserve), sponsor::sig(sponsor2)); env.close(); diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 03c20668f0e..62c2d8d9fc4 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -144,9 +144,11 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (!sle) return tecNO_ENTRY; + auto const ownerCountDelta = getLedgerEntryOwnerCount(sle); + auto const owner = getLedgerEntryOwner(ctx.view, sle, ctx.tx[sfAccount]); - if (!owner) + if (!owner || owner != ctx.tx[sfAccount]) return tecNO_PERMISSION; if (newSponsor) @@ -166,14 +168,13 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) return tecNO_PERMISSION; } - // check account have sufficient balance + // check new sponsor have sufficient balance if (auto const ter = checkInsufficientReserve( ctx.view, accSle, accSle->getFieldAmount(sfBalance), newSponsor, - // TODO: address variable ownerCount like PriceOracle - 1); + ownerCountDelta); !isTesSuccess(ter)) return ter; } From 613fe48d545d50ddeb8cdc52ea58ccebbe794525 Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 20 Sep 2025 11:44:47 +0900 Subject: [PATCH 032/249] update account_objects for sponsorship --- include/xrpl/protocol/jss.h | 1 + src/test/rpc/AccountObjects_test.cpp | 37 ++++++++++++++++++++++- src/xrpld/rpc/detail/RPCHelpers.cpp | 28 ++++++++++++++--- src/xrpld/rpc/detail/RPCHelpers.h | 1 + src/xrpld/rpc/handlers/AccountObjects.cpp | 13 ++++++++ 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 07b9e589275..dc6f1be28bb 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -582,6 +582,7 @@ JSS(source_currencies); // in: PathRequest, RipplePathFind JSS(source_tag); // out: AccountChannels JSS(sponsee); // in: LedgerEntry JSS(sponsor); // in: LedgerEntry +JSS(sponsored); // in: AccountObjects JSS(stand_alone); // out: NetworkOPs JSS(standard_deviation); // out: get_aggregate_price JSS(start); // in: TxHistory diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 546bbe87156..ceb091e72af 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -630,6 +630,7 @@ class AccountObjects_test : public beast::unit_test::suite BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::amm), 0)); BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::did), 0)); BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::permissioned_domain), 0)); + BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::sponsorship), 0)); // we expect invalid field type reported for the following types BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::amendments))); @@ -983,6 +984,39 @@ class AccountObjects_test : public beast::unit_test::suite BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == seq + 1); } + { + // Create a sponsorship + env(sponsor::set( + alice, + gw, + tfSponsorshipSetRequireSignForFee, + 200, + XRP(100), + drops(10))); + env.close(); + + // Find the sponsorship. + for (auto acct : {alice, gw}) + { + Json::Value const resp = acctObjs(acct, jss::sponsorship); + BEAST_EXPECT(acctObjsIsSize(resp, 1)); + + auto const& sponsorship = + resp[jss::result][jss::account_objects][0u]; + + BEAST_EXPECT(sponsorship[sfOwner.jsonName] == alice.human()); + BEAST_EXPECT(sponsorship[sfSponsee.jsonName] == gw.human()); + BEAST_EXPECT( + sponsorship[sfFlags.jsonName].asUInt() == + tfSponsorshipSetRequireSignForFee); + BEAST_EXPECT( + sponsorship[sfReserveCount.jsonName].asUInt() == 200); + BEAST_EXPECT( + sponsorship[sfFeeAmount.jsonName].asUInt() == 100000000); + BEAST_EXPECT(sponsorship[sfMaxFee.jsonName].asUInt() == 10); + } + } + { // See how "deletion_blockers_only" handles gw's directory. Json::Value params; @@ -997,7 +1031,8 @@ class AccountObjects_test : public beast::unit_test::suite jss::NFTokenPage.c_str(), jss::RippleState.c_str(), jss::PayChannel.c_str(), - jss::PermissionedDomain.c_str()}; + jss::PermissionedDomain.c_str(), + jss::Sponsorship.c_str()}; std::sort(v.begin(), v.end()); return v; }(); diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index 52a69eb79e6..2133f1f2c24 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -157,6 +157,7 @@ getAccountObjects( uint256 dirIndex, uint256 entryIndex, std::uint32_t const limit, + std::optional const sponsored, Json::Value& jvResult) { // check if dirIndex is valid @@ -169,6 +170,13 @@ getAccountObjects( return it != typeFilter.end(); }; + auto sponsoredMatchesFilter = [](bool const sponsored, + std::optional const& sponsor) { + if (sponsored) + return sponsor.has_value(); + return !sponsor.has_value(); + }; + // if dirIndex != 0, then all NFTs have already been returned. only // iterate NFT pages if the filter says so AND dirIndex == 0 bool iterateNFTPages = @@ -297,11 +305,23 @@ getAccountObjects( { auto const sleNode = ledger.read(keylet::child(*iter)); - if (!typeFilter.has_value() || - typeMatchesFilter(typeFilter.value(), sleNode->getType())) - { + bool canAppend = true; + + if (typeFilter.has_value() && + !typeMatchesFilter(typeFilter.value(), sleNode->getType())) + canAppend = false; + + std::optional const sponsor = + sleNode->isFieldPresent(sfSponsorAccount) + ? sleNode->getAccountID(sfSponsorAccount) + : std::optional(std::nullopt); + + if (sponsored.has_value() && + !sponsoredMatchesFilter(sponsored.value(), sponsor)) + canAppend = false; + + if (canAppend) jvObjects.append(sleNode->getJson(JsonOptions::none)); - } if (++i == mlimit) { diff --git a/src/xrpld/rpc/detail/RPCHelpers.h b/src/xrpld/rpc/detail/RPCHelpers.h index 1d33d694596..a0af01baf3e 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.h +++ b/src/xrpld/rpc/detail/RPCHelpers.h @@ -109,6 +109,7 @@ getAccountObjects( uint256 dirIndex, uint256 entryIndex, std::uint32_t const limit, + std::optional const sponsored, Json::Value& jvResult); /** Get ledger by hash diff --git a/src/xrpld/rpc/handlers/AccountObjects.cpp b/src/xrpld/rpc/handlers/AccountObjects.cpp index 2b2496a1dde..6df319aad76 100644 --- a/src/xrpld/rpc/handlers/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/AccountObjects.cpp @@ -43,6 +43,7 @@ namespace ripple { type: // optional, defaults to all account objects types limit: // optional marker: // optional, resume previous query + sponsored: // optional, defaults to null } */ @@ -226,6 +227,7 @@ doAccountObjects(RPC::JsonContext& context) {jss::mptoken, ltMPTOKEN}, {jss::permissioned_domain, ltPERMISSIONED_DOMAIN}, {jss::vault, ltVAULT}, + {jss::sponsorship, ltSPONSORSHIP}, }; typeFilter.emplace(); @@ -284,6 +286,16 @@ doAccountObjects(RPC::JsonContext& context) return RPC::invalid_field_error(jss::marker); } + std::optional sponsored; + if (params.isMember(jss::sponsored)) + { + auto const& sponsoredJv = params[jss::sponsored]; + if (!sponsoredJv.isBool()) + return RPC::expected_field_error(jss::sponsored, "boolean"); + + sponsored = sponsoredJv.asBool(); + } + if (!RPC::getAccountObjects( *ledger, accountID, @@ -291,6 +303,7 @@ doAccountObjects(RPC::JsonContext& context) dirIndex, entryIndex, limit, + sponsored, result)) return RPC::invalid_field_error(jss::marker); From f7e1d4bbb9f3ac8d1cbf022f57b7a50fb3c9da4d Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 20 Sep 2025 12:20:29 +0900 Subject: [PATCH 033/249] add AccountTx tests for Sponsorship --- src/test/rpc/AccountTx_test.cpp | 82 +++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 82809b5c5b5..b27646dc2d4 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -835,6 +835,87 @@ class AccountTx_test : public beast::unit_test::suite checkAliceAcctTx(9, jss::Payment); } + void + testSponsorship() + { + // test all sponsored transactions are in sponsor and sponsee's account + // tx list + testcase("Sponsorship"); + + using namespace test::jtx; + Env env(*this); + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, sponsor, sponsor2); + env.close(); + + // check the latest sponsorship-related txn is in account tx list + auto const checkTx = [&](Account const& account, + Json::StaticString txType) { + Json::Value params; + params[jss::account] = account.human(); + params[jss::limit] = 100; + auto const jv = + env.rpc("json", "account_tx", to_string(params))[jss::result]; + + auto const& tx0(jv[jss::transactions][0u][jss::tx]); + BEAST_EXPECT(tx0[jss::TransactionType] == txType); + + std::string const txHash{ + env.tx()->getJson(JsonOptions::none)[jss::hash].asString()}; + BEAST_EXPECT(tx0[jss::hash] == txHash); + }; + + // fee sponsorship + env(noop(alice), + sponsor::as(sponsor, tfSponsorFee), + sponsor::sig(sponsor)); + env.close(); + checkTx(alice, jss::AccountSet); + checkTx(sponsor, jss::AccountSet); + + // set sponsor + env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); + env.close(); + checkTx(alice, jss::SponsorshipSet); + checkTx(sponsor, jss::SponsorshipSet); + + // create a ticket with sponsor + auto const seq = env.seq(alice); + env(ticket::create(alice, 1), sponsor::as(sponsor, tfSponsorReserve)); + env.close(); + checkTx(alice, jss::TicketCreate); + checkTx(sponsor, jss::TicketCreate); + + // transfer object sponsorship + env(sponsor::transfer(alice, keylet::ticket(alice, seq + 1).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + checkTx(alice, jss::SponsorshipTransfer); + checkTx(sponsor, jss::SponsorshipTransfer); + checkTx(sponsor2, jss::SponsorshipTransfer); + + // use a ticket + env(noop(alice), + ticket::use(seq + 1), + sponsor::as(sponsor, tfSponsorFee), + sponsor::sig(sponsor)); + env.close(); + checkTx(alice, jss::AccountSet); + checkTx(sponsor, jss::AccountSet); + checkTx(sponsor2, jss::AccountSet); + + // account sponsorship + env(sponsor::transfer(alice), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + checkTx(alice, jss::SponsorshipTransfer); + checkTx(sponsor, jss::SponsorshipTransfer); + } + public: void run() override @@ -844,6 +925,7 @@ class AccountTx_test : public beast::unit_test::suite testContents(); testAccountDelete(); testMPT(); + testSponsorship(); } }; BEAST_DEFINE_TESTSUITE(AccountTx, rpc, ripple); From fed56b2eebc2bee659962eecb0ac5332ae28d678 Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 20 Sep 2025 14:29:33 +0900 Subject: [PATCH 034/249] address SponsorAccount/Sponsee field changes --- .../xrpl/protocol/detail/transactions.macro | 2 +- src/test/app/Sponsor_test.cpp | 157 +++++++++++++----- src/test/jtx/impl/sponsor.cpp | 20 +-- src/test/jtx/sponsor.h | 20 ++- src/test/rpc/AccountObjects_test.cpp | 12 +- src/test/rpc/AccountTx_test.cpp | 4 +- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 47 ++++-- 7 files changed, 181 insertions(+), 81 deletions(-) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index b66f09f6847..f7135fe9196 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -534,7 +534,7 @@ TRANSACTION(ttSPONSORSHIP_TRANSFER, 72, SponsorshipTransfer, Delegation::notDele /** This transaction create sponsorship object */ TRANSACTION(ttSPONSORSHIP_SET, 73, SponsorshipSet, Delegation::notDelegatable, ({ {sfSponsorAccount, soeOPTIONAL}, - {sfSponsee, soeREQUIRED}, + {sfSponsee, soeOPTIONAL}, {sfFeeAmount, soeOPTIONAL}, {sfMaxFee, soeOPTIONAL}, {sfReserveCount, soeOPTIONAL}, diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 04478744e0a..7f07071c132 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -45,7 +45,7 @@ class Sponsor_test : public beast::unit_test::suite ter(temDISABLED)); env(sponsor::transfer(alice), ter(temDISABLED)); - env(sponsor::set(sponsor, alice, 0), ter(temDISABLED)); + env(sponsor::set(sponsor, 0), ter(temDISABLED)); } void @@ -70,22 +70,22 @@ class Sponsor_test : public beast::unit_test::suite // Invalid flags { - env(sponsor::set( - sponsor, alice, ~tfSponsorshipSetMask - tfInnerBatchTxn), + env(sponsor::set(sponsor, ~tfSponsorshipSetMask - tfInnerBatchTxn), + sponsor::sponseeAcc(alice), ter(temINVALID_FLAG)); env(sponsor::set( sponsor, - alice, tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee), + sponsor::sponseeAcc(alice), ter(temINVALID_FLAG)); env(sponsor::set( sponsor, - alice, tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve), + sponsor::sponseeAcc(alice), ter(temINVALID_FLAG)); for (auto flag : @@ -94,38 +94,46 @@ class Sponsor_test : public beast::unit_test::suite tfSponsorshipSetRequireSignForReserve, tfSponsorshipClearRequireSignForReserve}) { - env(sponsor::set(sponsor, alice, tfDeleteObject | flag), + env(sponsor::set(sponsor, tfDeleteObject | flag), + sponsor::sponseeAcc(alice), ter(temINVALID_FLAG)); } } - // invalid SponsorAccount - env(sponsor::set(alice, sponsor, tfDeleteObject), + // invalid SponsorAccount / Sponsee + // Account = Sponsor + env(sponsor::set(alice, tfDeleteObject), sponsor::sponsorAcc(alice), ter(temMALFORMED)); - env(sponsor::set(alice, sponsor, tfDeleteObject), - sponsor::sponsorAcc(bob), + // Account = Sponsee + env(sponsor::set(alice, tfDeleteObject), + sponsor::sponseeAcc(alice), ter(temMALFORMED)); - env(sponsor::set(alice, alice, 0), + // Both Sponsor and Sponsee are specified + env(sponsor::set(alice, 0), sponsor::sponsorAcc(sponsor), + sponsor::sponseeAcc(alice), ter(temMALFORMED)); - // Invalid Sponsee - env(sponsor::set(sponsor, sponsor, 0), ter(temMALFORMED)); - // Invalid feeAmount for (auto amt : {XRP(-1), XRP(0), USD(1)}) { - env(sponsor::set_fee(sponsor, alice, 0, amt), ter(temBAD_AMOUNT)); + env(sponsor::set_fee(sponsor, 0, amt), + sponsor::sponseeAcc(alice), + ter(temBAD_AMOUNT)); } // Invalid reserveCount - env(sponsor::set_reserve(sponsor, alice, 0, 0), ter(temMALFORMED)); + env(sponsor::set_reserve(sponsor, 0, 0), + sponsor::sponseeAcc(alice), + ter(temMALFORMED)); // Invalid Delete operation - env(sponsor::set_reserve(sponsor, alice, tfDeleteObject, 1), + env(sponsor::set_reserve(sponsor, tfDeleteObject, 1), + sponsor::sponseeAcc(alice), ter(temMALFORMED)); - env(sponsor::set_fee(sponsor, alice, tfDeleteObject, XRP(1)), + env(sponsor::set_fee(sponsor, tfDeleteObject, XRP(1)), + sponsor::sponseeAcc(alice), ter(temMALFORMED)); // TODO: test MaxFee with tfDeleteObject @@ -134,15 +142,21 @@ class Sponsor_test : public beast::unit_test::suite // // Invalid Sponsee - env(sponsor::set(sponsor, noFunded, 0), ter(tecNO_DST)); + env(sponsor::set(sponsor, 0), + sponsor::sponseeAcc(noFunded), + ter(tecNO_DST)); // Invalid Delete operation (not found) - env(sponsor::set(sponsor, alice, tfDeleteObject), ter(tecNO_ENTRY)); + env(sponsor::set(sponsor, tfDeleteObject), + sponsor::sponseeAcc(alice), + ter(tecNO_ENTRY)); // DisallowIncomingSponsor: tested in other testcase // create sponsor to use above tests - env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); } @@ -246,6 +260,49 @@ class Sponsor_test : public beast::unit_test::suite ter(tesSUCCESS)); } + void + testSimpleSponsorshipSet() + { + testcase("Simple SponsorshipSet"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + env.close(); + + // + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForFee | + tfSponsorshipSetRequireSignForReserve, + 100, + XRP(100), + XRP(1)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + // delete from sponsor + env(sponsor::del(sponsor), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env.close(); + + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForFee | + tfSponsorshipSetRequireSignForReserve, + 100, + XRP(100), + XRP(1)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + // delete from sponsee + env(sponsor::del(alice), sponsor::sponsorAcc(sponsor), ter(tesSUCCESS)); + env.close(); + } + void testTransferSponsor() { @@ -588,7 +645,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); } - env(sponsor::set_fee(sponsor, alice, 0, XRP(100))); + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::sponseeAcc(alice)); env.close(); { @@ -635,9 +693,10 @@ class Sponsor_test : public beast::unit_test::suite } // reset FeeAmount and MaxFee - env(sponsor::del(sponsor, alice)); + env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::set_fee(sponsor, alice, 0, XRP(10), XRP(1))); + env(sponsor::set_fee(sponsor, 0, XRP(10), XRP(1)), + sponsor::sponseeAcc(alice)); env.close(); { @@ -695,7 +754,8 @@ class Sponsor_test : public beast::unit_test::suite // set flag env(sponsor::set_fee( - sponsor, alice, tfSponsorshipSetRequireSignForFee, XRP(10))); + sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), + sponsor::sponseeAcc(alice)); env.close(); env(pay(alice, bob, XRP(100)), @@ -706,7 +766,8 @@ class Sponsor_test : public beast::unit_test::suite // clear flag env(sponsor::set_fee( - sponsor, alice, tfSponsorshipClearRequireSignForFee, XRP(10))); + sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), + sponsor::sponseeAcc(alice)); env.close(); env(pay(alice, bob, XRP(100)), @@ -785,7 +846,8 @@ class Sponsor_test : public beast::unit_test::suite // set flag env(sponsor::set_reserve( - sponsor, alice, tfSponsorshipSetRequireSignForReserve, 10)); + sponsor, tfSponsorshipSetRequireSignForReserve, 10), + sponsor::sponseeAcc(alice)); env.close(); env(check::create(alice, bob, XRP(100)), @@ -796,7 +858,8 @@ class Sponsor_test : public beast::unit_test::suite // clear flag env(sponsor::set_reserve( - sponsor, alice, tfSponsorshipClearRequireSignForReserve, 1)); + sponsor, tfSponsorshipClearRequireSignForReserve, 1), + sponsor::sponseeAcc(alice)); env.close(); env(check::create(alice, bob, XRP(100)), @@ -2273,7 +2336,8 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Create sponsor should fail - env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), ter(tecNO_PERMISSION)); env.close(); @@ -2282,7 +2346,9 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Create sponsor - env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); // set flag @@ -2290,11 +2356,15 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Update sponsor should success - env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); - // Delete sponsor shoud success - env(sponsor::set(sponsor, alice, tfDeleteObject), ter(tesSUCCESS)); + // Delete sponsor should success + env(sponsor::set(sponsor, tfDeleteObject), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); } void @@ -2313,7 +2383,8 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // set sponsor - env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), ter(tesSUCCESS)); env.close(); @@ -2389,16 +2460,19 @@ class Sponsor_test : public beast::unit_test::suite env.close(); auto const testFeePermission = [&](TER result) { // FeeAmount - env(sponsor::set(alice, bob, 0, std::nullopt, XRP(100)), + env(sponsor::set(alice, 0, std::nullopt, XRP(100)), + sponsor::sponseeAcc(bob), delegate::as(carol), ter(result)); // MaxFee env(sponsor::set( - alice, bob, 0, std::nullopt, std::nullopt, XRP(100)), + alice, 0, std::nullopt, std::nullopt, XRP(100)), + sponsor::sponseeAcc(bob), delegate::as(carol), ter(result)); // SetRequireSignForFee flag - env(sponsor::set(alice, bob, tfSponsorshipSetRequireSignForFee), + env(sponsor::set(alice, tfSponsorshipSetRequireSignForFee), + sponsor::sponseeAcc(bob), delegate::as(carol), ter(result)); env.close(); @@ -2430,12 +2504,13 @@ class Sponsor_test : public beast::unit_test::suite auto const testReservePermission = [&](TER result) { // ReserveCount - env(sponsor::set(alice, bob, 0, 100), + env(sponsor::set(alice, 0, 100), + sponsor::sponseeAcc(bob), delegate::as(carol), ter(result)); // SetRequireSignForReserve flag - env(sponsor::set( - alice, bob, tfSponsorshipSetRequireSignForReserve), + env(sponsor::set(alice, tfSponsorshipSetRequireSignForReserve), + sponsor::sponseeAcc(bob), delegate::as(carol), ter(result)); env.close(); @@ -2492,6 +2567,8 @@ class Sponsor_test : public beast::unit_test::suite testMultiSigning(); // testInvalidSigninig(); // borh TxnSignature and Signers are present // -> error + testSimpleSponsorshipSet(); + testTransferSponsor(); testSponsorFee(); testSponsorAccount(); diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 551a408ae10..befaaa337b5 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -33,7 +33,6 @@ namespace sponsor { Json::Value set(jtx::Account const& account, - jtx::Account const& sponsee, uint32_t flags, std::optional reserveCount, std::optional feeAmount, @@ -42,7 +41,6 @@ set(jtx::Account const& account, Json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); - jv[sfSponsee.jsonName] = sponsee.human(); jv[sfFlags.jsonName] = flags; if (reserveCount) jv[sfReserveCount.jsonName] = *reserveCount; @@ -56,7 +54,6 @@ set(jtx::Account const& account, Json::Value set_fee( jtx::Account const& account, - jtx::Account const& sponsee, uint32_t flags, STAmount feeAmount, std::optional maxFee) @@ -64,7 +61,6 @@ set_fee( Json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); - jv[sfSponsee.jsonName] = sponsee.human(); jv[sfFlags.jsonName] = flags; jv[sfFeeAmount.jsonName] = feeAmount.getJson(JsonOptions::none); if (maxFee) @@ -73,28 +69,22 @@ set_fee( } Json::Value -set_reserve( - jtx::Account const& account, - jtx::Account const& sponsee, - uint32_t flags, - uint32_t reserveCount) +set_reserve(jtx::Account const& account, uint32_t flags, uint32_t reserveCount) { Json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); - jv[sfSponsee.jsonName] = sponsee.human(); jv[sfFlags.jsonName] = flags; jv[sfReserveCount.jsonName] = reserveCount; return jv; } Json::Value -del(jtx::Account const& account, jtx::Account const& sponsee) +del(jtx::Account const& account) { Json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); - jv[sfSponsee.jsonName] = sponsee.human(); jv[sfFlags.jsonName] = tfDeleteObject; return jv; } @@ -116,6 +106,12 @@ sponsorAcc::operator()(Env& env, JTx& jt) const jt.jv[sfSponsorAccount.jsonName] = sponsor_.human(); } +void +sponseeAcc::operator()(Env& env, JTx& jt) const +{ + jt.jv[sfSponsee.jsonName] = sponsee_.human(); +} + void as::operator()(Env& env, JTx& jt) const { diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index faab0bfb8c2..db350207c8e 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -31,7 +31,6 @@ namespace sponsor { Json::Value set(jtx::Account const& account, - jtx::Account const& sponsee, std::uint32_t flags, std::optional reserveCount = std::nullopt, std::optional feeAmount = std::nullopt, @@ -40,7 +39,6 @@ set(jtx::Account const& account, Json::Value set_fee( jtx::Account const& account, - jtx::Account const& sponsee, std::uint32_t flags, STAmount feeAmount, std::optional maxFee = std::nullopt); @@ -48,12 +46,11 @@ set_fee( Json::Value set_reserve( jtx::Account const& account, - jtx::Account const& sponsee, std::uint32_t flags, std::uint32_t reserveCount); Json::Value -del(jtx::Account const& account, jtx::Account const& sponsee); +del(jtx::Account const& account); Json::Value transfer( @@ -73,6 +70,21 @@ struct sponsorAcc void operator()(jtx::Env&, jtx::JTx& jtx) const; }; + +struct sponseeAcc +{ +private: + jtx::Account sponsee_; + +public: + sponseeAcc(jtx::Account const& account) : sponsee_(account) + { + } + + void + operator()(jtx::Env&, jtx::JTx& jtx) const; +}; + struct as { private: diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index ceb091e72af..0dd346d4f7d 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -987,12 +987,12 @@ class AccountObjects_test : public beast::unit_test::suite { // Create a sponsorship env(sponsor::set( - alice, - gw, - tfSponsorshipSetRequireSignForFee, - 200, - XRP(100), - drops(10))); + alice, + tfSponsorshipSetRequireSignForFee, + 200, + XRP(100), + drops(10)), + sponsor::sponseeAcc(gw)); env.close(); // Find the sponsorship. diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index b27646dc2d4..8e500cc865a 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -876,7 +876,9 @@ class AccountTx_test : public beast::unit_test::suite checkTx(sponsor, jss::AccountSet); // set sponsor - env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); checkTx(alice, jss::SponsorshipSet); checkTx(sponsor, jss::SponsorshipSet); diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 352d25cc2a9..38af340930a 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -36,7 +36,7 @@ SponsorshipSet::preflight(PreflightContext const& ctx) // check Flags { - if (ctx.tx.getFlags() & tfSponsorshipSetMask) + if (ctx.tx.isFlag(tfSponsorshipSetMask)) return temINVALID_FLAG; if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForFee) && @@ -59,24 +59,22 @@ SponsorshipSet::preflight(PreflightContext const& ctx) } } - if (ctx.tx.isFieldPresent(sfSponsorAccount)) - { - // SponsorAccount is used when sponsee Deleting ltSponsorship - // Account => Sponsee of Sponsorshop - // SponsorAccount => Sponsor of Sponsorshop - // Sponsee => Sponsee of Sponsorshop - if (ctx.tx.getAccountID(sfAccount) == - ctx.tx.getAccountID(sfSponsorAccount) || - ctx.tx.getAccountID(sfSponsee) != - ctx.tx.getAccountID(sfSponsorAccount) || - !ctx.tx.isFlag(tfDeleteObject)) - return temMALFORMED; - } + if ((ctx.tx.isFieldPresent(sfSponsorAccount) && + ctx.tx.isFieldPresent(sfSponsee)) || + (!ctx.tx.isFieldPresent(sfSponsorAccount) && + !ctx.tx.isFieldPresent(sfSponsee))) + return temMALFORMED; + + // if (ctx.tx.isFieldPresent(sfSponsorAccount) && + // !ctx.tx.isFlag(tfDeleteObject)) + // return temMALFORMED; auto const sponsor = ctx.tx.isFieldPresent(sfSponsorAccount) ? ctx.tx.getAccountID(sfSponsorAccount) : ctx.tx.getAccountID(sfAccount); - auto const sponsee = ctx.tx.getAccountID(sfSponsee); + auto const sponsee = ctx.tx.isFieldPresent(sfSponsee) + ? ctx.tx.getAccountID(sfSponsee) + : ctx.tx.getAccountID(sfAccount); if (sponsee == sponsor) return temMALFORMED; @@ -119,6 +117,11 @@ SponsorshipSet::preflight(PreflightContext const& ctx) ctx.tx.isFieldPresent(sfMaxFee)) return temMALFORMED; } + else + { + if (!ctx.tx.isFieldPresent(sfSponsee)) + return temMALFORMED; + } return preflight2(ctx); } @@ -165,7 +168,12 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) auto const sponsor = ctx.tx.isFieldPresent(sfSponsorAccount) ? ctx.tx.getAccountID(sfSponsorAccount) : ctx.tx.getAccountID(sfAccount); - auto const sponsee = ctx.tx[sfSponsee]; + auto const sponsee = ctx.tx.isFieldPresent(sfSponsee) + ? ctx.tx.getAccountID(sfSponsee) + : ctx.tx.getAccountID(sfAccount); + + if (sponsee == sponsor) + return tecINTERNAL; // LCOV_EXCL_LINE // check Sponsee auto const sponseeSle = ctx.view.read(keylet::account(sponsee)); @@ -188,12 +196,17 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) TER SponsorshipSet::doApply() { - auto const sponseeAcc = ctx_.tx[sfSponsee]; + auto const sponseeAcc = ctx_.tx.isFieldPresent(sfSponsee) + ? ctx_.tx.getAccountID(sfSponsee) + : ctx_.tx.getAccountID(sfAccount); auto const sponsorAcc = ctx_.tx.isFieldPresent(sfSponsorAccount) ? ctx_.tx.getAccountID(sfSponsorAccount) : account_; + if (sponseeAcc == sponsorAcc) + return tecINTERNAL; // LCOV_EXCL_LINE + auto const keylet = keylet::sponsor(sponsorAcc, sponseeAcc); auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorAcc)); From d4947386d736456309fdef1affea880d109d5be9 Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 20 Sep 2025 17:03:20 +0900 Subject: [PATCH 035/249] Fully Support NFTokenMint/Burn --- src/test/app/Sponsor_test.cpp | 112 ++++++++++++++---- .../app/tx/detail/NFTokenAcceptOffer.cpp | 5 +- src/xrpld/app/tx/detail/NFTokenMint.cpp | 5 +- src/xrpld/app/tx/detail/NFTokenUtils.cpp | 74 ++++++++---- src/xrpld/app/tx/detail/NFTokenUtils.h | 6 +- 5 files changed, 148 insertions(+), 54 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 7f07071c132..1ffcf23e8a5 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1644,26 +1644,75 @@ class Sponsor_test : public beast::unit_test::suite void testNFToken() { - // testcase("NFToken"); - // using namespace test::jtx; - // Env env{*this, testable_amendments()}; - // Account const alice("alice"); - // Account const bob("bob"); - // Account const sponsor("sponsor"); - // Account const sponsor2("sponsor2"); - - // env.fund(XRP(1000000), alice, bob, sponsor); - // env.close(); + testcase("NFToken"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); - // // NFTokenMint - // env(token::mint(alice), - // sponsor::as(sponsor, tfSponsorReserve), - // sponsor::sig(sponsor)); - // env.close(); + { + Env env{*this, testable_amendments()}; - // BEAST_EXPECT(ownerCount(env, alice) == 1); - // BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // NFTokenMint + uint256 const nftId{token::getNextID(env, alice, 0)}; + env(token::mint(alice), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // NFTokenBurn + env(token::burn(alice, nftId)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + { + // multiple nft page process + Env env{*this, testable_amendments()}; + + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + auto const nftCount = 200; + + // NFTokenMint + for (auto i = 0; i < nftCount; i++) + { + env(token::mint(alice), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + } + env.close(); + + BEAST_EXPECT( + ownerCount(env, alice) == sponsoredOwnerCount(env, alice)); + BEAST_EXPECT( + sponsoredOwnerCount(env, alice) == + sponsoringOwnerCount(env, sponsor)); + + // NFTokenBurn + for (auto i = 0; i < nftCount; i++) + { + auto const nftId = token::getID(env, alice, 0, i, 0, 0); + env(token::burn(alice, nftId)); + } + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } } void @@ -1694,7 +1743,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 1); // NFTokenOfferCreate - uint256 const offerIndex = + uint256 const offerIndex1 = keylet::nftoffer(alice, env.seq(alice)).key; env(token::createOffer(alice, nftId, XRP(1)), token::destination(bob), @@ -1707,19 +1756,32 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + uint256 const offerIndex2 = + keylet::nftoffer(alice, env.seq(alice)).key; + env(token::createOffer(alice, nftId, XRP(1)), + token::destination(bob), + txflags(tfSellNFToken), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 3); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + // transfer sponsor - env(sponsor::transfer(alice, offerIndex), + env(sponsor::transfer(alice, offerIndex1), sponsor::as(sponsor2, tfSponsorReserve), sponsor::sig(sponsor2)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(ownerCount(env, alice) == 3); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); // NFTokenOfferCancel - env(token::cancelOffer(alice, {offerIndex})); + env(token::cancelOffer(alice, {offerIndex1, offerIndex2})); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2546,7 +2608,7 @@ class Sponsor_test : public beast::unit_test::suite testDID(); testEscrow(); testMPToken(); - // testNFToken(); + testNFToken(); testNFTokenOffer(); testPayChan(); testPermissionedDomain(); diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp index d145f4b5be5..ba2d0769d6c 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp @@ -445,8 +445,9 @@ NFTokenAcceptOffer::transferNFToken( std::uint32_t const buyerOwnerCountBefore = sleBuyer->getFieldU32(sfOwnerCount); - auto const insertRet = - nft::insertToken(view(), buyer, std::move(tokenAndPage->token)); + auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto const insertRet = nft::insertToken( + view(), buyer, sponsor, std::move(tokenAndPage->token)); // if fixNFTokenReserve is enabled, check if the buyer has sufficient // reserve to own a new object, if their OwnerCount changed. diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/xrpld/app/tx/detail/NFTokenMint.cpp index 313111f08a9..f14377f045d 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/xrpld/app/tx/detail/NFTokenMint.cpp @@ -319,8 +319,9 @@ NFTokenMint::doApply() object.setFieldVL(sfURI, *uri); }); - if (TER const ret = - nft::insertToken(ctx_.view(), account_, std::move(newToken)); + auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + if (TER const ret = nft::insertToken( + ctx_.view(), account_, sponsor, std::move(newToken)); ret != tesSUCCESS) return ret; diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.cpp b/src/xrpld/app/tx/detail/NFTokenUtils.cpp index 6df2a053b69..be5ed513ba4 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.cpp +++ b/src/xrpld/app/tx/detail/NFTokenUtils.cpp @@ -66,8 +66,13 @@ static std::shared_ptr getPageForToken( ApplyView& view, AccountID const& owner, + std::optional const& sponsor, uint256 const& id, - std::function const& createCallback) + std::function const&, + AccountID const&, + std::optional const&)> const& createCallback) { auto const base = keylet::nftpage_min(owner); auto const first = keylet::nftpage(base, id); @@ -87,7 +92,7 @@ getPageForToken( cp = std::make_shared(last); cp->setFieldArray(sfNFTokens, arr); view.insert(cp); - createCallback(view, owner); + createCallback(view, cp, owner, sponsor); return cp; } @@ -215,7 +220,7 @@ getPageForToken( cp->setFieldH256(sfPreviousPageMin, np->key()); view.update(cp); - createCallback(view, owner); + createCallback(view, np, owner, sponsor); // fixNFTokenDirV1 corrects a bug in the initial implementation that // would put an NFT in the wrong page. The problem was caused by an @@ -277,7 +282,11 @@ changeTokenURI( /** Insert the token in the owner's token directory. */ TER -insertToken(ApplyView& view, AccountID owner, STObject&& nft) +insertToken( + ApplyView& view, + AccountID owner, + std::optional const& sponsor, + STObject&& nft) { XRPL_ASSERT( nft.isFieldPresent(sfNFTokenID), @@ -289,14 +298,22 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft) std::shared_ptr page = getPageForToken( view, owner, + sponsor, nft[sfNFTokenID], - [](ApplyView& view, AccountID const& owner) { + [](ApplyView& view, + std::shared_ptr const& newPage, + AccountID const& owner, + std::optional const& sponsor) { + std::optional> const sponsorSle = sponsor + ? view.peek(keylet::account(*sponsor)) + : std::optional>{std::nullopt}; adjustOwnerCount( view, view.peek(keylet::account(owner)), - std::nullopt, + sponsorSle, 1, beast::Journal{beast::Journal::getNullSink()}); + addSponsorToLedgerEntry(newPage, sponsorSle); }); if (!page) @@ -450,22 +467,25 @@ removeToken( curr->setFieldArray(sfNFTokens, arr); view.update(curr); - int cnt = 0; - if (prev && mergePages(view, prev, curr)) - cnt--; + { + auto const sponsor = getLedgerEntryReserveSponsor(view, prev); + adjustOwnerCount( + view, + view.peek(keylet::account(owner)), + sponsor, + -1, + beast::Journal{beast::Journal::getNullSink()}); + } if (next && mergePages(view, curr, next)) - cnt--; - - if (cnt != 0) { auto const sponsor = getLedgerEntryReserveSponsor(view, curr); adjustOwnerCount( view, view.peek(keylet::account(owner)), sponsor, - cnt, + -1, beast::Journal{beast::Journal::getNullSink()}); } @@ -501,7 +521,7 @@ removeToken( curr->makeFieldAbsent(sfPreviousPageMin); } - auto const sponsor = getLedgerEntryReserveSponsor(view, curr); + auto const sponsor = getLedgerEntryReserveSponsor(view, prev); adjustOwnerCount( view, view.peek(keylet::account(owner)), @@ -535,9 +555,15 @@ removeToken( view.update(next); } - view.erase(curr); + auto const sponsor = getLedgerEntryReserveSponsor(view, curr); + adjustOwnerCount( + view, + view.peek(keylet::account(owner)), + getLedgerEntryReserveSponsor(view, curr), + -1, + beast::Journal{beast::Journal::getNullSink()}); - int cnt = 1; + view.erase(curr); // Since we're here, try to consolidate the previous and current pages // of the page we removed (if any) into one. mergePages() _should_ @@ -552,14 +578,14 @@ removeToken( view, view.peek(Keylet(ltNFTOKEN_PAGE, prev->key())), view.peek(Keylet(ltNFTOKEN_PAGE, next->key())))) - cnt++; - - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - std::nullopt, - -1 * cnt, - beast::Journal{beast::Journal::getNullSink()}); + { + adjustOwnerCount( + view, + view.peek(keylet::account(owner)), + getLedgerEntryReserveSponsor(view, prev), + -1, + beast::Journal{beast::Journal::getNullSink()}); + } return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.h b/src/xrpld/app/tx/detail/NFTokenUtils.h index e0e276db27b..2b3a1e47972 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.h +++ b/src/xrpld/app/tx/detail/NFTokenUtils.h @@ -70,7 +70,11 @@ findTokenAndPage( /** Insert the token in the owner's token directory. */ TER -insertToken(ApplyView& view, AccountID owner, STObject&& nft); +insertToken( + ApplyView& view, + AccountID owner, + std::optional const& sponsor, + STObject&& nft); /** Remove the token from the owner's token directory. */ TER From ea72198a1dbdf38b6d6d026ee812c8ffe63e01c5 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 13:42:05 +0900 Subject: [PATCH 036/249] Transfer Trustline Sponsorship --- src/test/app/Sponsor_test.cpp | 53 +++++++++++----- .../app/tx/detail/SponsorshipTransfer.cpp | 62 +++++++++++++++++-- 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 1ffcf23e8a5..74d6e772a1b 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2299,16 +2299,16 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // // transfer sponsor - // env(sponsor::transfer(alice, keylet::signers(alice).key), - // sponsor::as(sponsor2, tfSponsorReserve), - // sponsor::sig(sponsor2)); - // env.close(); + // transfer sponsor + env(sponsor::transfer(alice, keylet::signers(alice).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - // BEAST_EXPECT(ownerCount(env, alice) == 1); - // BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); // Delete env(signers(alice, none)); @@ -2329,8 +2329,9 @@ class Sponsor_test : public beast::unit_test::suite Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, bob, sponsor); + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); auto const& highAcc = alice > bob ? alice : bob; @@ -2352,11 +2353,33 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + auto const line = env.le(keylet::line(user, issuer, USD.currency)); + BEAST_EXPECT( + line->getAccountID( + isIssuerHigh ? sfLowSponsorAccount + : sfHighSponsorAccount) == sponsor.id()); + BEAST_EXPECT(!line->isFieldPresent( + isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); + + // transfer sponsor + env(sponsor::transfer( + user, keylet::line(user, issuer, USD.currency).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, user) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + auto const line2 = env.le(keylet::line(user, issuer, USD.currency)); BEAST_EXPECT( - env.le(keylet::line(user, issuer, USD.currency)) - ->getAccountID( - isIssuerHigh ? sfLowSponsorAccount - : sfHighSponsorAccount) == sponsor.id()); + line2->getAccountID( + isIssuerHigh ? sfLowSponsorAccount + : sfHighSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(!line2->isFieldPresent( + isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); // delete TrustLine env(trust(user, USD(0))); @@ -2368,7 +2391,6 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::line(user, issuer, USD.currency))); } - // TODO: transfer sponsor } void @@ -2598,6 +2620,7 @@ class Sponsor_test : public beast::unit_test::suite void testSponsorReserve() { + // TODO: add checks fo InsufficientReserve for Sponsoring ledger entry testRequireFlag(); testCheck(); testOffer(); diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index c9ce9285722..84442ae3627 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -95,7 +95,23 @@ getLedgerEntryOwner( uint256 const& key = sle->key(); return AccountID::fromVoid(key.data()); } - // case ltRIPPLE_STATE: + case ltRIPPLE_STATE: { + if (sle->isFlag(lsfHighReserve)) + { + auto const highAccount = + sle->getFieldAmount(sfHighLimit).getIssuer(); + if (highAccount == account) + return highAccount; + } + if (sle->isFlag(lsfLowReserve)) + { + auto const lowAccount = + sle->getFieldAmount(sfLowLimit).getIssuer(); + if (lowAccount == account) + return lowAccount; + } + return std::nullopt; + } case ltACCOUNT_ROOT: { // AccountRoot is not supported for object sponsorship return std::nullopt; @@ -126,6 +142,37 @@ getLedgerEntryOwnerCount(T const& sle) } }; +template +inline SF_ACCOUNT const& +getLedgerEntrySponsorField(T const& sle, AccountID const& owner) +{ + switch (sle->getType()) + { + case ltRIPPLE_STATE: { + if (sle->isFlag(lsfHighReserve)) + { + auto const highAccount = + sle->getFieldAmount(sfHighLimit).getIssuer(); + if (highAccount == owner) + return sfHighSponsorAccount; + } + if (sle->isFlag(lsfLowReserve)) + { + auto const lowAccount = + sle->getFieldAmount(sfLowLimit).getIssuer(); + if (lowAccount == owner) + return sfLowSponsorAccount; + } + XRPL_ASSERT( + false, + "Should not happen. Owner should be checked before calling " + "this function."); + } + default: + return sfSponsorAccount; + } +}; + TER SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { @@ -151,11 +198,14 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (!owner || owner != ctx.tx[sfAccount]) return tecNO_PERMISSION; + auto const& sponsorField = getLedgerEntrySponsorField(sle, *owner); + if (newSponsor) { - if (sle->isFieldPresent(sfSponsorAccount)) + if (sle->isFieldPresent(sponsorField)) { // transfer sponsor + // check if the object owner isn't the same as the new sponsor if ((*newSponsor)->getAccountID(sfAccount) == owner) return tecNO_PERMISSION; } @@ -164,7 +214,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { // dissolve sponsor // check object is sponsored - if (!sle->isFieldPresent(sfSponsorAccount)) + if (!sle->isFieldPresent(sponsorField)) return tecNO_PERMISSION; } @@ -242,10 +292,12 @@ SponsorshipTransfer::doApply() auto const ownerCountDelta = getLedgerEntryOwnerCount(objSle); + auto const& sponsorField = getLedgerEntrySponsorField(objSle, *owner); + if (tx.isFieldPresent(sfSponsor)) { auto const sponsorObj = tx.getFieldObject(sfSponsor); - auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); + auto const oldSponsor = objSle->getAccountID(sponsorField); auto const newSponsor = sponsorObj[sfAccount]; // decrement old sponsoring count if exists if (auto const oldSponsorSle = @@ -279,7 +331,7 @@ SponsorshipTransfer::doApply() ownerCountDelta); view().update(newSponsorSle); - objSle->setAccountID(sfSponsorAccount, newSponsor); + objSle->setAccountID(sponsorField, newSponsor); view().update(objSle); } else From ab4ac64c9299f9293897fde6bed85905eebc5784 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 15:05:31 +0900 Subject: [PATCH 037/249] test Sponsor Reserve checks for Checks --- src/test/app/Sponsor_test.cpp | 141 +++++++++++++++++++------- src/xrpld/app/tx/detail/CashCheck.cpp | 12 ++- 2 files changed, 109 insertions(+), 44 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 74d6e772a1b..fc797b46379 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -25,6 +25,34 @@ namespace ripple { namespace test { +static STAmount +reserve(jtx::Env& env, std::uint32_t count) +{ + return env.current()->fees().accountReserve(count); +} + +static void +adjustAccountXRPBalance( + jtx::Env& env, + jtx::Account const& account, + STAmount const& balanceTo) +{ + using namespace test::jtx; + XRPL_ASSERT( + isXRP(balanceTo), "adjustAccountXRPBalance: balanceTo must be XRP"); + auto const currentBalance = env.balance(account); + if (currentBalance == balanceTo) + return; + + if (currentBalance > balanceTo) + env(pay(account, env.master, currentBalance - balanceTo + XRP(1)), + fee(XRP(1))); + else + env(pay(env.master, account, balanceTo - currentBalance), fee(XRP(1))); + + env.close(); +} + class Sponsor_test : public beast::unit_test::suite { public: @@ -874,7 +902,6 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Check"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); Account const gw("gw"); @@ -883,38 +910,10 @@ class Sponsor_test : public beast::unit_test::suite auto const USD = gw["USD"]; - auto const reserve = env.current()->fees().reserve; - auto const increment = env.current()->fees().increment; - - env.fund(XRP(10000), alice, bob, gw, sponsor2); - env.fund(drops(reserve) + drops(increment) - drops(1), sponsor); - env.close(); - - env.trust(USD(100), alice); - env.close(); - env(pay(gw, alice, USD(100))); - env.close(); - { - BEAST_EXPECT( - env.balance(sponsor) < drops(reserve) + drops(increment)); - - // check sponsor balance - env(check::create(alice, bob, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), - ter(tecINSUFFICIENT_RESERVE)); + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, sponsor, sponsor2); env.close(); - } - - env(pay(env.master, sponsor, drops(1))); - env.close(); - - { - BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); // CheckCreate -> CheckCancel auto const seq = env.seq(alice); @@ -923,7 +922,7 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(ownerCount(env, alice) == 1); // Check BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); @@ -938,7 +937,7 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor2)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(ownerCount(env, alice) == 1); // Check BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); @@ -952,13 +951,17 @@ class Sponsor_test : public beast::unit_test::suite env(check::cancel(alice, keylet.key)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } { + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + // CheckCreate -> CheckCash auto const seq2 = env.seq(alice); env(check::create(alice, bob, XRP(1)), @@ -966,7 +969,7 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(ownerCount(env, alice) == 1); // Check BEAST_EXPECT(ownerCount(env, bob) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); @@ -979,18 +982,24 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sig(sponsor)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } - env(pay(env.master, sponsor, drops(env.current()->fees().increment))); - env.close(); - // RippleState sponsor (CheckCashMakesTrustLine) { + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor, sponsor2); + env.close(); + + env.trust(USD(100), alice); + env.close(); + env(pay(gw, alice, USD(100))); + env.close(); + // CheckCreate -> CheckCash auto const seq2 = env.seq(alice); env(check::create(alice, bob, USD(1)), @@ -1020,6 +1029,60 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); } + + { + // check INSUFFICIENT_RESERVE + + { + // CheckCreate + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + adjustAccountXRPBalance( + env, sponsor, reserve(env, 1) - drops(1)); + + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); + + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tesSUCCESS)); + env.close(); + } + + { + // CheckCash (CheckCashMakesTrustLine) + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor); + env.close(); + + env.trust(USD(100), alice); + env.close(); + env(pay(gw, alice, USD(100))); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, USD(1))); + env.close(); + + adjustAccountXRPBalance( + env, sponsor, reserve(env, 1) - drops(1)); + + auto const keylet = keylet::check(alice, seq); + env(check::cash(bob, keylet.key, USD(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecNO_LINE_INSUF_RESERVE)); + env.close(); + } + } } void diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 573d8b97fa7..8278b166497 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -272,11 +272,6 @@ CashCheck::doApply() return tecFAILED_PROCESSING; } - auto const sponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); - std::optional> sponsorSle = std::nullopt; - if (sponsorAcc) - sponsorSle = psb.peek(keylet::account(*sponsorAcc)); - // Preclaim already checked that source has at least the requested // funds. // @@ -372,6 +367,11 @@ CashCheck::doApply() auto const sleDst = psb.peek(keylet::account(account_)); + auto const sponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); + std::optional> sponsorSle = std::nullopt; + if (sponsorAcc) + sponsorSle = psb.peek(keylet::account(*sponsorAcc)); + // Can the account cover the trust line's reserve? if (auto const ret = checkInsufficientReserve( psb, sleDst, mPriorBalance, sponsorSle, 1); @@ -515,6 +515,8 @@ CashCheck::doApply() } // If we succeeded, update the check owner's reserve. + + auto const sponsorSle = getLedgerEntryReserveSponsor(psb, sleCheck); adjustOwnerCount( psb, psb.peek(keylet::account(srcId)), sponsorSle, -1, viewJ); From 350130911a6a6c0e26bd6a80e5461f4408b71fa9 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 15:34:55 +0900 Subject: [PATCH 038/249] test Sponsor Reserve checks for OfferCreate --- src/test/app/Sponsor_test.cpp | 72 +++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index fc797b46379..e812bb1f091 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1144,6 +1144,49 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } + { + Env env{*this, testable_amendments()}; + + env.fund(XRP(10000), alice, gw, sponsor1, sponsor2); + env.close(); + + // OfferCreate + auto const seq = env.seq(alice); + env(offer(alice, USD(1), XRP(1)), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + + // OfferCreate with Cancel (new sponsor) + auto const seq2 = env.seq(alice); + env(offer(alice, USD(1), XRP(1)), + json(jss::OfferSequence, seq), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // OfferCreate with Cancel (no sponsor) + env(offer(alice, USD(1), XRP(1)), json(jss::OfferSequence, seq2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + // test Offer Execution doesn't sponsor new trustline { Env env{*this, testable_amendments()}; @@ -1194,6 +1237,35 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, bob) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } + + { + // check INSUFFICIENT_RESERVE for OfferCreate + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor1, sponsor2); + env.close(); + + env.trust(USD(100), alice); + env.close(); + env(pay(gw, alice, USD(100))); + env.close(); + + adjustAccountXRPBalance(env, sponsor1, reserve(env, 1) - drops(1)); + + // fullly not crossed + env(offer(alice, USD(1), XRP(1)), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1), + ter(tecINSUF_RESERVE_OFFER)); + + // partially crossed + env(offer(gw, XRP(1), USD(1))); + env.close(); + + env(offer(alice, USD(5), XRP(5)), + sponsor::as(sponsor1, tfSponsorReserve), + sponsor::sig(sponsor1), + ter(tecINSUF_RESERVE_OFFER)); + } } void From c4b798e8edef86ffc669c07c8e28a6b0fe68a7d4 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 15:41:58 +0900 Subject: [PATCH 039/249] test Sponsor Reserve checks for TicketCreate --- src/test/app/Sponsor_test.cpp | 92 ++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e812bb1f091..5ee210bd5b0 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1273,52 +1273,76 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Ticket"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const sponsor("master"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, sponsor, sponsor2); - env.close(); + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); - // TicketCreate - std::uint32_t const ticketSeq{env.seq(alice) + 1}; - env(ticket::create(alice, 250), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // TicketCreate + std::uint32_t const ticketSeq{env.seq(alice) + 1}; + env(ticket::create(alice, 250), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 250); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 250); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 250); + BEAST_EXPECT(ownerCount(env, alice) == 250); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 250); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 250); - auto const keylet = keylet::ticket(alice, ticketSeq); - BEAST_EXPECT( - env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); + auto const keylet = keylet::ticket(alice, ticketSeq); + BEAST_EXPECT( + env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); - // transfer sponsor - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + // transfer sponsor + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 250); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 250); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 249); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 250); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 250); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 249); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + BEAST_EXPECT( + env.le(keylet)->getAccountID(sfSponsorAccount) == + sponsor2.id()); - BEAST_EXPECT( - env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor2.id()); + // use a Ticket + env(noop(alice), ticket::use(ticketSeq)); + env.close(); - // use a Ticket - env(noop(alice), ticket::use(ticketSeq)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 249); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 249); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 249); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } - BEAST_EXPECT(ownerCount(env, alice) == 249); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 249); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 249); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + { + // check INSUFFICIENT_RESERVE for TicketCreate + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(ticket::create(alice, 1), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 249)); + env(ticket::create(alice, 250), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } } void From 9694de92da8beec054e04392598576f472d66d9c Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 15:54:48 +0900 Subject: [PATCH 040/249] test Sponsor Reserve checks for Credentials --- src/test/app/Sponsor_test.cpp | 43 +++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 5ee210bd5b0..e09455d9e7e 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1350,20 +1350,20 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Credentials"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const issuer("issuer"); Account const subject("subject"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), issuer, subject, sponsor, sponsor2); - env.close(); - auto const credType = std::string("credType"); auto const credTypeSlice = Slice(credType.data(), credType.size()); // CredentialsCreate { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), issuer, subject, sponsor, sponsor2); + env.close(); + env(credentials::create(subject, issuer, credType), credentials::uri("uri"), sponsor::as(sponsor, tfSponsorReserve), @@ -1417,6 +1417,10 @@ class Sponsor_test : public beast::unit_test::suite } { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), issuer, subject, sponsor); + env.close(); + // Accept Sponsored Credentials without sponsoring env(credentials::create(subject, issuer, credType), sponsor::as(sponsor, tfSponsorReserve), @@ -1443,6 +1447,37 @@ class Sponsor_test : public beast::unit_test::suite env(credentials::deleteCred(subject, subject, issuer, credType)); env.close(); } + + { + // check INSUFFICIENT_RESERVE for Credentials + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), issuer, subject, sponsor); + env.close(); + + // CredentialsCreate + { + adjustAccountXRPBalance( + env, sponsor, reserve(env, 1) - drops(1)); + env(credentials::create(subject, issuer, credType), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } + // CredentialsAccept + { + env(credentials::create(subject, issuer, credType)); + env.close(); + + adjustAccountXRPBalance( + env, sponsor, reserve(env, 1) - drops(1)); + env(credentials::accept(subject, issuer, credType), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } + } } void From 0c93bd6f0861b726a5fdead5c1c675e0091d7f0d Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 15:57:49 +0900 Subject: [PATCH 041/249] test Sponsor Reserve checks for DelegateSet --- src/test/app/Sponsor_test.cpp | 70 +++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e09455d9e7e..549053266eb 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1485,44 +1485,60 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Delegate"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); - env.close(); + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); - // DelegateSet - env(delegate::set(alice, bob, {"Payment"}), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // DelegateSet + env(delegate::set(alice, bob, {"Payment"}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // transfer sponsor - auto const keylet = keylet::delegate(alice, bob); - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + // transfer sponsor + auto const keylet = keylet::delegate(alice, bob); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - // delete - env(delegate::set(alice, bob, {})); - env.close(); + // delete + env(delegate::set(alice, bob, {})); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + { + // check INSUFFICIENT_RESERVE for DelegateSet + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(delegate::set(alice, bob, {"Payment"}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } } void From b78885d98c7e1dd13d071b4c2aad3ffedd03c745 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 15:59:06 +0900 Subject: [PATCH 042/249] test Sponsor Reserve checks for DepositPreauth --- src/test/app/Sponsor_test.cpp | 72 +++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 549053266eb..ccb15576da1 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1546,44 +1546,60 @@ class Sponsor_test : public beast::unit_test::suite { testcase("DepositPreauth"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, sponsor, sponsor2); - env.close(); + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); - // DepositPreauthSet - env(deposit::auth(alice, sponsor), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // DepositPreauthSet + env(deposit::auth(alice, sponsor), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // transfer sponsor - auto const keylet = keylet::depositPreauth(alice, sponsor); - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + // transfer sponsor + auto const keylet = keylet::depositPreauth(alice, sponsor); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - // DepositPreauthDelete - env(deposit::unauth(alice, sponsor)); - env.close(); + // DepositPreauthDelete + env(deposit::unauth(alice, sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + { + // check INSUFFICIENT_RESERVE for DepositPreauthSet + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(deposit::auth(alice, sponsor), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } } void From cc4b07dbd607013907311f02708e3184d2fd1524 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 16:18:09 +0900 Subject: [PATCH 043/249] test Sponsor Reserve checks for DID, Escrow --- src/test/app/Sponsor_test.cpp | 122 ++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 29 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index ccb15576da1..f743b317f3b 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1607,45 +1607,62 @@ class Sponsor_test : public beast::unit_test::suite { testcase("DID"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, sponsor, sponsor2); - env.close(); + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); - // DIDSet - env(did::set(alice), - did::uri("uri"), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // DIDSet + env(did::set(alice), + did::uri("uri"), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // transfer sponsor - auto const keylet = keylet::did(alice); - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + // transfer sponsor + auto const keylet = keylet::did(alice); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - // DIDDelete - env(did::del(alice)); - env.close(); + // DIDDelete + env(did::del(alice)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + { + // check INSUFFICIENT_RESERVE for DIDSet + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(did::set(alice), + did::uri("uri"), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } } void @@ -1770,6 +1787,53 @@ class Sponsor_test : public beast::unit_test::suite env.le(keylet::line(bob, gw, USD.currency)) ->getAccountID(sfHighSponsorAccount) == sponsor2.id()); } + + { + // check INSUFFICIENT_RESERVE for EscrowCreate, EscrowFinish + Env env{*this, testable_amendments()}; + auto const baseFee = env.current()->fees().base; + env.fund(XRP(1000000), alice, bob, gw, sponsor); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(escrow::create(alice, bob, XRP(100)), + escrow::condition(escrow::cb1), + escrow::cancel_time(env.now() + 10s), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + env(fset(gw, asfAllowTrustLineLocking)); + env.close(); + + env.trust(USD(1000000), alice); + env.close(); + env(pay(gw, alice, USD(10000))); + env.close(); + + env(escrow::create(alice, bob, USD(100)), + escrow::condition(escrow::cb1), + escrow::cancel_time(env.now() + 10s), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + auto const seq = env.seq(alice); + env(escrow::create(alice, bob, USD(100)), + escrow::condition(escrow::cb1), + escrow::cancel_time(env.now() + 10s)); + env.close(); + + env(escrow::finish(bob, alice, seq), + escrow::condition(escrow::cb1), + escrow::fulfillment(escrow::fb1), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + fee(baseFee * 150), + ter(tecNO_LINE_INSUF_RESERVE)); + env.close(); + } } void From ec11763def973b1aad81ea0cc88fd116a3e7b03f Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 17:12:02 +0900 Subject: [PATCH 044/249] test Sponsor Reserve checks for NFToken --- src/test/app/Sponsor_test.cpp | 276 +++++++++++++++++++++++++--------- 1 file changed, 204 insertions(+), 72 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index f743b317f3b..c5e19e8794e 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -22,6 +22,8 @@ #include #include +#include "test/jtx/ticket.h" + namespace ripple { namespace test { @@ -1841,94 +1843,157 @@ class Sponsor_test : public beast::unit_test::suite { testcase("MPToken"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); - env.close(); + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); - // MPTokenIssuanceCreate - Json::Value jv = {}; - jv[sfAccount] = alice.human(); - jv[sfTransactionType] = jss::MPTokenIssuanceCreate; - auto const mptid = makeMptID(env.seq(alice), alice.id()); - env(jv, sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); - env.close(); + // MPTokenIssuanceCreate + Json::Value jv = {}; + jv[sfAccount] = alice.human(); + jv[sfTransactionType] = jss::MPTokenIssuanceCreate; + auto const mptid = makeMptID(env.seq(alice), alice.id()); + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // transfer sponsor - auto const mptIssuanceKeylet = keylet::mptIssuance(mptid); - env(sponsor::transfer(alice, mptIssuanceKeylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + // transfer sponsor + auto const mptIssuanceKeylet = keylet::mptIssuance(mptid); + env(sponsor::transfer(alice, mptIssuanceKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - // MPTokenAuthorize - jv = {}; - jv[sfTransactionType] = jss::MPTokenAuthorize; - jv[sfAccount] = bob.human(); - jv[sfMPTokenIssuanceID] = to_string(mptid); - env(jv, sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); - env.close(); + // MPTokenAuthorize + jv = {}; + jv[sfTransactionType] = jss::MPTokenAuthorize; + jv[sfAccount] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(ownerCount(env, bob) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // transfer sponsor - auto const mptTokenKeylet = keylet::mptoken(mptid, bob); - env(sponsor::transfer(bob, mptTokenKeylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + // transfer sponsor + auto const mptTokenKeylet = keylet::mptoken(mptid, bob); + env(sponsor::transfer(bob, mptTokenKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(ownerCount(env, bob) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); - - // MPTokenAuthorize Unauthorize - jv = {}; - jv[sfTransactionType] = jss::MPTokenAuthorize; - jv[sfAccount] = bob.human(); - jv[sfMPTokenIssuanceID] = to_string(mptid); - jv[sfFlags] = tfMPTUnauthorize; - env(jv); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(ownerCount(env, bob) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // MPTokenAuthorize Unauthorize + jv = {}; + jv[sfTransactionType] = jss::MPTokenAuthorize; + jv[sfAccount] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + jv[sfFlags] = tfMPTUnauthorize; + env(jv); + env.close(); - // MPTokenIssuanceDestroy - jv = {}; - jv[sfTransactionType] = jss::MPTokenIssuanceDestroy; - jv[sfAccount] = alice.human(); - jv[sfMPTokenIssuanceID] = to_string(mptid); - env(jv); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + // MPTokenIssuanceDestroy + jv = {}; + jv[sfTransactionType] = jss::MPTokenIssuanceDestroy; + jv[sfAccount] = alice.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + env(jv); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + { + // check INSUFFICIENT_RESERVE for MPToken + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + { + // MPTokenIssuanceCreate + Json::Value jv = {}; + jv[sfAccount] = alice.human(); + jv[sfTransactionType] = jss::MPTokenIssuanceCreate; + // auto const mptid = makeMptID(env.seq(alice), alice.id()); + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } + + { + // MPTokenAuthorize + Json::Value jv = {}; + jv[sfAccount] = alice.human(); + jv[sfTransactionType] = jss::MPTokenIssuanceCreate; + auto const mptid = makeMptID(env.seq(alice), alice.id()); + env(jv); + env.close(); + + // for free mptoken checks + std::uint32_t bobTicketSeq{env.seq(bob) + 1}; + env(ticket::create(bob, 2)); + env.close(); + + jv = {}; + jv[sfTransactionType] = jss::MPTokenAuthorize; + jv[sfAccount] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + // error (non-free mptoken) + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + env(noop(bob), ticket::use(bobTicketSeq)); + env.close(); + + // pass (free mptoken) + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tesSUCCESS)); + env.close(); + } + } } void @@ -1965,6 +2030,17 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // NFTokenMintOffer + env(token::mint(alice), + token::amount(XRP(10000)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); } { @@ -2003,6 +2079,28 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } + + { + // check INSUFFICIENT_RESERVE for NFTokenMint + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(token::mint(alice), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); + env(token::mint(alice), + token::amount(XRP(10000)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } } void @@ -2209,6 +2307,40 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } + + { + // check INSUFFICIENT_RESERVE for NFTokenOffer + + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + + auto const nextNftId = token::getNextID(env, alice, 0u, 0u); + env(token::mint(alice)); + env.close(); + + // NFTokenOfferCreate + env(token::createOffer(alice, nextNftId, XRP(1)), + txflags(tfSellNFToken), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + auto const offerIndex = keylet::nftoffer(alice, env.seq(alice)).key; + env(token::createOffer(alice, nextNftId, XRP(1)), + txflags(tfSellNFToken)); + env.close(); + + // NFTokenOfferAccept (buyer) + env(token::acceptSellOffer(bob, offerIndex), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } } void From 71b81b743eeff3e55753d2114107c97faa0291de Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 17:35:23 +0900 Subject: [PATCH 045/249] test Sponsor Reserve checks for PayChan, PermissionedDomain, Oracle --- src/test/app/Sponsor_test.cpp | 482 +++++++++++++++++++--------------- 1 file changed, 271 insertions(+), 211 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index c5e19e8794e..b8d8b4e2427 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2349,48 +2349,68 @@ class Sponsor_test : public beast::unit_test::suite testcase("PayChan"); using namespace test::jtx; using namespace std::literals::chrono_literals; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); - env.close(); + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); - // PayChanCreate - auto const pk = alice.pk(); - auto const settleDelay = 10s; - auto const chan = paychan::channel(alice, bob, env.seq(alice)); - env(paychan::create(alice, bob, XRP(100), settleDelay, pk), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // PayChanCreate + auto const pk = alice.pk(); + auto const settleDelay = 10s; + auto const chan = paychan::channel(alice, bob, env.seq(alice)); + env(paychan::create(alice, bob, XRP(100), settleDelay, pk), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // transfer sponsor - env(sponsor::transfer(alice, chan), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + // transfer sponsor + env(sponsor::transfer(alice, chan), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - env.close(env.now() + settleDelay); - // PayChanClaim (delete PayChan) - env(paychan::claim(bob, chan), txflags(tfClose)); - env.close(); + env.close(env.now() + settleDelay); + // PayChanClaim (delete PayChan) + env(paychan::claim(bob, chan), txflags(tfClose)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + { + // check INSUFFICIENT_RESERVE for PayChan + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + + // PayChanCreate + auto const pk = alice.pk(); + auto const settleDelay = 10s; + env(paychan::create(alice, bob, XRP(100), settleDelay, pk), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } } void @@ -2398,48 +2418,65 @@ class Sponsor_test : public beast::unit_test::suite { testcase("PermissionedDomain"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); - env.fund(XRP(1000000), alice, sponsor, sponsor2); - env.close(); + // PermissionedDomainSet + auto const seq = env.seq(alice); + pdomain::Credentials credentials{{alice, "first credential"}}; + env(pdomain::setTx(alice, credentials), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - // PermissionedDomainSet - auto const seq = env.seq(alice); - pdomain::Credentials credentials{{alice, "first credential"}}; - env(pdomain::setTx(alice, credentials), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // transfer sponsor + env(sponsor::transfer( + alice, keylet::permissionedDomain(alice, seq).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - // transfer sponsor - env(sponsor::transfer( - alice, keylet::permissionedDomain(alice, seq).key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // PermissionedDomainDelete + auto objects = pdomain::getObjects(alice, env); + auto const domain = objects.begin()->first; + env(pdomain::deleteTx(alice, domain)); + env.close(); - // PermissionedDomainDelete - auto objects = pdomain::getObjects(alice, env); - auto const domain = objects.begin()->first; - env(pdomain::deleteTx(alice, domain)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + { + // check INSUFFICIENT_RESERVE for PermissionedDomain + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + + // PermissionedDomainSet + pdomain::Credentials credentials{{alice, "first credential"}}; + env(pdomain::setTx(alice, credentials), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } } void @@ -2451,16 +2488,12 @@ class Sponsor_test : public beast::unit_test::suite using DataSeries = std::vector< std::tuple>; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, sponsor, sponsor2); - env.close(); - auto const oracleSet = - [&env](Account const& account, uint8_t dataSeriesSize) { + [](Env& env, Account const& account, uint8_t dataSeriesSize) { auto const now = env.timeKeeper().now(); env.close(now + oracle::testStartTime - epoch_offset); Json::Value jv; @@ -2517,182 +2550,209 @@ class Sponsor_test : public beast::unit_test::suite }; { - // OracleSet (reserve 1) - env(oracleSet(alice, 5), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor, sponsor2); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + { + // OracleSet (reserve 1) + env(oracleSet(env, alice, 5), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - // transfer sponsor - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // transfer sponsor + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - // OracleDelete - env(oracleDelete(alice)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); - } - { - // OracleSet (reserve 2) - env(oracleSet(alice, 6), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // OracleDelete + env(oracleDelete(alice)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + { + // OracleSet (reserve 2) + env(oracleSet(env, alice, 6), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - // transfer sponsor - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); + // transfer sponsor + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - // OracleDelete - env(oracleDelete(alice)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); - } - { - // OracleSet (reserve 1->2, sponsor1 -> no-sponsor) - env(oracleSet(alice, 5), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // OracleDelete + env(oracleDelete(alice)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + { + // OracleSet (reserve 1->2, sponsor1 -> no-sponsor) + env(oracleSet(env, alice, 5), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - // reserve 1->2 - env(oracleSet(alice, 6)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + // reserve 1->2 + env(oracleSet(env, alice, 6)); + env.close(); - // OracleDelete - env(oracleDelete(alice)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - } - { - // OracleSet (reserve 1->2, sponsor1 -> sponsor2) - env(oracleSet(alice, 5), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + // OracleDelete + env(oracleDelete(alice)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + { + // OracleSet (reserve 1->2, sponsor1 -> sponsor2) + env(oracleSet(env, alice, 5), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - // reserve 1->2 - env(oracleSet(alice, 6), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); + // reserve 1->2 + env(oracleSet(env, alice, 6), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - // OracleDelete - env(oracleDelete(alice)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); - } - { - // OracleSet (reserve 1->2, non-sponsor -> sponsor1) - env(oracleSet(alice, 5)); - env.close(); + // OracleDelete + env(oracleDelete(alice)); + env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + { + // OracleSet (reserve 1->2, non-sponsor -> sponsor1) + env(oracleSet(env, alice, 5)); + env.close(); - // reserve 1->2 - env(oracleSet(alice, 6), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + // reserve 1->2 + env(oracleSet(env, alice, 6), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - // OracleDelete - env(oracleDelete(alice)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + // OracleDelete + env(oracleDelete(alice)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + for (bool isTwoOwnerCount : {false, true}) + { + // test sponsor transfer + auto const dataSeriesSize = isTwoOwnerCount ? 6 : 5; + auto const ocount = isTwoOwnerCount ? 2 : 1; + env(oracleSet(env, alice, dataSeriesSize), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == ocount); + + // transfer sponsor + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == ocount); + + // disolve sponsor + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == ocount); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } } - for (bool isTwoOwnerCount : {false, true}) + { - // test sponsor transfer - auto const dataSeriesSize = isTwoOwnerCount ? 6 : 5; - auto const ocount = isTwoOwnerCount ? 2 : 1; - env(oracleSet(alice, dataSeriesSize), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + // check INSUFFICIENT_RESERVE for OracleSet + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == ocount); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == ocount); - - // transfer sponsor - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(oracleSet(env, alice, 5), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == ocount); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == ocount); - - // disolve sponsor - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key)); + adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); + env(oracleSet(env, alice, 6), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == ocount); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } } From 699297f01acc64b6a691fa0d6f6ec159fd58ee58 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 18:59:16 +0900 Subject: [PATCH 046/249] test Sponsor Reserve checks for SignerListSet --- src/test/app/Sponsor_test.cpp | 71 +++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index b8d8b4e2427..cfde84c5495 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2761,45 +2761,60 @@ class Sponsor_test : public beast::unit_test::suite { testcase("SignerList"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); + Account const bob("bob"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, sponsor, sponsor2); - env.close(); + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); - Account const bob("bob"); + // SignerListSet + env(signers(alice, 1, {{bob, 1}}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - // SignerListSet - env(signers(alice, 1, {{bob, 1}}), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // transfer sponsor + env(sponsor::transfer(alice, keylet::signers(alice).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - // transfer sponsor - env(sponsor::transfer(alice, keylet::signers(alice).key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // Delete + env(signers(alice, none)); + env.close(); - // Delete - env(signers(alice, none)); - env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + { + // check INSUFFICIENT_RESERVE for SignerListSet + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(signers(alice, 1, {{bob, 1}}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } } void From d5a1314c4794e1d7735d472ed6e6ce9365ecde01 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 19:05:22 +0900 Subject: [PATCH 047/249] test Sponsor Reserve checks for TrustSet --- include/xrpl/ledger/View.h | 3 + src/libxrpl/ledger/View.cpp | 14 ++- src/test/app/Sponsor_test.cpp | 153 +++++++++++++++++---------- src/xrpld/app/tx/detail/SetTrust.cpp | 13 ++- 4 files changed, 122 insertions(+), 61 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 433418cd708..ca8867f0f5f 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -451,6 +451,9 @@ areCompatible( beast::Journal::Stream& s, char const* reason); +uint32_t +ownerCount(std::shared_ptr const& sponsorSle); + TER checkInsufficientReserve( ReadView const& view, diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 56a31019c46..d2fd26809b0 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1029,6 +1029,18 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) return std::nullopt; } +uint32_t +ownerCount(std::shared_ptr const& sponsorSle) +{ + auto const ownerCount = sponsorSle->getFieldU32(sfOwnerCount); + auto const sponsoredOwnerCount = + sponsorSle->getFieldU32(sfSponsoredOwnerCount); + auto const sponsoringOwnerCount = + sponsorSle->getFieldU32(sfSponsoringOwnerCount); + + return ownerCount + sponsoringOwnerCount - sponsoredOwnerCount; +} + TER checkInsufficientReserve( ReadView const& view, @@ -1479,7 +1491,7 @@ authorizeMPToken( // an account owns, in the case of MPTokens we only // *enforce* a reserve if the user owns more than two // items. This is similar to the reserve requirements of trust lines. - if (sleAcct->getFieldU32(sfOwnerCount) >= 2) + if (ownerCount(sponsor.value_or(sleAcct)) >= 2) { if (auto const ret = checkInsufficientReserve( view, sleAcct, priorBalance, sponsor, 1); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index cfde84c5495..3ba39ada4b1 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -47,10 +47,10 @@ adjustAccountXRPBalance( return; if (currentBalance > balanceTo) - env(pay(account, env.master, currentBalance - balanceTo + XRP(1)), + env(pay(account, env.master, currentBalance - (balanceTo + XRP(1))), fee(XRP(1))); else - env(pay(env.master, account, balanceTo - currentBalance), fee(XRP(1))); + env(pay(env.master, account, balanceTo - currentBalance)); env.close(); } @@ -1968,10 +1968,13 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // for free mptoken checks - std::uint32_t bobTicketSeq{env.seq(bob) + 1}; - env(ticket::create(bob, 2)); + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + std::uint32_t ticketSeq{env.seq(sponsor) + 1}; + env(ticket::create(sponsor, 2)); env.close(); + adjustAccountXRPBalance( + env, sponsor, reserve(env, 3) - drops(1)); jv = {}; jv[sfTransactionType] = jss::MPTokenAuthorize; jv[sfAccount] = bob.human(); @@ -1983,9 +1986,12 @@ class Sponsor_test : public beast::unit_test::suite ter(tecINSUFFICIENT_RESERVE)); env.close(); - env(noop(bob), ticket::use(bobTicketSeq)); + env(noop(sponsor), ticket::use(ticketSeq)); env.close(); + adjustAccountXRPBalance( + env, sponsor, reserve(env, 2) - drops(1)); + // pass (free mptoken) env(jv, sponsor::as(sponsor, tfSponsorReserve), @@ -2822,71 +2828,112 @@ class Sponsor_test : public beast::unit_test::suite { testcase("TrustSet"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); - env.close(); - - auto const& highAcc = alice > bob ? alice : bob; - auto const& lowAcc = alice > bob ? bob : alice; - for (bool isIssuerHigh : {false, true}) { - auto const& issuer = isIssuerHigh ? highAcc : lowAcc; - auto const& user = isIssuerHigh ? lowAcc : highAcc; + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); - auto const USD = issuer["USD"]; + auto const& highAcc = alice > bob ? alice : bob; + auto const& lowAcc = alice > bob ? bob : alice; + for (bool isIssuerHigh : {false, true}) + { + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; - // create TrustLine - env(trust(user, USD(100)), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); - env.close(); + auto const USD = issuer["USD"]; - BEAST_EXPECT(ownerCount(env, user) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // create TrustLine + env(trust(user, USD(100)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); - auto const line = env.le(keylet::line(user, issuer, USD.currency)); - BEAST_EXPECT( - line->getAccountID( - isIssuerHigh ? sfLowSponsorAccount - : sfHighSponsorAccount) == sponsor.id()); - BEAST_EXPECT(!line->isFieldPresent( - isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); + BEAST_EXPECT(ownerCount(env, user) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // transfer sponsor - env(sponsor::transfer( - user, keylet::line(user, issuer, USD.currency).key), - sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); - env.close(); + auto const line = + env.le(keylet::line(user, issuer, USD.currency)); + BEAST_EXPECT( + line->getAccountID( + isIssuerHigh ? sfLowSponsorAccount + : sfHighSponsorAccount) == sponsor.id()); + BEAST_EXPECT(!line->isFieldPresent( + isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); - BEAST_EXPECT(ownerCount(env, user) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // transfer sponsor + env(sponsor::transfer( + user, keylet::line(user, issuer, USD.currency).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - auto const line2 = env.le(keylet::line(user, issuer, USD.currency)); - BEAST_EXPECT( - line2->getAccountID( - isIssuerHigh ? sfLowSponsorAccount - : sfHighSponsorAccount) == sponsor2.id()); - BEAST_EXPECT(!line2->isFieldPresent( - isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); + BEAST_EXPECT(ownerCount(env, user) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - // delete TrustLine - env(trust(user, USD(0))); + auto const line2 = + env.le(keylet::line(user, issuer, USD.currency)); + BEAST_EXPECT( + line2->getAccountID( + isIssuerHigh ? sfLowSponsorAccount + : sfHighSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(!line2->isFieldPresent( + isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); + + // delete TrustLine + env(trust(user, USD(0))); + env.close(); + + BEAST_EXPECT(ownerCount(env, user) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + BEAST_EXPECT(!env.le(keylet::line(user, issuer, USD.currency))); + } + } + + { + // check INSUFFICIENT_RESERVE for TrustSet + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); env.close(); - BEAST_EXPECT(ownerCount(env, user) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + auto const& highAcc = alice > bob ? alice : bob; + auto const& lowAcc = alice > bob ? bob : alice; + for (bool isIssuerHigh : {false}) + { + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; + auto const USD = issuer["USD"]; + + adjustAccountXRPBalance(env, sponsor, reserve(env, 100)); + + // free trustline + std::uint32_t const ticketSeq{env.seq(sponsor) + 1}; + env(ticket::create(sponsor, 2)); + env.close(); + + adjustAccountXRPBalance( + env, sponsor, reserve(env, 3) - drops(1)); - BEAST_EXPECT(!env.le(keylet::line(user, issuer, USD.currency))); + // create TrustLine + env(trust(user, USD(100)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecNO_LINE_INSUF_RESERVE)); + env.close(); + + env(noop(sponsor), ticket::use(ticketSeq)); + env(noop(sponsor), ticket::use(ticketSeq + 1)); + env.close(); + } } } diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 6725ecc958b..7e1e2a77fa0 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -382,8 +382,6 @@ SetTrust::doApply() if (!sle) return tefINTERNAL; - std::uint32_t const uOwnerCount = sle->getFieldU32(sfOwnerCount); - // The reserve that is required to create the line. Note // that although the reserve increases with every item // an account owns, in the case of trust lines we only @@ -401,7 +399,13 @@ SetTrust::doApply() // but the incremental reserve for the trust line as // well. A person with no intention of using the gateway // could use the extra XRP for their own purposes. + auto const txSponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); + + std::optional> txSponsorSle = std::nullopt; + if (txSponsorAcc) + txSponsorSle = view().peek(keylet::account(*txSponsorAcc)); + std::uint32_t const uOwnerCount = ownerCount(txSponsorSle.value_or(sle)); bool const freeTrustLine = uOwnerCount < 2; std::uint32_t uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0); @@ -453,11 +457,6 @@ SetTrust::doApply() SLE::pointer sleRippleState = view().peek(keylet::line(account_, uDstAccountID, currency)); - auto const txSponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); - std::optional> txSponsorSle = std::nullopt; - if (txSponsorAcc) - txSponsorSle = view().peek(keylet::account(*txSponsorAcc)); - if (sleRippleState) { STAmount saLowBalance; From e82c300de78f4f7fc041c80e2adcbf39502f25b0 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 25 Sep 2025 19:23:07 +0900 Subject: [PATCH 048/249] test Sponsor Reserve checks for AMM --- include/xrpl/ledger/View.h | 1 + src/libxrpl/ledger/View.cpp | 53 +++- src/test/app/Sponsor_test.cpp | 333 ++++++++++++++++++++++ src/xrpld/app/paths/AMMOffer.h | 3 +- src/xrpld/app/tx/detail/AMMCreate.cpp | 9 +- src/xrpld/app/tx/detail/AMMDeposit.cpp | 22 +- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 20 +- src/xrpld/app/tx/detail/VaultClawback.cpp | 2 + src/xrpld/app/tx/detail/VaultDeposit.cpp | 4 + src/xrpld/app/tx/detail/VaultWithdraw.cpp | 3 + 10 files changed, 426 insertions(+), 24 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index ca8867f0f5f..270311173f9 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -812,6 +812,7 @@ accountSend( AccountID const& to, STAmount const& saAmount, beast::Journal j, + std::optional const& sponsorAcc = std::nullopt, WaiveTransferFee waiveFee = WaiveTransferFee::No); [[nodiscard]] TER diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index d2fd26809b0..69127f4b57f 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1801,6 +1801,9 @@ trustDelete( return tefBAD_LEDGER; } + removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsorAccount); + removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsorAccount); + JLOG(j.trace()) << "trustDelete: Deleting ripple line: state"; view.erase(sleRippleState); @@ -1876,6 +1879,7 @@ rippleCreditIOU( AccountID const& uReceiverID, STAmount const& saAmount, bool bCheckIssuer, + std::optional const& sponsorAccount, beast::Journal j) { AccountID const& issuer = saAmount.getIssuer(); @@ -2029,7 +2033,7 @@ rippleCreditIOU( saReceiverLimit, 0, 0, - std::nullopt, + sponsorAccount, j); } @@ -2044,6 +2048,7 @@ rippleSendIOU( STAmount const& saAmount, STAmount& saActual, beast::Journal j, + std::optional const& sponsorAccount, WaiveTransferFee waiveFee) { auto const issuer = saAmount.getIssuer(); @@ -2058,8 +2063,8 @@ rippleSendIOU( if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) { // Direct send: redeeming IOUs and/or sending own IOUs. - auto const ter = - rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j); + auto const ter = rippleCreditIOU( + view, uSenderID, uReceiverID, saAmount, false, sponsorAccount, j); if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS) return ter; saActual = saAmount; @@ -2079,11 +2084,12 @@ rippleSendIOU( << " : deliver=" << saAmount.getFullText() << " cost=" << saActual.getFullText(); - TER terResult = - rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j); + TER terResult = rippleCreditIOU( + view, issuer, uReceiverID, saAmount, true, sponsorAccount, j); if (tesSUCCESS == terResult) - terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j); + terResult = rippleCreditIOU( + view, uSenderID, issuer, saActual, true, sponsorAccount, j); return terResult; } @@ -2095,6 +2101,7 @@ accountSendIOU( AccountID const& uReceiverID, STAmount const& saAmount, beast::Journal j, + std::optional const& sponsorAccount, WaiveTransferFee waiveFee) { if (view.rules().enabled(fixAMMv1_1)) @@ -2126,7 +2133,14 @@ accountSendIOU( << saAmount.getFullText(); return rippleSendIOU( - view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); + view, + uSenderID, + uReceiverID, + saAmount, + saActual, + j, + sponsorAccount, + waiveFee); } /* XRP send which does not check reserve and can do pure adjustment. @@ -2368,13 +2382,20 @@ accountSend( AccountID const& uReceiverID, STAmount const& saAmount, beast::Journal j, + std::optional const& sponsorAcc, WaiveTransferFee waiveFee) { return std::visit( [&](TIss const& issue) { if constexpr (std::is_same_v) return accountSendIOU( - view, uSenderID, uReceiverID, saAmount, j, waiveFee); + view, + uSenderID, + uReceiverID, + saAmount, + j, + sponsorAcc, + waiveFee); else return accountSendMPT( view, uSenderID, uReceiverID, saAmount, j, waiveFee); @@ -2423,7 +2444,7 @@ updateTrustLine( // Clear the reserve of the sender, possibly delete the line! auto const currentSponsor = getLedgerEntryReserveSponsor( view, - sle, + state, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); adjustOwnerCount(view, sle, currentSponsor, -1, j); @@ -3021,7 +3042,11 @@ deleteAMMTrustLine( if (!(sleState->getFlags() & uFlags)) return tecINTERNAL; - adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, std::nullopt, -1, j); + auto const sponsorSle = getLedgerEntryReserveSponsor( + view, sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount); + adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, -1, j); + removeSponsorFromLedgerEntry( + sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount); return tesSUCCESS; } @@ -3040,7 +3065,13 @@ rippleCredit( if constexpr (std::is_same_v) { return rippleCreditIOU( - view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j); + view, + uSenderID, + uReceiverID, + saAmount, + bCheckIssuer, + std::nullopt, + j); } else { diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 3ba39ada4b1..fd2ea8e752d 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -899,6 +899,338 @@ class Sponsor_test : public beast::unit_test::suite env.close(); } + void + testAMM() + { + testcase("AMM"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const gw("gw"); + Account const sponsor("sponsor"); + + auto const USD = gw["USD"]; + auto const EUR = gw["EUR"]; + + auto const ammCreate = [&](Env& env, + Account const& account, + STAmount const& amount1, + STAmount const& amount2) { + Json::Value jv; + jv[jss::TransactionType] = jss::AMMCreate; + jv[jss::Account] = account.human(); + jv[jss::Amount] = amount1.getJson(JsonOptions::none); + jv[jss::Amount2] = amount2.getJson(JsonOptions::none); + jv[jss::TradingFee] = 0; + jv[jss::Fee] = + std::to_string(env.current()->fees().increment.drops()); + return jv; + }; + + auto const ammDeposit = [&](Env& env, + Account const& account, + STAmount const& amount1, + STAmount const& amount2) { + Json::Value jv; + jv[jss::TransactionType] = jss::AMMDeposit; + jv[jss::Account] = account.human(); + jv[jss::Asset] = + STIssue(sfAsset, amount1.issue()).getJson(JsonOptions::none); + jv[jss::Asset2] = + STIssue(sfAsset, amount2.issue()).getJson(JsonOptions::none); + jv[jss::Amount] = amount1.value().getJson(JsonOptions::none); + jv[jss::Amount2] = amount2.value().getJson(JsonOptions::none); + jv[jss::Flags] = tfTwoAsset; + return jv; + }; + + { + // AMMCreate + // - sponsor LPToken + // - doesn't sponsor AMM object + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, gw, sponsor); + env.close(); + + env(trust(alice, USD(10000))); + env(trust(alice, EUR(10000))); + env.close(); + + env(pay(gw, alice, USD(1000))); + env(pay(gw, alice, EUR(1000))); + env.close(); + + { + // AMMCreate doesn't check INSUFFICIENT_RESERVE now + // see: https://github.com/XRPLF/rippled/issues/5812 + // check INSUFFICIENT_RESERVE + adjustAccountXRPBalance( + env, sponsor, reserve(env, 1) - drops(1)); + + env(ammCreate(env, alice, USD(100), EUR(100)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUF_RESERVE_LINE)); + env.close(); + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + } + + env(ammCreate(env, alice, USD(100), EUR(100)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + auto const amm = + env.current()->read(keylet::amm(USD.issue(), EUR.issue())); + auto const ammAccount = + Account("amm", amm->getAccountID(sfAccount)); + + BEAST_EXPECT( + ownerCount(env, alice) == 3); // RippleState (USD,EUR/LP Token) + BEAST_EXPECT(ownerCount(env, ammAccount) == 2); // USD, EUR + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); // LPToken + BEAST_EXPECT(sponsoredOwnerCount(env, ammAccount) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken + BEAST_EXPECT(!env.le(keylet::amm(USD.issue(), EUR.issue())) + ->isFieldPresent(sfSponsorAccount)); + } + { + // AMMDeposit + // - sponsor new LPToken + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor); + env.close(); + + env(trust(alice, USD(10000))); + env(trust(alice, EUR(10000))); + env(trust(bob, USD(10000))); + env(trust(bob, EUR(10000))); + env.close(); + + env(pay(gw, alice, USD(1000))); + env(pay(gw, alice, EUR(1000))); + env(pay(gw, bob, USD(1000))); + env(pay(gw, bob, EUR(1000))); + env.close(); + + env(ammCreate(env, alice, USD(100), EUR(100))); + env.close(); + + BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState (USD,EUR) + + { + // check INSUFFICIENT_RESERVE + adjustAccountXRPBalance( + env, sponsor, reserve(env, 1) - drops(1)); + + env(ammDeposit(env, bob, USD(100), EUR(100)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUF_RESERVE_LINE)); + env.close(); + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + } + + env(ammDeposit(env, bob, USD(100), EUR(100)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT( + ownerCount(env, bob) == 3); // RippleState (USD,EUR/LP Token) + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // LPToken + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken + } + { + // AMMWithdraw + { + // Single Asset Withdraw + // - sponsor new RippleState + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor); + env.close(); + + env(trust(alice, USD(10000))); + env(trust(alice, EUR(10000))); + env.close(); + + env(pay(gw, alice, USD(1000))); + env(pay(gw, alice, EUR(1000))); + env.close(); + + env(ammCreate(env, alice, USD(1000), EUR(1000)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env(trust(alice, USD(0))); + env(trust(alice, EUR(0))); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); // LPToken + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); // LPToken + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == 1); // LPToken + + Json::Value jv; + jv[jss::TransactionType] = jss::AMMWithdraw; + jv[jss::Account] = alice.human(); + jv[jss::Asset] = + STIssue(sfAsset, USD.issue()).getJson(JsonOptions::none); + jv[jss::Asset2] = + STIssue(sfAsset, EUR.issue()).getJson(JsonOptions::none); + jv[jss::Amount] = USD(100).value().getJson(JsonOptions::none); + jv[jss::Flags] = tfSingleAsset; + + { + env(ticket::create( + sponsor, 1)); // adjust for free trustline + env.close(); + BEAST_EXPECT(ownerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // check INSUFFICIENT_RESERVE + adjustAccountXRPBalance( + env, sponsor, reserve(env, 2) - drops(1)); + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); + } + + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); // USD, LPToken + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + } + { + // Double Asset Withdraw + // - sponsor new RippleState * 2 + // - remove sponsored LPToken + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor); + env.close(); + + env(trust(alice, USD(10000))); + env(trust(alice, EUR(10000))); + env.close(); + + env(pay(gw, alice, USD(1000))); + env(pay(gw, alice, EUR(1000))); + env.close(); + + env(ammCreate(env, alice, USD(1000), EUR(1000)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env(trust(alice, USD(0))); + env(trust(alice, EUR(0))); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); // LPToken + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + Json::Value jv; + jv[jss::TransactionType] = jss::AMMWithdraw; + jv[jss::Account] = alice.human(); + jv[jss::Asset] = + STIssue(sfAsset, USD.issue()).getJson(JsonOptions::none); + jv[jss::Asset2] = + STIssue(sfAsset, EUR.issue()).getJson(JsonOptions::none); + jv[jss::Flags] = tfWithdrawAll; + + { + env(ticket::create( + sponsor, 1)); // adjust for free trustline + env.close(); + BEAST_EXPECT(ownerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + // check INSUFFICIENT_RESERVE for RippleStates * 2 + adjustAccountXRPBalance( + env, sponsor, reserve(env, 3) - drops(1)); + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + adjustAccountXRPBalance(env, sponsor, reserve(env, 4)); + } + + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); // USD, EUR + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + } + } + { + // AMMClawback + // - doesn't sponsor holder's new RippleState + // - remove sponsored LPToken + Account const gw2("gw2"); + auto const EUR2 = gw2["EUR"]; + + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, gw, gw2, sponsor); + env.close(); + + env(fset(gw, asfAllowTrustLineClawback)); + env.close(); + + env(trust(alice, USD(10000))); + env(trust(alice, EUR2(10000))); + env.close(); + + env(pay(gw, alice, USD(100))); + env(pay(gw2, alice, EUR2(100))); + env.close(); + + env(ammCreate(env, alice, USD(100), EUR2(100)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env(trust(alice, USD(0))); + env(trust(alice, EUR2(0))); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); // LPToken + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + { + // doesn't sponsor holder's new RippleState + env(amm::ammClawback(gw, alice, USD, EUR2, USD(10)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); // LPToken, EUR2 + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } + { + env(amm::ammClawback(gw, alice, USD, EUR2, std::nullopt), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); // EUR2 + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + } + } + void testCheck() { @@ -3166,6 +3498,7 @@ class Sponsor_test : public beast::unit_test::suite { // TODO: add checks fo InsufficientReserve for Sponsoring ledger entry testRequireFlag(); + testAMM(); testCheck(); testOffer(); testTicket(); diff --git a/src/xrpld/app/paths/AMMOffer.h b/src/xrpld/app/paths/AMMOffer.h index 3c218c0f5e1..9834d070c14 100644 --- a/src/xrpld/app/paths/AMMOffer.h +++ b/src/xrpld/app/paths/AMMOffer.h @@ -122,7 +122,8 @@ class AMMOffer static TER send(Args&&... args) { - return accountSend(std::forward(args)..., WaiveTransferFee::Yes); + return accountSend( + std::forward(args)..., std::nullopt, WaiveTransferFee::Yes); } bool diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 03c972f1cdb..e922530f5f5 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -141,8 +141,10 @@ AMMCreate::preclaim(PreclaimContext const& ctx) return terNO_RIPPLE; } + auto const sponsor = getTxReserveSponsorAccountID(ctx.tx); // Check the reserve for LPToken trustline - STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j); + STAmount const xrpBalance = + xrpLiquid(ctx.view, sponsor.value_or(accountID), 1, ctx.j); // Insufficient reserve if (xrpBalance <= beast::zero) { @@ -275,7 +277,9 @@ applyCreate( sb.insert(ammSle); // Send LPT to LP. - auto res = accountSend(sb, accountId, account_, lpTokens, ctx_.journal); + auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto res = + accountSend(sb, accountId, account_, lpTokens, ctx_.journal, sponsor); if (res != tesSUCCESS) { JLOG(j_.debug()) << "AMM Instance: failed to send LPT " << lpTokens; @@ -289,6 +293,7 @@ applyCreate( accountId, amount, ctx_.journal, + std::nullopt, // don't sponsor for AMM Trustline WaiveTransferFee::Yes)) return res; // Set AMM flag on AMM trustline diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index 614d788c718..5701bab0942 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -167,6 +167,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) { auto const accountID = ctx.tx[sfAccount]; + auto const sponsor = getTxReserveSponsorAccountID(ctx.tx); + auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); if (!ammSle) @@ -224,7 +226,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) // Adjust the reserve if LP doesn't have LPToken trustline auto const sle = ctx.view.read( keylet::line(accountID, lpIssue.account, lpIssue.currency)); - if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit) + if (xrpLiquid(ctx.view, sponsor.value_or(accountID), !sle, ctx.j) >= + deposit) return TER(tesSUCCESS); if (sle) return tecUNFUNDED_AMM; @@ -352,7 +355,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) // We checked above but need to check again if depositing IOU only. if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::zero) { - STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j); + STAmount const xrpBalance = + xrpLiquid(ctx.view, sponsor.value_or(accountID), 1, ctx.j); // Insufficient reserve if (xrpBalance <= beast::zero) { @@ -507,6 +511,8 @@ AMMDeposit::deposit( std::optional const& lpTokensDepositMin, std::uint16_t tfee) { + auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + // Check account has sufficient funds. // Return true if it does, false otherwise. auto checkBalance = [&](auto const& depositAmount) -> TER { @@ -518,7 +524,8 @@ AMMDeposit::deposit( // Adjust the reserve if LP doesn't have LPToken trustline auto const sle = view.read( keylet::line(account_, lpIssue.account, lpIssue.currency)); - if (xrpLiquid(view, account_, !sle, j_) >= depositAmount) + if (xrpLiquid(view, sponsor.value_or(account_), !sle, j_) >= + depositAmount) return tesSUCCESS; } else if ( @@ -578,6 +585,7 @@ AMMDeposit::deposit( ammAccount, amountDepositActual, ctx_.journal, + std::nullopt, // don't sponsor for AMM Trustline WaiveTransferFee::Yes); if (res != tesSUCCESS) { @@ -604,6 +612,7 @@ AMMDeposit::deposit( ammAccount, *amount2DepositActual, ctx_.journal, + std::nullopt, // don't sponsor for AMM Trustline WaiveTransferFee::Yes); if (res != tesSUCCESS) { @@ -615,7 +624,12 @@ AMMDeposit::deposit( // Deposit LP tokens res = accountSend( - view, ammAccount, account_, lpTokensDepositActual, ctx_.journal); + view, + ammAccount, + account_, + lpTokensDepositActual, + ctx_.journal, + sponsor); if (res != tesSUCCESS) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens"; diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index e0aa797f161..f19318f28ef 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -586,6 +586,12 @@ AMMWithdraw::withdraw( return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}}; } + // this is also called from AMMClawback, but only AMMWithdraw does sponsor + // the new trustline + auto const sponsor = tx[sfAccount] == account + ? getTxReserveSponsorAccountID(tx) + : std::nullopt; + // Check the reserve in case a trustline has to be created bool const enabledFixAMMv1_2 = view.rules().enabled(fixAMMv1_2); auto sufficientReserve = [&](Issue const& issue) -> TER { @@ -593,20 +599,20 @@ AMMWithdraw::withdraw( return tesSUCCESS; if (!view.exists(keylet::line(account, issue))) { - auto const sleAccount = view.read(keylet::account(account)); + auto const sleAccount = + view.read(keylet::account(sponsor.value_or(account))); if (!sleAccount) return tecINTERNAL; // LCOV_EXCL_LINE auto const balance = (*sleAccount)[sfBalance].xrp(); - std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount); - - if (ownerCount >= 2) + std::uint32_t const count = ownerCount(sleAccount); + if (count >= 2) { - auto const sponsor = getTxReserveSponsor(view, tx); if (auto const ret = checkInsufficientReserve( view, sleAccount, std::max(priorBalance, balance), - sponsor, + sponsor ? view.read(keylet::account(*sponsor)) + : std::optional>{}, 1); !isTesSuccess(ret)) return ret; @@ -625,6 +631,7 @@ AMMWithdraw::withdraw( account, amountWithdrawActual, journal, + sponsor, WaiveTransferFee::Yes); if (res != tesSUCCESS) { @@ -648,6 +655,7 @@ AMMWithdraw::withdraw( account, *amount2WithdrawActual, journal, + sponsor, WaiveTransferFee::Yes); if (res != tesSUCCESS) { diff --git a/src/xrpld/app/tx/detail/VaultClawback.cpp b/src/xrpld/app/tx/detail/VaultClawback.cpp index a81deed6695..2d09173ac22 100644 --- a/src/xrpld/app/tx/detail/VaultClawback.cpp +++ b/src/xrpld/app/tx/detail/VaultClawback.cpp @@ -278,6 +278,7 @@ VaultClawback::doApply() vaultAccount, sharesDestroyed, j_, + std::nullopt, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -317,6 +318,7 @@ VaultClawback::doApply() account_, assetsRecovered, j_, + std::nullopt, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/app/tx/detail/VaultDeposit.cpp b/src/xrpld/app/tx/detail/VaultDeposit.cpp index 377022b30ef..efe187fc043 100644 --- a/src/xrpld/app/tx/detail/VaultDeposit.cpp +++ b/src/xrpld/app/tx/detail/VaultDeposit.cpp @@ -302,6 +302,8 @@ VaultDeposit::doApply() if (maximum != 0 && *vault->at(sfAssetsTotal) > maximum) return tecLIMIT_EXCEEDED; + auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + // Transfer assets from depositor to vault. if (auto const ter = accountSend( view(), @@ -309,6 +311,7 @@ VaultDeposit::doApply() vaultAccount, assetsDeposited, j_, + sponsor, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -335,6 +338,7 @@ VaultDeposit::doApply() account_, sharesCreated, j_, + sponsor, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/app/tx/detail/VaultWithdraw.cpp b/src/xrpld/app/tx/detail/VaultWithdraw.cpp index f3b47424e77..2216df5420a 100644 --- a/src/xrpld/app/tx/detail/VaultWithdraw.cpp +++ b/src/xrpld/app/tx/detail/VaultWithdraw.cpp @@ -277,6 +277,7 @@ VaultWithdraw::doApply() view().update(vault); auto const& vaultAccount = vault->at(sfAccount); + auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); // Transfer shares from depositor to vault. if (auto const ter = accountSend( view(), @@ -284,6 +285,7 @@ VaultWithdraw::doApply() vaultAccount, sharesRedeemed, j_, + sponsor, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -329,6 +331,7 @@ VaultWithdraw::doApply() dstAcct, assetsWithdrawn, j_, + sponsor, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; From dbbfc1354c481054eda8083eab601fc5540fd2a2 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 26 Sep 2025 18:21:24 +0900 Subject: [PATCH 049/249] test for Vault --- src/libxrpl/ledger/View.cpp | 4 +- src/test/app/Sponsor_test.cpp | 208 +++++++++++++++++- .../app/tx/detail/MPTokenIssuanceCreate.cpp | 3 +- src/xrpld/app/tx/detail/VaultDelete.cpp | 3 +- src/xrpld/app/tx/detail/VaultDeposit.cpp | 6 +- 5 files changed, 216 insertions(+), 8 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 69127f4b57f..49edb98cb82 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1395,7 +1395,9 @@ addEmptyHolding( // If the line already exists, don't create it again. if (view.read(index)) return tecDUPLICATE; - auto const& sponsorAccountID = getTxReserveSponsorAccountID(tx); + auto const& sponsorAccountID = !isPseudoAccount(sleDst) + ? getTxReserveSponsorAccountID(tx) + : std::nullopt; return trustCreate( view, high, diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index fd2ea8e752d..17cac1750ba 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -22,6 +22,7 @@ #include #include +#include "test/jtx/Env.h" #include "test/jtx/ticket.h" namespace ripple { @@ -3272,6 +3273,211 @@ class Sponsor_test : public beast::unit_test::suite void testVault() { + testcase("Vault"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const gw("gw"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + Asset asset = gw["IOU"].asset(); + + // VaultCreate + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, gw, sponsor); + env.close(); + + Vault vault{env}; + auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); + env(tx, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); // Vault, MPToken(share) + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + BEAST_EXPECT( + env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); + } + // VaultDeposit + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, gw, sponsor); + env.close(); + + Vault vault{env}; + auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); + env(tx); + env.close(); + + env(trust(bob, asset(1000))); + env.close(); + env(pay(gw, bob, asset(1000))); + env.close(); + + BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState + + env(vault.deposit( + {.depositor = bob, .id = keylet.key, .amount = asset(100)}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT( + ownerCount(env, bob) == 2); // RippleState, MPToken(share) + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // MPToken(share) + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == 1); // MPToken(share) + } + // VaultWithdraw + { + // RippleState Vault + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, gw, sponsor); + env.close(); + + Vault vault{env}; + auto [tx, keylet] = + vault.create({.owner = alice, .asset = asset}); + env(tx); + env.close(); + + env(trust(bob, asset(100))); + env.close(); + env(pay(gw, bob, asset(100))); + env.close(); + + env(vault.deposit( + {.depositor = bob, + .id = keylet.key, + .amount = asset(100)}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + env(trust(bob, asset(0))); // remove trustline + env.close(); + + BEAST_EXPECT(ownerCount(env, bob) == 1); // MPToken(share) + BEAST_EXPECT( + sponsoredOwnerCount(env, bob) == 1); // MPToken(share) + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == 1); // MPToken(share) + + // create Trustline + env(vault.withdraw( + {.depositor = bob, + .id = keylet.key, + .amount = asset(50)}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT( + ownerCount(env, bob) == 2); // RippleState, MPToken(share) + BEAST_EXPECT( + sponsoredOwnerCount(env, bob) == + 2); // RippleState, MPToken(share) + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == + 2); // RippleState, MPToken(share) + + // remove sponsored MPToken(share) + env(vault.withdraw( + {.depositor = bob, + .id = keylet.key, + .amount = asset(50)}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState + BEAST_EXPECT( + sponsoredOwnerCount(env, bob) == 1); // RippleState + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == 1); // RippleState + } + // MPToken Vault + { + // VaultWithdraw doesn't create MPToken for depositor + } + } + // VaultClawback + { + // remove sponsored shares MPToken + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, gw, sponsor); + env.close(); + + env(fset(gw, asfAllowTrustLineClawback)); + env.close(); + + Vault vault{env}; + auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); + env(tx); + env.close(); + + env(trust(bob, asset(100))); + env.close(); + env(pay(gw, bob, asset(100))); + env.close(); + + env(vault.deposit( + {.depositor = bob, .id = keylet.key, .amount = asset(100)}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT( + ownerCount(env, bob) == 2); // RippleState, MPToken(share) + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // MPToken(share) + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == 1); // MPToken(share) + + env(vault.clawback( + {.issuer = gw, + .id = keylet.key, + .holder = bob, + .amount = asset(0)}), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + // VaultDelete + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, gw, sponsor); + env.close(); + + env(fset(gw, asfAllowTrustLineClawback)); + env.close(); + + Vault vault{env}; + auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); + env(tx, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); // Vault, MPToken(share) + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + + env(vault.del({.owner = alice, .id = keylet.key})); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } } void @@ -3515,7 +3721,7 @@ class Sponsor_test : public beast::unit_test::suite testOracle(); testSignerList(); testTrustSet(); - // testVault(); + testVault(); // testXChain(); } diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp index 494f7211d1d..cd0d9b1ca78 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp @@ -101,7 +101,8 @@ MPTokenIssuanceCreate::create( if (!acct) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE - auto const sponsor = getTxReserveSponsor(view, tx); + auto const sponsor = + !isPseudoAccount((acct)) ? getTxReserveSponsor(view, tx) : std::nullopt; if (args.priorBalance) { if (auto const ret = checkInsufficientReserve( diff --git a/src/xrpld/app/tx/detail/VaultDelete.cpp b/src/xrpld/app/tx/detail/VaultDelete.cpp index 4b0d92309ca..50d8ebacbf9 100644 --- a/src/xrpld/app/tx/detail/VaultDelete.cpp +++ b/src/xrpld/app/tx/detail/VaultDelete.cpp @@ -166,8 +166,7 @@ VaultDelete::doApply() return tefBAD_LEDGER; // LCOV_EXCL_STOP } - auto const mptSponsor = getLedgerEntryReserveSponsor(view(), mpt); - adjustOwnerCount(view(), pseudoAcct, mptSponsor, -1, j_); + adjustOwnerCount(view(), pseudoAcct, std::nullopt, -1, j_); view().erase(mpt); diff --git a/src/xrpld/app/tx/detail/VaultDeposit.cpp b/src/xrpld/app/tx/detail/VaultDeposit.cpp index efe187fc043..1daaf27efdc 100644 --- a/src/xrpld/app/tx/detail/VaultDeposit.cpp +++ b/src/xrpld/app/tx/detail/VaultDeposit.cpp @@ -302,8 +302,6 @@ VaultDeposit::doApply() if (maximum != 0 && *vault->at(sfAssetsTotal) > maximum) return tecLIMIT_EXCEEDED; - auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); - // Transfer assets from depositor to vault. if (auto const ter = accountSend( view(), @@ -311,7 +309,7 @@ VaultDeposit::doApply() vaultAccount, assetsDeposited, j_, - sponsor, + std::nullopt, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -331,6 +329,8 @@ VaultDeposit::doApply() // LCOV_EXCL_STOP } + auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + // Transfer shares from vault to depositor. if (auto const ter = accountSend( view(), From b09666210db0fc2441e0c4230e45b68128f53912 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 26 Sep 2025 18:25:49 +0900 Subject: [PATCH 050/249] add InvariantChecks for pseudo-account --- src/test/app/Invariants_test.cpp | 15 +++++++++++++++ src/xrpld/app/tx/detail/InvariantCheck.cpp | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index e75685ad9ad..451327a7c7a 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -1468,6 +1468,7 @@ class Invariants_test : public beast::unit_test::suite "pseudo-account sequence changed" "pseudo-account flags are not set" "pseudo-account has a regular key" + "pseudo-account has a sponsorship field" */ struct Mod { @@ -1496,6 +1497,20 @@ class Invariants_test : public beast::unit_test::suite sle->at(sfRegularKey) = Account("regular").id(); }, }, + { + "pseudo-account has a sponsorship field", + [](SLE::pointer& sle) { sle->at(sfSponsoredOwnerCount) = 1; }, + }, + { + "pseudo-account has a sponsorship field", + [](SLE::pointer& sle) { sle->at(sfSponsoringOwnerCount) = 1; }, + }, + { + "pseudo-account has a sponsorship field", + [](SLE::pointer& sle) { + sle->at(sfSponsorAccount) = Account("sponsor").id(); + }, + }, }); for (auto const& mod : mods) diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index 06ade6f959d..ac116ca3373 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -1726,6 +1726,8 @@ ValidPseudoAccounts::visitEntry( // 3. The lsfDisableMaster, lsfDefaultRipple, and lsfDepositAuth // flags are set. // 4. The RegularKey is not set. + // 5. The SponsoredOwnerCount, SponsoringOwnerCount, and + // SponsorAccount fields are not set. { std::vector const& fields = getPseudoAccountFields(); @@ -1757,6 +1759,12 @@ ValidPseudoAccounts::visitEntry( { errors_.emplace_back("pseudo-account has a regular key"); } + if (after->isFieldPresent(sfSponsoredOwnerCount) || + after->isFieldPresent(sfSponsoringOwnerCount) || + after->isFieldPresent(sfSponsorAccount)) + { + errors_.emplace_back("pseudo-account has a sponsorship field"); + } } } } From 5b64ff0dd68b8ad83d3766a0d986b9eb719797e8 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 30 Sep 2025 18:30:38 +0900 Subject: [PATCH 051/249] address 5594, 5592 changes --- src/test/app/Sponsor_test.cpp | 4 +- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 17 ++--- src/xrpld/app/tx/detail/SponsorshipSet.h | 3 + .../app/tx/detail/SponsorshipTransfer.cpp | 11 +-- src/xrpld/app/tx/detail/Transactor.cpp | 68 ++++--------------- src/xrpld/app/tx/detail/Transactor.h | 3 - 6 files changed, 25 insertions(+), 81 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 17cac1750ba..734e869e797 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -227,7 +227,7 @@ class Sponsor_test : public beast::unit_test::suite fee(XRP(1)), sponsor::as(invalid, tfSponsorReserve), sponsor::sig(invalid), - ter(tefBAD_AUTH)); + ter(terNO_ACCOUNT)); // Success env(noop(alice), @@ -273,7 +273,7 @@ class Sponsor_test : public beast::unit_test::suite fee(XRP(1)), sponsor::as(invalid, tfSponsorReserve), sponsor::msig({signer1}), - ter(tefBAD_AUTH)); + ter(tefNOT_MULTI_SIGNING)); env(noop(alice), fee(XRP(1)), diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 159d63b9920..8b80950e9ea 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -25,20 +25,17 @@ namespace ripple { +std::uint32_t +SponsorshipSet::getFlagsMask(PreflightContext const& ctx) +{ + return tfSponsorshipSetMask; +} + NotTEC SponsorshipSet::preflight(PreflightContext const& ctx) { - if (!ctx.rules.enabled(featureSponsor)) - return temDISABLED; - - if (auto const ter = preflight1(ctx)) - return ter; - // check Flags { - if (ctx.tx.isFlag(tfSponsorshipSetMask)) - return temINVALID_FLAG; - if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForFee) && ctx.tx.isFlag(tfSponsorshipClearRequireSignForFee)) return temINVALID_FLAG; @@ -123,7 +120,7 @@ SponsorshipSet::preflight(PreflightContext const& ctx) return temMALFORMED; } - return preflight2(ctx); + return tesSUCCESS; } TER diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.h b/src/xrpld/app/tx/detail/SponsorshipSet.h index ea037a686c3..89de23ee637 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.h +++ b/src/xrpld/app/tx/detail/SponsorshipSet.h @@ -33,6 +33,9 @@ class SponsorshipSet : public Transactor { } + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 84442ae3627..9a45bb77be0 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -34,16 +34,7 @@ namespace ripple { NotTEC SponsorshipTransfer::preflight(PreflightContext const& ctx) { - if (!ctx.rules.enabled(featureSponsor)) - return temDISABLED; - - if (auto const ter = preflight1(ctx)) - return ter; - - if (ctx.tx.getFlags() & tfUniversalMask) - return temINVALID_FLAG; - - return preflight2(ctx); + return tesSUCCESS; } template diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index e8dc544d274..0f73398e3a9 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -786,26 +786,28 @@ Transactor::checkSign( return tesSUCCESS; } - // If the pk is empty and not simulate or simulate and signers, - // then we must be multi-signing. - if (ctx.tx.isFieldPresent(sfSigners)) + if (sigObject.isFieldPresent(sfSponsor)) { - return checkMultiSign(ctx, idAccount, sigObject); - } - - if (ctx.tx.isFieldPresent(sfSponsor)) - { - auto const sponsorObj = ctx.tx.getFieldObject(sfSponsor); + auto const sponsorObj = sigObject.getFieldObject(sfSponsor); auto const isCoSigned = sponsorObj.isFieldPresent(sfTxnSignature) || !sponsorObj.getFieldVL(sfSigningPubKey).empty() || sponsorObj.isFieldPresent(sfSigners); if (isCoSigned) { - if (auto const ret = checkSponsorSign(ctx); !isTesSuccess(ret)) + auto const sponsorAcc = sponsorObj.getAccountID(sfAccount); + if (auto const ret = checkSign(ctx, sponsorAcc, sponsorObj); + !isTesSuccess(ret)) return ret; } } + // If the pk is empty and not simulate or simulate and signers, + // then we must be multi-signing. + if (sigObject.isFieldPresent(sfSigners)) + { + return checkMultiSign(ctx, idAccount, sigObject); + } + // Check Single Sign XRPL_ASSERT( !pkSigner.empty(), "ripple::Transactor::checkSign : non-empty signer"); @@ -880,52 +882,6 @@ Transactor::checkBatchSign(PreclaimContext const& ctx) return ret; } -NotTEC -Transactor::checkSponsorSign(PreclaimContext const& ctx) -{ - NotTEC ret = tesSUCCESS; - - if (!ctx.tx.isFieldPresent(sfSponsor)) - return tesSUCCESS; - - auto const sponsorObj = ctx.tx.getFieldObject(sfSponsor); - - auto const sponsorAcc = sponsorObj.getAccountID(sfAccount); - Blob const& pkSigner = sponsorObj.getFieldVL(sfSigningPubKey); - - auto const sleAccount = ctx.view.read(keylet::account(sponsorAcc)); - if (!sleAccount) - return tefBAD_AUTH; - - if (pkSigner.empty()) - { - STArray const& txSigners(sponsorObj.getFieldArray(sfSigners)); - for (auto const& txSigner : txSigners) - { - if (ret = checkMultiSign(ctx, sponsorAcc, txSigner); - !isTesSuccess(ret)) - return ret; - } - } - else - { - // LCOV_EXCL_START - if (!publicKeyType(makeSlice(pkSigner))) - return tefBAD_AUTH; - // LCOV_EXCL_STOP - - auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner))); - - if (ret = checkSingleSign(ctx, idSigner, sponsorAcc, sleAccount); - !isTesSuccess(ret)) - { - return ret; - } - } - - return ret; -} - NotTEC Transactor::checkSingleSign( PreclaimContext const& ctx, diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index 4d79f3b7219..583896b2637 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -210,9 +210,6 @@ class Transactor static NotTEC checkBatchSign(PreclaimContext const& ctx); - static NotTEC - checkSponsorSign(PreclaimContext const& ctx); - // Returns the fee in fee units, not scaled for load. static XRPAmount calculateBaseFee(ReadView const& view, STTx const& tx); From bc1ef1e8399c659128e29150ee1ff214fa699314 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 2 Oct 2025 18:29:12 +0900 Subject: [PATCH 052/249] add tests for XChainBridge --- src/test/app/Sponsor_test.cpp | 124 ++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 734e869e797..3973a7f0553 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -18,13 +18,14 @@ //============================================================================== #include +#include +#include +#include +#include #include #include -#include "test/jtx/Env.h" -#include "test/jtx/ticket.h" - namespace ripple { namespace test { @@ -3483,6 +3484,121 @@ class Sponsor_test : public beast::unit_test::suite void testXChain() { + testcase("XChain"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const doorA("doorA"); + Account const signer("signer"); + Account const sponsor("sponsor"); + + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, doorA); + env.close(); + + auto jvb = bridge(doorA, XRP, env.master, XRP); + + env(signers(doorA, 1, {signer})); + env.close(); + + // XChainCreateBridge + { + BEAST_EXPECT(ownerCount(env, doorA) == 1); // SignerList + BEAST_EXPECT(sponsoredOwnerCount(env, doorA) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + env(bridge_create(doorA, jvb, XRP(1), XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, doorA) == 2); // Bridge, SignerList + BEAST_EXPECT(sponsoredOwnerCount(env, doorA) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } + // XChainCreateClaimID + { + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + env(xchain_create_claim_id(alice, jvb, XRP(1), bob), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + // XChainOwnedClaimID created + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + } + // XChainCommit + { + BEAST_EXPECT(ownerCount(env, alice) == 1); // XChainOwnedClaimID + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + + env(xchain_commit(alice, jvb, 1, XRP(100), bob), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + // doesn't sponsor anything + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + } + // XChainAddClaimAttestation + { + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + + env(claim_attestation( + alice, jvb, bob, XRP(1), bob, false, 1, bob, signer), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + // XChainOwnedClaimID deleted + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } + // XChainClaim + { + // prepare for claim + { + env(xchain_create_claim_id(alice, jvb, XRP(1), bob), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env(xchain_commit( + alice, jvb, 2, XRP(100))); // omit destination + env(claim_attestation( + alice, + jvb, + bob, + XRP(100), + bob, + false, + 2, + std::nullopt, + signer)); + env.close(); + } + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + + env(xchain_claim(alice, jvb, 2, XRP(100), bob)); + env.close(); + + // XChainOwnedClaimID deleted + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } } void @@ -3722,7 +3838,7 @@ class Sponsor_test : public beast::unit_test::suite testSignerList(); testTrustSet(); testVault(); - // testXChain(); + testXChain(); } void From 59c3d5bddcb142886a031501b3113e2b9f6a051d Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 2 Oct 2025 23:37:44 +0900 Subject: [PATCH 053/249] add insufficient reserve check for xchain, vault --- src/test/app/Sponsor_test.cpp | 114 +++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 9 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 3973a7f0553..6c49b548318 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -3292,6 +3292,28 @@ class Sponsor_test : public beast::unit_test::suite Vault vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); + + { + auto const ticketSeq = env.seq(sponsor) + 1; + env(ticket::create(sponsor, 1)); + env.close(); + adjustAccountXRPBalance( + env, sponsor, reserve(env, 3) - drops(1)); + // check with OwnerCount=3 because free MPToken condition exists + env(tx, + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); + env(noop(sponsor), ticket::use(ticketSeq)); + env.close(); + } + + // get keylet using latest sequence + keylet = + std::get<1>(vault.create({.owner = alice, .asset = asset})); + env(tx, sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); @@ -3321,8 +3343,27 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState - env(vault.deposit( - {.depositor = bob, .id = keylet.key, .amount = asset(100)}), + auto const depositTx = vault.deposit( + {.depositor = bob, .id = keylet.key, .amount = asset(100)}); + { + // TODO: https://github.com/XRPLF/rippled/issues/5837 + // auto const ticketSeq = env.seq(sponsor) + 1; + // env(ticket::create(sponsor, 1)); + // env.close(); + // adjustAccountXRPBalance( + // env, sponsor, reserve(env, 3) - drops(1)); + // // check with OwnerCount=3 because free MPToken condition + // exists env(depositTx, + // sponsor::as(sponsor, tfSponsorReserve), + // sponsor::sig(sponsor), + // ter(tecINSUFFICIENT_RESERVE)); + // env.close(); + // adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); + // env(noop(sponsor), ticket::use(ticketSeq)); + // env.close(); + } + + env(depositTx, sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); env.close(); @@ -3352,10 +3393,29 @@ class Sponsor_test : public beast::unit_test::suite env(pay(gw, bob, asset(100))); env.close(); - env(vault.deposit( - {.depositor = bob, - .id = keylet.key, - .amount = asset(100)}), + auto const depositTx = vault.deposit( + {.depositor = bob, .id = keylet.key, .amount = asset(100)}); + + { + // https://github.com/XRPLF/rippled/issues/5837 + // auto const ticketSeq = env.seq(sponsor) + 1; + // env(ticket::create(sponsor, 1)); + // env.close(); + // adjustAccountXRPBalance( + // env, sponsor, reserve(env, 3) - drops(1)); + // // check with OwnerCount=3 because free MPToken condition + // // exists + // env(depositTx, + // sponsor::as(sponsor, tfSponsorReserve), + // sponsor::sig(sponsor), + // ter(tecINSUFFICIENT_RESERVE)); + // env.close(); + // adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); + // env(noop(sponsor), ticket::use(ticketSeq)); + // env.close(); + } + + env(depositTx, sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); env.close(); @@ -3427,8 +3487,29 @@ class Sponsor_test : public beast::unit_test::suite env(pay(gw, bob, asset(100))); env.close(); - env(vault.deposit( - {.depositor = bob, .id = keylet.key, .amount = asset(100)}), + auto const depositTx = vault.deposit( + {.depositor = bob, .id = keylet.key, .amount = asset(100)}); + + { + // https://github.com/XRPLF/rippled/issues/5837 + // auto const ticketSeq = env.seq(sponsor) + 1; + // env(ticket::create(sponsor, 1)); + // env.close(); + // adjustAccountXRPBalance( + // env, sponsor, reserve(env, 3) - drops(1)); + // // check with OwnerCount=3 because free MPToken condition + // // exists + // env(depositTx, + // sponsor::as(sponsor, tfSponsorReserve), + // sponsor::sig(sponsor), + // ter(tecINSUFFICIENT_RESERVE)); + // env.close(); + // adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); + // env(noop(sponsor), ticket::use(ticketSeq)); + // env.close(); + } + + env(depositTx, sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); env.close(); @@ -3507,6 +3588,14 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, doorA) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(bridge_create(doorA, jvb, XRP(1), XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); + env(bridge_create(doorA, jvb, XRP(1), XRP(1)), sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); @@ -3522,6 +3611,14 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); + env(xchain_create_claim_id(alice, jvb, XRP(1), bob), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(xchain_create_claim_id(alice, jvb, XRP(1), bob), sponsor::as(sponsor, tfSponsorReserve), sponsor::sig(sponsor)); @@ -3818,7 +3915,6 @@ class Sponsor_test : public beast::unit_test::suite void testSponsorReserve() { - // TODO: add checks fo InsufficientReserve for Sponsoring ledger entry testRequireFlag(); testAMM(); testCheck(); From ee385e9d3b12344490aa8081e2e113bcda0c84af Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 3 Oct 2025 15:04:33 +0900 Subject: [PATCH 054/249] fix bad merge --- src/xrpld/app/tx/detail/Transactor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 04e27f88213..4b3b5d4c88b 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -796,7 +796,8 @@ Transactor::checkSign( if (isCoSigned) { auto const sponsorAcc = sponsorObj.getAccountID(sfAccount); - if (auto const ret = checkSign(ctx, sponsorAcc, sponsorObj); + if (auto const ret = + checkSign(view, flags, sponsorAcc, sponsorObj, j); !isTesSuccess(ret)) return ret; } From 101b70f0e8f6af3ad0f31c220e63e263e33b53cf Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 3 Oct 2025 15:05:48 +0900 Subject: [PATCH 055/249] add assert when adding/removing sponsor field to LedgerEntry --- src/libxrpl/ledger/View.cpp | 12 ++++- src/test/app/Sponsor_test.cpp | 83 +++++++++++++++++++++++++++-------- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index aefcf6271cc..a8998efcd17 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1127,6 +1127,11 @@ addSponsorToLedgerEntry( std::optional> const& sponsorSle, SF_ACCOUNT const& field) { + XRPL_ASSERT( + (sle->getType() == ltRIPPLE_STATE && + (field == sfHighSponsorAccount || field == sfLowSponsorAccount)) || + (sle->getType() != ltRIPPLE_STATE && field == sfSponsorAccount), + "addSponsorToLedgerEntry : Invalid field to the LedgerEntry"); if (sponsorSle) sle->setAccountID(field, (*sponsorSle)->getAccountID(sfAccount)); } @@ -1136,6 +1141,11 @@ removeSponsorFromLedgerEntry( std::shared_ptr const& sle, SF_ACCOUNT const& field) { + XRPL_ASSERT( + (sle->getType() == ltRIPPLE_STATE && + (field == sfHighSponsorAccount || field == sfLowSponsorAccount)) || + (sle->getType() != ltRIPPLE_STATE && field == sfSponsorAccount), + "removeSponsorFromLedgerEntry : Invalid field to the LedgerEntry"); if (sle->isFieldPresent(field)) sle->makeFieldAbsent(field); } @@ -2455,7 +2465,7 @@ updateTrustLine( sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); removeSponsorFromLedgerEntry( - sle, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); + state, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); // Balance is zero, receiver reserve is clear. if (!after // Balance is zero. diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 6c49b548318..f2b17891c24 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -3172,14 +3172,31 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); + auto const validateSponsoredTrustline = + [&](std::shared_ptr const& sle, + bool isIssuerHigh, + Account const& sponsor) { + BEAST_EXPECT( + sle->getAccountID( + isIssuerHigh + ? sfLowSponsorAccount + : sfHighSponsorAccount) == sponsor.id()); + BEAST_EXPECT(!sle->isFieldPresent( + isIssuerHigh ? sfHighSponsorAccount + : sfLowSponsorAccount)); + }; + auto const& highAcc = alice > bob ? alice : bob; auto const& lowAcc = alice > bob ? bob : alice; + + // create and delete for (bool isIssuerHigh : {false, true}) { auto const& issuer = isIssuerHigh ? highAcc : lowAcc; auto const& user = isIssuerHigh ? lowAcc : highAcc; auto const USD = issuer["USD"]; + auto const currency = USD.currency; // create TrustLine env(trust(user, USD(100)), @@ -3191,18 +3208,12 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - auto const line = - env.le(keylet::line(user, issuer, USD.currency)); - BEAST_EXPECT( - line->getAccountID( - isIssuerHigh ? sfLowSponsorAccount - : sfHighSponsorAccount) == sponsor.id()); - BEAST_EXPECT(!line->isFieldPresent( - isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); + auto const line = env.le(keylet::line(user, issuer, currency)); + validateSponsoredTrustline(line, isIssuerHigh, sponsor); // transfer sponsor env(sponsor::transfer( - user, keylet::line(user, issuer, USD.currency).key), + user, keylet::line(user, issuer, currency).key), sponsor::as(sponsor2, tfSponsorReserve), sponsor::sig(sponsor2)); env.close(); @@ -3212,14 +3223,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - auto const line2 = - env.le(keylet::line(user, issuer, USD.currency)); - BEAST_EXPECT( - line2->getAccountID( - isIssuerHigh ? sfLowSponsorAccount - : sfHighSponsorAccount) == sponsor2.id()); - BEAST_EXPECT(!line2->isFieldPresent( - isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); + auto const line2 = env.le(keylet::line(user, issuer, currency)); + validateSponsoredTrustline(line2, isIssuerHigh, sponsor2); // delete TrustLine env(trust(user, USD(0))); @@ -3229,7 +3234,49 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(!env.le(keylet::line(user, issuer, USD.currency))); + BEAST_EXPECT(!env.le(keylet::line(user, issuer, currency))); + } + + // update + for (bool isIssuerHigh : {false, true}) + { + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; + + auto const USD = issuer["USD"]; + auto const currency = USD.currency; + + // create TrustLine from issuer + env(trust(issuer, user["USD"](100))); + env.close(); + + BEAST_EXPECT(env.le(keylet::line(user, issuer, currency))); + + // update TrustLine from user to make reserve + env(trust(user, USD(100)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, user) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + auto const line = env.le(keylet::line(user, issuer, currency)); + validateSponsoredTrustline(line, isIssuerHigh, sponsor); + + // update TrustLine from user to clear reserve + env(trust(user, USD(0))); + env.close(); + + BEAST_EXPECT(ownerCount(env, user) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(env.le(keylet::line(user, issuer, currency))); + + // remove TrustLine from issuer + env(trust(issuer, user["USD"](0))); + env.close(); } } From bc71d9c138d3e27244e0f6278b76a8daf04c7263 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Fri, 3 Oct 2025 14:56:39 -0400 Subject: [PATCH 056/249] Add support for extra transaction signature validation - Restructures `STTx` signature checking code to be able to handle a `sigObject`, which may be the full transaction, or may be an object field containing a separate signature. Either way, the `sigObject` can be a single- or multi-sign signature. - This is distinct from 550f90a75e (#5594), which changed the check in Transactor, which validates whether a given account is allowed to sign for the given transaction. This cryptographically checks the signature validity. --- include/xrpl/protocol/STTx.h | 40 ++++++++++-- src/libxrpl/protocol/STTx.cpp | 120 ++++++++++++++++++++++++---------- 2 files changed, 120 insertions(+), 40 deletions(-) diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index bee7d10c16e..f1fb8068e1d 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -88,7 +88,13 @@ class STTx final : public STObject, public CountedObject // Outer transaction functions / signature functions. Blob - getSignature() const; + getSignature(STObject const& sigObject) const; + + Blob + getSignature() const + { + return getSignature(*this); + } uint256 getSigningHash() const; @@ -125,13 +131,34 @@ class STTx final : public STObject, public CountedObject getJson(JsonOptions options, bool binary) const; void - sign(PublicKey const& publicKey, SecretKey const& secretKey); + sign( + PublicKey const& publicKey, + SecretKey const& secretKey, + std::optional> signatureTarget = + {}); + + enum class RequireFullyCanonicalSig : bool { no, yes }; /** Check the signature. + @param requireCanonicalSig If `true`, check that the signature is fully + canonical. If `false`, only check that the signature is valid. + @param rules The current ledger rules. + @param pSig Pointer to object that contains the signature fields, if not + using "this". Will most often be null @return `true` if valid signature. If invalid, the error message string. */ - enum class RequireFullyCanonicalSig : bool { no, yes }; + Expected + checkSign( + RequireFullyCanonicalSig requireCanonicalSig, + Rules const& rules, + STObject const* pSig) const; + /** Check the signature. + @param requireCanonicalSig If `true`, check that the signature is fully + canonical. If `false`, only check that the signature is valid. + @param rules The current ledger rules. + @return `true` if valid signature. If invalid, the error message string. + */ Expected checkSign(RequireFullyCanonicalSig requireCanonicalSig, Rules const& rules) const; @@ -166,12 +193,15 @@ class STTx final : public STObject, public CountedObject private: Expected - checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const; + checkSingleSign( + RequireFullyCanonicalSig requireCanonicalSig, + STObject const* pSig) const; Expected checkMultiSign( RequireFullyCanonicalSig requireCanonicalSig, - Rules const& rules) const; + Rules const& rules, + STObject const* pSig) const; Expected checkBatchSingleSign( diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 549fe3d7512..87e01190fbc 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -212,11 +212,11 @@ STTx::getSigningHash() const } Blob -STTx::getSignature() const +STTx::getSignature(STObject const& sigObject) const { try { - return getFieldVL(sfTxnSignature); + return sigObject.getFieldVL(sfTxnSignature); } catch (std::exception const&) { @@ -246,30 +246,44 @@ STTx::getSeqValue() const } void -STTx::sign(PublicKey const& publicKey, SecretKey const& secretKey) +STTx::sign( + PublicKey const& publicKey, + SecretKey const& secretKey, + std::optional> signatureTarget) { auto const data = getSigningData(*this); auto const sig = ripple::sign(publicKey, secretKey, makeSlice(data)); - setFieldVL(sfTxnSignature, sig); + if (signatureTarget) + { + auto& target = peekFieldObject(*signatureTarget); + target.setFieldVL(sfTxnSignature, sig); + } + else + { + setFieldVL(sfTxnSignature, sig); + } tid_ = getHash(HashPrefix::transactionID); } Expected STTx::checkSign( RequireFullyCanonicalSig requireCanonicalSig, - Rules const& rules) const + Rules const& rules, + STObject const* pSig) const { try { // Determine whether we're single- or multi-signing by looking // at the SigningPubKey. If it's empty we must be // multi-signing. Otherwise we're single-signing. - Blob const& signingPubKey = getFieldVL(sfSigningPubKey); + STObject const& sigObject{pSig ? *pSig : *this}; + + Blob const& signingPubKey = sigObject.getFieldVL(sfSigningPubKey); return signingPubKey.empty() - ? checkMultiSign(requireCanonicalSig, rules) - : checkSingleSign(requireCanonicalSig); + ? checkMultiSign(requireCanonicalSig, rules, pSig) + : checkSingleSign(requireCanonicalSig, pSig); } catch (std::exception const&) { @@ -277,6 +291,26 @@ STTx::checkSign( return Unexpected("Internal signature check failure."); } +Expected +STTx::checkSign( + RequireFullyCanonicalSig requireCanonicalSig, + Rules const& rules) const +{ + if (auto const ret = checkSign(requireCanonicalSig, rules, nullptr); !ret) + return ret; + + /* Placeholder for field that will be added by Lending Protocol + if (isFieldPresent(sfCounterpartySignature)) + { + auto const counterSig = getFieldObject(sfCounterpartySignature); + if (auto const ret = checkSign(requireCanonicalSig, rules, &counterSig); + !ret) + return Unexpected("Counterparty: " + ret.error()); + } + */ + return {}; +} + Expected STTx::checkBatchSign( RequireFullyCanonicalSig requireCanonicalSig, @@ -430,23 +464,23 @@ STTx::getMetaSQL( static Expected singleSignHelper( - STObject const& signer, + STObject const& sigObject, Slice const& data, bool const fullyCanonical) { // We don't allow both a non-empty sfSigningPubKey and an sfSigners. // That would allow the transaction to be signed two ways. So if both // fields are present the signature is invalid. - if (signer.isFieldPresent(sfSigners)) + if (sigObject.isFieldPresent(sfSigners)) return Unexpected("Cannot both single- and multi-sign."); bool validSig = false; try { - auto const spk = signer.getFieldVL(sfSigningPubKey); + auto const spk = sigObject.getFieldVL(sfSigningPubKey); if (publicKeyType(makeSlice(spk))) { - Blob const signature = signer.getFieldVL(sfTxnSignature); + Blob const signature = sigObject.getFieldVL(sfTxnSignature); validSig = verify( PublicKey(makeSlice(spk)), data, @@ -466,12 +500,15 @@ singleSignHelper( } Expected -STTx::checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const +STTx::checkSingleSign( + RequireFullyCanonicalSig requireCanonicalSig, + STObject const* pSig) const { + STObject const& sigObject{pSig ? *pSig : *this}; auto const data = getSigningData(*this); bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig) || (requireCanonicalSig == STTx::RequireFullyCanonicalSig::yes); - return singleSignHelper(*this, makeSlice(data), fullyCanonical); + return singleSignHelper(sigObject, makeSlice(data), fullyCanonical); } Expected @@ -499,31 +536,29 @@ STTx::checkSponsorSingleSign( Expected multiSignHelper( - STObject const& signerObj, + STObject const& sigObject, + std::optional txnAccountID, bool const fullyCanonical, std::function makeMsg, Rules const& rules) { // Make sure the MultiSigners are present. Otherwise they are not - // attempting multi-signing and we just have a bad Signers. - if (!signerObj.isFieldPresent(sfSigners)) + // attempting multi-signing and we just have a bad SigningPubKey. + if (!sigObject.isFieldPresent(sfSigners)) return Unexpected("Empty SigningPubKey."); // We don't allow both an sfSigners and an sfTxnSignature. Both fields // being present would indicate that the transaction is signed both ways. - if (signerObj.isFieldPresent(sfTxnSignature)) + if (sigObject.isFieldPresent(sfTxnSignature)) return Unexpected("Cannot both single- and multi-sign."); - STArray const& signers{signerObj.getFieldArray(sfSigners)}; + STArray const& signers{sigObject.getFieldArray(sfSigners)}; // There are well known bounds that the number of signers must be within. if (signers.size() < STTx::minMultiSigners || signers.size() > STTx::maxMultiSigners(&rules)) return Unexpected("Invalid Signers array size."); - // We also use the sfAccount field inside the loop. Get it once. - auto const txnAccountID = signerObj.getAccountID(sfAccount); - // Signers must be in sorted order by AccountID. AccountID lastAccountID(beast::zero); @@ -531,8 +566,10 @@ multiSignHelper( { auto const accountID = signer.getAccountID(sfAccount); - // The account owner may not multisign for themselves. - if (accountID == txnAccountID) + // The account owner may not usually multisign for themselves. + // If they can, txnAccountID will be unseated, which is not equal to any + // value. + if (txnAccountID == accountID) return Unexpected("Invalid multisigner."); // No duplicate signers allowed. @@ -548,6 +585,7 @@ multiSignHelper( // Verify the signature. bool validSig = false; + std::optional errorWhat; try { auto spk = signer.getFieldVL(sfSigningPubKey); @@ -561,15 +599,16 @@ multiSignHelper( fullyCanonical); } } - catch (std::exception const&) + catch (std::exception const& e) { // We assume any problem lies with the signature. validSig = false; + errorWhat = e.what(); } if (!validSig) return Unexpected( std::string("Invalid signature on account ") + - toBase58(accountID) + "."); + toBase58(accountID) + errorWhat.value_or("") + "."); } // All signatures verified. return {}; @@ -591,8 +630,9 @@ STTx::checkBatchMultiSign( serializeBatch(dataStart, getFlags(), getBatchTransactionIDs()); return multiSignHelper( batchSigner, + std::nullopt, fullyCanonical, - [&dataStart](AccountID const& accountID) mutable -> Serializer { + [&dataStart](AccountID const& accountID) -> Serializer { Serializer s = dataStart; finishMultiSigningData(accountID, s); return s; @@ -629,19 +669,28 @@ STTx::checkSponsorMultiSign( Expected STTx::checkMultiSign( RequireFullyCanonicalSig requireCanonicalSig, - Rules const& rules) const + Rules const& rules, + STObject const* pSig) const { + STObject const& sigObject{pSig ? *pSig : *this}; + bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig) || (requireCanonicalSig == RequireFullyCanonicalSig::yes); + // Used inside the loop in multiSignHelper to enforce that + // the account owner may not multisign for themselves. + auto const txnAccountID = + pSig ? std::nullopt : std::optional(getAccountID(sfAccount)); + // We can ease the computational load inside the loop a bit by // pre-constructing part of the data that we hash. Fill a Serializer // with the stuff that stays constant from signature to signature. Serializer dataStart = startMultiSigningData(*this); return multiSignHelper( - *this, + sigObject, + txnAccountID, fullyCanonical, - [&dataStart](AccountID const& accountID) mutable -> Serializer { + [&dataStart](AccountID const& accountID) -> Serializer { Serializer s = dataStart; finishMultiSigningData(accountID, s); return s; @@ -673,11 +722,12 @@ STTx::getBatchTransactionIDs() const XRPL_ASSERT( getFieldArray(sfRawTransactions).size() != 0, "STTx::getBatchTransactionIDs : empty raw transactions"); - if (batch_txn_ids_.size() != 0) - return batch_txn_ids_; - - for (STObject const& rb : getFieldArray(sfRawTransactions)) - batch_txn_ids_.push_back(rb.getHash(HashPrefix::transactionID)); + // Don't early return so that the size assert is always hit. + if (batch_txn_ids_.size() == 0) + { + for (STObject const& rb : getFieldArray(sfRawTransactions)) + batch_txn_ids_.push_back(rb.getHash(HashPrefix::transactionID)); + } XRPL_ASSERT( batch_txn_ids_.size() == getFieldArray(sfRawTransactions).size(), From d59772657e9e1955482e212f398a8aad387f9fa4 Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 4 Oct 2025 14:37:06 +0900 Subject: [PATCH 057/249] refactor: signature autofilling for Simulate RPC --- src/xrpld/rpc/handlers/Simulate.cpp | 63 ++++++++++++++++------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/src/xrpld/rpc/handlers/Simulate.cpp b/src/xrpld/rpc/handlers/Simulate.cpp index 092b0b45627..f8f3dc9f8cf 100644 --- a/src/xrpld/rpc/handlers/Simulate.cpp +++ b/src/xrpld/rpc/handlers/Simulate.cpp @@ -72,39 +72,23 @@ getAutofillSequence(Json::Value const& tx_json, RPC::JsonContext& context) } static std::optional -autofillTx(Json::Value& tx_json, RPC::JsonContext& context) +autofillSigunature(Json::Value& sigObject) { - if (!tx_json.isMember(jss::Fee)) - { - // autofill Fee - // Must happen after all the other autofills happen - // Error handling/messaging works better that way - auto feeOrError = RPC::getCurrentNetworkFee( - context.role, - context.app.config(), - context.app.getFeeTrack(), - context.app.getTxQ(), - context.app, - tx_json); - if (feeOrError.isMember(jss::error)) - return feeOrError; - tx_json[jss::Fee] = feeOrError; - } - - if (!tx_json.isMember(jss::SigningPubKey)) + if (!sigObject.isMember(jss::SigningPubKey)) { // autofill SigningPubKey - tx_json[jss::SigningPubKey] = ""; + sigObject[jss::SigningPubKey] = ""; } - if (tx_json.isMember(jss::Signers)) + if (sigObject.isMember(jss::Signers)) { - if (!tx_json[jss::Signers].isArray()) + if (!sigObject[jss::Signers].isArray()) return RPC::invalid_field_error("tx.Signers"); // check multisigned signers - for (unsigned index = 0; index < tx_json[jss::Signers].size(); index++) + for (unsigned index = 0; index < sigObject[jss::Signers].size(); + index++) { - auto& signer = tx_json[jss::Signers][index]; + auto& signer = sigObject[jss::Signers][index]; if (!signer.isObject() || !signer.isMember(jss::Signer) || !signer[jss::Signer].isObject()) return RPC::invalid_field_error( @@ -129,16 +113,41 @@ autofillTx(Json::Value& tx_json, RPC::JsonContext& context) } } - if (!tx_json.isMember(jss::TxnSignature)) + if (!sigObject.isMember(jss::TxnSignature)) { // autofill TxnSignature - tx_json[jss::TxnSignature] = ""; + sigObject[jss::TxnSignature] = ""; } - else if (tx_json[jss::TxnSignature] != "") + else if (sigObject[jss::TxnSignature] != "") { // Transaction must not be signed return rpcError(rpcTX_SIGNED); } + return std::nullopt; +} + +static std::optional +autofillTx(Json::Value& tx_json, RPC::JsonContext& context) +{ + if (!tx_json.isMember(jss::Fee)) + { + // autofill Fee + // Must happen after all the other autofills happen + // Error handling/messaging works better that way + auto feeOrError = RPC::getCurrentNetworkFee( + context.role, + context.app.config(), + context.app.getFeeTrack(), + context.app.getTxQ(), + context.app, + tx_json); + if (feeOrError.isMember(jss::error)) + return feeOrError; + tx_json[jss::Fee] = feeOrError; + } + + if (auto error = autofillSigunature(tx_json)) + return *error; if (!tx_json.isMember(jss::Sequence)) { From 8d32b0f85648afb73a11f35e08df441508a227a8 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Mon, 6 Oct 2025 15:10:54 -0400 Subject: [PATCH 058/249] Add jtx, STObject, and RPC support for sig object fields --- include/xrpl/protocol/STObject.h | 5 ++ include/xrpl/protocol/jss.h | 1 + src/libxrpl/protocol/STObject.cpp | 16 ++++ src/test/jtx/JTx.h | 7 +- src/test/jtx/TestHelpers.h | 2 +- src/test/jtx/amount.h | 8 +- src/test/jtx/impl/Env.cpp | 19 ++++- src/test/jtx/impl/mpt.cpp | 2 +- src/test/jtx/impl/multisign.cpp | 17 +++- src/test/jtx/impl/sig.cpp | 14 +++- src/test/jtx/impl/utility.cpp | 16 +++- src/test/jtx/mpt.h | 2 +- src/test/jtx/multisign.h | 53 ++++++++++++- src/test/jtx/sig.h | 25 +++++- src/test/jtx/utility.h | 6 ++ src/test/rpc/RPCCall_test.cpp | 98 +++++++++++++++++++----- src/xrpld/app/main/Main.cpp | 2 +- src/xrpld/app/tx/detail/Batch.cpp | 74 +++++++++++++----- src/xrpld/rpc/detail/RPCCall.cpp | 20 ++++- src/xrpld/rpc/detail/TransactionSign.cpp | 71 +++++++++++++++-- 20 files changed, 380 insertions(+), 78 deletions(-) diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index e045f8c98c1..7d316ddd78d 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -244,6 +244,9 @@ class STObject : public STBase, public CountedObject getFieldPathSet(SField const& field) const; STVector256 const& getFieldV256(SField const& field) const; + // If not found, returns an object constructed with the given field + STObject + getFieldObject(SField const& field) const; STArray const& getFieldArray(SField const& field) const; STObject const& @@ -392,6 +395,8 @@ class STObject : public STBase, public CountedObject setFieldV256(SField const& field, STVector256 const& v); void setFieldArray(SField const& field, STArray const& v); + void + setFieldObject(SField const& field, STObject const& v); template void diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 52cf8fba140..b6d306f2605 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -569,6 +569,7 @@ JSS(settle_delay); // out: AccountChannels JSS(severity); // in: LogLevel JSS(shares); // out: VaultInfo JSS(signature); // out: NetworkOPs, ChannelAuthorize +JSS(signature_target); // in: TransactionSign JSS(signature_verified); // out: ChannelVerify JSS(signing_key); // out: NetworkOPs JSS(signing_keys); // out: ValidatorList diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 8127e7987f2..3c8b76f3d7c 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -688,6 +688,16 @@ STObject::getFieldV256(SField const& field) const return getFieldByConstRef(field, empty); } +STObject +STObject::getFieldObject(SField const& field) const +{ + STObject const empty{field}; + auto ret = getFieldByConstRef(field, empty); + if (ret != empty) + ret.applyTemplateFromSField(field); + return ret; +} + STArray const& STObject::getFieldArray(SField const& field) const { @@ -840,6 +850,12 @@ STObject::setFieldArray(SField const& field, STArray const& v) setFieldUsingAssignment(field, v); } +void +STObject::setFieldObject(SField const& field, STObject const& v) +{ + setFieldUsingAssignment(field, v); +} + Json::Value STObject::getJson(JsonOptions options) const { diff --git a/src/test/jtx/JTx.h b/src/test/jtx/JTx.h index a074adb6000..59792c87887 100644 --- a/src/test/jtx/JTx.h +++ b/src/test/jtx/JTx.h @@ -55,8 +55,11 @@ struct JTx bool fill_sig = true; bool fill_netid = true; std::shared_ptr stx; - std::function signer; - std::function sponsorSigner; + // Functions that sign the transaction from the Account + std::vector> mainSigners; + // Functions that sign something else after the mainSigners, such as + // sfCounterpartySignature + std::vector> postSigners; JTx() = default; JTx(JTx const&) = default; diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index e98f0a521df..6e005182262 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -637,7 +637,7 @@ create( } // namespace check -static constexpr FeeLevel64 baseFeeLevel{256}; +static constexpr FeeLevel64 baseFeeLevel{TxQ::baseLevel}; static constexpr FeeLevel64 minEscalationFeeLevel = baseFeeLevel * 500; template diff --git a/src/test/jtx/amount.h b/src/test/jtx/amount.h index a793f3a2875..f14a65f6b8e 100644 --- a/src/test/jtx/amount.h +++ b/src/test/jtx/amount.h @@ -213,14 +213,16 @@ struct PrettyAsset template PrettyAmount - operator()(T v) const + operator()(T v, Number::rounding_mode rounding = Number::getround()) const { - return operator()(Number(v)); + return operator()(Number(v), rounding); } PrettyAmount - operator()(Number v) const + operator()(Number v, Number::rounding_mode rounding = Number::getround()) + const { + NumberRoundModeGuard mg(rounding); STAmount amount{asset_, v * scale_}; return {amount, ""}; } diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 22bc468772c..68e783e0e3c 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -558,8 +559,22 @@ void Env::autofill_sig(JTx& jt) { auto& jv = jt.jv; - if (jt.signer) - return jt.signer(*this, jt); + + scope_success success([&]() { + // Call all the post-signers after the main signers or autofill are done + for (auto const& signer : jt.postSigners) + signer(*this, jt); + }); + + // Call all the main signers + if (!jt.mainSigners.empty()) + { + for (auto const& signer : jt.mainSigners) + signer(*this, jt); + return; + } + + // If the sig is still needed, get it here. if (!jt.fill_sig) return; auto const account = jv.isMember(sfDelegate.jsonName) diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index f2f51492e32..aaa5e433f2c 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -507,7 +507,7 @@ MPTTester::getFlags(std::optional const& holder) const } MPT -MPTTester::operator[](std::string const& name) +MPTTester::operator[](std::string const& name) const { return MPT(name, issuanceID()); } diff --git a/src/test/jtx/impl/multisign.cpp b/src/test/jtx/impl/multisign.cpp index 6ed6df68049..fc6163a2f33 100644 --- a/src/test/jtx/impl/multisign.cpp +++ b/src/test/jtx/impl/multisign.cpp @@ -69,8 +69,15 @@ void msig::operator()(Env& env, JTx& jt) const { auto const mySigners = signers; - jt.signer = [mySigners, &env](Env&, JTx& jtx) { - jtx[sfSigningPubKey.getJsonName()] = ""; + auto callback = [subField = subField, mySigners, &env](Env&, JTx& jtx) { + // Where to put the signature. Supports sfCounterPartySignature. + auto& sigObject = subField ? jtx[*subField] : jtx.jv; + + // The signing pub key is only required at the top level. + if (!subField) + sigObject[sfSigningPubKey] = ""; + else if (sigObject.isNull()) + sigObject = Json::Value(Json::objectValue); std::optional st; try { @@ -81,7 +88,7 @@ msig::operator()(Env& env, JTx& jt) const env.test.log << pretty(jtx.jv) << std::endl; Rethrow(); } - auto& js = jtx[sfSigners.getJsonName()]; + auto& js = sigObject[sfSigners]; for (std::size_t i = 0; i < mySigners.size(); ++i) { auto const& e = mySigners[i]; @@ -96,6 +103,10 @@ msig::operator()(Env& env, JTx& jt) const strHex(Slice{sig.data(), sig.size()}); } }; + if (!subField) + jt.mainSigners.emplace_back(callback); + else + jt.postSigners.emplace_back(callback); } } // namespace jtx diff --git a/src/test/jtx/impl/sig.cpp b/src/test/jtx/impl/sig.cpp index fa1977fe080..6ea1c153cb7 100644 --- a/src/test/jtx/impl/sig.cpp +++ b/src/test/jtx/impl/sig.cpp @@ -29,12 +29,22 @@ sig::operator()(Env&, JTx& jt) const { if (!manual_) return; - jt.fill_sig = false; + if (!subField_) + jt.fill_sig = false; if (account_) { // VFALCO Inefficient pre-C++14 auto const account = *account_; - jt.signer = [account](Env&, JTx& jtx) { jtx::sign(jtx.jv, account); }; + auto callback = [subField = subField_, account](Env&, JTx& jtx) { + // Where to put the signature. Supports sfCounterPartySignature. + auto& sigObject = subField ? jtx[*subField] : jtx.jv; + + jtx::sign(jtx.jv, account, sigObject); + }; + if (!subField_) + jt.mainSigners.emplace_back(callback); + else + jt.postSigners.emplace_back(callback); } } diff --git a/src/test/jtx/impl/utility.cpp b/src/test/jtx/impl/utility.cpp index 0df9ac2cb0f..fbdbaee4ef7 100644 --- a/src/test/jtx/impl/utility.cpp +++ b/src/test/jtx/impl/utility.cpp @@ -43,13 +43,21 @@ parse(Json::Value const& jv) return std::move(*p.object); } +void +sign(Json::Value& jv, Account const& account, Json::Value& sigObject) +{ + sigObject[jss::SigningPubKey] = strHex(account.pk().slice()); + Serializer ss; + ss.add32(HashPrefix::txSign); + parse(jv).addWithoutSigningFields(ss); + auto const sig = ripple::sign(account.pk(), account.sk(), ss.slice()); + sigObject[jss::TxnSignature] = strHex(Slice{sig.data(), sig.size()}); +} + void sign(Json::Value& jv, Account const& account) { - jv[jss::SigningPubKey] = strHex(account.pk().slice()); - auto const blob = STTx::getSigningData(parse(jv)); - auto const sig = ripple::sign(account.pk(), account.sk(), makeSlice(blob)); - jv[jss::TxnSignature] = strHex(Slice{sig.data(), sig.size()}); + sign(jv, account, jv); } void diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index 2eacac68ec5..422afa8fab6 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -235,7 +235,7 @@ class MPTTester getBalance(Account const& account) const; MPT - operator[](std::string const& name); + operator[](std::string const& name) const; private: using SLEP = std::shared_ptr; diff --git a/src/test/jtx/multisign.h b/src/test/jtx/multisign.h index 1fed895c6d1..7a035a9ff0f 100644 --- a/src/test/jtx/multisign.h +++ b/src/test/jtx/multisign.h @@ -67,18 +67,63 @@ class msig { public: std::vector signers; - - msig(std::vector signers_) : signers(std::move(signers_)) + /** Alternative transaction object field in which to place the signer list. + * + * subField is only supported if an account_ is provided as well. + */ + SField const* const subField = nullptr; + /// Used solely as a convenience placeholder for ctors that do _not_ specify + /// a subfield. + static constexpr SField* const topLevel = nullptr; + + msig(SField const* subField_, std::vector signers_) + : signers(std::move(signers_)), subField(subField_) { sortSigners(signers); } + msig(SField const& subField_, std::vector signers_) + : msig{&subField_, signers_} + { + } + + msig(std::vector signers_) : msig(topLevel, signers_) + { + } + template requires std::convertible_to + explicit msig(SField const* subField_, AccountType&& a0, Accounts&&... aN) + : msig{ + subField_, + std::vector{ + std::forward(a0), + std::forward(aN)...}} + { + } + + template + requires std::convertible_to + explicit msig(SField const& subField_, AccountType&& a0, Accounts&&... aN) + : msig{ + &subField_, + std::vector{ + std::forward(a0), + std::forward(aN)...}} + { + } + + template + requires( + std::convertible_to && + !std::is_same_v) explicit msig(AccountType&& a0, Accounts&&... aN) - : signers{std::forward(a0), std::forward(aN)...} + : msig{ + topLevel, + std::vector{ + std::forward(a0), + std::forward(aN)...}} { - sortSigners(signers); } void diff --git a/src/test/jtx/sig.h b/src/test/jtx/sig.h index aa65a4f6979..b96a306a37e 100644 --- a/src/test/jtx/sig.h +++ b/src/test/jtx/sig.h @@ -35,7 +35,20 @@ class sig { private: bool manual_ = true; + /** Alternative transaction object field in which to place the signature. + * + * subField is only supported if an account_ is provided as well. + */ + SField const* const subField_ = nullptr; + /** Account that will generate the signature. + * + * If not provided, no signature will be added by this helper. See also + * Env::autofill_sig. + */ std::optional account_; + /// Used solely as a convenience placeholder for ctors that do _not_ specify + /// a subfield. + static constexpr SField* const topLevel = nullptr; public: explicit sig(autofill_t) : manual_(false) @@ -46,7 +59,17 @@ class sig { } - explicit sig(Account const& account) : account_(account) + explicit sig(SField const* subField, Account const& account) + : subField_(subField), account_(account) + { + } + + explicit sig(SField const& subField, Account const& account) + : sig(&subField, account) + { + } + + explicit sig(Account const& account) : sig(topLevel, account) { } diff --git a/src/test/jtx/utility.h b/src/test/jtx/utility.h index 2c21fbd3ff9..9e89c3bb93c 100644 --- a/src/test/jtx/utility.h +++ b/src/test/jtx/utility.h @@ -51,6 +51,12 @@ struct parse_error : std::logic_error STObject parse(Json::Value const& jv); +/** Sign automatically into a specific Json field of the jv object. + @note This only works on accounts with multi-signing off. +*/ +void +sign(Json::Value& jv, Account const& account, Json::Value& sigObject); + /** Sign automatically. @note This only works on accounts with multi-signing off. */ diff --git a/src/test/rpc/RPCCall_test.cpp b/src/test/rpc/RPCCall_test.cpp index d22896388d7..26a6a536ef8 100644 --- a/src/test/rpc/RPCCall_test.cpp +++ b/src/test/rpc/RPCCall_test.cpp @@ -4643,10 +4643,34 @@ static RPCCallTestData const rpcCallTestArray[] = { } ] })"}, - {"sign: too many arguments.", + {"sign: offline flag with signature_target.", __LINE__, {"sign", "my_secret", R"({"json_argument":true})", "offline", "extra"}, RPCCallTestData::no_exception, + R"({ + "method" : "sign", + "params" : [ + { + "api_version" : %API_VER%, + "offline" : true, + "secret" : "my_secret", + "signature_target" : "extra", + "tx_json" : + { + "json_argument" : true + } + } + ] + })"}, + {"sign: too many arguments.", + __LINE__, + {"sign", + "my_secret", + R"({"json_argument":true})", + "offline", + "CounterpartySignature", + "extra"}, + RPCCallTestData::no_exception, R"({ "method" : "sign", "params" : [ @@ -4675,20 +4699,24 @@ static RPCCallTestData const rpcCallTestArray[] = { } ] })"}, - {"sign: invalid final argument.", + {"sign: misspelled offline flag interpreted as signature_target.", __LINE__, {"sign", "my_secret", R"({"json_argument":true})", "offlin"}, RPCCallTestData::no_exception, R"({ - "method" : "sign", - "params" : [ - { - "error" : "invalidParams", - "error_code" : 31, - "error_message" : "Invalid parameters." - } - ] - })"}, + "method" : "sign", + "params" : [ + { + "api_version" : %API_VER%, + "secret" : "my_secret", + "signature_target" : "offlin", + "tx_json" : + { + "json_argument" : true + } + } + ] + })"}, // sign_for // -------------------------------------------------------------------- @@ -4880,10 +4908,34 @@ static RPCCallTestData const rpcCallTestArray[] = { } ] })"}, - {"submit: too many arguments.", + {"submit: offline flag with signature_target.", __LINE__, {"submit", "my_secret", R"({"json_argument":true})", "offline", "extra"}, RPCCallTestData::no_exception, + R"({ + "method" : "submit", + "params" : [ + { + "api_version" : %API_VER%, + "offline" : true, + "secret" : "my_secret", + "signature_target" : "extra", + "tx_json" : + { + "json_argument" : true + } + } + ] + })"}, + {"submit: too many arguments.", + __LINE__, + {"submit", + "my_secret", + R"({"json_argument":true})", + "offline", + "CounterpartySignature", + "extra"}, + RPCCallTestData::no_exception, R"({ "method" : "submit", "params" : [ @@ -4912,19 +4964,23 @@ static RPCCallTestData const rpcCallTestArray[] = { } ] })"}, - {"submit: last argument not \"offline\".", + {"submit: misspelled offline flag interpreted as signature_target.", __LINE__, {"submit", "my_secret", R"({"json_argument":true})", "offlne"}, RPCCallTestData::no_exception, R"({ - "method" : "submit", - "params" : [ - { - "error" : "invalidParams", - "error_code" : 31, - "error_message" : "Invalid parameters." - } - ] + "method" : "submit", + "params" : [ + { + "api_version" : %API_VER%, + "secret" : "my_secret", + "signature_target" : "offlne", + "tx_json" : + { + "json_argument" : true + } + } + ] })"}, // submit_multisigned diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index 2353d7acd1d..3dff9d4d5f7 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -173,7 +173,7 @@ printHelp(po::options_description const& desc) " server_state [counters]\n" " sign [offline]\n" " sign_for " - "[offline]\n" + "[offline] []\n" " stop\n" " simulate [|] []\n" " submit |[ ]\n" diff --git a/src/xrpld/app/tx/detail/Batch.cpp b/src/xrpld/app/tx/detail/Batch.cpp index cba89348d0e..4ffdd2d57c8 100644 --- a/src/xrpld/app/tx/detail/Batch.cpp +++ b/src/xrpld/app/tx/detail/Batch.cpp @@ -237,6 +237,39 @@ Batch::preflight(PreflightContext const& ctx) std::unordered_set uniqueHashes; std::unordered_map> accountSeqTicket; + auto checkSignatureFields = [&parentBatchId, &j = ctx.j]( + STObject const& sig, + uint256 const& hash, + char const* label = "") -> NotTEC { + if (sig.isFieldPresent(sfTxnSignature)) + { + JLOG(j.debug()) + << "BatchTrace[" << parentBatchId << "]: " + << "inner txn " << label << "cannot include TxnSignature. " + << "txID: " << hash; + return temBAD_SIGNATURE; + } + + if (sig.isFieldPresent(sfSigners)) + { + JLOG(j.debug()) + << "BatchTrace[" << parentBatchId << "]: " + << "inner txn " << label << " cannot include Signers. " + << "txID: " << hash; + return temBAD_SIGNER; + } + + if (!sig.getFieldVL(sfSigningPubKey).empty()) + { + JLOG(j.debug()) + << "BatchTrace[" << parentBatchId << "]: " + << "inner txn " << label << " SigningPubKey must be empty. " + << "txID: " << hash; + return temBAD_REGKEY; + } + + return tesSUCCESS; + }; for (STObject rb : rawTxns) { STTx const stx = STTx{std::move(rb)}; @@ -266,29 +299,23 @@ Batch::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } - if (stx.isFieldPresent(sfTxnSignature)) - { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "inner txn cannot include TxnSignature. " - << "txID: " << hash; - return temBAD_SIGNATURE; - } + if (auto const ret = checkSignatureFields(stx, hash)) + return ret; - if (stx.isFieldPresent(sfSigners)) + /* Placeholder for field that will be added by Lending Protocol + // Note that the CounterpartySignature is optional, and should not be + // included, but if it is, ensure it doesn't contain a signature. + if (stx.isFieldPresent(sfCounterpartySignature)) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "inner txn cannot include Signers. " - << "txID: " << hash; - return temBAD_SIGNER; - } - - if (!stx.getSigningPubKey().empty()) - { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "inner txn SigningPubKey must be empty. " - << "txID: " << hash; - return temBAD_REGKEY; + auto const counterpartySignature = + stx.getFieldObject(sfCounterpartySignature); + if (auto const ret = checkSignatureFields( + counterpartySignature, hash, "counterparty signature ")) + { + return ret; + } } + */ auto const innerAccount = stx.getAccountID(sfAccount); if (auto const preflightResult = ripple::preflight( @@ -385,6 +412,13 @@ Batch::preflightSigValidated(PreflightContext const& ctx) // inner account to the required signers set. if (innerAccount != outerAccount) requiredSigners.insert(innerAccount); + /* Placeholder for field that will be added by Lending Protocol + // Some transactions have a Counterparty, who must also sign the + // transaction if they are not the outer account + if (auto const counterparty = rb.at(~sfCounterparty); + counterparty && counterparty != outerAccount) + requiredSigners.insert(*counterparty); + */ } // Validation Batch Signers diff --git a/src/xrpld/rpc/detail/RPCCall.cpp b/src/xrpld/rpc/detail/RPCCall.cpp index 57432d920fd..822eb440b30 100644 --- a/src/xrpld/rpc/detail/RPCCall.cpp +++ b/src/xrpld/rpc/detail/RPCCall.cpp @@ -965,7 +965,16 @@ class RPCParser Json::Value txJSON; Json::Reader reader; bool const bOffline = - 3 == jvParams.size() && jvParams[2u].asString() == "offline"; + jvParams.size() >= 3 && jvParams[2u].asString() == "offline"; + std::optional const field = + [&jvParams, bOffline]() -> std::optional { + if (jvParams.size() < 3) + return std::nullopt; + if (jvParams.size() < 4 && bOffline) + return std::nullopt; + Json::UInt index = bOffline ? 3u : 2u; + return jvParams[index].asString(); + }(); if (1 == jvParams.size()) { @@ -978,7 +987,7 @@ class RPCParser return jvRequest; } else if ( - (2 == jvParams.size() || bOffline) && + (jvParams.size() >= 2 || bOffline) && reader.parse(jvParams[1u].asString(), txJSON)) { // Signing or submitting tx_json. @@ -990,6 +999,9 @@ class RPCParser if (bOffline) jvRequest[jss::offline] = true; + if (field) + jvRequest[jss::signature_target] = *field; + return jvRequest; } @@ -1270,11 +1282,11 @@ class RPCParser {"server_definitions", &RPCParser::parseServerDefinitions, 0, 1}, {"server_info", &RPCParser::parseServerInfo, 0, 1}, {"server_state", &RPCParser::parseServerInfo, 0, 1}, - {"sign", &RPCParser::parseSignSubmit, 2, 3}, + {"sign", &RPCParser::parseSignSubmit, 2, 4}, {"sign_for", &RPCParser::parseSignFor, 3, 4}, {"stop", &RPCParser::parseAsIs, 0, 0}, {"simulate", &RPCParser::parseSimulate, 1, 2}, - {"submit", &RPCParser::parseSignSubmit, 1, 3}, + {"submit", &RPCParser::parseSignSubmit, 1, 4}, {"submit_multisigned", &RPCParser::parseSubmitMultiSigned, 1, 1}, {"transaction_entry", &RPCParser::parseTransactionEntry, 2, 2}, {"tx", &RPCParser::parseTx, 1, 4}, diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 175fd84c9b0..aa7c706a19e 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ class SigningForParams AccountID const* const multiSigningAcctID_; std::optional multiSignPublicKey_; Buffer multiSignature_; + std::optional> signatureTarget_; public: explicit SigningForParams() : multiSigningAcctID_(nullptr) @@ -116,12 +118,25 @@ class SigningForParams return multiSignature_; } + std::optional> const& + getSignatureTarget() const + { + return signatureTarget_; + } + void setPublicKey(PublicKey const& multiSignPublicKey) { multiSignPublicKey_ = multiSignPublicKey; } + void + setSignatureTarget( + std::optional> const& field) + { + signatureTarget_ = field; + } + void moveMultiSignature(Buffer&& multiSignature) { @@ -427,6 +442,29 @@ transactionPreProcessImpl( bool const verify = !(params.isMember(jss::offline) && params[jss::offline].asBool()); + auto const signatureTarget = + [¶ms]() -> std::optional> { + if (params.isMember(jss::signature_target)) + return SField::getField(params[jss::signature_target].asString()); + return std::nullopt; + }(); + + // Make sure the signature target field is valid, if specified, and save the + // template for use later + auto const signatureTemplate = signatureTarget + ? InnerObjectFormats::getInstance().findSOTemplateBySField( + *signatureTarget) + : nullptr; + if (signatureTarget) + { + if (!signatureTemplate) + { // Invalid target field + return RPC::make_error( + rpcINVALID_PARAMS, signatureTarget->get().getName()); + } + signingArgs.setSignatureTarget(signatureTarget); + } + if (!params.isMember(jss::tx_json)) return RPC::missing_field_error(jss::tx_json); @@ -541,9 +579,10 @@ transactionPreProcessImpl( JLOG(j.trace()) << "verify: " << toBase58(calcAccountID(pk)) << " : " << toBase58(srcAddressID); - // Don't do this test if multisigning since the account and secret - // probably don't belong together in that case. - if (!signingArgs.isMultiSigning()) + // Don't do this test if multisigning or if the signature is going into + // an alternate field since the account and secret probably don't belong + // together in that case. + if (!signingArgs.isMultiSigning() && !signatureTarget) { // Make sure the account and secret belong together. if (tx_json.isMember(sfDelegate.jsonName)) @@ -598,7 +637,17 @@ transactionPreProcessImpl( { // If we're generating a multi-signature the SigningPubKey must be // empty, otherwise it must be the master account's public key. - parsed.object->setFieldVL( + STObject* sigObject = &*parsed.object; + if (signatureTarget) + { + // If the target object doesn't exist, make one. + if (!parsed.object->isFieldPresent(*signatureTarget)) + parsed.object->setFieldObject( + *signatureTarget, + STObject{*signatureTemplate, *signatureTarget}); + sigObject = &parsed.object->peekFieldObject(*signatureTarget); + } + sigObject->setFieldVL( sfSigningPubKey, signingArgs.isMultiSigning() ? Slice(nullptr, 0) : pk.slice()); @@ -630,7 +679,7 @@ transactionPreProcessImpl( } else if (signingArgs.isSingleSigning()) { - stTx->sign(pk, sk); + stTx->sign(pk, sk, signatureTarget); } return transactionPreProcessResult{std::move(stTx)}; @@ -1195,11 +1244,17 @@ transactionSignFor( signer.setFieldVL( sfSigningPubKey, signForParams.getPublicKey().slice()); + STObject& sigTarget = [&]() -> STObject& { + auto const target = signForParams.getSignatureTarget(); + if (target) + return sttx->peekFieldObject(*target); + return *sttx; + }(); // If there is not yet a Signers array, make one. - if (!sttx->isFieldPresent(sfSigners)) - sttx->setFieldArray(sfSigners, {}); + if (!sigTarget.isFieldPresent(sfSigners)) + sigTarget.setFieldArray(sfSigners, {}); - auto& signers = sttx->peekFieldArray(sfSigners); + auto& signers = sigTarget.peekFieldArray(sfSigners); signers.emplace_back(std::move(signer)); // The array must be sorted and validated. From 1441abcf015c4ccad682cd19ccf45a8de5b864b5 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 3 Oct 2025 22:09:46 +0900 Subject: [PATCH 059/249] use `sfSponsorSignature` --- include/xrpl/protocol/STObject.h | 2 - include/xrpl/protocol/STTx.h | 62 ----- include/xrpl/protocol/Sign.h | 25 ++ include/xrpl/protocol/detail/sfields.macro | 3 +- src/libxrpl/protocol/InnerObjectFormats.cpp | 5 + src/libxrpl/protocol/STObject.cpp | 7 - src/libxrpl/protocol/STTx.cpp | 186 +------------ src/libxrpl/protocol/Sign.cpp | 48 ++++ src/libxrpl/protocol/TxFormats.cpp | 1 + src/test/app/Sponsor_test.cpp | 290 ++++++++++---------- src/test/jtx/JTx.h | 3 +- src/test/jtx/impl/Env.cpp | 3 - src/test/jtx/impl/multisign.cpp | 3 +- src/test/jtx/impl/sponsor.cpp | 70 ----- src/test/jtx/sponsor.h | 29 -- src/test/rpc/AccountTx_test.cpp | 8 +- src/xrpld/app/tx/detail/Transactor.cpp | 21 +- src/xrpld/app/tx/detail/apply.cpp | 10 +- 18 files changed, 255 insertions(+), 521 deletions(-) diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index 7d316ddd78d..84706c58335 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -249,8 +249,6 @@ class STObject : public STBase, public CountedObject getFieldObject(SField const& field) const; STArray const& getFieldArray(SField const& field) const; - STObject const& - getFieldObject(SField const& field) const; STCurrency const& getFieldCurrency(SField const& field) const; STNumber const& diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index f1fb8068e1d..1e3695050de 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -115,12 +115,6 @@ class STTx final : public STObject, public CountedObject boost::container::flat_set getMentionedAccounts() const; - static Blob - getSigningData(STObject const& that); - - static Blob - getSponsorSigningData(STTx const& that); - uint256 getTransactionID() const; @@ -168,11 +162,6 @@ class STTx final : public STObject, public CountedObject RequireFullyCanonicalSig requireCanonicalSig, Rules const& rules) const; - Expected - checkSponsorSign( - RequireFullyCanonicalSig requireCanonicalSig, - Rules const& rules) const; - // SQL Functions with metadata. static std::string const& getMetaSQLInsertReplaceHeader(); @@ -208,23 +197,12 @@ class STTx final : public STObject, public CountedObject STObject const& batchSigner, RequireFullyCanonicalSig requireCanonicalSig) const; - Expected - checkSponsorSingleSign( - STObject const& signer, - RequireFullyCanonicalSig requireCanonicalSig) const; - Expected checkBatchMultiSign( STObject const& batchSigner, RequireFullyCanonicalSig requireCanonicalSig, Rules const& rules) const; - Expected - checkSponsorMultiSign( - STObject const& signer, - RequireFullyCanonicalSig requireCanonicalSig, - Rules const& rules) const; - STBase* copy(std::size_t n, void* buf) const override; STBase* @@ -273,46 +251,6 @@ STTx::getTransactionID() const return tid_; } -/** Return a Serializer suitable for computing a multisigning TxnSignature. */ -Serializer -buildMultiSigningData(STObject const& obj, AccountID const& signingID); - -/** Break the multi-signing hash computation into 2 parts for optimization. - - We can optimize verifying multiple multisignatures by splitting the - data building into two parts; - o A large part that is shared by all of the computations. - o A small part that is unique to each signer in the multisignature. - - The following methods support that optimization: - 1. startMultiSigningData provides the large part which can be shared. - 2. finishMultiSigningData caps the passed in serializer with each - signer's unique data. -*/ -Serializer -startMultiSigningData(STObject const& obj); - -inline void -finishMultiSigningData(AccountID const& signingID, Serializer& s) -{ - s.addBitString(signingID); -} - -Serializer -buildSponsorMultiSigningData( - STObject const& obj, - AccountID const& signingID, - uint32_t flags); - -Serializer -startSponsorSigningData(STObject const& obj); - -inline void -finishSponsorSigningData(AccountID const& signerID, Serializer& s) -{ - s.addBitString(signerID); -} - } // namespace ripple #endif diff --git a/include/xrpl/protocol/Sign.h b/include/xrpl/protocol/Sign.h index 512d82cb43c..5aa9fabddc3 100644 --- a/include/xrpl/protocol/Sign.h +++ b/include/xrpl/protocol/Sign.h @@ -61,6 +61,31 @@ verify( PublicKey const& pk, SF_VL const& sigField = sfSignature); +/** Return a Serializer suitable for computing a multisigning TxnSignature. */ +Serializer +buildMultiSigningData(STObject const& obj, AccountID const& signingID); + +/** Break the multi-signing hash computation into 2 parts for optimization. + + We can optimize verifying multiple multisignatures by splitting the + data building into two parts; + o A large part that is shared by all of the computations. + o A small part that is unique to each signer in the multisignature. + + The following methods support that optimization: + 1. startMultiSigningData provides the large part which can be shared. + 2. finishMultiSigningData caps the passed in serializer with each + signer's unique data. +*/ +Serializer +startMultiSigningData(STObject const& obj); + +inline void +finishMultiSigningData(AccountID const& signingID, Serializer& s) +{ + s.addBitString(signingID); +} + } // namespace ripple #endif diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 3f800a4a641..624f430ccaa 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -383,7 +383,8 @@ UNTYPED_SFIELD(sfCredential, OBJECT, 33) UNTYPED_SFIELD(sfRawTransaction, OBJECT, 34) UNTYPED_SFIELD(sfBatchSigner, OBJECT, 35) UNTYPED_SFIELD(sfBook, OBJECT, 36) -UNTYPED_SFIELD(sfSponsor, OBJECT, 37, SField::sMD_Default, SField::notSigning) +UNTYPED_SFIELD(sfSponsor, OBJECT, 37) +UNTYPED_SFIELD(sfSponsorSignature, OBJECT, 38, SField::sMD_Default, SField::notSigning) // array of objects (common) // ARRAY/1 is reserved for end of array diff --git a/src/libxrpl/protocol/InnerObjectFormats.cpp b/src/libxrpl/protocol/InnerObjectFormats.cpp index 4b82b1acfdf..962d2f3d8d4 100644 --- a/src/libxrpl/protocol/InnerObjectFormats.cpp +++ b/src/libxrpl/protocol/InnerObjectFormats.cpp @@ -178,6 +178,11 @@ InnerObjectFormats::InnerObjectFormats() { {sfAccount, soeREQUIRED}, {sfFlags, soeREQUIRED}, + }); + + add(sfSponsorSignature.jsonName.c_str(), + sfSponsorSignature.getCode(), + { {sfSigningPubKey, soeOPTIONAL}, {sfTxnSignature, soeOPTIONAL}, {sfSigners, soeOPTIONAL}, diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 3c8b76f3d7c..8fb21a638d4 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -705,13 +705,6 @@ STObject::getFieldArray(SField const& field) const return getFieldByConstRef(field, empty); } -STObject const& -STObject::getFieldObject(SField const& field) const -{ - static STObject const empty(field); - return getFieldByConstRef(field, empty); -} - STCurrency const& STObject::getFieldCurrency(SField const& field) const { diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 87e01190fbc..64c10419844 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -185,26 +184,15 @@ STTx::getMentionedAccounts() const return list; } -Blob -STTx::getSigningData(STObject const& that) +static Blob +getSigningData(STTx const& that) { Serializer s; s.add32(HashPrefix::txSign); that.addWithoutSigningFields(s); - if (that.isFieldPresent(sfSponsor)) - { - auto const sst = that.getFieldObject(sfSponsor); - addSerializeSponsorData(s, sst.getAccountID(sfAccount), sst.getFlags()); - } return s.getData(); } -Blob -STTx::getSponsorSigningData(STTx const& that) -{ - return startSponsorSigningData(that).getData(); -} - uint256 STTx::getSigningHash() const { @@ -299,15 +287,14 @@ STTx::checkSign( if (auto const ret = checkSign(requireCanonicalSig, rules, nullptr); !ret) return ret; - /* Placeholder for field that will be added by Lending Protocol - if (isFieldPresent(sfCounterpartySignature)) + if (isFieldPresent(sfSponsorSignature)) { - auto const counterSig = getFieldObject(sfCounterpartySignature); - if (auto const ret = checkSign(requireCanonicalSig, rules, &counterSig); + auto const sponsorSignatureObj = getFieldObject(sfSponsorSignature); + if (auto const ret = + checkSign(requireCanonicalSig, rules, &sponsorSignatureObj); !ret) - return Unexpected("Counterparty: " + ret.error()); + return Unexpected("Sponsor: " + ret.error()); } - */ return {}; } @@ -347,42 +334,6 @@ STTx::checkBatchSign( return Unexpected("Internal batch signature check failure."); } -Expected -STTx::checkSponsorSign( - RequireFullyCanonicalSig requireCanonicalSig, - Rules const& rules) const -{ - try - { - XRPL_ASSERT( - isFieldPresent(sfSponsor), - "STTx::checkSponsorSign: not a sponsored transaction"); - if (!isFieldPresent(sfSponsor)) - { - JLOG(debugLog().fatal()) << "not a sponsored transaction"; - return Unexpected("Not a sponsored transaction."); - } - STObject const& sponsorObj{getFieldObject(sfSponsor)}; - - Blob const& signingPubKey = sponsorObj.getFieldVL(sfSigningPubKey); - - auto const result = signingPubKey.empty() - ? checkSponsorMultiSign(sponsorObj, requireCanonicalSig, rules) - : checkSponsorSingleSign(sponsorObj, requireCanonicalSig); - - if (!result) - return result; - - return {}; - } - catch (std::exception const& e) - { - JLOG(debugLog().error()) - << "Sponsor signature check failed: " << e.what(); - } - return Unexpected("Sponsor signature check failure."); -} - Json::Value STTx::getJson(JsonOptions options) const { @@ -523,17 +474,6 @@ STTx::checkBatchSingleSign( return singleSignHelper(batchSigner, msg.slice(), fullyCanonical); } -Expected -STTx::checkSponsorSingleSign( - STObject const& signer, - RequireFullyCanonicalSig requireCanonicalSig) const -{ - auto const data = getSponsorSigningData(*this); - bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig) || - (requireCanonicalSig == STTx::RequireFullyCanonicalSig::yes); - return singleSignHelper(signer, makeSlice(data), fullyCanonical); -} - Expected multiSignHelper( STObject const& sigObject, @@ -640,32 +580,6 @@ STTx::checkBatchMultiSign( rules); } -Expected -STTx::checkSponsorMultiSign( - STObject const& sponsorObj, - RequireFullyCanonicalSig requireCanonicalSig, - Rules const& rules) const -{ - bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig) || - (requireCanonicalSig == RequireFullyCanonicalSig::yes); - - // We can ease the computational load inside the loop a bit by - // pre-constructing part of the data that we hash. Fill a Serializer - // with the stuff that stays constant from signature to signature. - auto const data = startSponsorSigningData(*this); - Serializer dataStart = Serializer(data.data(), data.size()); - - return multiSignHelper( - sponsorObj, - fullyCanonical, - [&dataStart](AccountID const& accountID) mutable -> Serializer { - Serializer s = dataStart; - finishSponsorSigningData(accountID, s); - return s; - }, - rules); -} - Expected STTx::checkMultiSign( RequireFullyCanonicalSig requireCanonicalSig, @@ -964,90 +878,4 @@ isPseudoTx(STObject const& tx) return tt == ttAMENDMENT || tt == ttFEE || tt == ttUNL_MODIFY; } -// Questions regarding buildMultiSigningData: -// -// Why do we include the Signer.Account in the blob to be signed? -// -// Unless you include the Account which is signing in the signing blob, -// you could swap out any Signer.Account for any other, which may also -// be on the SignerList and have a RegularKey matching the -// Signer.SigningPubKey. -// -// That RegularKey may be set to allow some 3rd party to sign transactions -// on the account's behalf, and that RegularKey could be common amongst all -// users of the 3rd party. That's just one example of sharing the same -// RegularKey amongst various accounts and just one vulnerability. -// -// "When you have something that's easy to do that makes entire classes of -// attacks clearly and obviously impossible, you need a damn good reason -// not to do it." -- David Schwartz -// -// Why would we include the signingFor account in the blob to be signed? -// -// In the current signing scheme, the account that a signer is `signing -// for/on behalf of` is the tx_json.Account. -// -// Later we might support more levels of signing. Suppose Bob is a signer -// for Alice, and Carol is a signer for Bob, so Carol can sign for Bob who -// signs for Alice. But suppose Alice has two signers: Bob and Dave. If -// Carol is a signer for both Bob and Dave, then the signature needs to -// distinguish between Carol signing for Bob and Carol signing for Dave. -// -// So, if we support multiple levels of signing, then we'll need to -// incorporate the "signing for" accounts into the signing data as well. -Serializer -buildMultiSigningData(STObject const& obj, AccountID const& signingID) -{ - Serializer s{startMultiSigningData(obj)}; - finishMultiSigningData(signingID, s); - return s; -} - -Serializer -startMultiSigningData(STObject const& obj) -{ - Serializer s; - s.add32(HashPrefix::txMultiSign); - obj.addWithoutSigningFields(s); - // if (obj.isFieldPresent(sfSponsor)) - // { - // auto const sst = obj.getFieldObject(sfSponsor); - // addSerializeSponsorData(s, sst.getAccountID(sfAccount), - // sst.getFlags()); - // } - return s; -} - -Serializer -buildSponsorMultiSigningData( - STObject const& obj, - AccountID const& signingID, - uint32_t flags) -{ - Serializer s{startSponsorSigningData(obj)}; - finishSponsorSigningData(signingID, s); - return s; -} - -Serializer -startSponsorSigningData(STObject const& obj) -{ - Serializer s; - s.add32(HashPrefix::sponsor); - STObject tmp = obj; - tmp.setFieldVL(sfSigningPubKey, Blob{}); - tmp.addWithoutSigningFields(s); - - XRPL_ASSERT( - tmp.isFieldPresent(sfSponsor), - "STTx::getSponsorSigningData : sponsor is not set"); - - if (tmp.isFieldPresent(sfSponsor)) - { - auto const sst = tmp.getFieldObject(sfSponsor); - addSerializeSponsorData(s, sst.getAccountID(sfAccount), sst.getFlags()); - } - return s; -} - } // namespace ripple diff --git a/src/libxrpl/protocol/Sign.cpp b/src/libxrpl/protocol/Sign.cpp index b72742336d1..27c2b0435c4 100644 --- a/src/libxrpl/protocol/Sign.cpp +++ b/src/libxrpl/protocol/Sign.cpp @@ -61,4 +61,52 @@ verify( pk, Slice(ss.data(), ss.size()), Slice(sig->data(), sig->size())); } +// Questions regarding buildMultiSigningData: +// +// Why do we include the Signer.Account in the blob to be signed? +// +// Unless you include the Account which is signing in the signing blob, +// you could swap out any Signer.Account for any other, which may also +// be on the SignerList and have a RegularKey matching the +// Signer.SigningPubKey. +// +// That RegularKey may be set to allow some 3rd party to sign transactions +// on the account's behalf, and that RegularKey could be common amongst all +// users of the 3rd party. That's just one example of sharing the same +// RegularKey amongst various accounts and just one vulnerability. +// +// "When you have something that's easy to do that makes entire classes of +// attacks clearly and obviously impossible, you need a damn good reason +// not to do it." -- David Schwartz +// +// Why would we include the signingFor account in the blob to be signed? +// +// In the current signing scheme, the account that a signer is `signing +// for/on behalf of` is the tx_json.Account. +// +// Later we might support more levels of signing. Suppose Bob is a signer +// for Alice, and Carol is a signer for Bob, so Carol can sign for Bob who +// signs for Alice. But suppose Alice has two signers: Bob and Dave. If +// Carol is a signer for both Bob and Dave, then the signature needs to +// distinguish between Carol signing for Bob and Carol signing for Dave. +// +// So, if we support multiple levels of signing, then we'll need to +// incorporate the "signing for" accounts into the signing data as well. +Serializer +buildMultiSigningData(STObject const& obj, AccountID const& signingID) +{ + Serializer s{startMultiSigningData(obj)}; + finishMultiSigningData(signingID, s); + return s; +} + +Serializer +startMultiSigningData(STObject const& obj) +{ + Serializer s; + s.add32(HashPrefix::txMultiSign); + obj.addWithoutSigningFields(s); + return s; +} + } // namespace ripple diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 9cc1b2ea07a..caa3533687d 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -48,6 +48,7 @@ TxFormats::TxFormats() {sfNetworkID, soeOPTIONAL}, {sfDelegate, soeOPTIONAL}, {sfSponsor, soeOPTIONAL}, + {sfSponsorSignature, soeOPTIONAL}, }; #pragma push_macro("UNWRAP") diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index f2b17891c24..28906f195a9 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -73,7 +73,7 @@ class Sponsor_test : public beast::unit_test::suite env(noop(alice), fee(XRP(1)), sponsor::as(sponsor), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(temDISABLED)); env(sponsor::transfer(alice), ter(temDISABLED)); @@ -208,7 +208,7 @@ class Sponsor_test : public beast::unit_test::suite // Signature doesn't exist auto tx = noop(alice); tx[sfSponsor.jsonName][sfAccount.jsonName] = sponsor.human(); - tx[sfSponsor.jsonName][sfSigningPubKey.jsonName] = + tx[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = strHex(sponsor.pk().slice()); env(tx, @@ -217,7 +217,7 @@ class Sponsor_test : public beast::unit_test::suite ter(telENV_RPC_FAILED)); // Invalid signature - tx[sfSponsor.jsonName][sfTxnSignature.jsonName] = "DEADBEEF"; + tx[sfSponsorSignature.jsonName][sfTxnSignature.jsonName] = "DEADBEEF"; env(tx, fee(XRP(1)), sponsor::as(sponsor, tfSponsorReserve), @@ -227,14 +227,14 @@ class Sponsor_test : public beast::unit_test::suite env(noop(alice), fee(XRP(1)), sponsor::as(invalid, tfSponsorReserve), - sponsor::sig(invalid), + sig(sfSponsorSignature, invalid), ter(terNO_ACCOUNT)); // Success env(noop(alice), fee(XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tesSUCCESS)); } @@ -259,8 +259,8 @@ class Sponsor_test : public beast::unit_test::suite // Invalid signature auto tx = noop(alice); - auto& signers1 = - tx[sfSponsor.jsonName][sfSigners.jsonName][0U][sfSigner.jsonName]; + auto& signers1 = tx[sfSponsorSignature.jsonName][sfSigners.jsonName][0U] + [sfSigner.jsonName]; signers1[sfAccount.jsonName] = signer1.human(); signers1[sfSigningPubKey.jsonName] = strHex(signer1.pk().slice()); signers1[sfTxnSignature.jsonName] = "DEADBEEF"; @@ -273,13 +273,13 @@ class Sponsor_test : public beast::unit_test::suite env(noop(alice), fee(XRP(1)), sponsor::as(invalid, tfSponsorReserve), - sponsor::msig({signer1}), + msig(sfSponsorSignature, {signer1}), ter(tefNOT_MULTI_SIGNING)); env(noop(alice), fee(XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::msig({signer1}), + msig(sfSponsorSignature, {signer1}), ter(tesSUCCESS)); env(signers(sponsor, 2, {{signer1, 1}, {signer2, 1}})); @@ -288,7 +288,7 @@ class Sponsor_test : public beast::unit_test::suite env(noop(alice), fee(XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::msig({signer1, signer2}), + msig(sfSponsorSignature, {signer1, signer2}), ter(tesSUCCESS)); } @@ -353,7 +353,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer(alice), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1), + sig(sfSponsorSignature, sponsor1), ter(tecINSUFFICIENT_RESERVE)); env(pay(alice, sponsor1, drops(1))); @@ -361,7 +361,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer(alice), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1)); + sig(sfSponsorSignature, sponsor1)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -377,7 +377,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2), + sig(sfSponsorSignature, sponsor2), ter(tecINSUFFICIENT_RESERVE)); env(pay(alice, sponsor2, drops(1))); @@ -385,7 +385,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer(alice), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -456,7 +456,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer(alice, checkId), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1), + sig(sfSponsorSignature, sponsor1), ter(tecINSUFFICIENT_RESERVE)); env.close(); @@ -466,14 +466,14 @@ class Sponsor_test : public beast::unit_test::suite // Invalid Owner env(sponsor::transfer(bob, checkId), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1), + sig(sfSponsorSignature, sponsor1), ter(tecNO_PERMISSION)); env.close(); // Valid Owner env(sponsor::transfer(alice, checkId), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1)); + sig(sfSponsorSignature, sponsor1)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -490,7 +490,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, checkId), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2), + sig(sfSponsorSignature, sponsor2), ter(tecINSUFFICIENT_RESERVE)); env(pay(alice, sponsor2, drops(1))); @@ -498,7 +498,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer(alice, checkId), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -575,7 +575,7 @@ class Sponsor_test : public beast::unit_test::suite env(pay(alice, bob, XRP(100)), fee(XRP(2000)), sponsor::as(sponsor, tfSponsorFee), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(terNO_ACCOUNT)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); @@ -597,7 +597,7 @@ class Sponsor_test : public beast::unit_test::suite env(pay(alice, bob, sendAmt), fee(feeAmt), sponsor::as(sponsor, tfSponsorFee), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt); BEAST_EXPECT(env.balance(bob) == bobBalance + sendAmt); @@ -613,7 +613,7 @@ class Sponsor_test : public beast::unit_test::suite env(pay(alice, bob, XRP(100)), fee(XRP(2000)), sponsor::as(sponsor, tfSponsorFee), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(terINSUF_FEE_B)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); @@ -632,7 +632,7 @@ class Sponsor_test : public beast::unit_test::suite env(pay(alice, bob, XRP(20000)), fee(feeAmt), sponsor::as(sponsor, tfSponsorFee), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecUNFUNDED_PAYMENT)); env.close(); @@ -829,7 +829,7 @@ class Sponsor_test : public beast::unit_test::suite bob, STAmount(env.current()->fees().accountReserve(0))), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); auto const bobSle = env.le(keylet::account(bob)); @@ -971,7 +971,7 @@ class Sponsor_test : public beast::unit_test::suite env(ammCreate(env, alice, USD(100), EUR(100)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUF_RESERVE_LINE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); @@ -979,7 +979,7 @@ class Sponsor_test : public beast::unit_test::suite env(ammCreate(env, alice, USD(100), EUR(100)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); auto const amm = @@ -1027,7 +1027,7 @@ class Sponsor_test : public beast::unit_test::suite env(ammDeposit(env, bob, USD(100), EUR(100)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUF_RESERVE_LINE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); @@ -1035,7 +1035,7 @@ class Sponsor_test : public beast::unit_test::suite env(ammDeposit(env, bob, USD(100), EUR(100)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT( @@ -1062,7 +1062,7 @@ class Sponsor_test : public beast::unit_test::suite env(ammCreate(env, alice, USD(1000), EUR(1000)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); env(trust(alice, USD(0))); @@ -1095,7 +1095,7 @@ class Sponsor_test : public beast::unit_test::suite env, sponsor, reserve(env, 2) - drops(1)); env(jv, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); @@ -1103,7 +1103,7 @@ class Sponsor_test : public beast::unit_test::suite env(jv, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); // USD, LPToken @@ -1128,7 +1128,7 @@ class Sponsor_test : public beast::unit_test::suite env(ammCreate(env, alice, USD(1000), EUR(1000)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); env(trust(alice, USD(0))); @@ -1159,7 +1159,7 @@ class Sponsor_test : public beast::unit_test::suite env, sponsor, reserve(env, 3) - drops(1)); env(jv, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 4)); @@ -1167,7 +1167,7 @@ class Sponsor_test : public beast::unit_test::suite env(jv, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); // USD, EUR @@ -1199,7 +1199,7 @@ class Sponsor_test : public beast::unit_test::suite env(ammCreate(env, alice, USD(100), EUR2(100)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); env(trust(alice, USD(0))); @@ -1213,7 +1213,7 @@ class Sponsor_test : public beast::unit_test::suite // doesn't sponsor holder's new RippleState env(amm::ammClawback(gw, alice, USD, EUR2, USD(10)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); // LPToken, EUR2 @@ -1223,7 +1223,7 @@ class Sponsor_test : public beast::unit_test::suite { env(amm::ammClawback(gw, alice, USD, EUR2, std::nullopt), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // EUR2 @@ -1255,7 +1255,7 @@ class Sponsor_test : public beast::unit_test::suite auto const seq = env.seq(alice); env(check::create(alice, bob, XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // Check @@ -1270,7 +1270,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // Check @@ -1302,7 +1302,7 @@ class Sponsor_test : public beast::unit_test::suite auto const seq2 = env.seq(alice); env(check::create(alice, bob, XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // Check @@ -1315,7 +1315,7 @@ class Sponsor_test : public beast::unit_test::suite auto const checkId2 = keylet::check(alice, seq2).key; env(check::cash(bob, checkId2, XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); @@ -1340,7 +1340,7 @@ class Sponsor_test : public beast::unit_test::suite auto const seq2 = env.seq(alice); env(check::create(alice, bob, USD(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check @@ -1356,7 +1356,7 @@ class Sponsor_test : public beast::unit_test::suite // CheckCash env(check::cash(bob, keylet.key, USD(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState @@ -1380,7 +1380,7 @@ class Sponsor_test : public beast::unit_test::suite env(check::create(alice, bob, XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); @@ -1388,7 +1388,7 @@ class Sponsor_test : public beast::unit_test::suite env(check::create(alice, bob, XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tesSUCCESS)); env.close(); } @@ -1414,7 +1414,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::check(alice, seq); env(check::cash(bob, keylet.key, USD(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecNO_LINE_INSUF_RESERVE)); env.close(); } @@ -1445,7 +1445,7 @@ class Sponsor_test : public beast::unit_test::suite auto const seq = env.seq(alice); env(offer(alice, USD(1), XRP(1)), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1)); + sig(sfSponsorSignature, sponsor1)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1457,7 +1457,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::offer(alice, seq); env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1490,7 +1490,7 @@ class Sponsor_test : public beast::unit_test::suite auto const seq = env.seq(alice); env(offer(alice, USD(1), XRP(1)), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1)); + sig(sfSponsorSignature, sponsor1)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1503,7 +1503,7 @@ class Sponsor_test : public beast::unit_test::suite env(offer(alice, USD(1), XRP(1)), json(jss::OfferSequence, seq), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1543,7 +1543,7 @@ class Sponsor_test : public beast::unit_test::suite // OfferCreate env(offer(alice, EUR(1), USD(1)), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1)); + sig(sfSponsorSignature, sponsor1)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -1559,7 +1559,7 @@ class Sponsor_test : public beast::unit_test::suite // OfferCreate (cross offer) env(offer(bob, USD(1), EUR(1)), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -1590,7 +1590,7 @@ class Sponsor_test : public beast::unit_test::suite // fullly not crossed env(offer(alice, USD(1), XRP(1)), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1), + sig(sfSponsorSignature, sponsor1), ter(tecINSUF_RESERVE_OFFER)); // partially crossed @@ -1599,7 +1599,7 @@ class Sponsor_test : public beast::unit_test::suite env(offer(alice, USD(5), XRP(5)), sponsor::as(sponsor1, tfSponsorReserve), - sponsor::sig(sponsor1), + sig(sfSponsorSignature, sponsor1), ter(tecINSUF_RESERVE_OFFER)); } } @@ -1622,7 +1622,7 @@ class Sponsor_test : public beast::unit_test::suite std::uint32_t const ticketSeq{env.seq(alice) + 1}; env(ticket::create(alice, 250), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 250); @@ -1637,7 +1637,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 250); @@ -1668,14 +1668,14 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); env(ticket::create(alice, 1), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 249)); env(ticket::create(alice, 250), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -1703,7 +1703,7 @@ class Sponsor_test : public beast::unit_test::suite env(credentials::create(subject, issuer, credType), credentials::uri("uri"), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 1); @@ -1717,7 +1717,7 @@ class Sponsor_test : public beast::unit_test::suite keylet::credential(subject, issuer, credTypeSlice); env(sponsor::transfer(issuer, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 1); @@ -1730,7 +1730,7 @@ class Sponsor_test : public beast::unit_test::suite // CredentialsAccept env(credentials::accept(subject, issuer, credType), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 0); @@ -1760,7 +1760,7 @@ class Sponsor_test : public beast::unit_test::suite // Accept Sponsored Credentials without sponsoring env(credentials::create(subject, issuer, credType), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, issuer) == 1); @@ -1796,7 +1796,7 @@ class Sponsor_test : public beast::unit_test::suite env, sponsor, reserve(env, 1) - drops(1)); env(credentials::create(subject, issuer, credType), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -1809,7 +1809,7 @@ class Sponsor_test : public beast::unit_test::suite env, sponsor, reserve(env, 1) - drops(1)); env(credentials::accept(subject, issuer, credType), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -1834,7 +1834,7 @@ class Sponsor_test : public beast::unit_test::suite // DelegateSet env(delegate::set(alice, bob, {"Payment"}), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1845,7 +1845,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::delegate(alice, bob); env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1871,7 +1871,7 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); env(delegate::set(alice, bob, {"Payment"}), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -1894,7 +1894,7 @@ class Sponsor_test : public beast::unit_test::suite // DepositPreauthSet env(deposit::auth(alice, sponsor), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1905,7 +1905,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::depositPreauth(alice, sponsor); env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1932,7 +1932,7 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); env(deposit::auth(alice, sponsor), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -1956,7 +1956,7 @@ class Sponsor_test : public beast::unit_test::suite env(did::set(alice), did::uri("uri"), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1967,7 +1967,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::did(alice); env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1995,7 +1995,7 @@ class Sponsor_test : public beast::unit_test::suite env(did::set(alice), did::uri("uri"), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -2028,7 +2028,7 @@ class Sponsor_test : public beast::unit_test::suite escrow::condition(escrow::cb1), escrow::cancel_time(env.now() + 100s), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2042,7 +2042,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, keylet::escrow(alice, seq).key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2091,7 +2091,7 @@ class Sponsor_test : public beast::unit_test::suite escrow::condition(escrow::cb1), escrow::cancel_time(env.now() + 10s), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -2107,7 +2107,7 @@ class Sponsor_test : public beast::unit_test::suite escrow::condition(escrow::cb1), escrow::fulfillment(escrow::fb1), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2), + sig(sfSponsorSignature, sponsor2), fee(baseFee * 150)); env.close(); @@ -2136,7 +2136,7 @@ class Sponsor_test : public beast::unit_test::suite escrow::condition(escrow::cb1), escrow::cancel_time(env.now() + 10s), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); @@ -2152,7 +2152,7 @@ class Sponsor_test : public beast::unit_test::suite escrow::condition(escrow::cb1), escrow::cancel_time(env.now() + 10s), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); auto const seq = env.seq(alice); @@ -2165,7 +2165,7 @@ class Sponsor_test : public beast::unit_test::suite escrow::condition(escrow::cb1), escrow::fulfillment(escrow::fb1), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), fee(baseFee * 150), ter(tecNO_LINE_INSUF_RESERVE)); env.close(); @@ -2194,7 +2194,7 @@ class Sponsor_test : public beast::unit_test::suite auto const mptid = makeMptID(env.seq(alice), alice.id()); env(jv, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2205,7 +2205,7 @@ class Sponsor_test : public beast::unit_test::suite auto const mptIssuanceKeylet = keylet::mptIssuance(mptid); env(sponsor::transfer(alice, mptIssuanceKeylet.key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2220,7 +2220,7 @@ class Sponsor_test : public beast::unit_test::suite jv[sfMPTokenIssuanceID] = to_string(mptid); env(jv, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2234,7 +2234,7 @@ class Sponsor_test : public beast::unit_test::suite auto const mptTokenKeylet = keylet::mptoken(mptid, bob); env(sponsor::transfer(bob, mptTokenKeylet.key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2287,7 +2287,7 @@ class Sponsor_test : public beast::unit_test::suite // auto const mptid = makeMptID(env.seq(alice), alice.id()); env(jv, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -2316,7 +2316,7 @@ class Sponsor_test : public beast::unit_test::suite // error (non-free mptoken) env(jv, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); @@ -2329,7 +2329,7 @@ class Sponsor_test : public beast::unit_test::suite // pass (free mptoken) env(jv, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tesSUCCESS)); env.close(); } @@ -2356,7 +2356,7 @@ class Sponsor_test : public beast::unit_test::suite uint256 const nftId{token::getNextID(env, alice, 0)}; env(token::mint(alice), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2375,7 +2375,7 @@ class Sponsor_test : public beast::unit_test::suite env(token::mint(alice), token::amount(XRP(10000)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -2397,7 +2397,7 @@ class Sponsor_test : public beast::unit_test::suite { env(token::mint(alice), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); } env.close(); @@ -2429,7 +2429,7 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); env(token::mint(alice), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); @@ -2437,7 +2437,7 @@ class Sponsor_test : public beast::unit_test::suite env(token::mint(alice), token::amount(XRP(10000)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -2477,7 +2477,7 @@ class Sponsor_test : public beast::unit_test::suite token::destination(bob), txflags(tfSellNFToken), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -2490,7 +2490,7 @@ class Sponsor_test : public beast::unit_test::suite token::destination(bob), txflags(tfSellNFToken), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 3); @@ -2500,7 +2500,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, offerIndex1), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 3); @@ -2539,7 +2539,7 @@ class Sponsor_test : public beast::unit_test::suite token::destination(bob), txflags(tfSellNFToken), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -2577,7 +2577,7 @@ class Sponsor_test : public beast::unit_test::suite token::owner(alice), token::destination(alice), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, bob) == 1); @@ -2615,7 +2615,7 @@ class Sponsor_test : public beast::unit_test::suite token::owner(alice), token::destination(broker), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, bob) == 1); @@ -2629,7 +2629,7 @@ class Sponsor_test : public beast::unit_test::suite txflags(tfSellNFToken), token::destination(broker), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -2665,7 +2665,7 @@ class Sponsor_test : public beast::unit_test::suite env(token::createOffer(alice, nextNftId, XRP(1)), txflags(tfSellNFToken), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); @@ -2677,7 +2677,7 @@ class Sponsor_test : public beast::unit_test::suite // NFTokenOfferAccept (buyer) env(token::acceptSellOffer(bob, offerIndex), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -2705,7 +2705,7 @@ class Sponsor_test : public beast::unit_test::suite auto const chan = paychan::channel(alice, bob, env.seq(alice)); env(paychan::create(alice, bob, XRP(100), settleDelay, pk), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2715,7 +2715,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, chan), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2747,7 +2747,7 @@ class Sponsor_test : public beast::unit_test::suite auto const settleDelay = 10s; env(paychan::create(alice, bob, XRP(100), settleDelay, pk), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -2771,7 +2771,7 @@ class Sponsor_test : public beast::unit_test::suite pdomain::Credentials credentials{{alice, "first credential"}}; env(pdomain::setTx(alice, credentials), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2782,7 +2782,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer( alice, keylet::permissionedDomain(alice, seq).key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2813,7 +2813,7 @@ class Sponsor_test : public beast::unit_test::suite pdomain::Credentials credentials{{alice, "first credential"}}; env(pdomain::setTx(alice, credentials), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -2898,7 +2898,7 @@ class Sponsor_test : public beast::unit_test::suite // OracleSet (reserve 1) env(oracleSet(env, alice, 5), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2908,7 +2908,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2929,7 +2929,7 @@ class Sponsor_test : public beast::unit_test::suite // OracleSet (reserve 2) env(oracleSet(env, alice, 6), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -2939,7 +2939,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -2960,7 +2960,7 @@ class Sponsor_test : public beast::unit_test::suite // OracleSet (reserve 1->2, sponsor1 -> no-sponsor) env(oracleSet(env, alice, 5), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2987,7 +2987,7 @@ class Sponsor_test : public beast::unit_test::suite // OracleSet (reserve 1->2, sponsor1 -> sponsor2) env(oracleSet(env, alice, 5), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2997,7 +2997,7 @@ class Sponsor_test : public beast::unit_test::suite // reserve 1->2 env(oracleSet(env, alice, 6), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -3024,7 +3024,7 @@ class Sponsor_test : public beast::unit_test::suite // reserve 1->2 env(oracleSet(env, alice, 6), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -3046,7 +3046,7 @@ class Sponsor_test : public beast::unit_test::suite auto const ocount = isTwoOwnerCount ? 2 : 1; env(oracleSet(env, alice, dataSeriesSize), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == ocount); @@ -3056,7 +3056,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == ocount); @@ -3083,14 +3083,14 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); env(oracleSet(env, alice, 5), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); env(oracleSet(env, alice, 6), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -3114,7 +3114,7 @@ class Sponsor_test : public beast::unit_test::suite // SignerListSet env(signers(alice, 1, {{bob, 1}}), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -3124,7 +3124,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, keylet::signers(alice).key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -3151,7 +3151,7 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); env(signers(alice, 1, {{bob, 1}}), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } @@ -3201,7 +3201,7 @@ class Sponsor_test : public beast::unit_test::suite // create TrustLine env(trust(user, USD(100)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, user) == 1); @@ -3215,7 +3215,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer( user, keylet::line(user, issuer, currency).key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(ownerCount(env, user) == 1); @@ -3255,7 +3255,7 @@ class Sponsor_test : public beast::unit_test::suite // update TrustLine from user to make reserve env(trust(user, USD(100)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, user) == 1); @@ -3307,7 +3307,7 @@ class Sponsor_test : public beast::unit_test::suite // create TrustLine env(trust(user, USD(100)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecNO_LINE_INSUF_RESERVE)); env.close(); @@ -3349,7 +3349,7 @@ class Sponsor_test : public beast::unit_test::suite // check with OwnerCount=3 because free MPToken condition exists env(tx, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); @@ -3363,7 +3363,7 @@ class Sponsor_test : public beast::unit_test::suite env(tx, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); // Vault, MPToken(share) @@ -3402,7 +3402,7 @@ class Sponsor_test : public beast::unit_test::suite // // check with OwnerCount=3 because free MPToken condition // exists env(depositTx, // sponsor::as(sponsor, tfSponsorReserve), - // sponsor::sig(sponsor), + // sig(sfSponsorSignature,sponsor), // ter(tecINSUFFICIENT_RESERVE)); // env.close(); // adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); @@ -3412,7 +3412,7 @@ class Sponsor_test : public beast::unit_test::suite env(depositTx, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT( @@ -3454,7 +3454,7 @@ class Sponsor_test : public beast::unit_test::suite // // exists // env(depositTx, // sponsor::as(sponsor, tfSponsorReserve), - // sponsor::sig(sponsor), + // sig(sfSponsorSignature,sponsor), // ter(tecINSUFFICIENT_RESERVE)); // env.close(); // adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); @@ -3464,7 +3464,7 @@ class Sponsor_test : public beast::unit_test::suite env(depositTx, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); env(trust(bob, asset(0))); // remove trustline @@ -3482,7 +3482,7 @@ class Sponsor_test : public beast::unit_test::suite .id = keylet.key, .amount = asset(50)}), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT( @@ -3500,7 +3500,7 @@ class Sponsor_test : public beast::unit_test::suite .id = keylet.key, .amount = asset(50)}), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState @@ -3548,7 +3548,7 @@ class Sponsor_test : public beast::unit_test::suite // // exists // env(depositTx, // sponsor::as(sponsor, tfSponsorReserve), - // sponsor::sig(sponsor), + // sig(sfSponsorSignature,sponsor), // ter(tecINSUFFICIENT_RESERVE)); // env.close(); // adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); @@ -3558,7 +3558,7 @@ class Sponsor_test : public beast::unit_test::suite env(depositTx, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT( @@ -3573,7 +3573,7 @@ class Sponsor_test : public beast::unit_test::suite .holder = bob, .amount = asset(0)}), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState @@ -3593,7 +3593,7 @@ class Sponsor_test : public beast::unit_test::suite auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx, sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); // Vault, MPToken(share) @@ -3638,14 +3638,14 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); env(bridge_create(doorA, jvb, XRP(1), XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); env(bridge_create(doorA, jvb, XRP(1), XRP(1)), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, doorA) == 2); // Bridge, SignerList @@ -3661,14 +3661,14 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); env(xchain_create_claim_id(alice, jvb, XRP(1), bob), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), + sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); env(xchain_create_claim_id(alice, jvb, XRP(1), bob), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); // XChainOwnedClaimID created @@ -3684,7 +3684,7 @@ class Sponsor_test : public beast::unit_test::suite env(xchain_commit(alice, jvb, 1, XRP(100), bob), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); // doesn't sponsor anything @@ -3701,7 +3701,7 @@ class Sponsor_test : public beast::unit_test::suite env(claim_attestation( alice, jvb, bob, XRP(1), bob, false, 1, bob, signer), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); // XChainOwnedClaimID deleted @@ -3715,7 +3715,7 @@ class Sponsor_test : public beast::unit_test::suite { env(xchain_create_claim_id(alice, jvb, XRP(1), bob), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env(xchain_commit( alice, jvb, 2, XRP(100))); // omit destination env(claim_attestation( diff --git a/src/test/jtx/JTx.h b/src/test/jtx/JTx.h index 59792c87887..1b6cab1af4f 100644 --- a/src/test/jtx/JTx.h +++ b/src/test/jtx/JTx.h @@ -20,7 +20,6 @@ #ifndef RIPPLE_TEST_JTX_JTX_H_INCLUDED #define RIPPLE_TEST_JTX_JTX_H_INCLUDED -#include #include #include @@ -58,7 +57,7 @@ struct JTx // Functions that sign the transaction from the Account std::vector> mainSigners; // Functions that sign something else after the mainSigners, such as - // sfCounterpartySignature + // sfCounterpartySignature and sfSponsorSignature std::vector> postSigners; JTx() = default; diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 68e783e0e3c..18b1520c47e 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -592,9 +592,6 @@ Env::autofill_sig(JTx& jt) jtx::sign(jv, lookup(ar->getAccountID(sfRegularKey))); else jtx::sign(jv, account); - - if (jt.sponsorSigner) - jt.sponsorSigner(*this, jt); } void diff --git a/src/test/jtx/impl/multisign.cpp b/src/test/jtx/impl/multisign.cpp index fc6163a2f33..4a61554705e 100644 --- a/src/test/jtx/impl/multisign.cpp +++ b/src/test/jtx/impl/multisign.cpp @@ -70,7 +70,8 @@ msig::operator()(Env& env, JTx& jt) const { auto const mySigners = signers; auto callback = [subField = subField, mySigners, &env](Env&, JTx& jtx) { - // Where to put the signature. Supports sfCounterPartySignature. + // Where to put the signature. Supports sfCounterPartySignature and + // sfSponsorSignature. auto& sigObject = subField ? jtx[*subField] : jtx.jv; // The signing pub key is only required at the top level. diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index befaaa337b5..e5729130c34 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -119,76 +119,6 @@ as::operator()(Env& env, JTx& jt) const jt.jv[sfSponsor.jsonName][sfFlags.jsonName] = flags; } -void -sig::operator()(Env& env, JTx& jt) const -{ - auto const signer = signer_; - - jt.jv[sfSponsor.jsonName][sfAccount.jsonName] = signer.acct.human(); - jt.jv[sfSponsor.jsonName][sfSigningPubKey.jsonName] = - strHex(signer.sig.pk().slice()); - - jt.sponsorSigner = [signer, &env](Env&, JTx& jtx) { - std::optional st; - try - { - Json::Value jv = jtx.jv; - st = parse(jv); - } - catch (parse_error const&) - { - env.test.log << pretty(jtx.jv) << std::endl; - Rethrow(); - } - - auto const sst = st->getFieldObject(sfSponsor); - - auto const signingData = - STTx::getSponsorSigningData(STTx{std::move(*st)}); - - auto const sig = ripple::sign( - signer.acct.pk(), signer.acct.sk(), makeSlice(signingData)); - jtx.jv[sfSponsor.jsonName][jss::TxnSignature] = - strHex(Slice{sig.data(), sig.size()}); - }; -} - -void -msig::operator()(Env& env, JTx& jt) const -{ - auto const mySigners = signers; - jt.sponsorSigner = [mySigners, &env](Env&, JTx& jtx) { - std::optional st; - try - { - Json::Value jv = jtx.jv; - jv[jss::SigningPubKey] = ""; - st = parse(jv); - } - catch (parse_error const&) - { - env.test.log << pretty(jtx.jv) << std::endl; - Rethrow(); - } - auto const sst = st->getFieldObject(sfSponsor); - auto& js = jtx[sfSponsor.jsonName][sfSigners.jsonName]; - for (std::size_t i = 0; i < mySigners.size(); ++i) - { - auto const& e = mySigners[i]; - auto& jo = js[i][sfSigner.jsonName]; - jo[jss::Account] = e.acct.human(); - jo[jss::SigningPubKey] = strHex(e.sig.pk().slice()); - - Serializer ss{ - buildSponsorMultiSigningData(*st, e.acct.id(), sst.getFlags())}; - - auto const sig = ripple::sign( - *publicKeyType(e.sig.pk().slice()), e.sig.sk(), ss.slice()); - jo[sfTxnSignature.jsonName] = strHex(Slice{sig.data(), sig.size()}); - } - }; -} - Json::Value ledgerEntry( jtx::Env& env, diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index db350207c8e..64e09a96c17 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -101,35 +101,6 @@ struct as operator()(jtx::Env&, jtx::JTx& jtx) const; }; -struct sig -{ -private: - Reg signer_; - -public: - sig(Reg signer) : signer_(std::move(signer)) - { - } - - void - operator()(jtx::Env&, jtx::JTx& jtx) const; -}; - -struct msig -{ -private: - std::vector signers; - -public: - msig(std::vector signers_) : signers(std::move(signers_)) - { - sortSigners(signers); - } - - void - operator()(jtx::Env&, jtx::JTx& jtx) const; -}; - Json::Value ledgerEntry( jtx::Env& env, diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 8e500cc865a..a0e91dce216 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -870,7 +870,7 @@ class AccountTx_test : public beast::unit_test::suite // fee sponsorship env(noop(alice), sponsor::as(sponsor, tfSponsorFee), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); checkTx(alice, jss::AccountSet); checkTx(sponsor, jss::AccountSet); @@ -893,7 +893,7 @@ class AccountTx_test : public beast::unit_test::suite // transfer object sponsorship env(sponsor::transfer(alice, keylet::ticket(alice, seq + 1).key), sponsor::as(sponsor2, tfSponsorReserve), - sponsor::sig(sponsor2)); + sig(sfSponsorSignature, sponsor2)); env.close(); checkTx(alice, jss::SponsorshipTransfer); checkTx(sponsor, jss::SponsorshipTransfer); @@ -903,7 +903,7 @@ class AccountTx_test : public beast::unit_test::suite env(noop(alice), ticket::use(seq + 1), sponsor::as(sponsor, tfSponsorFee), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); checkTx(alice, jss::AccountSet); checkTx(sponsor, jss::AccountSet); @@ -912,7 +912,7 @@ class AccountTx_test : public beast::unit_test::suite // account sponsorship env(sponsor::transfer(alice), sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor)); + sig(sfSponsorSignature, sponsor)); env.close(); checkTx(alice, jss::SponsorshipTransfer); checkTx(sponsor, jss::SponsorshipTransfer); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 4b3b5d4c88b..1243953f845 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -322,9 +322,7 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) auto const sponsorAcc = txSponsor.getAccountID(sfAccount); auto const sponseeAcc = tx.getAccountID(sfAccount); - auto const hasSponsorSignature = txSponsor.isFieldPresent(sfTxnSignature) || - !txSponsor.getFieldVL(sfSigningPubKey).empty() || - txSponsor.isFieldPresent(sfSigners); + auto const hasSponsorSignature = tx.isFieldPresent(sfSponsorSignature); if (!hasSponsorSignature) { @@ -787,17 +785,20 @@ Transactor::checkSign( return tesSUCCESS; } - if (sigObject.isFieldPresent(sfSponsor)) + if (sigObject.isFieldPresent(sfSponsorSignature)) { + if (!sigObject.isFieldPresent(sfSponsor)) + return temMALFORMED; + auto const sponsorObj = sigObject.getFieldObject(sfSponsor); - auto const isCoSigned = sponsorObj.isFieldPresent(sfTxnSignature) || - !sponsorObj.getFieldVL(sfSigningPubKey).empty() || - sponsorObj.isFieldPresent(sfSigners); + auto const isCoSigned = sigObject.isFieldPresent(sfSponsorSignature); if (isCoSigned) { auto const sponsorAcc = sponsorObj.getAccountID(sfAccount); + auto const sponsorSignature = + sigObject.getFieldObject(sfSponsorSignature); if (auto const ret = - checkSign(view, flags, sponsorAcc, sponsorObj, j); + checkSign(view, flags, sponsorAcc, sponsorSignature, j); !isTesSuccess(ret)) return ret; } @@ -1266,9 +1267,7 @@ Transactor::getFeePayer(STTx const& tx) tx.getFieldObject(sfSponsor).isFlag(tfSponsorFee)) { auto const sponsor = tx.getFieldObject(sfSponsor); - auto const hasSignature = sponsor.isFieldPresent(sfTxnSignature) || - !sponsor.getFieldVL(sfSigningPubKey).empty() || - sponsor.isFieldPresent(sfSigners); + auto const hasSignature = tx.isFieldPresent(sfSponsorSignature); if (!hasSignature) { diff --git a/src/xrpld/app/tx/detail/apply.cpp b/src/xrpld/app/tx/detail/apply.cpp index 6b91314ad07..065f9cb8329 100644 --- a/src/xrpld/app/tx/detail/apply.cpp +++ b/src/xrpld/app/tx/detail/apply.cpp @@ -86,13 +86,13 @@ checkValidity( if (tx.isFieldPresent(sfSponsor) && rules.enabled(featureSponsor)) { auto const sponsorObj = tx.getFieldObject(sfSponsor); - auto const isCoSigned = sponsorObj.isFieldPresent(sfTxnSignature) || - !sponsorObj.getFieldVL(sfSigningPubKey).empty() || - sponsorObj.isFieldPresent(sfSigners); + auto const isCoSigned = tx.isFieldPresent(sfSponsorSignature); if (isCoSigned) { - auto const sigVerify = - tx.checkSponsorSign(requireCanonicalSig, rules); + auto const sponsorSignatureObj = + tx.getFieldObject(sfSponsorSignature); + auto const sigVerify = tx.checkSign( + requireCanonicalSig, rules, &sponsorSignatureObj); if (!sigVerify) { router.setFlags(id, SF_SIGBAD); From 28c5cadbea98b58e804e426a686a033f683ac79b Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 7 Oct 2025 10:32:13 +0900 Subject: [PATCH 060/249] fix transactions.macro format --- .../xrpl/protocol/detail/transactions.macro | 1129 +++++++---------- 1 file changed, 459 insertions(+), 670 deletions(-) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index fba6654d5c5..fa82c0742b8 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -41,745 +41,615 @@ /** This transaction type executes a payment. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPAYMENT, - 0, - Payment, +TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegatable, uint256{}, createAcct, ({ - {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - {sfSendMax, soeOPTIONAL, soeMPTSupported}, - {sfPaths, soeDEFAULT}, - {sfInvoiceID, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - {sfDeliverMin, soeOPTIONAL, soeMPTSupported}, - {sfCredentialIDs, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, + {sfSendMax, soeOPTIONAL, soeMPTSupported}, + {sfPaths, soeDEFAULT}, + {sfInvoiceID, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, + {sfDeliverMin, soeOPTIONAL, soeMPTSupported}, + {sfCredentialIDs, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, +})) /** This transaction type creates an escrow object. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttESCROW_CREATE, - 1, - EscrowCreate, +TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegatable, uint256{}, noPriv, ({ - {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - {sfCondition, soeOPTIONAL}, - {sfCancelAfter, soeOPTIONAL}, - {sfFinishAfter, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, + {sfCondition, soeOPTIONAL}, + {sfCancelAfter, soeOPTIONAL}, + {sfFinishAfter, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, +})) /** This transaction type completes an existing escrow. */ -TRANSACTION( - ttESCROW_FINISH, - 2, - EscrowFinish, +TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegatable, uint256{}, noPriv, ({ - {sfOwner, soeREQUIRED}, - {sfOfferSequence, soeREQUIRED}, - {sfFulfillment, soeOPTIONAL}, - {sfCondition, soeOPTIONAL}, - {sfCredentialIDs, soeOPTIONAL}, - })) + {sfOwner, soeREQUIRED}, + {sfOfferSequence, soeREQUIRED}, + {sfFulfillment, soeOPTIONAL}, + {sfCondition, soeOPTIONAL}, + {sfCredentialIDs, soeOPTIONAL}, +})) + /** This transaction type adjusts various account settings. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttACCOUNT_SET, - 3, - AccountSet, +TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegatable, uint256{}, noPriv, ({ - {sfEmailHash, soeOPTIONAL}, - {sfWalletLocator, soeOPTIONAL}, - {sfWalletSize, soeOPTIONAL}, - {sfMessageKey, soeOPTIONAL}, - {sfDomain, soeOPTIONAL}, - {sfTransferRate, soeOPTIONAL}, - {sfSetFlag, soeOPTIONAL}, - {sfClearFlag, soeOPTIONAL}, - {sfTickSize, soeOPTIONAL}, - {sfNFTokenMinter, soeOPTIONAL}, - })) + {sfEmailHash, soeOPTIONAL}, + {sfWalletLocator, soeOPTIONAL}, + {sfWalletSize, soeOPTIONAL}, + {sfMessageKey, soeOPTIONAL}, + {sfDomain, soeOPTIONAL}, + {sfTransferRate, soeOPTIONAL}, + {sfSetFlag, soeOPTIONAL}, + {sfClearFlag, soeOPTIONAL}, + {sfTickSize, soeOPTIONAL}, + {sfNFTokenMinter, soeOPTIONAL}, +})) /** This transaction type cancels an existing escrow. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttESCROW_CANCEL, - 4, - EscrowCancel, +TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, Delegation::delegatable, uint256{}, noPriv, ({ - {sfOwner, soeREQUIRED}, - {sfOfferSequence, soeREQUIRED}, - })) + {sfOwner, soeREQUIRED}, + {sfOfferSequence, soeREQUIRED}, +})) /** This transaction type sets or clears an account's "regular key". */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttREGULAR_KEY_SET, - 5, - SetRegularKey, +TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, Delegation::notDelegatable, uint256{}, noPriv, ({ - {sfRegularKey, soeOPTIONAL}, - })) + {sfRegularKey, soeOPTIONAL}, +})) // 6 deprecated /** This transaction type creates an offer to trade one asset for another. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttOFFER_CREATE, - 7, - OfferCreate, +TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegatable, uint256{}, noPriv, ({ - {sfTakerPays, soeREQUIRED}, - {sfTakerGets, soeREQUIRED}, - {sfExpiration, soeOPTIONAL}, - {sfOfferSequence, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - })) + {sfTakerPays, soeREQUIRED}, + {sfTakerGets, soeREQUIRED}, + {sfExpiration, soeOPTIONAL}, + {sfOfferSequence, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, +})) -/** This transaction type cancels existing offers to trade one asset for - * another. */ +/** This transaction type cancels existing offers to trade one asset for another. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttOFFER_CANCEL, - 8, - OfferCancel, +TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, Delegation::delegatable, uint256{}, noPriv, ({ - {sfOfferSequence, soeREQUIRED}, - })) + {sfOfferSequence, soeREQUIRED}, +})) // 9 deprecated /** This transaction type creates a new set of tickets. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttTICKET_CREATE, - 10, - TicketCreate, +TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegatable, featureTicketBatch, noPriv, ({ - {sfTicketCount, soeREQUIRED}, - })) + {sfTicketCount, soeREQUIRED}, +})) // 11 deprecated -/** This transaction type modifies the signer list associated with an account. - */ +/** This transaction type modifies the signer list associated with an account. */ // The SignerEntries are optional because a SignerList is deleted by // setting the SignerQuorum to zero and omitting SignerEntries. #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttSIGNER_LIST_SET, - 12, - SignerListSet, +TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, Delegation::notDelegatable, uint256{}, noPriv, ({ - {sfSignerQuorum, soeREQUIRED}, - {sfSignerEntries, soeOPTIONAL}, - })) + {sfSignerQuorum, soeREQUIRED}, + {sfSignerEntries, soeOPTIONAL}, +})) /** This transaction type creates a new unidirectional XRP payment channel. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPAYCHAN_CREATE, - 13, - PaymentChannelCreate, +TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegatable, uint256{}, noPriv, ({ - {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfSettleDelay, soeREQUIRED}, - {sfPublicKey, soeREQUIRED}, - {sfCancelAfter, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfSettleDelay, soeREQUIRED}, + {sfPublicKey, soeREQUIRED}, + {sfCancelAfter, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, +})) -/** This transaction type funds an existing unidirectional XRP payment channel. - */ -TRANSACTION( - ttPAYCHAN_FUND, - 14, - PaymentChannelFund, +/** This transaction type funds an existing unidirectional XRP payment channel. */ +TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, Delegation::delegatable, uint256{}, noPriv, ({ - {sfChannel, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfExpiration, soeOPTIONAL}, - })) - -/** This transaction type submits a claim against an existing unidirectional - * payment channel. */ -TRANSACTION( - ttPAYCHAN_CLAIM, - 15, - PaymentChannelClaim, + {sfChannel, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfExpiration, soeOPTIONAL}, +})) + +/** This transaction type submits a claim against an existing unidirectional payment channel. */ +TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegatable, uint256{}, noPriv, ({ - {sfChannel, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - {sfBalance, soeOPTIONAL}, - {sfSignature, soeOPTIONAL}, - {sfPublicKey, soeOPTIONAL}, - {sfCredentialIDs, soeOPTIONAL}, - })) + {sfChannel, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, + {sfBalance, soeOPTIONAL}, + {sfSignature, soeOPTIONAL}, + {sfPublicKey, soeOPTIONAL}, + {sfCredentialIDs, soeOPTIONAL}, +})) /** This transaction type creates a new check. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCHECK_CREATE, - 16, - CheckCreate, +TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegatable, featureChecks, noPriv, ({ - {sfDestination, soeREQUIRED}, - {sfSendMax, soeREQUIRED}, - {sfExpiration, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - {sfInvoiceID, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfSendMax, soeREQUIRED}, + {sfExpiration, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, + {sfInvoiceID, soeOPTIONAL}, +})) /** This transaction type cashes an existing check. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCHECK_CASH, - 17, - CheckCash, +TRANSACTION(ttCHECK_CASH, 17, CheckCash, Delegation::delegatable, featureChecks, noPriv, ({ - {sfCheckID, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - {sfDeliverMin, soeOPTIONAL}, - })) + {sfCheckID, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, + {sfDeliverMin, soeOPTIONAL}, +})) /** This transaction type cancels an existing check. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCHECK_CANCEL, - 18, - CheckCancel, +TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, Delegation::delegatable, featureChecks, noPriv, ({ - {sfCheckID, soeREQUIRED}, - })) + {sfCheckID, soeREQUIRED}, +})) /** This transaction type grants or revokes authorization to transfer funds. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttDEPOSIT_PREAUTH, - 19, - DepositPreauth, +TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, Delegation::delegatable, featureDepositPreauth, noPriv, ({ - {sfAuthorize, soeOPTIONAL}, - {sfUnauthorize, soeOPTIONAL}, - {sfAuthorizeCredentials, soeOPTIONAL}, - {sfUnauthorizeCredentials, soeOPTIONAL}, - })) + {sfAuthorize, soeOPTIONAL}, + {sfUnauthorize, soeOPTIONAL}, + {sfAuthorizeCredentials, soeOPTIONAL}, + {sfUnauthorizeCredentials, soeOPTIONAL}, +})) /** This transaction type modifies a trustline between two accounts. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttTRUST_SET, - 20, - TrustSet, +TRANSACTION(ttTRUST_SET, 20, TrustSet, Delegation::delegatable, uint256{}, noPriv, ({ - {sfLimitAmount, soeOPTIONAL}, - {sfQualityIn, soeOPTIONAL}, - {sfQualityOut, soeOPTIONAL}, - })) + {sfLimitAmount, soeOPTIONAL}, + {sfQualityIn, soeOPTIONAL}, + {sfQualityOut, soeOPTIONAL}, +})) /** This transaction type deletes an existing account. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttACCOUNT_DELETE, - 21, - AccountDelete, +TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegatable, uint256{}, mustDeleteAcct, ({ - {sfDestination, soeREQUIRED}, - {sfDestinationTag, soeOPTIONAL}, - {sfCredentialIDs, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfDestinationTag, soeOPTIONAL}, + {sfCredentialIDs, soeOPTIONAL}, +})) // 22 reserved /** This transaction mints a new NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_MINT, - 25, - NFTokenMint, +TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, Delegation::delegatable, featureNonFungibleTokensV1, changeNFTCounts, ({ - {sfNFTokenTaxon, soeREQUIRED}, - {sfTransferFee, soeOPTIONAL}, - {sfIssuer, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - {sfAmount, soeOPTIONAL}, - {sfDestination, soeOPTIONAL}, - {sfExpiration, soeOPTIONAL}, - })) + {sfNFTokenTaxon, soeREQUIRED}, + {sfTransferFee, soeOPTIONAL}, + {sfIssuer, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, + {sfAmount, soeOPTIONAL}, + {sfDestination, soeOPTIONAL}, + {sfExpiration, soeOPTIONAL}, +})) /** This transaction burns (i.e. destroys) an existing NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_BURN, - 26, - NFTokenBurn, +TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, Delegation::delegatable, featureNonFungibleTokensV1, changeNFTCounts, ({ - {sfNFTokenID, soeREQUIRED}, - {sfOwner, soeOPTIONAL}, - })) + {sfNFTokenID, soeREQUIRED}, + {sfOwner, soeOPTIONAL}, +})) /** This transaction creates a new offer to buy or sell an NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_CREATE_OFFER, - 27, - NFTokenCreateOffer, +TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, Delegation::delegatable, featureNonFungibleTokensV1, noPriv, ({ - {sfNFTokenID, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfDestination, soeOPTIONAL}, - {sfOwner, soeOPTIONAL}, - {sfExpiration, soeOPTIONAL}, - })) + {sfNFTokenID, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfDestination, soeOPTIONAL}, + {sfOwner, soeOPTIONAL}, + {sfExpiration, soeOPTIONAL}, +})) -/** This transaction cancels an existing offer to buy or sell an existing NFT. - */ +/** This transaction cancels an existing offer to buy or sell an existing NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_CANCEL_OFFER, - 28, - NFTokenCancelOffer, +TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, Delegation::delegatable, featureNonFungibleTokensV1, noPriv, ({ - {sfNFTokenOffers, soeREQUIRED}, - })) + {sfNFTokenOffers, soeREQUIRED}, +})) -/** This transaction accepts an existing offer to buy or sell an existing NFT. - */ +/** This transaction accepts an existing offer to buy or sell an existing NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_ACCEPT_OFFER, - 29, - NFTokenAcceptOffer, +TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, Delegation::delegatable, featureNonFungibleTokensV1, noPriv, ({ - {sfNFTokenBuyOffer, soeOPTIONAL}, - {sfNFTokenSellOffer, soeOPTIONAL}, - {sfNFTokenBrokerFee, soeOPTIONAL}, - })) + {sfNFTokenBuyOffer, soeOPTIONAL}, + {sfNFTokenSellOffer, soeOPTIONAL}, + {sfNFTokenBrokerFee, soeOPTIONAL}, +})) /** This transaction claws back issued tokens. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCLAWBACK, - 30, - Clawback, +TRANSACTION(ttCLAWBACK, 30, Clawback, Delegation::delegatable, featureClawback, noPriv, ({ - {sfAmount, soeREQUIRED, soeMPTSupported}, - {sfHolder, soeOPTIONAL}, - })) + {sfAmount, soeREQUIRED, soeMPTSupported}, + {sfHolder, soeOPTIONAL}, +})) /** This transaction claws back tokens from an AMM pool. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_CLAWBACK, - 31, - AMMClawback, +TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, Delegation::delegatable, featureAMMClawback, mayDeleteAcct | overrideFreeze, ({ - {sfHolder, soeREQUIRED}, - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - })) + {sfHolder, soeREQUIRED}, + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, +})) /** This transaction type creates an AMM instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_CREATE, - 35, - AMMCreate, +TRANSACTION(ttAMM_CREATE, 35, AMMCreate, Delegation::delegatable, featureAMM, createPseudoAcct, ({ - {sfAmount, soeREQUIRED}, - {sfAmount2, soeREQUIRED}, - {sfTradingFee, soeREQUIRED}, - })) + {sfAmount, soeREQUIRED}, + {sfAmount2, soeREQUIRED}, + {sfTradingFee, soeREQUIRED}, +})) /** This transaction type deposits into an AMM instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_DEPOSIT, - 36, - AMMDeposit, +TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, Delegation::delegatable, featureAMM, noPriv, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - {sfAmount2, soeOPTIONAL}, - {sfEPrice, soeOPTIONAL}, - {sfLPTokenOut, soeOPTIONAL}, - {sfTradingFee, soeOPTIONAL}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, + {sfAmount2, soeOPTIONAL}, + {sfEPrice, soeOPTIONAL}, + {sfLPTokenOut, soeOPTIONAL}, + {sfTradingFee, soeOPTIONAL}, +})) /** This transaction type withdraws from an AMM instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_WITHDRAW, - 37, - AMMWithdraw, +TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, Delegation::delegatable, featureAMM, mayDeleteAcct, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - {sfAmount2, soeOPTIONAL}, - {sfEPrice, soeOPTIONAL}, - {sfLPTokenIn, soeOPTIONAL}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, + {sfAmount2, soeOPTIONAL}, + {sfEPrice, soeOPTIONAL}, + {sfLPTokenIn, soeOPTIONAL}, +})) /** This transaction type votes for the trading fee */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_VOTE, - 38, - AMMVote, +TRANSACTION(ttAMM_VOTE, 38, AMMVote, Delegation::delegatable, featureAMM, noPriv, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfTradingFee, soeREQUIRED}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfTradingFee, soeREQUIRED}, +})) /** This transaction type bids for the auction slot */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_BID, - 39, - AMMBid, +TRANSACTION(ttAMM_BID, 39, AMMBid, Delegation::delegatable, featureAMM, noPriv, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfBidMin, soeOPTIONAL}, - {sfBidMax, soeOPTIONAL}, - {sfAuthAccounts, soeOPTIONAL}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfBidMin, soeOPTIONAL}, + {sfBidMax, soeOPTIONAL}, + {sfAuthAccounts, soeOPTIONAL}, +})) /** This transaction type deletes AMM in the empty state */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_DELETE, - 40, - AMMDelete, +TRANSACTION(ttAMM_DELETE, 40, AMMDelete, Delegation::delegatable, featureAMM, mustDeleteAcct, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, +})) /** This transactions creates a crosschain sequence number */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttXCHAIN_CREATE_CLAIM_ID, - 41, - XChainCreateClaimID, +TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID, Delegation::delegatable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfSignatureReward, soeREQUIRED}, - {sfOtherChainSource, soeREQUIRED}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfSignatureReward, soeREQUIRED}, + {sfOtherChainSource, soeREQUIRED}, +})) /** This transactions initiates a crosschain transaction */ -TRANSACTION( - ttXCHAIN_COMMIT, - 42, - XChainCommit, +TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, Delegation::delegatable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfXChainClaimID, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfOtherChainDestination, soeOPTIONAL}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfXChainClaimID, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfOtherChainDestination, soeOPTIONAL}, +})) /** This transaction completes a crosschain transaction */ -TRANSACTION( - ttXCHAIN_CLAIM, - 43, - XChainClaim, +TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, Delegation::delegatable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfXChainClaimID, soeREQUIRED}, - {sfDestination, soeREQUIRED}, - {sfDestinationTag, soeOPTIONAL}, - {sfAmount, soeREQUIRED}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfXChainClaimID, soeREQUIRED}, + {sfDestination, soeREQUIRED}, + {sfDestinationTag, soeOPTIONAL}, + {sfAmount, soeREQUIRED}, +})) /** This transaction initiates a crosschain account create transaction */ -TRANSACTION( - ttXCHAIN_ACCOUNT_CREATE_COMMIT, - 44, - XChainAccountCreateCommit, +TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, Delegation::delegatable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfSignatureReward, soeREQUIRED}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfSignatureReward, soeREQUIRED}, +})) /** This transaction adds an attestation to a claim */ -TRANSACTION( - ttXCHAIN_ADD_CLAIM_ATTESTATION, - 45, - XChainAddClaimAttestation, +TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, Delegation::delegatable, featureXChainBridge, createAcct, ({ - {sfXChainBridge, soeREQUIRED}, + {sfXChainBridge, soeREQUIRED}, - {sfAttestationSignerAccount, soeREQUIRED}, - {sfPublicKey, soeREQUIRED}, - {sfSignature, soeREQUIRED}, - {sfOtherChainSource, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfAttestationRewardAccount, soeREQUIRED}, - {sfWasLockingChainSend, soeREQUIRED}, + {sfAttestationSignerAccount, soeREQUIRED}, + {sfPublicKey, soeREQUIRED}, + {sfSignature, soeREQUIRED}, + {sfOtherChainSource, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfAttestationRewardAccount, soeREQUIRED}, + {sfWasLockingChainSend, soeREQUIRED}, - {sfXChainClaimID, soeREQUIRED}, - {sfDestination, soeOPTIONAL}, - })) + {sfXChainClaimID, soeREQUIRED}, + {sfDestination, soeOPTIONAL}, +})) /** This transaction adds an attestation to an account */ -TRANSACTION( - ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, - 46, +TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateAttestation, Delegation::delegatable, featureXChainBridge, createAcct, ({ - {sfXChainBridge, soeREQUIRED}, + {sfXChainBridge, soeREQUIRED}, - {sfAttestationSignerAccount, soeREQUIRED}, - {sfPublicKey, soeREQUIRED}, - {sfSignature, soeREQUIRED}, - {sfOtherChainSource, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfAttestationRewardAccount, soeREQUIRED}, - {sfWasLockingChainSend, soeREQUIRED}, + {sfAttestationSignerAccount, soeREQUIRED}, + {sfPublicKey, soeREQUIRED}, + {sfSignature, soeREQUIRED}, + {sfOtherChainSource, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfAttestationRewardAccount, soeREQUIRED}, + {sfWasLockingChainSend, soeREQUIRED}, - {sfXChainAccountCreateCount, soeREQUIRED}, - {sfDestination, soeREQUIRED}, - {sfSignatureReward, soeREQUIRED}, - })) + {sfXChainAccountCreateCount, soeREQUIRED}, + {sfDestination, soeREQUIRED}, + {sfSignatureReward, soeREQUIRED}, +})) /** This transaction modifies a sidechain */ -TRANSACTION( - ttXCHAIN_MODIFY_BRIDGE, - 47, - XChainModifyBridge, +TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge, Delegation::delegatable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfSignatureReward, soeOPTIONAL}, - {sfMinAccountCreateAmount, soeOPTIONAL}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfSignatureReward, soeOPTIONAL}, + {sfMinAccountCreateAmount, soeOPTIONAL}, +})) /** This transactions creates a sidechain */ -TRANSACTION( - ttXCHAIN_CREATE_BRIDGE, - 48, - XChainCreateBridge, +TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, Delegation::delegatable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfSignatureReward, soeREQUIRED}, - {sfMinAccountCreateAmount, soeOPTIONAL}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfSignatureReward, soeREQUIRED}, + {sfMinAccountCreateAmount, soeOPTIONAL}, +})) /** This transaction type creates or updates a DID */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttDID_SET, - 49, - DIDSet, +TRANSACTION(ttDID_SET, 49, DIDSet, Delegation::delegatable, featureDID, noPriv, ({ - {sfDIDDocument, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - {sfData, soeOPTIONAL}, - })) + {sfDIDDocument, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, + {sfData, soeOPTIONAL}, +})) /** This transaction type deletes a DID */ -TRANSACTION( - ttDID_DELETE, - 50, - DIDDelete, +TRANSACTION(ttDID_DELETE, 50, DIDDelete, Delegation::delegatable, featureDID, noPriv, @@ -787,448 +657,367 @@ TRANSACTION( /** This transaction type creates an Oracle instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttORACLE_SET, - 51, - OracleSet, +TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegatable, featurePriceOracle, noPriv, ({ - {sfOracleDocumentID, soeREQUIRED}, - {sfProvider, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - {sfAssetClass, soeOPTIONAL}, - {sfLastUpdateTime, soeREQUIRED}, - {sfPriceDataSeries, soeREQUIRED}, - })) + {sfOracleDocumentID, soeREQUIRED}, + {sfProvider, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, + {sfAssetClass, soeOPTIONAL}, + {sfLastUpdateTime, soeREQUIRED}, + {sfPriceDataSeries, soeREQUIRED}, +})) /** This transaction type deletes an Oracle instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttORACLE_DELETE, - 52, - OracleDelete, +TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, Delegation::delegatable, featurePriceOracle, noPriv, ({ - {sfOracleDocumentID, soeREQUIRED}, - })) + {sfOracleDocumentID, soeREQUIRED}, +})) /** This transaction type fixes a problem in the ledger state */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLEDGER_STATE_FIX, - 53, - LedgerStateFix, +TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, Delegation::delegatable, fixNFTokenPageLinks, noPriv, ({ - {sfLedgerFixType, soeREQUIRED}, - {sfOwner, soeOPTIONAL}, - })) + {sfLedgerFixType, soeREQUIRED}, + {sfOwner, soeOPTIONAL}, +})) /** This transaction type creates a MPTokensIssuance instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttMPTOKEN_ISSUANCE_CREATE, - 54, - MPTokenIssuanceCreate, +TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, Delegation::delegatable, featureMPTokensV1, createMPTIssuance, ({ - {sfAssetScale, soeOPTIONAL}, - {sfTransferFee, soeOPTIONAL}, - {sfMaximumAmount, soeOPTIONAL}, - {sfMPTokenMetadata, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - {sfMutableFlags, soeOPTIONAL}, - })) + {sfAssetScale, soeOPTIONAL}, + {sfTransferFee, soeOPTIONAL}, + {sfMaximumAmount, soeOPTIONAL}, + {sfMPTokenMetadata, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, + {sfMutableFlags, soeOPTIONAL}, +})) /** This transaction type destroys a MPTokensIssuance instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttMPTOKEN_ISSUANCE_DESTROY, - 55, - MPTokenIssuanceDestroy, +TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, Delegation::delegatable, featureMPTokensV1, destroyMPTIssuance, ({ - {sfMPTokenIssuanceID, soeREQUIRED}, - })) + {sfMPTokenIssuanceID, soeREQUIRED}, +})) -/** This transaction type sets flags on a MPTokensIssuance or MPToken instance - */ +/** This transaction type sets flags on a MPTokensIssuance or MPToken instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttMPTOKEN_ISSUANCE_SET, - 56, - MPTokenIssuanceSet, +TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, Delegation::delegatable, featureMPTokensV1, noPriv, ({ - {sfMPTokenIssuanceID, soeREQUIRED}, - {sfHolder, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - {sfMPTokenMetadata, soeOPTIONAL}, - {sfTransferFee, soeOPTIONAL}, - {sfMutableFlags, soeOPTIONAL}, - })) + {sfMPTokenIssuanceID, soeREQUIRED}, + {sfHolder, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, + {sfMPTokenMetadata, soeOPTIONAL}, + {sfTransferFee, soeOPTIONAL}, + {sfMutableFlags, soeOPTIONAL}, +})) /** This transaction type authorizes a MPToken instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttMPTOKEN_AUTHORIZE, - 57, - MPTokenAuthorize, +TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, Delegation::delegatable, featureMPTokensV1, mustAuthorizeMPT, ({ - {sfMPTokenIssuanceID, soeREQUIRED}, - {sfHolder, soeOPTIONAL}, - })) + {sfMPTokenIssuanceID, soeREQUIRED}, + {sfHolder, soeOPTIONAL}, +})) /** This transaction type create an Credential instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCREDENTIAL_CREATE, - 58, - CredentialCreate, +TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegatable, featureCredentials, noPriv, ({ - {sfSubject, soeREQUIRED}, - {sfCredentialType, soeREQUIRED}, - {sfExpiration, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - })) + {sfSubject, soeREQUIRED}, + {sfCredentialType, soeREQUIRED}, + {sfExpiration, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, +})) /** This transaction type accept an Credential object */ -TRANSACTION( - ttCREDENTIAL_ACCEPT, - 59, - CredentialAccept, +TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, Delegation::delegatable, featureCredentials, noPriv, ({ - {sfIssuer, soeREQUIRED}, - {sfCredentialType, soeREQUIRED}, - })) + {sfIssuer, soeREQUIRED}, + {sfCredentialType, soeREQUIRED}, +})) /** This transaction type delete an Credential object */ -TRANSACTION( - ttCREDENTIAL_DELETE, - 60, - CredentialDelete, +TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, Delegation::delegatable, featureCredentials, noPriv, ({ - {sfSubject, soeOPTIONAL}, - {sfIssuer, soeOPTIONAL}, - {sfCredentialType, soeREQUIRED}, - })) + {sfSubject, soeOPTIONAL}, + {sfIssuer, soeOPTIONAL}, + {sfCredentialType, soeREQUIRED}, +})) /** This transaction type modify a NFToken */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_MODIFY, - 61, - NFTokenModify, +TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, Delegation::delegatable, featureDynamicNFT, noPriv, ({ - {sfNFTokenID, soeREQUIRED}, - {sfOwner, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - })) + {sfNFTokenID, soeREQUIRED}, + {sfOwner, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, +})) /** This transaction type creates or modifies a Permissioned Domain */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPERMISSIONED_DOMAIN_SET, - 62, - PermissionedDomainSet, +TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, Delegation::delegatable, featurePermissionedDomains, noPriv, ({ - {sfDomainID, soeOPTIONAL}, - {sfAcceptedCredentials, soeREQUIRED}, - })) + {sfDomainID, soeOPTIONAL}, + {sfAcceptedCredentials, soeREQUIRED}, +})) /** This transaction type deletes a Permissioned Domain */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPERMISSIONED_DOMAIN_DELETE, - 63, - PermissionedDomainDelete, +TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, Delegation::delegatable, featurePermissionedDomains, noPriv, ({ - {sfDomainID, soeREQUIRED}, - })) + {sfDomainID, soeREQUIRED}, +})) /** This transaction type delegates authorized account specified permissions */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttDELEGATE_SET, - 64, - DelegateSet, +TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, Delegation::notDelegatable, featurePermissionDelegation, noPriv, ({ - {sfAuthorize, soeREQUIRED}, - {sfPermissions, soeREQUIRED}, - })) + {sfAuthorize, soeREQUIRED}, + {sfPermissions, soeREQUIRED}, +})) /** This transaction creates a single asset vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_CREATE, - 65, - VaultCreate, +TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, Delegation::delegatable, featureSingleAssetVault, createPseudoAcct | createMPTIssuance, ({ - {sfAsset, soeREQUIRED, soeMPTSupported}, - {sfAssetsMaximum, soeOPTIONAL}, - {sfMPTokenMetadata, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - {sfWithdrawalPolicy, soeOPTIONAL}, - {sfData, soeOPTIONAL}, - {sfScale, soeOPTIONAL}, - })) + {sfAsset, soeREQUIRED, soeMPTSupported}, + {sfAssetsMaximum, soeOPTIONAL}, + {sfMPTokenMetadata, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, + {sfWithdrawalPolicy, soeOPTIONAL}, + {sfData, soeOPTIONAL}, + {sfScale, soeOPTIONAL}, +})) /** This transaction updates a single asset vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_SET, - 66, - VaultSet, +TRANSACTION(ttVAULT_SET, 66, VaultSet, Delegation::delegatable, featureSingleAssetVault, noPriv, ({ - {sfVaultID, soeREQUIRED}, - {sfAssetsMaximum, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - {sfData, soeOPTIONAL}, - })) + {sfVaultID, soeREQUIRED}, + {sfAssetsMaximum, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, + {sfData, soeOPTIONAL}, +})) /** This transaction deletes a single asset vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_DELETE, - 67, - VaultDelete, +TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, Delegation::delegatable, featureSingleAssetVault, mustDeleteAcct | destroyMPTIssuance, ({ - {sfVaultID, soeREQUIRED}, - })) + {sfVaultID, soeREQUIRED}, +})) /** This transaction trades assets for shares with a vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_DEPOSIT, - 68, - VaultDeposit, +TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, Delegation::delegatable, featureSingleAssetVault, mayAuthorizeMPT, ({ - {sfVaultID, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - })) + {sfVaultID, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, +})) /** This transaction trades shares for assets with a vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_WITHDRAW, - 69, - VaultWithdraw, +TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, Delegation::delegatable, featureSingleAssetVault, mayDeleteMPT, ({ - {sfVaultID, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - {sfDestination, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - })) + {sfVaultID, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, + {sfDestination, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, +})) /** This transaction claws back tokens from a vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_CLAWBACK, - 70, - VaultClawback, +TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, Delegation::delegatable, featureSingleAssetVault, mayDeleteMPT, ({ - {sfVaultID, soeREQUIRED}, - {sfHolder, soeREQUIRED}, - {sfAmount, soeOPTIONAL, soeMPTSupported}, - })) + {sfVaultID, soeREQUIRED}, + {sfHolder, soeREQUIRED}, + {sfAmount, soeOPTIONAL, soeMPTSupported}, +})) /** This transaction type batches together transactions. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttBATCH, - 71, - Batch, +TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, featureBatch, noPriv, ({ - {sfRawTransactions, soeREQUIRED}, - {sfBatchSigners, soeOPTIONAL}, - })) + {sfRawTransactions, soeREQUIRED}, + {sfBatchSigners, soeOPTIONAL}, +})) /** This transaction transfer sponsorship */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttSPONSORSHIP_TRANSFER, - 72, - SponsorshipTransfer, +TRANSACTION(ttSPONSORSHIP_TRANSFER, 72, SponsorshipTransfer, Delegation::notDelegatable, featureSponsor, noPriv, ({ - {sfObjectID, soeOPTIONAL}, - })) + {sfObjectID, soeOPTIONAL}, +})) /** This transaction create sponsorship object */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttSPONSORSHIP_SET, - 73, - SponsorshipSet, +TRANSACTION(ttSPONSORSHIP_SET, 73, SponsorshipSet, Delegation::notDelegatable, featureSponsor, noPriv, ({ - {sfSponsorAccount, soeOPTIONAL}, - {sfSponsee, soeOPTIONAL}, - {sfFeeAmount, soeOPTIONAL}, - {sfMaxFee, soeOPTIONAL}, - {sfReserveCount, soeOPTIONAL}, - })) + {sfSponsorAccount, soeOPTIONAL}, + {sfSponsee, soeOPTIONAL}, + {sfFeeAmount, soeOPTIONAL}, + {sfMaxFee, soeOPTIONAL}, + {sfReserveCount, soeOPTIONAL}, +})) -/** This system-generated transaction type is used to update the status of the - various amendments. +/** This system-generated transaction type is used to update the status of the various amendments. For details, see: https://xrpl.org/amendments.html */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMENDMENT, - 100, - EnableAmendment, +TRANSACTION(ttAMENDMENT, 100, EnableAmendment, Delegation::notDelegatable, uint256{}, noPriv, ({ - {sfLedgerSequence, soeREQUIRED}, - {sfAmendment, soeREQUIRED}, - })) + {sfLedgerSequence, soeREQUIRED}, + {sfAmendment, soeREQUIRED}, +})) -/** This system-generated transaction type is used to update the network's fee - settings. For details, see: https://xrpl.org/fee-voting.html +/** This system-generated transaction type is used to update the network's fee settings. + For details, see: https://xrpl.org/fee-voting.html */ -TRANSACTION( - ttFEE, - 101, - SetFee, +TRANSACTION(ttFEE, 101, SetFee, Delegation::notDelegatable, uint256{}, noPriv, ({ - {sfLedgerSequence, soeOPTIONAL}, - // Old version uses raw numbers - {sfBaseFee, soeOPTIONAL}, - {sfReferenceFeeUnits, soeOPTIONAL}, - {sfReserveBase, soeOPTIONAL}, - {sfReserveIncrement, soeOPTIONAL}, - // New version uses Amounts - {sfBaseFeeDrops, soeOPTIONAL}, - {sfReserveBaseDrops, soeOPTIONAL}, - {sfReserveIncrementDrops, soeOPTIONAL}, - })) - -/** This system-generated transaction type is used to update the network's - negative UNL + {sfLedgerSequence, soeOPTIONAL}, + // Old version uses raw numbers + {sfBaseFee, soeOPTIONAL}, + {sfReferenceFeeUnits, soeOPTIONAL}, + {sfReserveBase, soeOPTIONAL}, + {sfReserveIncrement, soeOPTIONAL}, + // New version uses Amounts + {sfBaseFeeDrops, soeOPTIONAL}, + {sfReserveBaseDrops, soeOPTIONAL}, + {sfReserveIncrementDrops, soeOPTIONAL}, +})) + +/** This system-generated transaction type is used to update the network's negative UNL For details, see: https://xrpl.org/negative-unl.html */ -TRANSACTION( - ttUNL_MODIFY, - 102, - UNLModify, +TRANSACTION(ttUNL_MODIFY, 102, UNLModify, Delegation::notDelegatable, uint256{}, noPriv, ({ - {sfUNLModifyDisabling, soeREQUIRED}, - {sfLedgerSequence, soeREQUIRED}, - {sfUNLModifyValidator, soeREQUIRED}, - })) + {sfUNLModifyDisabling, soeREQUIRED}, + {sfLedgerSequence, soeREQUIRED}, + {sfUNLModifyValidator, soeREQUIRED}, +})) From a6b0efff875dcd87115bb88e663e44a6a3e39b79 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 7 Oct 2025 11:19:31 +0900 Subject: [PATCH 061/249] typo --- src/xrpld/rpc/handlers/Simulate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xrpld/rpc/handlers/Simulate.cpp b/src/xrpld/rpc/handlers/Simulate.cpp index f8f3dc9f8cf..35cb7587d47 100644 --- a/src/xrpld/rpc/handlers/Simulate.cpp +++ b/src/xrpld/rpc/handlers/Simulate.cpp @@ -72,7 +72,7 @@ getAutofillSequence(Json::Value const& tx_json, RPC::JsonContext& context) } static std::optional -autofillSigunature(Json::Value& sigObject) +autofillSignature(Json::Value& sigObject) { if (!sigObject.isMember(jss::SigningPubKey)) { @@ -146,7 +146,7 @@ autofillTx(Json::Value& tx_json, RPC::JsonContext& context) tx_json[jss::Fee] = feeOrError; } - if (auto error = autofillSigunature(tx_json)) + if (auto error = autofillSignature(tx_json)) return *error; if (!tx_json.isMember(jss::Sequence)) From 754f59707822bd84dde86baad0100e977380b767 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 7 Oct 2025 16:02:02 +0900 Subject: [PATCH 062/249] Support sfSponsorSignature autofilling in Simulate RPC --- src/test/rpc/Simulate_test.cpp | 85 +++++++++++++++++++++++++++++ src/xrpld/rpc/handlers/Simulate.cpp | 7 +++ 2 files changed, 92 insertions(+) diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 0a36a8a841c..b5c5a8428c3 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -592,6 +592,91 @@ class Simulate_test : public beast::unit_test::suite // test without autofill testTx(env, tx, validateOutput); } + + { + // autofill sponsor signature + + auto validateOutput = [&](Json::Value const& resp, + Json::Value const& tx) { + auto result = resp[jss::result]; + checkBasicReturnValidity( + result, tx, 2, env.current()->fees().base); + + BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS"); + BEAST_EXPECT(result[jss::engine_result_code] == 0); + BEAST_EXPECT( + result[jss::engine_result_message] == + "The simulated transaction would have been applied."); + + if (BEAST_EXPECT( + result.isMember(jss::meta) || + result.isMember(jss::meta_blob))) + { + Json::Value const metadata = getJsonMetadata(result); + + if (BEAST_EXPECT( + metadata.isMember(sfAffectedNodes.jsonName))) + { + BEAST_EXPECT( + metadata[sfAffectedNodes.jsonName].size() == 2); + + auto node = metadata[sfAffectedNodes.jsonName][0u]; + if (BEAST_EXPECT( + node.isMember(sfModifiedNode.jsonName))) + { + auto modifiedNode = node[sfModifiedNode]; + BEAST_EXPECT( + modifiedNode[sfLedgerEntryType] == + "AccountRoot"); + auto previousFields = + modifiedNode[sfPreviousFields]; + BEAST_EXPECT( + !previousFields.isMember(sfBalance.jsonName)); + } + + auto node2 = metadata[sfAffectedNodes.jsonName][1u]; + if (BEAST_EXPECT( + node2.isMember(sfModifiedNode.jsonName))) + { + auto modifiedNode = node2[sfModifiedNode]; + BEAST_EXPECT( + modifiedNode[sfLedgerEntryType] == + "AccountRoot"); + + auto previousFields = + modifiedNode[sfPreviousFields]; + BEAST_EXPECT( + previousFields.isMember(sfBalance.jsonName)); + } + } + BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0); + BEAST_EXPECT( + metadata[sfTransactionResult.jsonName] == "tesSUCCESS"); + } + }; + + Account const sponsor("sponsor"); + env.fund(XRP(10000), sponsor); + env.close(); + + Json::Value tx; + + tx[jss::Account] = env.master.human(); + tx[jss::TransactionType] = jss::AccountSet; + tx[sfDomain] = newDomain; + tx[sfSponsor.jsonName][sfAccount.jsonName] = sponsor.human(); + tx[sfSponsor.jsonName][sfFlags.jsonName] = tfSponsorFee; + tx[sfSponsorSignature.jsonName] = Json::objectValue; + + // test with autofill + testTx(env, tx, validateOutput); + + tx[sfSponsorSignature.jsonName][sfTxnSignature.jsonName] = ""; + tx[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = ""; + + // test without autofill + testTx(env, tx, validateOutput); + } } void diff --git a/src/xrpld/rpc/handlers/Simulate.cpp b/src/xrpld/rpc/handlers/Simulate.cpp index 35cb7587d47..56e87d3d299 100644 --- a/src/xrpld/rpc/handlers/Simulate.cpp +++ b/src/xrpld/rpc/handlers/Simulate.cpp @@ -149,6 +149,13 @@ autofillTx(Json::Value& tx_json, RPC::JsonContext& context) if (auto error = autofillSignature(tx_json)) return *error; + if (tx_json.isMember(sfSponsorSignature.jsonName)) + { + if (auto error = + autofillSignature(tx_json[sfSponsorSignature.jsonName])) + return *error; + } + if (!tx_json.isMember(jss::Sequence)) { auto const seq = getAutofillSequence(tx_json, context); From 7f922c49195b3c2404785a2d7aa2809bb3df2ca3 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 8 Oct 2025 02:50:08 +0900 Subject: [PATCH 063/249] reserve co-signing --- include/xrpl/ledger/View.h | 37 ++++- src/libxrpl/ledger/CredentialHelpers.cpp | 2 +- src/libxrpl/ledger/View.cpp | 133 ++++++++++++++---- src/test/app/Sponsor_test.cpp | 6 +- src/xrpld/app/paths/AMMOffer.h | 5 +- src/xrpld/app/tx/detail/AMMCreate.cpp | 12 +- src/xrpld/app/tx/detail/AMMDeposit.cpp | 6 +- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 5 + src/xrpld/app/tx/detail/CancelCheck.cpp | 2 +- src/xrpld/app/tx/detail/CashCheck.cpp | 10 +- src/xrpld/app/tx/detail/Change.cpp | 4 +- src/xrpld/app/tx/detail/CreateCheck.cpp | 8 +- src/xrpld/app/tx/detail/CreateOffer.cpp | 4 +- src/xrpld/app/tx/detail/CreateTicket.cpp | 10 +- src/xrpld/app/tx/detail/Credentials.cpp | 10 +- src/xrpld/app/tx/detail/DID.cpp | 6 +- src/xrpld/app/tx/detail/DelegateSet.cpp | 7 +- src/xrpld/app/tx/detail/DeleteOracle.cpp | 2 +- src/xrpld/app/tx/detail/DepositPreauth.cpp | 10 +- src/xrpld/app/tx/detail/Escrow.cpp | 20 +-- .../app/tx/detail/MPTokenIssuanceCreate.cpp | 4 +- .../app/tx/detail/MPTokenIssuanceDestroy.cpp | 2 +- .../app/tx/detail/NFTokenAcceptOffer.cpp | 4 +- src/xrpld/app/tx/detail/NFTokenMint.cpp | 3 +- src/xrpld/app/tx/detail/NFTokenUtils.cpp | 26 ++-- src/xrpld/app/tx/detail/NFTokenUtils.h | 1 + src/xrpld/app/tx/detail/PayChan.cpp | 15 +- .../tx/detail/PermissionedDomainDelete.cpp | 2 +- .../app/tx/detail/PermissionedDomainSet.cpp | 4 +- src/xrpld/app/tx/detail/SetOracle.cpp | 9 +- src/xrpld/app/tx/detail/SetSignerList.cpp | 6 +- src/xrpld/app/tx/detail/SetTrust.cpp | 16 ++- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 8 +- .../app/tx/detail/SponsorshipTransfer.cpp | 2 + src/xrpld/app/tx/detail/Transactor.cpp | 2 +- src/xrpld/app/tx/detail/VaultClawback.cpp | 2 + src/xrpld/app/tx/detail/VaultCreate.cpp | 6 +- src/xrpld/app/tx/detail/VaultDelete.cpp | 4 +- src/xrpld/app/tx/detail/VaultDeposit.cpp | 3 + src/xrpld/app/tx/detail/VaultWithdraw.cpp | 4 + src/xrpld/app/tx/detail/XChainBridge.cpp | 20 +-- src/xrpld/app/tx/detail/apply.cpp | 2 +- 42 files changed, 315 insertions(+), 129 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 270311173f9..27d50f25669 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -454,12 +454,16 @@ areCompatible( uint32_t ownerCount(std::shared_ptr const& sponsorSle); +bool +isSponsorReserveCoSigning(STTx const& tx); + TER checkInsufficientReserve( ReadView const& view, + STTx const& tx, std::shared_ptr accSle, STAmount const& accBalance, - std::optional> const& _sponsorSle, + std::optional> const& sponsorSle, std::int32_t ownerCountDelta, std::int32_t accountCountDelta = 0); @@ -502,11 +506,26 @@ adjustOwnerCount( std::shared_ptr const& accountSle, std::optional> const& sponsorSle, std::int32_t amount, + bool const isSponsorCoSigning, beast::Journal j); inline void adjustOwnerCount( ApplyView& view, + STTx const& tx, + std::shared_ptr const& accountSle, + std::optional> const& sponsorSle, + std::int32_t amount, + beast::Journal j) +{ + auto const isCoSigning = isSponsorReserveCoSigning(tx); + adjustOwnerCount(view, accountSle, sponsorSle, amount, isCoSigning, j); +} + +inline void +adjustOwnerCount( + ApplyView& view, + STTx const& tx, AccountID const& account, std::optional const& sponsor, std::int32_t amount, @@ -514,6 +533,7 @@ adjustOwnerCount( { return adjustOwnerCount( view, + tx, view.peek(keylet::account(account)), sponsor ? view.peek(keylet::account(*sponsor)) : std::optional>(), @@ -521,6 +541,19 @@ adjustOwnerCount( j); } +inline void +reduceOwnerCount( + ApplyView& view, + std::shared_ptr const& accountSle, + std::optional> const& sponsorSle, + std::int32_t amount, + beast::Journal j) +{ + XRPL_ASSERT( + amount <= 0, "ripple::reduceOwnerCount : amount must be negative"); + adjustOwnerCount(view, accountSle, sponsorSle, amount, true, j); +} + /** @{ */ /** Returns the first entry in the directory, advancing the index @@ -711,6 +744,7 @@ trustCreate( std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, std::optional const& sponsorAccountID, + bool const isSponsorCoSigning, beast::Journal j); [[nodiscard]] TER @@ -813,6 +847,7 @@ accountSend( STAmount const& saAmount, beast::Journal j, std::optional const& sponsorAcc = std::nullopt, + bool isSponsorCoSigning = false, WaiveTransferFee waiveFee = WaiveTransferFee::No); [[nodiscard]] TER diff --git a/src/libxrpl/ledger/CredentialHelpers.cpp b/src/libxrpl/ledger/CredentialHelpers.cpp index 8e0ea9588a3..a6a4539c277 100644 --- a/src/libxrpl/ledger/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/CredentialHelpers.cpp @@ -95,7 +95,7 @@ deleteSLE( { auto const sponsor = getLedgerEntryReserveSponsor(view, sleCredential); - adjustOwnerCount(view, sleAccount, sponsor, -1, j); + reduceOwnerCount(view, sleAccount, sponsor, -1, j); } return tesSUCCESS; diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index a8998efcd17..ef807dbde5a 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1041,9 +1041,19 @@ ownerCount(std::shared_ptr const& sponsorSle) return ownerCount + sponsoringOwnerCount - sponsoredOwnerCount; } +bool +isSponsorReserveCoSigning(STTx const& tx) +{ + if (!tx.isFieldPresent(sfSponsorSignature)) + return false; + auto const sponsor = tx.getFieldObject(sfSponsor); + return sponsor.isFlag(tfSponsorReserve); +} + TER checkInsufficientReserve( ReadView const& view, + STTx const& tx, std::shared_ptr accSle, STAmount const& accBalance, std::optional> const& sponsorSle, @@ -1052,18 +1062,39 @@ checkInsufficientReserve( { if (sponsorSle) { - auto const sponsorBalance = (*sponsorSle)->getFieldAmount(sfBalance); - STAmount const sponsorReserve{view.fees().accountReserve( - (*sponsorSle)->getFieldU32(sfOwnerCount), - (*sponsorSle)->getFieldU32(sfSponsoredOwnerCount), - (*sponsorSle)->getFieldU32(sfSponsoringOwnerCount) + - ownerCountDelta, - (*sponsorSle)->isFieldPresent(sfSponsorAccount), - (*sponsorSle)->getFieldU32(sfSponsoringAccountCount) + - accountCountDelta)}; - - if (sponsorBalance < sponsorReserve) - return tecINSUFFICIENT_RESERVE; + auto const isCoSigning = isSponsorReserveCoSigning(tx); + + if (isCoSigning) + { + auto const sponsorBalance = + (*sponsorSle)->getFieldAmount(sfBalance); + STAmount const sponsorReserve{view.fees().accountReserve( + (*sponsorSle)->getFieldU32(sfOwnerCount), + (*sponsorSle)->getFieldU32(sfSponsoredOwnerCount), + (*sponsorSle)->getFieldU32(sfSponsoringOwnerCount) + + ownerCountDelta, + (*sponsorSle)->isFieldPresent(sfSponsorAccount), + (*sponsorSle)->getFieldU32(sfSponsoringAccountCount) + + accountCountDelta)}; + + if (sponsorBalance < sponsorReserve) + return tecINSUFFICIENT_RESERVE; + } + else + { + // pre funded + auto const sle = view.read(keylet::sponsor( + (*sponsorSle)->getAccountID(sfAccount), + accSle->getAccountID(sfAccount))); + if (!sle) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const reserveCountAllowed = sle->getFieldU32(sfReserveCount); + if (reserveCountAllowed < ownerCountDelta) + { + return tecINSUFFICIENT_RESERVE; + } + } } else { @@ -1162,6 +1193,7 @@ adjustOwnerCount( std::shared_ptr const& accountSle, std::optional> const& sponsorSle, std::int32_t amount, + bool const isSponsorCoSigning, beast::Journal j) { if (!accountSle) @@ -1199,6 +1231,24 @@ adjustOwnerCount( accountSle->at(sfSponsoredOwnerCount) = adjusted; view.update(accountSle); } + + if (!isSponsorCoSigning && amount > 0) + { + // pre funded + // modify sponsor's ReserveCount + auto sle = view.peek(keylet::sponsor( + (*sponsorSle)->getAccountID(sfAccount), + accountSle->getAccountID(sfAccount))); + + XRPL_ASSERT( + sle, "ripple::adjustOwnerCount : co-signing sponsor not found"); + XRPL_ASSERT( + sle->at(sfReserveCount) >= amount, + "ripple::adjustOwnerCount : reserve count not enough"); + + sle->at(sfReserveCount) = sle->getFieldU32(sfReserveCount) + amount; + view.update(sle); + } } std::uint32_t const current{accountSle->getFieldU32(sfOwnerCount)}; AccountID const id = (*accountSle)[sfAccount]; @@ -1408,6 +1458,7 @@ addEmptyHolding( auto const& sponsorAccountID = !isPseudoAccount(sleDst) ? getTxReserveSponsorAccountID(tx) : std::nullopt; + auto const isSponsorCoSigning = tx.isFieldPresent(sfSponsorSignature); return trustCreate( view, high, @@ -1424,6 +1475,7 @@ addEmptyHolding( /*qualityIn=*/0, /*qualityOut=*/0, sponsorAccountID, + isSponsorCoSigning, journal); } @@ -1486,7 +1538,7 @@ authorizeMPToken( return tecINTERNAL; // LCOV_EXCL_LINE auto const sponsor = getLedgerEntryReserveSponsor(view, sleMpt); - adjustOwnerCount(view, sleAcct, sponsor, -1, journal); + reduceOwnerCount(view, sleAcct, sponsor, -1, journal); view.erase(sleMpt); return tesSUCCESS; @@ -1506,7 +1558,7 @@ authorizeMPToken( if (ownerCount(sponsor.value_or(sleAcct)) >= 2) { if (auto const ret = checkInsufficientReserve( - view, sleAcct, priorBalance, sponsor, 1); + view, tx, sleAcct, priorBalance, sponsor, 1); !isTesSuccess(ret)) return ret; } @@ -1522,7 +1574,7 @@ authorizeMPToken( view.insert(mptoken); // Update owner count. - adjustOwnerCount(view, sleAcct, sponsor, 1, journal); + adjustOwnerCount(view, tx, sleAcct, sponsor, 1, journal); addSponsorToLedgerEntry(mptoken, sponsor); return tesSUCCESS; @@ -1580,6 +1632,7 @@ trustCreate( std::uint32_t uQualityIn, std::uint32_t uQualityOut, std::optional const& sponsorAccountID, + bool const isSponsorCoSigning, beast::Journal j) { JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", " @@ -1673,7 +1726,7 @@ trustCreate( sponsorSle = view.peek(keylet::account(*sponsorAccountID)); sleRippleState->setFieldU32(sfFlags, uFlags); - adjustOwnerCount(view, sleAccount, sponsorSle, 1, j); + adjustOwnerCount(view, sleAccount, sponsorSle, 1, isSponsorCoSigning, j); addSponsorToLedgerEntry( sleRippleState, @@ -1724,7 +1777,7 @@ removeEmptyHolding( view.peek(keylet::account(line->at(sfLowLimit)->getIssuer())); if (!sleLowAccount) return tecINTERNAL; - adjustOwnerCount(view, sleLowAccount, std::nullopt, -1, journal); + reduceOwnerCount(view, sleLowAccount, std::nullopt, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. @@ -1738,7 +1791,7 @@ removeEmptyHolding( view.peek(keylet::account(line->at(sfHighLimit)->getIssuer())); if (!sleHighAccount) return tecINTERNAL; - adjustOwnerCount(view, sleHighAccount, std::nullopt, -1, journal); + reduceOwnerCount(view, sleHighAccount, std::nullopt, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. @@ -1873,7 +1926,7 @@ offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) } auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, view.peek(keylet::account(owner)), sponsor, -1, j); + reduceOwnerCount(view, view.peek(keylet::account(owner)), sponsor, -1, j); view.erase(sle); @@ -1892,6 +1945,7 @@ rippleCreditIOU( STAmount const& saAmount, bool bCheckIssuer, std::optional const& sponsorAccount, + bool isSponsorCoSigning, beast::Journal j) { AccountID const& issuer = saAmount.getIssuer(); @@ -1971,7 +2025,7 @@ rippleCreditIOU( view, sleRippleState, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); - adjustOwnerCount( + reduceOwnerCount( view, view.peek(keylet::account(uSenderID)), currentSponsor, @@ -2046,6 +2100,7 @@ rippleCreditIOU( 0, 0, sponsorAccount, + isSponsorCoSigning, j); } @@ -2061,6 +2116,7 @@ rippleSendIOU( STAmount& saActual, beast::Journal j, std::optional const& sponsorAccount, + bool isSponsorCoSigning, WaiveTransferFee waiveFee) { auto const issuer = saAmount.getIssuer(); @@ -2076,7 +2132,14 @@ rippleSendIOU( { // Direct send: redeeming IOUs and/or sending own IOUs. auto const ter = rippleCreditIOU( - view, uSenderID, uReceiverID, saAmount, false, sponsorAccount, j); + view, + uSenderID, + uReceiverID, + saAmount, + false, + sponsorAccount, + isSponsorCoSigning, + j); if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS) return ter; saActual = saAmount; @@ -2097,11 +2160,25 @@ rippleSendIOU( << " cost=" << saActual.getFullText(); TER terResult = rippleCreditIOU( - view, issuer, uReceiverID, saAmount, true, sponsorAccount, j); + view, + issuer, + uReceiverID, + saAmount, + true, + sponsorAccount, + isSponsorCoSigning, + j); if (tesSUCCESS == terResult) terResult = rippleCreditIOU( - view, uSenderID, issuer, saActual, true, sponsorAccount, j); + view, + uSenderID, + issuer, + saActual, + true, + sponsorAccount, + isSponsorCoSigning, + j); return terResult; } @@ -2114,6 +2191,7 @@ accountSendIOU( STAmount const& saAmount, beast::Journal j, std::optional const& sponsorAccount, + bool isSponsorCoSigning, WaiveTransferFee waiveFee) { if (view.rules().enabled(fixAMMv1_1)) @@ -2152,6 +2230,7 @@ accountSendIOU( saActual, j, sponsorAccount, + isSponsorCoSigning, waiveFee); } @@ -2395,6 +2474,7 @@ accountSend( STAmount const& saAmount, beast::Journal j, std::optional const& sponsorAcc, + bool isSponsorCoSigning, WaiveTransferFee waiveFee) { return std::visit( @@ -2407,6 +2487,7 @@ accountSend( saAmount, j, sponsorAcc, + isSponsorCoSigning, waiveFee); else return accountSendMPT( @@ -2458,7 +2539,7 @@ updateTrustLine( view, state, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); - adjustOwnerCount(view, sle, currentSponsor, -1, j); + reduceOwnerCount(view, sle, currentSponsor, -1, j); // Clear reserve flag. state->setFieldU32( @@ -2573,6 +2654,7 @@ issueIOU( 0, 0, std::nullopt, + false, j); } @@ -3056,7 +3138,7 @@ deleteAMMTrustLine( auto const sponsorSle = getLedgerEntryReserveSponsor( view, sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount); - adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, -1, j); + reduceOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, -1, j); removeSponsorFromLedgerEntry( sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount); @@ -3083,6 +3165,7 @@ rippleCredit( saAmount, bCheckIssuer, std::nullopt, + false, j); } else diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 28906f195a9..d23a56aa32f 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -26,6 +26,8 @@ #include #include +#include "test/jtx/sponsor.h" + namespace ripple { namespace test { @@ -2271,7 +2273,6 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } - { // check INSUFFICIENT_RESERVE for MPToken Env env{*this, testable_amendments()}; @@ -3992,8 +3993,7 @@ class Sponsor_test : public beast::unit_test::suite testSingleSigning(); testMultiSigning(); - // testInvalidSigninig(); // borh TxnSignature and Signers are present - // -> error + testSimpleSponsorshipSet(); testTransferSponsor(); diff --git a/src/xrpld/app/paths/AMMOffer.h b/src/xrpld/app/paths/AMMOffer.h index 9834d070c14..27608886ba5 100644 --- a/src/xrpld/app/paths/AMMOffer.h +++ b/src/xrpld/app/paths/AMMOffer.h @@ -123,7 +123,10 @@ class AMMOffer send(Args&&... args) { return accountSend( - std::forward(args)..., std::nullopt, WaiveTransferFee::Yes); + std::forward(args)..., + std::nullopt, + false, + WaiveTransferFee::Yes); } bool diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 9a4361ca498..622c6fa3da1 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -272,8 +272,15 @@ applyCreate( // Send LPT to LP. auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); - auto res = - accountSend(sb, accountId, account_, lpTokens, ctx_.journal, sponsor); + auto const isSponsorCoSigning = isSponsorReserveCoSigning(ctx_.tx); + auto res = accountSend( + sb, + accountId, + account_, + lpTokens, + ctx_.journal, + sponsor, + isSponsorCoSigning); if (res != tesSUCCESS) { JLOG(j_.debug()) << "AMM Instance: failed to send LPT " << lpTokens; @@ -288,6 +295,7 @@ applyCreate( amount, ctx_.journal, std::nullopt, // don't sponsor for AMM Trustline + false, WaiveTransferFee::Yes)) return res; // Set AMM flag on AMM trustline diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index ba8a0bf016a..063373d955a 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -514,6 +514,7 @@ AMMDeposit::deposit( std::uint16_t tfee) { auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto const isSponsorCoSigning = isSponsorReserveCoSigning(ctx_.tx); // Check account has sufficient funds. // Return true if it does, false otherwise. @@ -588,6 +589,7 @@ AMMDeposit::deposit( amountDepositActual, ctx_.journal, std::nullopt, // don't sponsor for AMM Trustline + false, WaiveTransferFee::Yes); if (res != tesSUCCESS) { @@ -615,6 +617,7 @@ AMMDeposit::deposit( *amount2DepositActual, ctx_.journal, std::nullopt, // don't sponsor for AMM Trustline + false, WaiveTransferFee::Yes); if (res != tesSUCCESS) { @@ -631,7 +634,8 @@ AMMDeposit::deposit( account_, lpTokensDepositActual, ctx_.journal, - sponsor); + sponsor, + isSponsorCoSigning); if (res != tesSUCCESS) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens"; diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index 073cfd3ccf5..abe76a08d8a 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -610,6 +610,7 @@ AMMWithdraw::withdraw( { if (auto const ret = checkInsufficientReserve( view, + tx, sleAccount, std::max(priorBalance, balance), sponsor ? view.read(keylet::account(*sponsor)) @@ -625,6 +626,8 @@ AMMWithdraw::withdraw( if (auto const err = sufficientReserve(amountWithdrawActual.issue())) return {err, STAmount{}, STAmount{}, STAmount{}}; + auto const isSponsorCoSigning = isSponsorReserveCoSigning(tx); + // Withdraw amountWithdraw auto res = accountSend( view, @@ -633,6 +636,7 @@ AMMWithdraw::withdraw( amountWithdrawActual, journal, sponsor, + isSponsorCoSigning, WaiveTransferFee::Yes); if (res != tesSUCCESS) { @@ -657,6 +661,7 @@ AMMWithdraw::withdraw( *amount2WithdrawActual, journal, sponsor, + isSponsorCoSigning, WaiveTransferFee::Yes); if (res != tesSUCCESS) { diff --git a/src/xrpld/app/tx/detail/CancelCheck.cpp b/src/xrpld/app/tx/detail/CancelCheck.cpp index 0393775dbdc..3cb16b2b36e 100644 --- a/src/xrpld/app/tx/detail/CancelCheck.cpp +++ b/src/xrpld/app/tx/detail/CancelCheck.cpp @@ -110,7 +110,7 @@ CancelCheck::doApply() // If we succeeded, update the check owner's reserve. auto const sleSrc = view().peek(keylet::account(srcId)); auto const sponsor = getLedgerEntryReserveSponsor(view(), sleCheck); - adjustOwnerCount(view(), sleSrc, sponsor, -1, viewJ); + reduceOwnerCount(view(), sleSrc, sponsor, -1, viewJ); // Remove check from ledger. view().erase(sleCheck); diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 98eaa4f0e83..25052c97297 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -360,7 +360,7 @@ CashCheck::doApply() // Can the account cover the trust line's reserve? if (auto const ret = checkInsufficientReserve( - psb, sleDst, mPriorBalance, sponsorSle, 1); + psb, ctx_.tx, sleDst, mPriorBalance, sponsorSle, 1); !isTesSuccess(ret)) { JLOG(j_.trace()) << "Trust line does not exist. " @@ -373,6 +373,9 @@ CashCheck::doApply() STAmount initialBalance(flowDeliver.issue()); initialBalance.setIssuer(noAccount()); + auto const isSponsorCoSigning = + isSponsorReserveCoSigning(ctx_.tx); + // clang-format off if (TER const ter = trustCreate( psb, // payment sandbox @@ -389,7 +392,8 @@ CashCheck::doApply() Issue(currency, account_), // limit of zero 0, // quality in 0, // quality out - sponsorAcc, // sponsor + sponsorAcc, // sponsor + isSponsorCoSigning, // is sponsor co-signing viewJ); // journal !isTesSuccess(ter)) { @@ -503,7 +507,7 @@ CashCheck::doApply() // If we succeeded, update the check owner's reserve. auto const sponsorSle = getLedgerEntryReserveSponsor(psb, sleCheck); - adjustOwnerCount( + reduceOwnerCount( psb, psb.peek(keylet::account(srcId)), sponsorSle, -1, viewJ); // Remove check from ledger. diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index 26fa3541196..18a87fd85dc 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -213,7 +213,7 @@ Change::activateTrustLinesToSelfFix() } if (tl->getFlags() & lsfLowReserve) - adjustOwnerCount( + reduceOwnerCount( sb, sb.peek(keylet::account(lo.getIssuer())), std::nullopt, @@ -221,7 +221,7 @@ Change::activateTrustLinesToSelfFix() j_); if (tl->getFlags() & lsfHighReserve) - adjustOwnerCount( + reduceOwnerCount( sb, sb.peek(keylet::account(hi.getIssuer())), std::nullopt, diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/xrpld/app/tx/detail/CreateCheck.cpp index 80e6aa20262..d412c34afec 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/xrpld/app/tx/detail/CreateCheck.cpp @@ -165,11 +165,10 @@ CreateCheck::doApply() // check the starting balance because we want to allow dipping into the // reserve to pay fees. auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); - if (auto const ret = - checkInsufficientReserve(view(), sle, mPriorBalance, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sle, mPriorBalance, sponsor, 1); !isTesSuccess(ret)) return ret; - // Note that we use the value from the sequence or ticket as the // Check sequence. For more explanation see comments in SeqProxy.h. std::uint32_t const seq = ctx_.tx.getSeqValue(); @@ -228,7 +227,8 @@ CreateCheck::doApply() sleCheck->setFieldU64(sfOwnerNode, *page); } // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sle, sponsor, 1, viewJ); + + adjustOwnerCount(view(), ctx_.tx, sle, sponsor, 1, viewJ); addSponsorToLedgerEntry(sleCheck, sponsor); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/CreateOffer.cpp b/src/xrpld/app/tx/detail/CreateOffer.cpp index b101e08e03f..a504b2d66f0 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.cpp +++ b/src/xrpld/app/tx/detail/CreateOffer.cpp @@ -821,7 +821,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) { auto const sponsor = getTxReserveSponsor(sb, ctx_.tx); if (auto const ret = checkInsufficientReserve( - sb, sleCreator, mPriorBalance, sponsor, 1); + sb, ctx_.tx, sleCreator, mPriorBalance, sponsor, 1); !isTesSuccess(ret)) { // If we are here, the signing account had an insufficient reserve @@ -855,7 +855,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) // Update owner count. auto const sponsor = getTxReserveSponsor(sb, ctx_.tx); - adjustOwnerCount(sb, sleCreator, sponsor, 1, viewJ); + adjustOwnerCount(sb, ctx_.tx, sleCreator, sponsor, 1, viewJ); JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue()) << " : " << to_string(saTakerGets.issue()) diff --git a/src/xrpld/app/tx/detail/CreateTicket.cpp b/src/xrpld/app/tx/detail/CreateTicket.cpp index d2821626b23..4868fb224cd 100644 --- a/src/xrpld/app/tx/detail/CreateTicket.cpp +++ b/src/xrpld/app/tx/detail/CreateTicket.cpp @@ -84,7 +84,12 @@ CreateTicket::doApply() std::uint32_t const ticketCount = ctx_.tx[sfTicketCount]; auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - view(), sleAccountRoot, mPriorBalance, sponsor, ticketCount); + view(), + ctx_.tx, + sleAccountRoot, + mPriorBalance, + sponsor, + ticketCount); !isTesSuccess(ret)) return ret; @@ -135,7 +140,8 @@ CreateTicket::doApply() sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount); // Every added Ticket counts against the creator's reserve. - adjustOwnerCount(view(), sleAccountRoot, sponsor, ticketCount, viewJ); + adjustOwnerCount( + view(), ctx_.tx, sleAccountRoot, sponsor, ticketCount, viewJ); // TicketCreate is the only transaction that can cause an account root's // Sequence field to increase by more than one. October 2018. diff --git a/src/xrpld/app/tx/detail/Credentials.cpp b/src/xrpld/app/tx/detail/Credentials.cpp index dbde4caa1a7..9b9b04fb0e0 100644 --- a/src/xrpld/app/tx/detail/Credentials.cpp +++ b/src/xrpld/app/tx/detail/Credentials.cpp @@ -141,7 +141,7 @@ CredentialCreate::doApply() auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - view(), sleIssuer, mPriorBalance, sponsor, 1); + view(), ctx_.tx, sleIssuer, mPriorBalance, sponsor, 1); !isTesSuccess(ret)) return ret; @@ -164,7 +164,7 @@ CredentialCreate::doApply() return tecDIR_FULL; sleCred->setFieldU64(sfIssuerNode, *page); - adjustOwnerCount(view(), sleIssuer, sponsor, 1, j_); + adjustOwnerCount(view(), ctx_.tx, sleIssuer, sponsor, 1, j_); addSponsorToLedgerEntry(sleCred, sponsor); } @@ -346,7 +346,7 @@ CredentialAccept::doApply() auto const newSponsor = getTxReserveSponsor(view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - view(), sleSubject, mPriorBalance, newSponsor, 1); + view(), ctx_.tx, sleSubject, mPriorBalance, newSponsor, 1); !isTesSuccess(ret)) return ret; @@ -366,9 +366,9 @@ CredentialAccept::doApply() sleCred->setFieldU32(sfFlags, lsfAccepted); view().update(sleCred); - adjustOwnerCount(view(), sleIssuer, currentSponsor, -1, j_); + reduceOwnerCount(view(), sleIssuer, currentSponsor, -1, j_); removeSponsorFromLedgerEntry(sleCred); - adjustOwnerCount(view(), sleSubject, newSponsor, 1, j_); + adjustOwnerCount(view(), ctx_.tx, sleSubject, newSponsor, 1, j_); addSponsorToLedgerEntry(sleCred, newSponsor); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/DID.cpp b/src/xrpld/app/tx/detail/DID.cpp index fb0388940dc..b0688c84c31 100644 --- a/src/xrpld/app/tx/detail/DID.cpp +++ b/src/xrpld/app/tx/detail/DID.cpp @@ -82,7 +82,7 @@ addSLE( auto const sponsor = getTxReserveSponsor(ctx.view(), ctx.tx); auto const balance = STAmount((*sleAccount)[sfBalance]).xrp(); if (auto const ret = checkInsufficientReserve( - ctx.view(), sleAccount, balance, sponsor, 1); + ctx.view(), ctx.tx, sleAccount, balance, sponsor, 1); !isTesSuccess(ret)) return ret; @@ -97,7 +97,7 @@ addSLE( return tecDIR_FULL; (*sle)[sfOwnerNode] = *page; } - adjustOwnerCount(ctx.view(), sleAccount, sponsor, 1, ctx.journal); + adjustOwnerCount(ctx.view(), ctx.tx, sleAccount, sponsor, 1, ctx.journal); addSponsorToLedgerEntry(sle, sponsor); ctx.view().update(sleAccount); @@ -197,7 +197,7 @@ DIDDelete::deleteSLE( return tecINTERNAL; auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, sleOwner, sponsor, -1, j); + reduceOwnerCount(view, sleOwner, sponsor, -1, j); view.update(sleOwner); // Remove object from ledger diff --git a/src/xrpld/app/tx/detail/DelegateSet.cpp b/src/xrpld/app/tx/detail/DelegateSet.cpp index 2bc666370ab..5acfc2f184b 100644 --- a/src/xrpld/app/tx/detail/DelegateSet.cpp +++ b/src/xrpld/app/tx/detail/DelegateSet.cpp @@ -111,7 +111,7 @@ DelegateSet::doApply() auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - view(), sleOwner, mPriorBalance, sponsor, 1); + view(), ctx_.tx, sleOwner, mPriorBalance, sponsor, 1); !isTesSuccess(ret)) return ret; @@ -133,7 +133,8 @@ DelegateSet::doApply() (*sle)[sfOwnerNode] = *page; ctx_.view().insert(sle); - adjustOwnerCount(ctx_.view(), sleOwner, sponsor, 1, ctx_.journal); + adjustOwnerCount( + ctx_.view(), ctx_.tx, sleOwner, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(sle, sponsor); } @@ -164,7 +165,7 @@ DelegateSet::deleteDelegate( return tecINTERNAL; // LCOV_EXCL_LINE auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, sleOwner, sponsor, -1, j); + reduceOwnerCount(view, sleOwner, sponsor, -1, j); view.erase(sle); diff --git a/src/xrpld/app/tx/detail/DeleteOracle.cpp b/src/xrpld/app/tx/detail/DeleteOracle.cpp index e84cc2c333f..705b2ae6398 100644 --- a/src/xrpld/app/tx/detail/DeleteOracle.cpp +++ b/src/xrpld/app/tx/detail/DeleteOracle.cpp @@ -83,7 +83,7 @@ DeleteOracle::deleteOracle( sle->getFieldArray(sfPriceDataSeries).size() > 5 ? -2 : -1; auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, sleOwner, sponsor, count, j); + reduceOwnerCount(view, sleOwner, sponsor, count, j); view.erase(sle); diff --git a/src/xrpld/app/tx/detail/DepositPreauth.cpp b/src/xrpld/app/tx/detail/DepositPreauth.cpp index 7a5a4b7e02b..22a3e41f528 100644 --- a/src/xrpld/app/tx/detail/DepositPreauth.cpp +++ b/src/xrpld/app/tx/detail/DepositPreauth.cpp @@ -172,7 +172,7 @@ DepositPreauth::doApply() // reserve to pay fees. auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - view(), sleOwner, mPriorBalance, sponsor, 1); + view(), ctx_.tx, sleOwner, mPriorBalance, sponsor, 1); !isTesSuccess(ret)) return ret; @@ -201,7 +201,7 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); + adjustOwnerCount(view(), ctx_.tx, sleOwner, sponsor, 1, j_); addSponsorToLedgerEntry(slePreauth, sponsor); } else if (ctx_.tx.isFieldPresent(sfUnauthorize)) @@ -222,7 +222,7 @@ DepositPreauth::doApply() // reserve to pay fees. auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - view(), sleOwner, mPriorBalance, sponsor, 1); + view(), ctx_.tx, sleOwner, mPriorBalance, sponsor, 1); !isTesSuccess(ret)) return ret; @@ -264,7 +264,7 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); + adjustOwnerCount(view(), ctx_.tx, sleOwner, sponsor, 1, j_); addSponsorToLedgerEntry(slePreauth, sponsor); } else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials)) @@ -307,7 +307,7 @@ DepositPreauth::removeFromLedger( return tefINTERNAL; auto const sponsor = getLedgerEntryReserveSponsor(view, slePreauth); - adjustOwnerCount(view, sleOwner, sponsor, -1, j); + reduceOwnerCount(view, sleOwner, sponsor, -1, j); // Remove DepositPreauth from ledger. view.erase(slePreauth); diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 22a22db1b6c..70a60c1ab2c 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -498,7 +498,7 @@ EscrowCreate::doApply() auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - ctx_.view(), sle, mSourceBalance, sponsor, 1); + ctx_.view(), ctx_.tx, sle, mSourceBalance, sponsor, 1); !isTesSuccess(ret)) return ret; @@ -506,6 +506,7 @@ EscrowCreate::doApply() { if (auto const ret = checkInsufficientReserve( ctx_.view(), + ctx_.tx, sle, mSourceBalance - STAmount(amount).xrp(), std::optional>(), @@ -610,7 +611,7 @@ EscrowCreate::doApply() } // increment owner count - adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal); + adjustOwnerCount(ctx_.view(), ctx_.tx, sle, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(slep, sponsor); ctx_.view().update(sle); return tesSUCCESS; @@ -861,7 +862,7 @@ escrowUnlockApplyHelper( if (sponeorAcc) sponsorSle = view.peek(keylet::account(*sponeorAcc)); if (auto const ret = checkInsufficientReserve( - view, sleDest, xrpBalance, sponsorSle, 1); + view, tx, sleDest, xrpBalance, sponsorSle, 1); !isTesSuccess(ret)) { JLOG(journal.trace()) << "Trust line does not exist. " @@ -874,6 +875,8 @@ escrowUnlockApplyHelper( STAmount initialBalance(amount.issue()); initialBalance.setIssuer(noAccount()); + auto const isSponsorCoSigning = isSponsorReserveCoSigning(tx); + // clang-format off if (TER const ter = trustCreate( view, // payment sandbox @@ -891,6 +894,7 @@ escrowUnlockApplyHelper( 0, // quality in 0, // quality out sponeorAcc, // sponsor + isSponsorCoSigning, // is sponsor co-signing journal); // journal !isTesSuccess(ter)) { @@ -990,8 +994,8 @@ escrowUnlockApplyHelper( createAsset && !receiverIssuer) { auto const sponsor = getTxReserveSponsor(view, tx); - if (auto const ret = - checkInsufficientReserve(view, sleDest, xrpBalance, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + view, tx, sleDest, xrpBalance, sponsor, 1); !isTesSuccess(ret)) return ret; @@ -1003,7 +1007,7 @@ escrowUnlockApplyHelper( } // update owner count. - adjustOwnerCount(view, sleDest, sponsor, 1, journal); + adjustOwnerCount(view, tx, sleDest, sponsor, 1, journal); addSponsorToLedgerEntry(sleDest, sponsor); } @@ -1221,7 +1225,7 @@ EscrowFinish::doApply() // Adjust source owner count auto const sle = ctx_.view().peek(keylet::account(account)); auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep); - adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); + reduceOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); ctx_.view().update(sle); // Remove escrow from ledger @@ -1435,7 +1439,7 @@ EscrowCancel::doApply() } auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep); - adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); + reduceOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); ctx_.view().update(sle); // Remove escrow from ledger diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp index 60fde01c932..5eb0b1b673b 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp @@ -112,7 +112,7 @@ MPTokenIssuanceCreate::create( if (args.priorBalance) { if (auto const ret = checkInsufficientReserve( - view, acct, *(args.priorBalance), sponsor, 1); + view, tx, acct, *(args.priorBalance), sponsor, 1); !isTesSuccess(ret)) return Unexpected(ret); // tecINSUFFICIENT_RESERVE } @@ -161,7 +161,7 @@ MPTokenIssuanceCreate::create( } // Update owner count. - adjustOwnerCount(view, acct, sponsor, 1, journal); + adjustOwnerCount(view, tx, acct, sponsor, 1, journal); return mptId; } diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp index 928f330baa0..1b197a8771a 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp @@ -75,7 +75,7 @@ MPTokenIssuanceDestroy::doApply() view().erase(mpt); auto const sponsor = getLedgerEntryReserveSponsor(view(), mpt); - adjustOwnerCount( + reduceOwnerCount( view(), view().peek(keylet::account(account_)), sponsor, -1, j_); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp index 681e18bee1f..6a1ec898ac8 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp @@ -444,7 +444,7 @@ NFTokenAcceptOffer::transferNFToken( auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); auto const insertRet = nft::insertToken( - view(), buyer, sponsor, std::move(tokenAndPage->token)); + view(), ctx_.tx, buyer, sponsor, std::move(tokenAndPage->token)); // if fixNFTokenReserve is enabled, check if the buyer has sufficient // reserve to own a new object, if their OwnerCount changed. @@ -468,7 +468,7 @@ NFTokenAcceptOffer::transferNFToken( ? getTxReserveSponsor(ctx_.view(), ctx_.tx) : std::optional>(); if (auto const ret = checkInsufficientReserve( - ctx_.view(), sleBuyer, buyerBalance, sponsor, 0); + ctx_.view(), ctx_.tx, sleBuyer, buyerBalance, sponsor, 0); !isTesSuccess(ret)) return ret; } diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/xrpld/app/tx/detail/NFTokenMint.cpp index 05f1e759262..69b2f943fff 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/xrpld/app/tx/detail/NFTokenMint.cpp @@ -325,7 +325,7 @@ NFTokenMint::doApply() auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); if (TER const ret = nft::insertToken( - ctx_.view(), account_, sponsor, std::move(newToken)); + ctx_.view(), ctx_.tx, account_, sponsor, std::move(newToken)); ret != tesSUCCESS) return ret; @@ -360,6 +360,7 @@ NFTokenMint::doApply() auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( ctx_.view(), + ctx_.tx, view().read(keylet::account(account_)), mPriorBalance, sponsor, diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.cpp b/src/xrpld/app/tx/detail/NFTokenUtils.cpp index 36672115cd9..8583273bed0 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.cpp +++ b/src/xrpld/app/tx/detail/NFTokenUtils.cpp @@ -65,11 +65,13 @@ locatePage(ApplyView& view, AccountID const& owner, uint256 const& id) static std::shared_ptr getPageForToken( ApplyView& view, + STTx const& tx, AccountID const& owner, std::optional const& sponsor, uint256 const& id, std::function const&, AccountID const&, std::optional const&)> const& createCallback) @@ -92,7 +94,7 @@ getPageForToken( cp = std::make_shared(last); cp->setFieldArray(sfNFTokens, arr); view.insert(cp); - createCallback(view, cp, owner, sponsor); + createCallback(view, tx, cp, owner, sponsor); return cp; } @@ -220,7 +222,7 @@ getPageForToken( cp->setFieldH256(sfPreviousPageMin, np->key()); view.update(cp); - createCallback(view, np, owner, sponsor); + createCallback(view, tx, np, owner, sponsor); // fixNFTokenDirV1 corrects a bug in the initial implementation that // would put an NFT in the wrong page. The problem was caused by an @@ -284,6 +286,7 @@ changeTokenURI( TER insertToken( ApplyView& view, + STTx const& tx, AccountID owner, std::optional const& sponsor, STObject&& nft) @@ -297,10 +300,12 @@ insertToken( // the NFT. std::shared_ptr page = getPageForToken( view, + tx, owner, sponsor, nft[sfNFTokenID], [](ApplyView& view, + STTx const& tx, std::shared_ptr const& newPage, AccountID const& owner, std::optional const& sponsor) { @@ -309,6 +314,7 @@ insertToken( : std::optional>{std::nullopt}; adjustOwnerCount( view, + tx, view.peek(keylet::account(owner)), sponsorSle, 1, @@ -470,7 +476,7 @@ removeToken( if (prev && mergePages(view, prev, curr)) { auto const sponsor = getLedgerEntryReserveSponsor(view, prev); - adjustOwnerCount( + reduceOwnerCount( view, view.peek(keylet::account(owner)), sponsor, @@ -481,7 +487,7 @@ removeToken( if (next && mergePages(view, curr, next)) { auto const sponsor = getLedgerEntryReserveSponsor(view, curr); - adjustOwnerCount( + reduceOwnerCount( view, view.peek(keylet::account(owner)), sponsor, @@ -522,7 +528,7 @@ removeToken( } auto const sponsor = getLedgerEntryReserveSponsor(view, prev); - adjustOwnerCount( + reduceOwnerCount( view, view.peek(keylet::account(owner)), sponsor, @@ -556,7 +562,7 @@ removeToken( } auto const sponsor = getLedgerEntryReserveSponsor(view, curr); - adjustOwnerCount( + reduceOwnerCount( view, view.peek(keylet::account(owner)), getLedgerEntryReserveSponsor(view, curr), @@ -579,7 +585,7 @@ removeToken( view.peek(Keylet(ltNFTOKEN_PAGE, prev->key())), view.peek(Keylet(ltNFTOKEN_PAGE, next->key())))) { - adjustOwnerCount( + reduceOwnerCount( view, view.peek(keylet::account(owner)), getLedgerEntryReserveSponsor(view, prev), @@ -738,7 +744,7 @@ deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer) return false; auto const sponsor = getLedgerEntryReserveSponsor(view, offer); - adjustOwnerCount( + reduceOwnerCount( view, view.peek(keylet::account(owner)), sponsor, @@ -1073,7 +1079,7 @@ tokenOfferCreateApply( auto const acct = view.read(acctKeylet); auto const sponsor = getTxReserveSponsor(view, tx); if (auto const ret = - checkInsufficientReserve(view, acct, priorBalance, sponsor, 1); + checkInsufficientReserve(view, tx, acct, priorBalance, sponsor, 1); !isTesSuccess(ret)) return ret; @@ -1130,7 +1136,7 @@ tokenOfferCreateApply( } // Update owner count. - adjustOwnerCount(view, view.peek(acctKeylet), sponsor, 1, j); + adjustOwnerCount(view, tx, view.peek(acctKeylet), sponsor, 1, j); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.h b/src/xrpld/app/tx/detail/NFTokenUtils.h index 7798f592222..60379408391 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.h +++ b/src/xrpld/app/tx/detail/NFTokenUtils.h @@ -72,6 +72,7 @@ findTokenAndPage( TER insertToken( ApplyView& view, + STTx const& tx, AccountID owner, std::optional const& sponsor, STObject&& nft); diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index 20f1ecae906..7d53f878860 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -156,7 +156,7 @@ closeChannel( (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; auto const sponsor = getLedgerEntryReserveSponsor(view, slep); - adjustOwnerCount(view, sle, sponsor, -1, j); + reduceOwnerCount(view, sle, sponsor, -1, j); view.update(sle); // Remove PayChan from ledger @@ -206,13 +206,13 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) { auto const balance = (*sle)[sfBalance]; auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); - if (auto const ret = - checkInsufficientReserve(ctx.view, sle, balance, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + ctx.view, ctx.tx, sle, balance, sponsor, 1); !isTesSuccess(ret)) return ret; if (auto const ret = checkInsufficientReserve( - ctx.view, sle, balance - ctx.tx[sfAmount], sponsor, 1); + ctx.view, ctx.tx, sle, balance - ctx.tx[sfAmount], sponsor, 1); !isTesSuccess(ret)) return tecUNFUNDED; } @@ -321,7 +321,7 @@ PayChanCreate::doApply() // Deduct owner's balance, increment owner count (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal); + adjustOwnerCount(ctx_.view(), ctx_.tx, sle, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(slep, sponsor); ctx_.view().update(sle); @@ -401,13 +401,14 @@ PayChanFund::doApply() // Check reserve and funds availability auto const balance = (*sle)[sfBalance]; auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - if (auto const ret = - checkInsufficientReserve(ctx_.view(), sle, balance, sponsor, 0); + if (auto const ret = checkInsufficientReserve( + ctx_.view(), ctx_.tx, sle, balance, sponsor, 0); !isTesSuccess(ret)) return ret; if (auto const ret = checkInsufficientReserve( ctx_.view(), + ctx_.tx, sle, balance - ctx_.tx[sfAmount], std::optional>(), diff --git a/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp b/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp index f71538b925a..8e45e8c9feb 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp +++ b/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp @@ -76,7 +76,7 @@ PermissionedDomainDelete::doApply() ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0, "ripple::PermissionedDomainDelete::doApply : nonzero owner count"); auto const sponsor = getLedgerEntryReserveSponsor(view(), slePd); - adjustOwnerCount(view(), ownerSle, sponsor, -1, ctx_.journal); + reduceOwnerCount(view(), ownerSle, sponsor, -1, ctx_.journal); view().erase(slePd); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp b/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp index 4eb1d1fcde5..8043863b9f9 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp +++ b/src/xrpld/app/tx/detail/PermissionedDomainSet.cpp @@ -116,7 +116,7 @@ PermissionedDomainSet::doApply() auto const balance = STAmount((*ownerSle)[sfBalance]).xrp(); auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - ctx_.view(), ownerSle, balance, sponsor, 1); + ctx_.view(), ctx_.tx, ownerSle, balance, sponsor, 1); !isTesSuccess(ret)) return ret; @@ -136,7 +136,7 @@ PermissionedDomainSet::doApply() slePd->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), ownerSle, sponsor, 1, ctx_.journal); + adjustOwnerCount(view(), ctx_.tx, ownerSle, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(slePd, sponsor); view().insert(slePd); } diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index 4fd45ea4c49..c35e8d1a1bb 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -166,7 +166,7 @@ SetOracle::preclaim(PreclaimContext const& ctx) auto const& balance = sleSetter->getFieldAmount(sfBalance); auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); if (auto const ret = checkInsufficientReserve( - ctx.view, sleSetter, balance, sponsor, adjustReserve); + ctx.view, ctx.tx, sleSetter, balance, sponsor, adjustReserve); !isTesSuccess(ret)) return ret; @@ -182,7 +182,12 @@ adjustOwnerCount( if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) { - adjustOwnerCount(ctx.view(), sleAccount, sponsor, count, ctx.journal); + if (count > 0) + adjustOwnerCount( + ctx.view(), ctx.tx, sleAccount, sponsor, count, ctx.journal); + else + reduceOwnerCount( + ctx.view(), sleAccount, sponsor, count, ctx.journal); return true; } diff --git a/src/xrpld/app/tx/detail/SetSignerList.cpp b/src/xrpld/app/tx/detail/SetSignerList.cpp index 9af69ae652b..08166ee7bac 100644 --- a/src/xrpld/app/tx/detail/SetSignerList.cpp +++ b/src/xrpld/app/tx/detail/SetSignerList.cpp @@ -229,7 +229,7 @@ removeSignersFromLedger( } auto const sponsor = getLedgerEntryReserveSponsor(view, signers); - adjustOwnerCount( + reduceOwnerCount( view, view.peek(accountKeylet), sponsor, @@ -366,7 +366,7 @@ SetSignerList::replaceSignerList() // with CreateTicket. auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - ctx_.view(), sle, mPriorBalance, sponsor, addedOwnerCount); + ctx_.view(), ctx_.tx, sle, mPriorBalance, sponsor, addedOwnerCount); !isTesSuccess(ret)) return ret; @@ -390,7 +390,7 @@ SetSignerList::replaceSignerList() // If we succeeded, the new entry counts against the // creator's reserve. - adjustOwnerCount(view(), sle, sponsor, addedOwnerCount, viewJ); + adjustOwnerCount(view(), ctx_.tx, sle, sponsor, addedOwnerCount, viewJ); addSponsorToLedgerEntry(signerList, sponsor); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 4f7c7e59f32..d866fa08475 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -640,7 +640,8 @@ SetTrust::doApply() if (bLowReserveSet && !bLowReserved) { // Set reserve for low account. - adjustOwnerCount(view(), sleLowAccount, txSponsorSle, 1, viewJ); + adjustOwnerCount( + view(), ctx_.tx, sleLowAccount, txSponsorSle, 1, viewJ); uFlagsOut |= lsfLowReserve; addSponsorToLedgerEntry( @@ -653,7 +654,7 @@ SetTrust::doApply() if (bLowReserveClear && bLowReserved) { // Clear reserve for low account. - adjustOwnerCount( + reduceOwnerCount( view(), sleLowAccount, currentLowSponsor, -1, viewJ); uFlagsOut &= ~lsfLowReserve; @@ -663,7 +664,8 @@ SetTrust::doApply() if (bHighReserveSet && !bHighReserved) { // Set reserve for high account. - adjustOwnerCount(view(), sleHighAccount, txSponsorSle, 1, viewJ); + adjustOwnerCount( + view(), ctx_.tx, sleHighAccount, txSponsorSle, 1, viewJ); uFlagsOut |= lsfHighReserve; addSponsorToLedgerEntry( @@ -676,7 +678,7 @@ SetTrust::doApply() if (bHighReserveClear && bHighReserved) { // Clear reserve for high account. - adjustOwnerCount( + reduceOwnerCount( view(), sleHighAccount, currentHighSponsor, -1, viewJ); uFlagsOut &= ~lsfHighReserve; @@ -695,7 +697,7 @@ SetTrust::doApply() } // Reserve is not scaled by load. else if (auto const ret = checkInsufficientReserve( - view(), sle, mPriorBalance, txSponsorSle, 0); + view(), ctx_.tx, sle, mPriorBalance, txSponsorSle, 0); !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " @@ -727,6 +729,7 @@ SetTrust::doApply() } else if (auto const ret = checkInsufficientReserve( ctx_.view(), + ctx_.tx, sle, mPriorBalance, txSponsorSle, @@ -751,6 +754,8 @@ SetTrust::doApply() JLOG(j_.trace()) << "doTrustSet: Creating ripple line: " << to_string(k.key); + auto const isSponsorCoSigning = isSponsorReserveCoSigning(ctx_.tx); + // Create a new ripple line. terResult = trustCreate( view(), @@ -768,6 +773,7 @@ SetTrust::doApply() uQualityIn, uQualityOut, txSponsorAcc, + isSponsorCoSigning, viewJ); } diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 8b80950e9ea..ab9c37a681e 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -220,7 +220,7 @@ SponsorshipSet::doApply() auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), sponsorObjSle); - adjustOwnerCount(ctx_.view(), sponsorAccSle, sponsor, -1, ctx_.journal); + reduceOwnerCount(ctx_.view(), sponsorAccSle, sponsor, -1, ctx_.journal); ctx_.view().dirRemove( keylet::ownerDir(sponsorAcc), @@ -255,6 +255,7 @@ SponsorshipSet::doApply() if (auto const ret = checkInsufficientReserve( ctx_.view(), + ctx_.tx, sponsorAccSle, mPriorBalance, reserveSponsorAccSle, @@ -297,7 +298,8 @@ SponsorshipSet::doApply() auto viewJ = ctx_.app.journal("View"); - adjustOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, viewJ); + adjustOwnerCount( + view(), ctx_.tx, sponsorAccSle, reserveSponsorAccSle, 1, viewJ); addSponsorToLedgerEntry(newSle, reserveSponsorAccSle); ctx_.view().insert(newSle); @@ -361,7 +363,7 @@ SponsorshipSet::deleteSponsorship( (*sponsorAccSle)[sfBalance] += feeAmount; auto const reserveSponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, sponsorAccSle, reserveSponsor, -1, j); + reduceOwnerCount(view, sponsorAccSle, reserveSponsor, -1, j); view.update(sponsorAccSle); diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 9a45bb77be0..a7f3ccf1743 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -212,6 +212,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) // check new sponsor have sufficient balance if (auto const ter = checkInsufficientReserve( ctx.view, + ctx.tx, accSle, accSle->getFieldAmount(sfBalance), newSponsor, @@ -242,6 +243,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) // check account have sufficient balance if (auto const ter = checkInsufficientReserve( ctx.view, + ctx.tx, accSle, accSle->getFieldAmount(sfBalance), newSponsor, diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 1243953f845..603ffd153aa 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -703,7 +703,7 @@ Transactor::ticketDelete( // Update the Ticket owner's reserve. auto const sponsor = getLedgerEntryReserveSponsor(view, sleTicket); - adjustOwnerCount(view, sleAccount, sponsor, -1, j); + reduceOwnerCount(view, sleAccount, sponsor, -1, j); // Remove Ticket from ledger. view.erase(sleTicket); diff --git a/src/xrpld/app/tx/detail/VaultClawback.cpp b/src/xrpld/app/tx/detail/VaultClawback.cpp index 6d42f5b5975..ab93f1302d9 100644 --- a/src/xrpld/app/tx/detail/VaultClawback.cpp +++ b/src/xrpld/app/tx/detail/VaultClawback.cpp @@ -270,6 +270,7 @@ VaultClawback::doApply() sharesDestroyed, j_, std::nullopt, + false, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -310,6 +311,7 @@ VaultClawback::doApply() assetsRecovered, j_, std::nullopt, + false, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/xrpld/app/tx/detail/VaultCreate.cpp index 4e499e57723..151e4e369ad 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/xrpld/app/tx/detail/VaultCreate.cpp @@ -162,10 +162,10 @@ VaultCreate::doApply() if (auto ter = dirLink(view(), account_, vault)) return ter; auto const sponsor = getTxReserveSponsor(view(), tx); - adjustOwnerCount(view(), owner, sponsor, 1, j_); + adjustOwnerCount(view(), tx, owner, sponsor, 1, j_); addSponsorToLedgerEntry(vault, sponsor); - if (auto const ret = - checkInsufficientReserve(view(), owner, mPriorBalance, sponsor, 0); + if (auto const ret = checkInsufficientReserve( + view(), tx, owner, mPriorBalance, sponsor, 0); !isTesSuccess(ret)) return ret; diff --git a/src/xrpld/app/tx/detail/VaultDelete.cpp b/src/xrpld/app/tx/detail/VaultDelete.cpp index 656c3710654..fdacc185f83 100644 --- a/src/xrpld/app/tx/detail/VaultDelete.cpp +++ b/src/xrpld/app/tx/detail/VaultDelete.cpp @@ -157,7 +157,7 @@ VaultDelete::doApply() return tefBAD_LEDGER; // LCOV_EXCL_STOP } - adjustOwnerCount(view(), pseudoAcct, std::nullopt, -1, j_); + reduceOwnerCount(view(), pseudoAcct, std::nullopt, -1, j_); view().erase(mpt); @@ -191,7 +191,7 @@ VaultDelete::doApply() // LCOV_EXCL_STOP } auto const vaultSponsor = getLedgerEntryReserveSponsor(view(), vault); - adjustOwnerCount(view(), owner, vaultSponsor, -1, j_); + reduceOwnerCount(view(), owner, vaultSponsor, -1, j_); // Destroy the vault. view().erase(vault); diff --git a/src/xrpld/app/tx/detail/VaultDeposit.cpp b/src/xrpld/app/tx/detail/VaultDeposit.cpp index ccb7a8dc971..626d73291bf 100644 --- a/src/xrpld/app/tx/detail/VaultDeposit.cpp +++ b/src/xrpld/app/tx/detail/VaultDeposit.cpp @@ -301,6 +301,7 @@ VaultDeposit::doApply() assetsDeposited, j_, std::nullopt, + false, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -321,6 +322,7 @@ VaultDeposit::doApply() } auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto const isSponsorCoSigning = ctx_.tx.isFieldPresent(sfSponsorSignature); // Transfer shares from vault to depositor. if (auto const ter = accountSend( @@ -330,6 +332,7 @@ VaultDeposit::doApply() sharesCreated, j_, sponsor, + isSponsorCoSigning, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/app/tx/detail/VaultWithdraw.cpp b/src/xrpld/app/tx/detail/VaultWithdraw.cpp index 365ab437b06..dd51af0cd1c 100644 --- a/src/xrpld/app/tx/detail/VaultWithdraw.cpp +++ b/src/xrpld/app/tx/detail/VaultWithdraw.cpp @@ -269,6 +269,8 @@ VaultWithdraw::doApply() auto const& vaultAccount = vault->at(sfAccount); auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto const isSponsorCoSigning = isSponsorReserveCoSigning(ctx_.tx); + // Transfer shares from depositor to vault. if (auto const ter = accountSend( view(), @@ -277,6 +279,7 @@ VaultWithdraw::doApply() sharesRedeemed, j_, sponsor, + isSponsorCoSigning, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -323,6 +326,7 @@ VaultWithdraw::doApply() assetsWithdrawn, j_, sponsor, + isSponsorCoSigning, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index a7ff41aa3e1..862aac13890 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -767,7 +767,7 @@ finalizeClaimHelper( auto const sponsor = getLedgerEntryReserveSponsor(outerSb, sleClaimID); - adjustOwnerCount(outerSb, sleOwner, sponsor, -1, j); + reduceOwnerCount(outerSb, sleOwner, sponsor, -1, j); } } @@ -1079,8 +1079,8 @@ applyCreateAccountAttestations( // Check reserve auto const balance = (*sleDoor)[sfBalance]; auto const sponsor = std::optional>(); - if (auto const ret = - checkInsufficientReserve(psb, sleDoor, balance, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + psb, tx, sleDoor, balance, sponsor, 1); !isTesSuccess(ret)) return Unexpected(ret); // tecINSUFFICIENT_RESERVE } @@ -1194,7 +1194,7 @@ applyCreateAccountAttestations( // Reserve was already checked auto const sponsor = getTxReserveSponsor(psb, tx); - adjustOwnerCount(psb, sleDoor, sponsor, 1, j); + adjustOwnerCount(psb, tx, sleDoor, sponsor, 1, j); addSponsorToLedgerEntry(createdSleClaimID, sponsor); psb.insert(createdSleClaimID); psb.update(sleDoor); @@ -1502,8 +1502,8 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) auto const balance = (*sleAcc)[sfBalance]; auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); - if (auto const ret = - checkInsufficientReserve(ctx.view, sleAcc, balance, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + ctx.view, ctx.tx, sleAcc, balance, sponsor, 1); !isTesSuccess(ret)) return ret; } @@ -1548,7 +1548,7 @@ XChainCreateBridge::doApply() } auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal); + adjustOwnerCount(ctx_.view(), ctx_.tx, sleAcct, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(sleBridge, sponsor); ctx_.view().insert(sleBridge); @@ -2039,8 +2039,8 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) auto const balance = (*sleAcc)[sfBalance]; auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); - if (auto const ret = - checkInsufficientReserve(ctx.view, sleAcc, balance, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + ctx.view, ctx.tx, sleAcc, balance, sponsor, 1); !isTesSuccess(ret)) return ret; } @@ -2096,7 +2096,7 @@ XChainCreateClaimID::doApply() } auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal); + adjustOwnerCount(ctx_.view(), ctx_.tx, sleAcct, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(sleClaimID, sponsor); ctx_.view().insert(sleClaimID); diff --git a/src/xrpld/app/tx/detail/apply.cpp b/src/xrpld/app/tx/detail/apply.cpp index 065f9cb8329..f997f43c459 100644 --- a/src/xrpld/app/tx/detail/apply.cpp +++ b/src/xrpld/app/tx/detail/apply.cpp @@ -86,7 +86,7 @@ checkValidity( if (tx.isFieldPresent(sfSponsor) && rules.enabled(featureSponsor)) { auto const sponsorObj = tx.getFieldObject(sfSponsor); - auto const isCoSigned = tx.isFieldPresent(sfSponsorSignature); + auto const isCoSigned = isSponsorReserveCoSigning(tx); if (isCoSigned) { auto const sponsorSignatureObj = From a65cf6e07d4d3ed5510a3d6a7d5fcf47eefcbd5e Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 8 Oct 2025 13:52:28 +0900 Subject: [PATCH 064/249] add tests for sponsor flags --- src/test/app/Sponsor_test.cpp | 103 ++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index d23a56aa32f..9a3ea775dda 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -26,8 +26,6 @@ #include #include -#include "test/jtx/sponsor.h" - namespace ripple { namespace test { @@ -317,6 +315,14 @@ class Sponsor_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + auto sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->getFieldU32(sfReserveCount) == 100); + BEAST_EXPECT(sle->getFieldAmount(sfFeeAmount) == XRP(100)); + BEAST_EXPECT(sle->getFieldAmount(sfMaxFee) == XRP(1)); + BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForFee)); + BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForReserve)); + // delete from sponsor env(sponsor::del(sponsor), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); env.close(); @@ -868,39 +874,76 @@ class Sponsor_test : public beast::unit_test::suite void testRequireFlag() { - testcase("SponsorshipRequireSignForReserve"); using namespace test::jtx; + { + testcase("SponsorshipRequireSignForReserve"); - Env env{*this, testable_amendments()}; - Account const alice("alice"); - Account const bob("bob"); - Account const sponsor("sponsor"); - env.fund(XRP(10000), alice, bob, sponsor); - env.close(); + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); - // set flag - env(sponsor::set_reserve( - sponsor, tfSponsorshipSetRequireSignForReserve, 10), - sponsor::sponseeAcc(alice)); - env.close(); + // set flag + env(sponsor::set_reserve( + sponsor, tfSponsorshipSetRequireSignForReserve, 10), + sponsor::sponseeAcc(alice)); + env.close(); - env(check::create(alice, bob, XRP(100)), - fee(XRP(10)), - sponsor::as(sponsor, tfSponsorReserve), - ter(terNO_SPONSORSHIP)); - env.close(); + env(check::create(alice, bob, XRP(100)), + fee(XRP(10)), + sponsor::as(sponsor, tfSponsorReserve), + ter(terNO_SPONSORSHIP)); + env.close(); - // clear flag - env(sponsor::set_reserve( - sponsor, tfSponsorshipClearRequireSignForReserve, 1), - sponsor::sponseeAcc(alice)); - env.close(); + // clear flag + env(sponsor::set_reserve( + sponsor, tfSponsorshipClearRequireSignForReserve, 1), + sponsor::sponseeAcc(alice)); + env.close(); - env(check::create(alice, bob, XRP(100)), - fee(XRP(10)), - sponsor::as(sponsor, tfSponsorReserve), - ter(tesSUCCESS)); - env.close(); + env(check::create(alice, bob, XRP(100)), + fee(XRP(10)), + sponsor::as(sponsor, tfSponsorReserve), + ter(tesSUCCESS)); + env.close(); + } + + { + testcase("SponsorshipRequireSignForFee"); + + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // set flag + env(sponsor::set_fee( + sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), + sponsor::sponseeAcc(alice)); + env.close(); + + env(check::create(alice, bob, XRP(100)), + fee(XRP(10)), + sponsor::as(sponsor, tfSponsorFee), + ter(terNO_SPONSORSHIP)); + env.close(); + + // clear flag + env(sponsor::set_fee( + sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), + sponsor::sponseeAcc(alice)); + env.close(); + + env(check::create(alice, bob, XRP(100)), + fee(XRP(10)), + sponsor::as(sponsor, tfSponsorFee), + ter(tesSUCCESS)); + env.close(); + } } void @@ -965,8 +1008,6 @@ class Sponsor_test : public beast::unit_test::suite env.close(); { - // AMMCreate doesn't check INSUFFICIENT_RESERVE now - // see: https://github.com/XRPLF/rippled/issues/5812 // check INSUFFICIENT_RESERVE adjustAccountXRPBalance( env, sponsor, reserve(env, 1) - drops(1)); From d8dc000488b064da14dc0b1f5b16ef3cadfa02ef Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 8 Oct 2025 16:48:47 +0900 Subject: [PATCH 065/249] add prefunded sponsor tests --- include/xrpl/ledger/View.h | 14 + src/libxrpl/ledger/View.cpp | 51 +- src/test/app/Sponsor_test.cpp | 2671 +++++++++++--------- src/xrpld/app/tx/detail/AMMCreate.cpp | 17 +- src/xrpld/app/tx/detail/AMMDeposit.cpp | 29 +- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 7 +- src/xrpld/app/tx/detail/NFTokenUtils.cpp | 49 +- src/xrpld/app/tx/detail/SetOracle.cpp | 30 +- src/xrpld/app/tx/detail/SetTrust.cpp | 29 +- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 14 +- src/xrpld/app/tx/detail/VaultCreate.cpp | 25 +- 11 files changed, 1659 insertions(+), 1277 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 27d50f25669..8fa0d6ce665 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -454,6 +454,9 @@ areCompatible( uint32_t ownerCount(std::shared_ptr const& sponsorSle); +bool +isReserveSponsored(STTx const& tx); + bool isSponsorReserveCoSigning(STTx const& tx); @@ -476,12 +479,23 @@ getTxReserveSponsor(ApplyView& view, STTx const& tx); std::optional> getTxReserveSponsor(ReadView const& view, STTx const& tx); +std::optional +getLedgerEntryReserveSponsorAccountID( + std::shared_ptr sle, + SF_ACCOUNT const& field = sfSponsorAccount); + std::optional> getLedgerEntryReserveSponsor( ApplyView& view, std::shared_ptr sle, SF_ACCOUNT const& field = sfSponsorAccount); +std::optional> +getLedgerEntryReserveSponsor( + ReadView const& view, + std::shared_ptr sle, + SF_ACCOUNT const& field = sfSponsorAccount); + void addSponsorToLedgerEntry( std::shared_ptr const& sle, diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index ef807dbde5a..d0894a8494c 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1041,13 +1041,19 @@ ownerCount(std::shared_ptr const& sponsorSle) return ownerCount + sponsoringOwnerCount - sponsoredOwnerCount; } +bool +isReserveSponsored(STTx const& tx) +{ + auto const sponsor = tx.getFieldObject(sfSponsor); + return sponsor.isFlag(tfSponsorReserve); +} + bool isSponsorReserveCoSigning(STTx const& tx) { if (!tx.isFieldPresent(sfSponsorSignature)) return false; - auto const sponsor = tx.getFieldObject(sfSponsor); - return sponsor.isFlag(tfSponsorReserve); + return isReserveSponsored(tx); } TER @@ -1141,14 +1147,37 @@ getTxReserveSponsor(ReadView const& view, STTx const& tx) return std::nullopt; } +std::optional +getLedgerEntryReserveSponsorAccountID( + std::shared_ptr sle, + SF_ACCOUNT const& field) +{ + if (sle->isFieldPresent(field)) + return sle->getAccountID(field); + return std::nullopt; +} + std::optional> getLedgerEntryReserveSponsor( ApplyView& view, std::shared_ptr sle, SF_ACCOUNT const& field) { - if (sle->isFieldPresent(field)) - return view.peek(keylet::account(sle->getAccountID(field))); + auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); + if (sponsorID) + return view.peek(keylet::account(*sponsorID)); + return std::nullopt; +} + +std::optional> +getLedgerEntryReserveSponsor( + ReadView const& view, + std::shared_ptr sle, + SF_ACCOUNT const& field) +{ + auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); + if (sponsorID) + return view.read(keylet::account(*sponsorID)); return std::nullopt; } @@ -1242,11 +1271,13 @@ adjustOwnerCount( XRPL_ASSERT( sle, "ripple::adjustOwnerCount : co-signing sponsor not found"); + + auto const currentReserveCount = sle->getFieldU32(sfReserveCount); XRPL_ASSERT( - sle->at(sfReserveCount) >= amount, + currentReserveCount >= amount, "ripple::adjustOwnerCount : reserve count not enough"); - sle->at(sfReserveCount) = sle->getFieldU32(sfReserveCount) + amount; + sle->at(sfReserveCount) = currentReserveCount - amount; view.update(sle); } } @@ -1550,12 +1581,18 @@ authorizeMPToken( auto const sponsor = getTxReserveSponsor(view, tx); + auto const isSponsoredAndPreFunded = + sponsor && !isSponsorReserveCoSigning(tx); + // The reserve that is required to create the MPToken. Note // that although the reserve increases with every item // an account owns, in the case of MPTokens we only // *enforce* a reserve if the user owns more than two // items. This is similar to the reserve requirements of trust lines. - if (ownerCount(sponsor.value_or(sleAcct)) >= 2) + // If PreFunded Sponsor, it must be checked whether sufficient + // ReserveCount exists. + if (ownerCount(sponsor.value_or(sleAcct)) >= 2 || + isSponsoredAndPreFunded) { if (auto const ret = checkInsufficientReserve( view, tx, sleAcct, priorBalance, sponsor, 1); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 9a3ea775dda..4ba271a412a 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -48,11 +48,14 @@ adjustAccountXRPBalance( if (currentBalance == balanceTo) return; + auto const baseFee = env.current()->fees().base; if (currentBalance > balanceTo) - env(pay(account, env.master, currentBalance - (balanceTo + XRP(1))), - fee(XRP(1))); + env(pay(account, env.master, currentBalance - (balanceTo)), + fee(XRP(1)), + sponsor::as(env.master, tfSponsorFee), + sig(sfSponsorSignature, env.master)); else - env(pay(env.master, account, balanceTo - currentBalance)); + env(pay(env.master, account, balanceTo - currentBalance), fee(baseFee)); env.close(); } @@ -303,44 +306,124 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, sponsor); env.close(); - // - env(sponsor::set( - sponsor, - tfSponsorshipSetRequireSignForFee | - tfSponsorshipSetRequireSignForReserve, - 100, - XRP(100), - XRP(1)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); - env.close(); + { + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForFee | + tfSponsorshipSetRequireSignForReserve, + 100, + XRP(100), + XRP(1)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); - auto sle = env.le(keylet::sponsor(sponsor, alice)); - BEAST_EXPECT(sle); - BEAST_EXPECT(sle->getFieldU32(sfReserveCount) == 100); - BEAST_EXPECT(sle->getFieldAmount(sfFeeAmount) == XRP(100)); - BEAST_EXPECT(sle->getFieldAmount(sfMaxFee) == XRP(1)); - BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForFee)); - BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForReserve)); + auto sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfReserveCount) == 100); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100)); + BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1)); + BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForFee)); + BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForReserve)); - // delete from sponsor - env(sponsor::del(sponsor), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); - env.close(); + // update sponsorship + env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(2)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); - env(sponsor::set( - sponsor, - tfSponsorshipSetRequireSignForFee | - tfSponsorshipSetRequireSignForReserve, - 100, - XRP(100), - XRP(1)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); - env.close(); + auto sle2 = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle2); + BEAST_EXPECT(sle2->at(sfReserveCount) == 200); // add 100 + BEAST_EXPECT(sle2->at(sfFeeAmount) == XRP(200)); // add 100 + BEAST_EXPECT(sle2->at(sfMaxFee) == XRP(2)); // update to 2 - // delete from sponsee - env(sponsor::del(alice), sponsor::sponsorAcc(sponsor), ter(tesSUCCESS)); - env.close(); + // delete from sponsor + env(sponsor::del(sponsor), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForFee | + tfSponsorshipSetRequireSignForReserve, + 100, + XRP(100), + XRP(1)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + // delete from sponsee + env(sponsor::del(alice), + sponsor::sponsorAcc(sponsor), + ter(tesSUCCESS)); + env.close(); + } + + { + // Update Sponsorship (FeeAmount) + // set empty FeeAmount + env(sponsor::set_reserve(sponsor, 0, 100), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + // add FeeAmount + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + env(sponsor::del(alice), + sponsor::sponsorAcc(sponsor), + ter(tesSUCCESS)); + env.close(); + } + { + // Update Sponsorship (ReserveCount) + // set empty ReserveCount + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + // add ReserveCount + env(sponsor::set_reserve(sponsor, 0, 100), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + env(sponsor::del(alice), + sponsor::sponsorAcc(sponsor), + ter(tesSUCCESS)); + env.close(); + } + { + // delete Sponsorship (only with FeeAmount) + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + env(sponsor::del(alice), + sponsor::sponsorAcc(sponsor), + ter(tesSUCCESS)); + env.close(); + } + { + // delete Sponsorship (only with ReserveCount) + env(sponsor::set_reserve(sponsor, 0, 100), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + env(sponsor::del(alice), + sponsor::sponsorAcc(sponsor), + ter(tesSUCCESS)); + env.close(); + } } void @@ -946,8 +1029,141 @@ class Sponsor_test : public beast::unit_test::suite } } + // test helper for both cosigning and pre-funded sponsorship + template + void + testEachSponsorship( + test::jtx::Env& env, + bool cosigning, + jtx::Account const& sponsor, + jtx::Account const& sponsee, + uint32_t reserveCount, + uint32_t sponsorReserveCount, + TER insufficientReserveResult, + SubmitCallback callback, + std::optional> expected = std::nullopt) + { + using namespace test::jtx; + // auto const sponsorOwnerCountBefore = ownerCount(env, sponsor); + auto const sponseeOwnerCountBefore = ownerCount(env, sponsee); + auto const sponseeSponsoredOwnerCountBefore = + sponsoredOwnerCount(env, sponsee); + auto const sponseeSponsoringOwnerCountBefore = + sponsoringOwnerCount(env, sponsee); + auto const sponsorSponsoringOwnerCountBefore = + sponsoringOwnerCount(env, sponsor); + + std::optional sponsorSig = cosigning + ? std::optional(sig(sfSponsorSignature, sponsor)) + : std::nullopt; + + auto const sponsorCurrentOwnerCount = ownerCount(env, sponsor) - + sponsoredOwnerCount(env, sponsor) + + sponsoringOwnerCount(env, sponsor); + + auto submit = [&](TER _ter) { + return [&, _ter](Json::Value const& jv, auto const&... fN) { + if (sponsorSig) + env(jv, + fN..., + sponsor::as(sponsor, tfSponsorReserve), + *sponsorSig, + ter(_ter)); + else + env(jv, + fN..., + sponsor::as(sponsor, tfSponsorReserve), + ter(_ter)); + }; + }; + + // Insufficient Reserve + { + if (cosigning) + { + adjustAccountXRPBalance( + env, + sponsor, + reserve( + env, sponsorCurrentOwnerCount + sponsorReserveCount) - + drops(1)); + } + else + { + // cleanup previous sponsorship + if (env.le(keylet::sponsor(sponsor, sponsee))) + { + env(sponsor::del(sponsor), sponsor::sponseeAcc(sponsee)); + env.close(); + } + + if (sponsorReserveCount - 1 > 0) + env(sponsor::set( + sponsor, 0, sponsorReserveCount - 1, XRP(1)), + sponsor::sponseeAcc(sponsee)); + else + // just create sponsor object + env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), + sponsor::sponseeAcc(sponsee)); + env.close(); + } + callback(env, submit(insufficientReserveResult)); + env.close(); + } + + // Success + { + if (cosigning) + { + adjustAccountXRPBalance( + env, + sponsor, + reserve( + env, sponsorCurrentOwnerCount + sponsorReserveCount)); + } + else + { + // reset sponsorship + env(sponsor::del(sponsor), sponsor::sponseeAcc(sponsee)); + env(sponsor::set(sponsor, 0, sponsorReserveCount, XRP(1)), + sponsor::sponseeAcc(sponsee)); + env.close(); + } + callback(env, submit(tesSUCCESS)); + env.close(); + + if (!cosigning) + { + // cleanup sponsorship + env(sponsor::del(sponsor), sponsor::sponseeAcc(sponsee)); + env.close(); + } + } + + if (expected) + (*expected)(); + else + { + BEAST_EXPECT( + ownerCount(env, sponsee) - sponseeOwnerCountBefore == + reserveCount); + BEAST_EXPECT( + sponsoredOwnerCount(env, sponsee) - + sponseeSponsoredOwnerCountBefore == + sponsorReserveCount); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsee) - + sponseeSponsoringOwnerCountBefore == + 0); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) - + sponsorSponsoringOwnerCountBefore == + sponsorReserveCount); + } + }; + void - testAMM() + testAMM(bool cosigning) { testcase("AMM"); using namespace test::jtx; @@ -1007,37 +1223,35 @@ class Sponsor_test : public beast::unit_test::suite env(pay(gw, alice, EUR(1000))); env.close(); - { - // check INSUFFICIENT_RESERVE - adjustAccountXRPBalance( - env, sponsor, reserve(env, 1) - drops(1)); - - env(ammCreate(env, alice, USD(100), EUR(100)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUF_RESERVE_LINE)); - env.close(); - adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); - } - - env(ammCreate(env, alice, USD(100), EUR(100)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - auto const amm = - env.current()->read(keylet::amm(USD.issue(), EUR.issue())); - auto const ammAccount = - Account("amm", amm->getAccountID(sfAccount)); - - BEAST_EXPECT( - ownerCount(env, alice) == 3); // RippleState (USD,EUR/LP Token) - BEAST_EXPECT(ownerCount(env, ammAccount) == 2); // USD, EUR - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); // LPToken - BEAST_EXPECT(sponsoredOwnerCount(env, ammAccount) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken - BEAST_EXPECT(!env.le(keylet::amm(USD.issue(), EUR.issue())) - ->isFieldPresent(sfSponsorAccount)); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUF_RESERVE_LINE, + [&](Env& env, auto const& submit) { + submit(ammCreate(env, alice, USD(100), EUR(100))); + }, + [&]() { + auto const amm = env.current()->read( + keylet::amm(USD.issue(), EUR.issue())); + auto const ammAccount = + Account("amm", amm->getAccountID(sfAccount)); + BEAST_EXPECT( + ownerCount(env, alice) == + 3); // RippleState (USD,EUR/LP Token) + BEAST_EXPECT( + ownerCount(env, ammAccount) == 2); // USD, EUR + BEAST_EXPECT( + sponsoredOwnerCount(env, alice) == 1); // LPToken + BEAST_EXPECT(sponsoredOwnerCount(env, ammAccount) == 0); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == 1); // LPToken + BEAST_EXPECT(!env.le(keylet::amm(USD.issue(), EUR.issue())) + ->isFieldPresent(sfSponsorAccount)); + }); } { // AMMDeposit @@ -1063,28 +1277,17 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState (USD,EUR) - { - // check INSUFFICIENT_RESERVE - adjustAccountXRPBalance( - env, sponsor, reserve(env, 1) - drops(1)); - - env(ammDeposit(env, bob, USD(100), EUR(100)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUF_RESERVE_LINE)); - env.close(); - adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); - } - - env(ammDeposit(env, bob, USD(100), EUR(100)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT( - ownerCount(env, bob) == 3); // RippleState (USD,EUR/LP Token) - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // LPToken - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUF_RESERVE_LINE, + [&](Env& env, auto const& submit) { + submit(ammDeposit(env, bob, USD(100), EUR(100))); + }); } { // AMMWithdraw @@ -1127,31 +1330,18 @@ class Sponsor_test : public beast::unit_test::suite jv[jss::Amount] = USD(100).value().getJson(JsonOptions::none); jv[jss::Flags] = tfSingleAsset; - { - env(ticket::create( - sponsor, 1)); // adjust for free trustline - env.close(); - BEAST_EXPECT(ownerCount(env, sponsor) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // check INSUFFICIENT_RESERVE - adjustAccountXRPBalance( - env, sponsor, reserve(env, 2) - drops(1)); - env(jv, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); - } - - env(jv, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(ticket::create(sponsor, 1)); // adjust for free env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // USD, LPToken - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(jv); }); } { // Double Asset Withdraw @@ -1191,31 +1381,24 @@ class Sponsor_test : public beast::unit_test::suite STIssue(sfAsset, EUR.issue()).getJson(JsonOptions::none); jv[jss::Flags] = tfWithdrawAll; - { - env(ticket::create( - sponsor, 1)); // adjust for free trustline - env.close(); - BEAST_EXPECT(ownerCount(env, sponsor) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // check INSUFFICIENT_RESERVE for RippleStates * 2 - adjustAccountXRPBalance( - env, sponsor, reserve(env, 3) - drops(1)); - env(jv, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - adjustAccountXRPBalance(env, sponsor, reserve(env, 4)); - } - - env(jv, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(ticket::create(sponsor, 1)); // adjust for free trustline env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // USD, EUR - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 2, + 2, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(jv); }, + [&]() { + // LPToken deleted, USD, EUR created + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + }); } } { @@ -1264,9 +1447,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); } { - env(amm::ammClawback(gw, alice, USD, EUR2, std::nullopt), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + // remove sponsored LPToken + env(amm::ammClawback(gw, alice, USD, EUR2, std::nullopt)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // EUR2 @@ -1277,7 +1459,7 @@ class Sponsor_test : public beast::unit_test::suite } void - testCheck() + testCheck(bool cosigning) { testcase("Check"); using namespace test::jtx; @@ -1295,11 +1477,20 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // CheckCreate -> CheckCancel - auto const seq = env.seq(alice); - env(check::create(alice, bob, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + + uint32_t seq; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit(check::create(alice, bob, XRP(1))); + }); BEAST_EXPECT(ownerCount(env, alice) == 1); // Check BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -1310,11 +1501,25 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT( env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); - // transfer sponsor - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + // transfer sponsor + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + // transfer sponsor + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); // Check BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -1342,23 +1547,26 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // CheckCreate -> CheckCash - auto const seq2 = env.seq(alice); - env(check::create(alice, bob, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + uint32_t seq2; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq2 = env.seq(alice); + submit(check::create(alice, bob, XRP(1))); + }); - BEAST_EXPECT(ownerCount(env, alice) == 1); // Check BEAST_EXPECT(ownerCount(env, bob) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // CheckCash auto const checkId2 = keylet::check(alice, seq2).key; - env(check::cash(bob, checkId2, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(check::cash(bob, checkId2, XRP(1))); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); @@ -1380,92 +1588,51 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // CheckCreate -> CheckCash - auto const seq2 = env.seq(alice); - env(check::create(alice, bob, USD(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + uint32_t seq2; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq2 = env.seq(alice); + submit(check::create(alice, bob, USD(1))); + }); - BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check BEAST_EXPECT(ownerCount(env, bob) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); auto const keylet = keylet::check(alice, seq2); BEAST_EXPECT( env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); // CheckCash - env(check::cash(bob, keylet.key, USD(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState - BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { + submit(check::cash(bob, keylet.key, USD(1))); + }, + [&]() { + BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + }); } - - { - // check INSUFFICIENT_RESERVE - - { - // CheckCreate - Env env{*this, testable_amendments()}; - env.fund(XRP(10000), alice, bob, sponsor); - env.close(); - - adjustAccountXRPBalance( - env, sponsor, reserve(env, 1) - drops(1)); - - env(check::create(alice, bob, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); - - env(check::create(alice, bob, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tesSUCCESS)); - env.close(); - } - - { - // CheckCash (CheckCashMakesTrustLine) - Env env{*this, testable_amendments()}; - env.fund(XRP(10000), alice, bob, gw, sponsor); - env.close(); - - env.trust(USD(100), alice); - env.close(); - env(pay(gw, alice, USD(100))); - env.close(); - - auto const seq = env.seq(alice); - env(check::create(alice, bob, USD(1))); - env.close(); - - adjustAccountXRPBalance( - env, sponsor, reserve(env, 1) - drops(1)); - - auto const keylet = keylet::check(alice, seq); - env(check::cash(bob, keylet.key, USD(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecNO_LINE_INSUF_RESERVE)); - env.close(); - } - } - } + } void - testOffer() + testOffer(bool cosigning) { testcase("Offer"); using namespace test::jtx; @@ -1485,23 +1652,39 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // OfferCreate - auto const seq = env.seq(alice); - env(offer(alice, USD(1), XRP(1)), - sponsor::as(sponsor1, tfSponsorReserve), - sig(sfSponsorSignature, sponsor1)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + uint32_t seq; + testEachSponsorship( + env, + cosigning, + sponsor1, + alice, + 1, + 1, + tecINSUF_RESERVE_OFFER, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit(offer(alice, USD(1), XRP(1))); + }); // transfer sponsor auto const keylet = keylet::offer(alice, seq); - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -1530,24 +1713,41 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // OfferCreate - auto const seq = env.seq(alice); - env(offer(alice, USD(1), XRP(1)), - sponsor::as(sponsor1, tfSponsorReserve), - sig(sfSponsorSignature, sponsor1)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + uint32_t seq; + testEachSponsorship( + env, + cosigning, + sponsor1, + alice, + 1, + 1, + tecINSUF_RESERVE_OFFER, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit(offer(alice, USD(1), XRP(1))); + }); // OfferCreate with Cancel (new sponsor) auto const seq2 = env.seq(alice); - env(offer(alice, USD(1), XRP(1)), - json(jss::OfferSequence, seq), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(offer(alice, USD(1), XRP(1)), + json(jss::OfferSequence, seq), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(offer(alice, USD(1), XRP(1)), + json(jss::OfferSequence, seq), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -1584,10 +1784,23 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, bob) == 1); // OfferCreate - env(offer(alice, EUR(1), USD(1)), - sponsor::as(sponsor1, tfSponsorReserve), - sig(sfSponsorSignature, sponsor1)); - env.close(); + if (cosigning) + { + env(offer(alice, EUR(1), USD(1)), + sponsor::as(sponsor1, tfSponsorReserve), + sig(sfSponsorSignature, sponsor1)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor1, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(offer(alice, EUR(1), USD(1)), + sponsor::as(sponsor1, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 2); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -1600,10 +1813,23 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); // OfferCreate (cross offer) - env(offer(bob, USD(1), EUR(1)), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(offer(bob, USD(1), EUR(1)), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(bob)); + env.close(); + + env(offer(bob, USD(1), EUR(1)), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 2); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -1616,44 +1842,15 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, bob) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } - - { - // check INSUFFICIENT_RESERVE for OfferCreate - Env env{*this, testable_amendments()}; - env.fund(XRP(10000), alice, bob, gw, sponsor1, sponsor2); - env.close(); - - env.trust(USD(100), alice); - env.close(); - env(pay(gw, alice, USD(100))); - env.close(); - - adjustAccountXRPBalance(env, sponsor1, reserve(env, 1) - drops(1)); - - // fullly not crossed - env(offer(alice, USD(1), XRP(1)), - sponsor::as(sponsor1, tfSponsorReserve), - sig(sfSponsorSignature, sponsor1), - ter(tecINSUF_RESERVE_OFFER)); - - // partially crossed - env(offer(gw, XRP(1), USD(1))); - env.close(); - - env(offer(alice, USD(5), XRP(5)), - sponsor::as(sponsor1, tfSponsorReserve), - sig(sfSponsorSignature, sponsor1), - ter(tecINSUF_RESERVE_OFFER)); - } } void - testTicket() + testTicket(bool cosigning) { testcase("Ticket"); using namespace test::jtx; Account const alice("alice"); - Account const sponsor("master"); + Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); { @@ -1662,26 +1859,43 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // TicketCreate - std::uint32_t const ticketSeq{env.seq(alice) + 1}; - env(ticket::create(alice, 250), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + uint32_t ticketSeq; - BEAST_EXPECT(ownerCount(env, alice) == 250); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 250); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 250); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 250, + 250, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + ticketSeq = env.seq(alice) + 1; + submit(ticket::create(alice, 250)); + }); auto const keylet = keylet::ticket(alice, ticketSeq); BEAST_EXPECT( env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); // transfer sponsor - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 250); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 250); @@ -1701,31 +1915,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 249); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } - - { - // check INSUFFICIENT_RESERVE for TicketCreate - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, sponsor, sponsor2); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(ticket::create(alice, 1), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 249)); - env(ticket::create(alice, 250), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } } void - testCredentials() + testCredentials(bool cosigning) { testcase("Credentials"); using namespace test::jtx; @@ -1743,25 +1936,43 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(1000000), issuer, subject, sponsor, sponsor2); env.close(); - env(credentials::create(subject, issuer, credType), - credentials::uri("uri"), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + testEachSponsorship( + env, + cosigning, + sponsor, + issuer, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit( + credentials::create(subject, issuer, credType), + credentials::uri("uri")); + }); - BEAST_EXPECT(ownerCount(env, issuer) == 1); BEAST_EXPECT(ownerCount(env, subject) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // transfer sponsor auto const keylet = keylet::credential(subject, issuer, credTypeSlice); - env(sponsor::transfer(issuer, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(issuer, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(issuer)); + env.close(); + + env(sponsor::transfer(issuer, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, issuer) == 1); BEAST_EXPECT(ownerCount(env, subject) == 0); @@ -1771,10 +1982,17 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); // CredentialsAccept - env(credentials::accept(subject, issuer, credType), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + testEachSponsorship( + env, + cosigning, + sponsor, + subject, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(credentials::accept(subject, issuer, credType)); + }); BEAST_EXPECT(ownerCount(env, issuer) == 0); BEAST_EXPECT(ownerCount(env, subject) == 1); @@ -1801,14 +2019,17 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Accept Sponsored Credentials without sponsoring - env(credentials::create(subject, issuer, credType), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, issuer) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + issuer, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(credentials::create(subject, issuer, credType)); + }); env(credentials::accept(subject, issuer, credType)); env.close(); @@ -1826,41 +2047,10 @@ class Sponsor_test : public beast::unit_test::suite env(credentials::deleteCred(subject, subject, issuer, credType)); env.close(); } - - { - // check INSUFFICIENT_RESERVE for Credentials - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), issuer, subject, sponsor); - env.close(); - - // CredentialsCreate - { - adjustAccountXRPBalance( - env, sponsor, reserve(env, 1) - drops(1)); - env(credentials::create(subject, issuer, credType), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } - // CredentialsAccept - { - env(credentials::create(subject, issuer, credType)); - env.close(); - - adjustAccountXRPBalance( - env, sponsor, reserve(env, 1) - drops(1)); - env(credentials::accept(subject, issuer, credType), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } - } } void - testDelegate() + testDelegate(bool cosigning) { testcase("Delegate"); using namespace test::jtx; @@ -1875,21 +2065,37 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // DelegateSet - env(delegate::set(alice, bob, {"Payment"}), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(delegate::set(alice, bob, {"Payment"})); + }); // transfer sponsor auto const keylet = keylet::delegate(alice, bob); - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -1904,24 +2110,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } - - { - // check INSUFFICIENT_RESERVE for DelegateSet - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(delegate::set(alice, bob, {"Payment"}), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } } void - testDepositPreauth() + testDepositPreauth(bool cosigning) { testcase("DepositPreauth"); using namespace test::jtx; @@ -1935,21 +2127,37 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // DepositPreauthSet - env(deposit::auth(alice, sponsor), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(deposit::auth(alice, sponsor)); + }); // transfer sponsor auto const keylet = keylet::depositPreauth(alice, sponsor); - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -1965,24 +2173,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } - - { - // check INSUFFICIENT_RESERVE for DepositPreauthSet - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, sponsor); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(deposit::auth(alice, sponsor), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } } void - testDID() + testDID(bool cosigning) { testcase("DID"); using namespace test::jtx; @@ -1996,22 +2190,36 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // DIDSet - env(did::set(alice), - did::uri("uri"), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(did::set(alice), did::uri("uri")); + }); // transfer sponsor auto const keylet = keylet::did(alice); - env(sponsor::transfer(alice, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -2027,25 +2235,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } - - { - // check INSUFFICIENT_RESERVE for DIDSet - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, sponsor); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(did::set(alice), - did::uri("uri"), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } } void - testEscrow() + testEscrow(bool cosigning) { testcase("Escrow"); using namespace test::jtx; @@ -2053,10 +2246,8 @@ class Sponsor_test : public beast::unit_test::suite Account const alice("alice"); Account const bob("bob"); - Account const gw("gw"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - auto const USD = gw["USD"]; { // Native Escrow Env env{*this, testable_amendments()}; @@ -2066,27 +2257,44 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // EscrowCreate - auto const seq = env.seq(alice); - env(escrow::create(alice, bob, XRP(100)), - escrow::condition(escrow::cb1), - escrow::cancel_time(env.now() + 100s), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - + uint32_t seq; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit( + escrow::create(alice, bob, XRP(100)), + escrow::condition(escrow::cb1), + escrow::cancel_time(env.now() + 100s)); + }); BEAST_EXPECT( env.le(keylet::escrow(alice, seq)) ->getAccountID(sfSponsorAccount) == sponsor.id()); // transfer sponsor - env(sponsor::transfer(alice, keylet::escrow(alice, seq).key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(alice, keylet::escrow(alice, seq).key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, keylet::escrow(alice, seq).key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -2110,6 +2318,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } + Account const gw("gw"); + auto const USD = gw["USD"]; { // IOU Escrow Env env{*this, testable_amendments()}; @@ -2129,94 +2339,56 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 1); // EscrowCreate - auto const seq = env.seq(alice); - env(escrow::create(alice, bob, USD(100)), - escrow::condition(escrow::cb1), - escrow::cancel_time(env.now() + 10s), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + uint32_t seq; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit( + escrow::create(alice, bob, USD(100)), + escrow::condition(escrow::cb1), + escrow::cancel_time(env.now() + 100s)); + }); BEAST_EXPECT( env.le(keylet::escrow(alice, seq)) ->getAccountID(sfSponsorAccount) == sponsor.id()); // EscrowFinish - env(escrow::finish(bob, alice, seq), - escrow::condition(escrow::cb1), - escrow::fulfillment(escrow::fb1), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2), - fee(baseFee * 150)); - env.close(); + testEachSponsorship( + env, + cosigning, + sponsor2, + bob, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { + submit( + escrow::finish(bob, alice, seq), + escrow::condition(escrow::cb1), + escrow::fulfillment(escrow::fb1), + fee(baseFee * 150)); + }); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(ownerCount(env, bob) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT( env.le(keylet::line(bob, gw, USD.currency)) ->getAccountID(sfHighSponsorAccount) == sponsor2.id()); } - - { - // check INSUFFICIENT_RESERVE for EscrowCreate, EscrowFinish - Env env{*this, testable_amendments()}; - auto const baseFee = env.current()->fees().base; - env.fund(XRP(1000000), alice, bob, gw, sponsor); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(escrow::create(alice, bob, XRP(100)), - escrow::condition(escrow::cb1), - escrow::cancel_time(env.now() + 10s), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - - env(fset(gw, asfAllowTrustLineLocking)); - env.close(); - - env.trust(USD(1000000), alice); - env.close(); - env(pay(gw, alice, USD(10000))); - env.close(); - - env(escrow::create(alice, bob, USD(100)), - escrow::condition(escrow::cb1), - escrow::cancel_time(env.now() + 10s), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - auto const seq = env.seq(alice); - env(escrow::create(alice, bob, USD(100)), - escrow::condition(escrow::cb1), - escrow::cancel_time(env.now() + 10s)); - env.close(); - - env(escrow::finish(bob, alice, seq), - escrow::condition(escrow::cb1), - escrow::fulfillment(escrow::fb1), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - fee(baseFee * 150), - ter(tecNO_LINE_INSUF_RESERVE)); - env.close(); - } } void - testMPToken() + testMPToken(bool cosigning) { testcase("MPToken"); using namespace test::jtx; @@ -2234,51 +2406,86 @@ class Sponsor_test : public beast::unit_test::suite Json::Value jv = {}; jv[sfAccount] = alice.human(); jv[sfTransactionType] = jss::MPTokenIssuanceCreate; - auto const mptid = makeMptID(env.seq(alice), alice.id()); - env(jv, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + MPTID mptid; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + mptid = makeMptID(env.seq(alice), alice.id()); + submit(jv); + }); // transfer sponsor auto const mptIssuanceKeylet = keylet::mptIssuance(mptid); - env(sponsor::transfer(alice, mptIssuanceKeylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + if (cosigning) + { + env(sponsor::transfer(alice, mptIssuanceKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, mptIssuanceKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } // MPTokenAuthorize jv = {}; jv[sfTransactionType] = jss::MPTokenAuthorize; jv[sfAccount] = bob.human(); jv[sfMPTokenIssuanceID] = to_string(mptid); - env(jv, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(ticket::create(sponsor, 2)); // adjust for free mptoken + env.close(); + } + + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(jv); }); BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(ownerCount(env, bob) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // transfer sponsor auto const mptTokenKeylet = keylet::mptoken(mptid, bob); - env(sponsor::transfer(bob, mptTokenKeylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(bob, mptTokenKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(bob)); + env.close(); + + env(sponsor::transfer(bob, mptTokenKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(ownerCount(env, bob) == 1); @@ -2320,66 +2527,78 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(1000000), alice, bob, sponsor); env.close(); - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + // MPTokenAuthorize + Json::Value jv = {}; + jv[sfAccount] = alice.human(); + jv[sfTransactionType] = jss::MPTokenIssuanceCreate; + auto const mptid = makeMptID(env.seq(alice), alice.id()); + env(jv); + env.close(); + + // for free mptoken checks + // adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + std::uint32_t ticketSeq{env.seq(sponsor) + 1}; + env(ticket::create(sponsor, 2)); + env.close(); + + // adjustAccountXRPBalance(env, sponsor, reserve(env, 3) - + // drops(1)); + jv = {}; + jv[sfTransactionType] = jss::MPTokenAuthorize; + jv[sfAccount] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + // error (non-free mptoken) + if (cosigning) { - // MPTokenIssuanceCreate - Json::Value jv = {}; - jv[sfAccount] = alice.human(); - jv[sfTransactionType] = jss::MPTokenIssuanceCreate; - // auto const mptid = makeMptID(env.seq(alice), alice.id()); + adjustAccountXRPBalance( + env, sponsor, reserve(env, 3) - drops(1)); env(jv, sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); } - + else { - // MPTokenAuthorize - Json::Value jv = {}; - jv[sfAccount] = alice.human(); - jv[sfTransactionType] = jss::MPTokenIssuanceCreate; - auto const mptid = makeMptID(env.seq(alice), alice.id()); - env(jv); - env.close(); - - // for free mptoken checks - adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); - std::uint32_t ticketSeq{env.seq(sponsor) + 1}; - env(ticket::create(sponsor, 2)); + env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), + sponsor::sponseeAcc(bob)); env.close(); - adjustAccountXRPBalance( - env, sponsor, reserve(env, 3) - drops(1)); - jv = {}; - jv[sfTransactionType] = jss::MPTokenAuthorize; - jv[sfAccount] = bob.human(); - jv[sfMPTokenIssuanceID] = to_string(mptid); - // error (non-free mptoken) env(jv, sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), ter(tecINSUFFICIENT_RESERVE)); env.close(); + } - env(noop(sponsor), ticket::use(ticketSeq)); - env.close(); + env(noop(sponsor), ticket::use(ticketSeq)); + env.close(); + // pass (free mptoken) + if (cosigning) + { adjustAccountXRPBalance( env, sponsor, reserve(env, 2) - drops(1)); - - // pass (free mptoken) env(jv, sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor), ter(tesSUCCESS)); env.close(); } + else + { + env(sponsor::set_reserve(sponsor, 0, 1), + sponsor::sponseeAcc(bob)); + env.close(); + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + ter(tesSUCCESS)); + env.close(); + } } } void - testNFToken() + testNFToken(bool cosigning) { testcase("NFToken"); using namespace test::jtx; @@ -2395,15 +2614,19 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // NFTokenMint - uint256 const nftId{token::getNextID(env, alice, 0)}; - env(token::mint(alice), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + uint256 nftId; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + nftId = token::getNextID(env, alice, 0); + submit(token::mint(alice)); + }); // NFTokenBurn env(token::burn(alice, nftId)); @@ -2414,15 +2637,24 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); // NFTokenMintOffer + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); env(token::mint(alice), token::amount(XRP(10000)), sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + // testEachSponsorship( + // env, + // cosigning, + // sponsor, + // alice, + // 2, + // 2, + // tecINSUFFICIENT_RESERVE, + // [&](Env& env, auto const& submit) { + // token::mint(alice), token::amount(XRP(100)); + // }); } { @@ -2435,11 +2667,25 @@ class Sponsor_test : public beast::unit_test::suite auto const nftCount = 200; // NFTokenMint - for (auto i = 0; i < nftCount; i++) + if (cosigning) { - env(token::mint(alice), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + for (auto i = 0; i < nftCount; i++) + { + env(token::mint(alice), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + } + } + else + { + env(sponsor::set_reserve(sponsor, 0, 8), + sponsor::sponseeAcc(alice)); + env.close(); + for (auto i = 0; i < nftCount; i++) + { + env(token::mint(alice), + sponsor::as(sponsor, tfSponsorReserve)); + } } env.close(); @@ -2461,32 +2707,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } - - { - // check INSUFFICIENT_RESERVE for NFTokenMint - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(token::mint(alice), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); - env(token::mint(alice), - token::amount(XRP(10000)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } } void - testNFTokenOffer() + testNFTokenOffer(bool cosigning) { testcase("NFTokenOffer"); using namespace test::jtx; @@ -2510,40 +2734,59 @@ class Sponsor_test : public beast::unit_test::suite env(token::mint(alice, taxon), txflags(tfTransferable)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - // NFTokenOfferCreate - uint256 const offerIndex1 = - keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftId, XRP(1)), - token::destination(bob), - txflags(tfSellNFToken), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - - uint256 const offerIndex2 = - keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftId, XRP(1)), - token::destination(bob), - txflags(tfSellNFToken), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 3); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + uint256 offerIndex1; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + offerIndex1 = keylet::nftoffer(alice, env.seq(alice)).key; + submit( + token::createOffer(alice, nftId, XRP(1)), + token::destination(bob), + txflags(tfSellNFToken)); + }); + + uint256 offerIndex2; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + offerIndex2 = keylet::nftoffer(alice, env.seq(alice)).key; + submit( + token::createOffer(alice, nftId, XRP(1)), + token::destination(bob), + txflags(tfSellNFToken)); + }); // transfer sponsor - env(sponsor::transfer(alice, offerIndex1), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(alice, offerIndex1), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, offerIndex1), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 3); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); @@ -2572,21 +2815,23 @@ class Sponsor_test : public beast::unit_test::suite env(token::mint(alice, taxon), txflags(tfTransferable)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - // NFTokenOfferCreate - uint256 const offerIndex = - keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftId, XRP(1)), - token::destination(bob), - txflags(tfSellNFToken), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + uint256 offerIndex; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + offerIndex = keylet::nftoffer(alice, env.seq(alice)).key; + submit( + token::createOffer(alice, nftId, XRP(1)), + token::destination(bob), + txflags(tfSellNFToken)); + }); // NFTokenOfferAccept env(token::acceptSellOffer(bob, offerIndex)); @@ -2611,20 +2856,23 @@ class Sponsor_test : public beast::unit_test::suite env(token::mint(alice, taxon), txflags(tfTransferable)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); - // NFTokenOfferCreate - uint256 const offerIndex = keylet::nftoffer(bob, env.seq(bob)).key; - env(token::createOffer(bob, nftId, XRP(1)), - token::owner(alice), - token::destination(alice), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, bob) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + uint256 offerIndex; + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + offerIndex = keylet::nftoffer(bob, env.seq(bob)).key; + submit( + token::createOffer(bob, nftId, XRP(1)), + token::owner(alice), + token::destination(alice)); + }); // NFTokenOfferAccept env(token::acceptBuyOffer(alice, offerIndex)); @@ -2651,32 +2899,41 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 1); // NFTokenOfferCreate (BuyOffer) - uint256 const buyOfferIndex = - keylet::nftoffer(bob, env.seq(bob)).key; - env(token::createOffer(bob, nftId, XRP(1)), - token::owner(alice), - token::destination(broker), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, bob) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + uint256 buyOfferIndex; + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + buyOfferIndex = keylet::nftoffer(bob, env.seq(bob)).key; + submit( + token::createOffer(bob, nftId, XRP(1)), + token::owner(alice), + token::destination(broker)); + }); // NFTokenOfferCreate (SellOffer) - uint256 const sellOfferIndex = - keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nftId, XRP(1)), - txflags(tfSellNFToken), - token::destination(broker), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + uint256 sellOfferIndex; + testEachSponsorship( + env, + cosigning, + sponsor2, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + sellOfferIndex = + keylet::nftoffer(alice, env.seq(alice)).key; + submit( + token::createOffer(alice, nftId, XRP(1)), + txflags(tfSellNFToken), + token::destination(broker)); + }); // NFTokenOfferAccept env(token::brokerOffers(broker, buyOfferIndex, sellOfferIndex)); @@ -2689,44 +2946,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } - - { - // check INSUFFICIENT_RESERVE for NFTokenOffer - - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - - auto const nextNftId = token::getNextID(env, alice, 0u, 0u); - env(token::mint(alice)); - env.close(); - - // NFTokenOfferCreate - env(token::createOffer(alice, nextNftId, XRP(1)), - txflags(tfSellNFToken), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - - auto const offerIndex = keylet::nftoffer(alice, env.seq(alice)).key; - env(token::createOffer(alice, nextNftId, XRP(1)), - txflags(tfSellNFToken)); - env.close(); - - // NFTokenOfferAccept (buyer) - env(token::acceptSellOffer(bob, offerIndex), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } } void - testPayChan() + testPayChan(bool cosigning) { testcase("PayChan"); using namespace test::jtx; @@ -2744,21 +2967,39 @@ class Sponsor_test : public beast::unit_test::suite // PayChanCreate auto const pk = alice.pk(); auto const settleDelay = 10s; - auto const chan = paychan::channel(alice, bob, env.seq(alice)); - env(paychan::create(alice, bob, XRP(100), settleDelay, pk), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + uint256 chan; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + chan = paychan::channel(alice, bob, env.seq(alice)); + submit( + paychan::create(alice, bob, XRP(100), settleDelay, pk)); + }); // transfer sponsor - env(sponsor::transfer(alice, chan), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(alice, chan), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, chan), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -2775,28 +3016,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } - - { - // check INSUFFICIENT_RESERVE for PayChan - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - - // PayChanCreate - auto const pk = alice.pk(); - auto const settleDelay = 10s; - env(paychan::create(alice, bob, XRP(100), settleDelay, pk), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } } void - testPermissionedDomain() + testPermissionedDomain(bool cosigning) { testcase("PermissionedDomain"); using namespace test::jtx; @@ -2809,23 +3032,40 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // PermissionedDomainSet - auto const seq = env.seq(alice); pdomain::Credentials credentials{{alice, "first credential"}}; - env(pdomain::setTx(alice, credentials), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + uint32_t seq; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit(pdomain::setTx(alice, credentials)); + }); // transfer sponsor - env(sponsor::transfer( - alice, keylet::permissionedDomain(alice, seq).key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + auto const keylet = keylet::permissionedDomain(alice, seq); + + if (cosigning) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -2843,26 +3083,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } - { - // check INSUFFICIENT_RESERVE for PermissionedDomain - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, sponsor); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - - // PermissionedDomainSet - pdomain::Credentials credentials{{alice, "first credential"}}; - env(pdomain::setTx(alice, credentials), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - } } void - testOracle() + testOracle(bool cosigning) { testcase("Oracle"); using namespace test::jtx; @@ -2938,20 +3162,36 @@ class Sponsor_test : public beast::unit_test::suite { // OracleSet (reserve 1) - env(oracleSet(env, alice, 5), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(oracleSet(env, alice, 5)); + }); // transfer sponsor - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + auto const keylet = keylet::oracle(alice, 1); + if (cosigning) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -2969,20 +3209,36 @@ class Sponsor_test : public beast::unit_test::suite } { // OracleSet (reserve 2) - env(oracleSet(env, alice, 6), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 2, + 2, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(oracleSet(env, alice, 6)); + }); // transfer sponsor - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + auto const keylet = keylet::oracle(alice, 1); + if (cosigning) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 2), + sponsor::sponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == 2); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); @@ -3000,14 +3256,17 @@ class Sponsor_test : public beast::unit_test::suite } { // OracleSet (reserve 1->2, sponsor1 -> no-sponsor) - env(oracleSet(env, alice, 5), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(oracleSet(env, alice, 5)); + }); // reserve 1->2 env(oracleSet(env, alice, 6)); @@ -3027,25 +3286,37 @@ class Sponsor_test : public beast::unit_test::suite } { // OracleSet (reserve 1->2, sponsor1 -> sponsor2) - env(oracleSet(env, alice, 5), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(oracleSet(env, alice, 5)); + }); + // return; // reserve 1->2 - env(oracleSet(env, alice, 6), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); + testEachSponsorship( + env, + cosigning, + sponsor2, + alice, + 1, + 2, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(oracleSet(env, alice, 6)); + }, + [&]() { + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); + }); // OracleDelete env(oracleDelete(alice)); @@ -3064,14 +3335,17 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 1); // reserve 1->2 - env(oracleSet(env, alice, 6), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 2); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 2, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(oracleSet(env, alice, 6)); + }); // OracleDelete env(oracleDelete(alice)); @@ -3086,20 +3360,36 @@ class Sponsor_test : public beast::unit_test::suite // test sponsor transfer auto const dataSeriesSize = isTwoOwnerCount ? 6 : 5; auto const ocount = isTwoOwnerCount ? 2 : 1; - env(oracleSet(env, alice, dataSeriesSize), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - BEAST_EXPECT(ownerCount(env, alice) == ocount); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == ocount); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + ocount, + ocount, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(oracleSet(env, alice, dataSeriesSize)); + }); // transfer sponsor - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); + if (cosigning) + { + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, ocount), + sponsor::sponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } BEAST_EXPECT(ownerCount(env, alice) == ocount); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount); @@ -3113,33 +3403,16 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == ocount); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); - } - } - - { - // check INSUFFICIENT_RESERVE for OracleSet - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, sponsor); - env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(oracleSet(env, alice, 5), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); - env(oracleSet(env, alice, 6), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); + // remove sponsor + env(oracleDelete(alice)); + env.close(); + } } } void - testSignerList() + testSignerList(bool cosigning) { testcase("SignerList"); using namespace test::jtx; @@ -3148,59 +3421,58 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - { - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, sponsor, sponsor2); - env.close(); - - // SignerListSet - env(signers(alice, 1, {{bob, 1}}), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); - // transfer sponsor + // SignerListSet + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(signers(alice, 1, {{bob, 1}})); + }); + + // transfer sponsor + if (cosigning) + { env(sponsor::transfer(alice, keylet::signers(alice).key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - - // Delete - env(signers(alice, none)); - env.close(); - - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } - + else { - // check INSUFFICIENT_RESERVE for SignerListSet - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor); + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); env.close(); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(signers(alice, 1, {{bob, 1}}), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); + env(sponsor::transfer(alice, keylet::signers(alice).key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // Delete + env(signers(alice, none)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } void - testTrustSet() + testTrustSet(bool cosigning) { testcase("TrustSet"); using namespace test::jtx; @@ -3210,10 +3482,6 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); { - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); - env.close(); - auto const validateSponsoredTrustline = [&](std::shared_ptr const& sle, bool isIssuerHigh, @@ -3234,6 +3502,10 @@ class Sponsor_test : public beast::unit_test::suite // create and delete for (bool isIssuerHigh : {false, true}) { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; auto const& user = isIssuerHigh ? lowAcc : highAcc; @@ -3241,32 +3513,25 @@ class Sponsor_test : public beast::unit_test::suite auto const currency = USD.currency; // create TrustLine - env(trust(user, USD(100)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, user) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - - auto const line = env.le(keylet::line(user, issuer, currency)); - validateSponsoredTrustline(line, isIssuerHigh, sponsor); - - // transfer sponsor - env(sponsor::transfer( - user, keylet::line(user, issuer, currency).key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); - - BEAST_EXPECT(ownerCount(env, user) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(ticket::create( + sponsor, 2)); // adjust for free trustline + env.close(); + } - auto const line2 = env.le(keylet::line(user, issuer, currency)); - validateSponsoredTrustline(line2, isIssuerHigh, sponsor2); + testEachSponsorship( + env, + cosigning, + sponsor, + user, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { + submit(trust(user, USD(100))); + }); // delete TrustLine env(trust(user, USD(0))); @@ -3282,6 +3547,10 @@ class Sponsor_test : public beast::unit_test::suite // update for (bool isIssuerHigh : {false, true}) { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; auto const& user = isIssuerHigh ? lowAcc : highAcc; @@ -3294,15 +3563,25 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet::line(user, issuer, currency))); - // update TrustLine from user to make reserve - env(trust(user, USD(100)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(ticket::create( + sponsor, 2)); // adjust for free trustline + env.close(); + } - BEAST_EXPECT(ownerCount(env, user) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + user, + 1, + 1, + tecINSUF_RESERVE_LINE, + [&](Env& env, auto const& submit) { + submit(trust(user, USD(100))); + }); auto const line = env.le(keylet::line(user, issuer, currency)); validateSponsoredTrustline(line, isIssuerHigh, sponsor); @@ -3319,49 +3598,13 @@ class Sponsor_test : public beast::unit_test::suite // remove TrustLine from issuer env(trust(issuer, user["USD"](0))); env.close(); - } - } - - { - // check INSUFFICIENT_RESERVE for TrustSet - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor); - env.close(); - - auto const& highAcc = alice > bob ? alice : bob; - auto const& lowAcc = alice > bob ? bob : alice; - for (bool isIssuerHigh : {false}) - { - auto const& issuer = isIssuerHigh ? highAcc : lowAcc; - auto const& user = isIssuerHigh ? lowAcc : highAcc; - auto const USD = issuer["USD"]; - - adjustAccountXRPBalance(env, sponsor, reserve(env, 100)); - - // free trustline - std::uint32_t const ticketSeq{env.seq(sponsor) + 1}; - env(ticket::create(sponsor, 2)); - env.close(); - - adjustAccountXRPBalance( - env, sponsor, reserve(env, 3) - drops(1)); - - // create TrustLine - env(trust(user, USD(100)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecNO_LINE_INSUF_RESERVE)); - env.close(); - - env(noop(sponsor), ticket::use(ticketSeq)); - env(noop(sponsor), ticket::use(ticketSeq + 1)); - env.close(); + BEAST_EXPECT(!env.le(keylet::line(user, issuer, currency))); } } } void - testVault() + testVault(bool cosigning) { testcase("Vault"); using namespace test::jtx; @@ -3382,35 +3625,23 @@ class Sponsor_test : public beast::unit_test::suite Vault vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); - { - auto const ticketSeq = env.seq(sponsor) + 1; - env(ticket::create(sponsor, 1)); - env.close(); - adjustAccountXRPBalance( - env, sponsor, reserve(env, 3) - drops(1)); - // check with OwnerCount=3 because free MPToken condition exists - env(tx, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); - env(noop(sponsor), ticket::use(ticketSeq)); - env.close(); - } - - // get keylet using latest sequence - keylet = - std::get<1>(vault.create({.owner = alice, .asset = asset})); - - env(tx, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(ticket::create(sponsor, 2)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // Vault, MPToken(share) - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 2, + 2, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + auto result = + vault.create({.owner = alice, .asset = asset}); + submit(std::get<0>(result)); + keylet = std::get<1>(result); + }); BEAST_EXPECT( env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); } @@ -3434,34 +3665,19 @@ class Sponsor_test : public beast::unit_test::suite auto const depositTx = vault.deposit( {.depositor = bob, .id = keylet.key, .amount = asset(100)}); - { - // TODO: https://github.com/XRPLF/rippled/issues/5837 - // auto const ticketSeq = env.seq(sponsor) + 1; - // env(ticket::create(sponsor, 1)); - // env.close(); - // adjustAccountXRPBalance( - // env, sponsor, reserve(env, 3) - drops(1)); - // // check with OwnerCount=3 because free MPToken condition - // exists env(depositTx, - // sponsor::as(sponsor, tfSponsorReserve), - // sig(sfSponsorSignature,sponsor), - // ter(tecINSUFFICIENT_RESERVE)); - // env.close(); - // adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); - // env(noop(sponsor), ticket::use(ticketSeq)); - // env.close(); - } - - env(depositTx, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + + env(ticket::create(sponsor, 2)); // for free MPToken env.close(); - BEAST_EXPECT( - ownerCount(env, bob) == 2); // RippleState, MPToken(share) - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // MPToken(share) - BEAST_EXPECT( - sponsoringOwnerCount(env, sponsor) == 1); // MPToken(share) + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(depositTx); }); } // VaultWithdraw { @@ -3485,30 +3701,19 @@ class Sponsor_test : public beast::unit_test::suite auto const depositTx = vault.deposit( {.depositor = bob, .id = keylet.key, .amount = asset(100)}); - { - // https://github.com/XRPLF/rippled/issues/5837 - // auto const ticketSeq = env.seq(sponsor) + 1; - // env(ticket::create(sponsor, 1)); - // env.close(); - // adjustAccountXRPBalance( - // env, sponsor, reserve(env, 3) - drops(1)); - // // check with OwnerCount=3 because free MPToken condition - // // exists - // env(depositTx, - // sponsor::as(sponsor, tfSponsorReserve), - // sig(sfSponsorSignature,sponsor), - // ter(tecINSUFFICIENT_RESERVE)); - // env.close(); - // adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); - // env(noop(sponsor), ticket::use(ticketSeq)); - // env.close(); - } - - env(depositTx, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(ticket::create(sponsor, 2)); // for free MPToken env.close(); + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(depositTx); }); + env(trust(bob, asset(0))); // remove trustline env.close(); @@ -3518,7 +3723,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT( sponsoringOwnerCount(env, sponsor) == 1); // MPToken(share) - // create Trustline + // create Trustline with vault withdraw + // https://github.com/XRPLF/rippled/pull/5857 env(vault.withdraw( {.depositor = bob, .id = keylet.key, @@ -3536,13 +3742,24 @@ class Sponsor_test : public beast::unit_test::suite sponsoringOwnerCount(env, sponsor) == 2); // RippleState, MPToken(share) + // testEachSponsorship( + // env, + // cosigning, + // sponsor, + // bob, + // 1, + // 1, + // tecINSUFFICIENT_RESERVE, + // [&](Env& env, auto const& submit) { + // submit(vault.withdraw( + // {.depositor = bob, + // .id = keylet.key, + // .amount = asset(50)})); + // }); + // remove sponsored MPToken(share) env(vault.withdraw( - {.depositor = bob, - .id = keylet.key, - .amount = asset(50)}), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + {.depositor = bob, .id = keylet.key, .amount = asset(50)})); env.close(); BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState @@ -3579,30 +3796,19 @@ class Sponsor_test : public beast::unit_test::suite auto const depositTx = vault.deposit( {.depositor = bob, .id = keylet.key, .amount = asset(100)}); - { - // https://github.com/XRPLF/rippled/issues/5837 - // auto const ticketSeq = env.seq(sponsor) + 1; - // env(ticket::create(sponsor, 1)); - // env.close(); - // adjustAccountXRPBalance( - // env, sponsor, reserve(env, 3) - drops(1)); - // // check with OwnerCount=3 because free MPToken condition - // // exists - // env(depositTx, - // sponsor::as(sponsor, tfSponsorReserve), - // sig(sfSponsorSignature,sponsor), - // ter(tecINSUFFICIENT_RESERVE)); - // env.close(); - // adjustAccountXRPBalance(env, sponsor, reserve(env, 3)); - // env(noop(sponsor), ticket::use(ticketSeq)); - // env.close(); - } - - env(depositTx, - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(ticket::create(sponsor, 2)); // for free MPToken env.close(); + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(depositTx); }); + BEAST_EXPECT( ownerCount(env, bob) == 2); // RippleState, MPToken(share) BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // MPToken(share) @@ -3652,7 +3858,7 @@ class Sponsor_test : public beast::unit_test::suite } void - testXChain() + testXChain(bool cosigning) { testcase("XChain"); using namespace test::jtx; @@ -3673,50 +3879,31 @@ class Sponsor_test : public beast::unit_test::suite // XChainCreateBridge { - BEAST_EXPECT(ownerCount(env, doorA) == 1); // SignerList - BEAST_EXPECT(sponsoredOwnerCount(env, doorA) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(bridge_create(doorA, jvb, XRP(1), XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); - - env(bridge_create(doorA, jvb, XRP(1), XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - BEAST_EXPECT(ownerCount(env, doorA) == 2); // Bridge, SignerList - BEAST_EXPECT(sponsoredOwnerCount(env, doorA) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + testEachSponsorship( + env, + cosigning, + sponsor, + doorA, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(bridge_create(doorA, jvb, XRP(1), XRP(1))); + }); } // XChainCreateClaimID { - BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - - adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); - env(xchain_create_claim_id(alice, jvb, XRP(1), bob), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); - env.close(); - adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); - - env(xchain_create_claim_id(alice, jvb, XRP(1), bob), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - // XChainOwnedClaimID created - BEAST_EXPECT(ownerCount(env, alice) == 1); - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(xchain_create_claim_id(alice, jvb, XRP(1), bob)); + }); } // XChainCommit { @@ -3724,10 +3911,26 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); - env(xchain_commit(alice, jvb, 1, XRP(100), bob), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + if (cosigning) + { + env(xchain_commit(alice, jvb, 1, XRP(100), bob), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(xchain_commit(alice, jvb, 1, XRP(100), bob), + sponsor::as(sponsor, tfSponsorReserve)); + env.close(); + + env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); + env.close(); + } // doesn't sponsor anything BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -3740,11 +3943,28 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); - env(claim_attestation( - alice, jvb, bob, XRP(1), bob, false, 1, bob, signer), - sponsor::as(sponsor, tfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); + if (cosigning) + { + env(claim_attestation( + alice, jvb, bob, XRP(1), bob, false, 1, bob, signer), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(claim_attestation( + alice, jvb, bob, XRP(1), bob, false, 1, bob, signer), + sponsor::as(sponsor, tfSponsorReserve)); + env.close(); + + env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); + env.close(); + } // XChainOwnedClaimID deleted BEAST_EXPECT(ownerCount(env, alice) == 0); @@ -4005,25 +4225,28 @@ class Sponsor_test : public beast::unit_test::suite testSponsorReserve() { testRequireFlag(); - testAMM(); - testCheck(); - testOffer(); - testTicket(); - testCredentials(); - testDelegate(); - testDepositPreauth(); - testDID(); - testEscrow(); - testMPToken(); - testNFToken(); - testNFTokenOffer(); - testPayChan(); - testPermissionedDomain(); - testOracle(); - testSignerList(); - testTrustSet(); - testVault(); - testXChain(); + for (auto cosigning : {false, true}) + { + testAMM(cosigning); + testCheck(cosigning); + testOffer(cosigning); + testTicket(cosigning); + testCredentials(cosigning); + testDelegate(cosigning); + testDepositPreauth(cosigning); + testDID(cosigning); + testEscrow(cosigning); + testMPToken(cosigning); + testNFToken(cosigning); + testNFTokenOffer(cosigning); + testPayChan(cosigning); + testPermissionedDomain(cosigning); + testOracle(cosigning); + testSignerList(cosigning); + testTrustSet(cosigning); + testVault(cosigning); + testXChain(cosigning); + } } void diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 622c6fa3da1..7777f2f66c3 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -135,17 +135,26 @@ AMMCreate::preclaim(PreclaimContext const& ctx) return terNO_RIPPLE; } - auto const sponsor = getTxReserveSponsorAccountID(ctx.tx); + auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); // Check the reserve for LPToken trustline - STAmount const xrpBalance = - xrpLiquid(ctx.view, sponsor.value_or(accountID), 1, ctx.j); // Insufficient reserve - if (xrpBalance <= beast::zero) + auto const accountSle = ctx.view.read(keylet::account(accountID)); + if (auto const ret = checkInsufficientReserve( + ctx.view, + ctx.tx, + accountSle, + accountSle->getFieldAmount(sfBalance), + sponsorSle, + 1); + !isTesSuccess(ret)) { JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves"; return tecINSUF_RESERVE_LINE; } + auto const ownerCountAdj = isReserveSponsored(ctx.tx) ? 0 : 1; + STAmount const xrpBalance = + xrpLiquid(ctx.view, accountID, ownerCountAdj, ctx.j); auto insufficientBalance = [&](STAmount const& asset) { if (isXRP(asset)) return xrpBalance < asset; diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index 063373d955a..50eb35d9d39 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -169,8 +169,6 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) { auto const accountID = ctx.tx[sfAccount]; - auto const sponsor = getTxReserveSponsorAccountID(ctx.tx); - auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); if (!ammSle) @@ -228,8 +226,18 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) // Adjust the reserve if LP doesn't have LPToken trustline auto const sle = ctx.view.read( keylet::line(accountID, lpIssue.account, lpIssue.currency)); - if (xrpLiquid(ctx.view, sponsor.value_or(accountID), !sle, ctx.j) >= - deposit) + + auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + auto const accountSle = ctx.view.read(keylet::account(accountID)); + if (auto const ret = checkInsufficientReserve( + ctx.view, + ctx.tx, + accountSle, + accountSle->getFieldAmount(sfBalance) - deposit, + sponsorSle, + 1, + !sle); + isTesSuccess(ret)) return TER(tesSUCCESS); if (sle) return tecUNFUNDED_AMM; @@ -357,10 +365,17 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) // We checked above but need to check again if depositing IOU only. if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::zero) { - STAmount const xrpBalance = - xrpLiquid(ctx.view, sponsor.value_or(accountID), 1, ctx.j); + auto const accountSle = ctx.view.read(keylet::account(accountID)); + auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); // Insufficient reserve - if (xrpBalance <= beast::zero) + if (auto const ret = checkInsufficientReserve( + ctx.view, + ctx.tx, + accountSle, + accountSle->getFieldAmount(sfBalance), + sponsor, + 1); + !isTesSuccess(ret)) { JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves"; return tecINSUF_RESERVE_LINE; diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index abe76a08d8a..aa30fdae10e 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -600,12 +600,13 @@ AMMWithdraw::withdraw( return tesSUCCESS; if (!view.exists(keylet::line(account, issue))) { - auto const sleAccount = - view.read(keylet::account(sponsor.value_or(account))); + auto const sleAccount = view.read(keylet::account(account)); + auto const sponsorSle = getTxReserveSponsor(view, tx); if (!sleAccount) return tecINTERNAL; // LCOV_EXCL_LINE auto const balance = (*sleAccount)[sfBalance].xrp(); - std::uint32_t const count = ownerCount(sleAccount); + std::uint32_t const count = + ownerCount(sponsorSle ? *sponsorSle : sleAccount); if (count >= 2) { if (auto const ret = checkInsufficientReserve( diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.cpp b/src/xrpld/app/tx/detail/NFTokenUtils.cpp index 8583273bed0..e890d7c0a29 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.cpp +++ b/src/xrpld/app/tx/detail/NFTokenUtils.cpp @@ -62,19 +62,19 @@ locatePage(ApplyView& view, AccountID const& owner, uint256 const& id) view.succ(first.key, last.key.next()).value_or(last.key))); } -static std::shared_ptr +static Expected, TER> getPageForToken( ApplyView& view, STTx const& tx, AccountID const& owner, std::optional const& sponsor, uint256 const& id, - std::function const&, - AccountID const&, - std::optional const&)> const& createCallback) + std::function< + TER(ApplyView&, + STTx const&, + std::shared_ptr const&, + AccountID const&, + std::optional const&)> const& createCallback) { auto const base = keylet::nftpage_min(owner); auto const first = keylet::nftpage(base, id); @@ -94,7 +94,10 @@ getPageForToken( cp = std::make_shared(last); cp->setFieldArray(sfNFTokens, arr); view.insert(cp); - createCallback(view, tx, cp, owner, sponsor); + + if (auto const ret = createCallback(view, tx, cp, owner, sponsor); + !isTesSuccess(ret)) + return Unexpected(ret); return cp; } @@ -222,7 +225,9 @@ getPageForToken( cp->setFieldH256(sfPreviousPageMin, np->key()); view.update(cp); - createCallback(view, tx, np, owner, sponsor); + if (auto const ret = createCallback(view, tx, np, owner, sponsor); + ret != tesSUCCESS) + return Unexpected(ret); // fixNFTokenDirV1 corrects a bug in the initial implementation that // would put an NFT in the wrong page. The problem was caused by an @@ -298,7 +303,7 @@ insertToken( // First, we need to locate the page the NFT belongs to, creating it // if necessary. This operation may fail if it is impossible to insert // the NFT. - std::shared_ptr page = getPageForToken( + auto page = getPageForToken( view, tx, owner, @@ -308,10 +313,20 @@ insertToken( STTx const& tx, std::shared_ptr const& newPage, AccountID const& owner, - std::optional const& sponsor) { + std::optional const& sponsor) -> TER { std::optional> const sponsorSle = sponsor ? view.peek(keylet::account(*sponsor)) : std::optional>{std::nullopt}; + + if (isReserveSponsored(tx)) + { + auto const ownerSle = view.read(keylet::account(owner)); + auto const ownerBalance = ownerSle->getFieldAmount(sfBalance); + if (auto const ret = checkInsufficientReserve( + view, tx, ownerSle, ownerBalance, sponsorSle, 1); + !isTesSuccess(ret)) + return ret; + } adjustOwnerCount( view, tx, @@ -320,13 +335,17 @@ insertToken( 1, beast::Journal{beast::Journal::getNullSink()}); addSponsorToLedgerEntry(newPage, sponsorSle); + return tesSUCCESS; }); - if (!page) + if (!page.has_value()) + return page.error(); + + if (!(*page)) return tecNO_SUITABLE_NFTOKEN_PAGE; { - auto arr = page->getFieldArray(sfNFTokens); + auto arr = (*page)->getFieldArray(sfNFTokens); arr.push_back(std::move(nft)); arr.sort([](STObject const& o1, STObject const& o2) { @@ -334,10 +353,10 @@ insertToken( o1.getFieldH256(sfNFTokenID), o2.getFieldH256(sfNFTokenID)); }); - page->setFieldArray(sfNFTokens, arr); + (*page)->setFieldArray(sfNFTokens, arr); } - view.update(page); + view.update((*page)); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index c35e8d1a1bb..f3264891089 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -58,6 +58,12 @@ SetOracle::preflight(PreflightContext const& ctx) return tesSUCCESS; } +uint32_t +calculateOracleReserve(std::size_t count) +{ + return count > 5 ? 2 : 1; +} + TER SetOracle::preclaim(PreclaimContext const& ctx) { @@ -143,10 +149,18 @@ SetOracle::preclaim(PreclaimContext const& ctx) if (!pairsDel.empty()) return tecTOKEN_PAIR_NOT_FOUND; - auto const oldCount = - sle->getFieldArray(sfPriceDataSeries).size() > 5 ? 2 : 1; - auto const newCount = pairs.size() > 5 ? 2 : 1; - adjustReserve = newCount - oldCount; + auto const oldCount = calculateOracleReserve( + sle->getFieldArray(sfPriceDataSeries).size()); + auto const newCount = calculateOracleReserve(pairs.size()); + + // if different sponsors, check with newCount + auto const currentSponsor = getLedgerEntryReserveSponsorAccountID(sle); + auto const newSponsor = getTxReserveSponsorAccountID(ctx.tx); + if ((!currentSponsor && !newSponsor) || + (currentSponsor && newSponsor && *currentSponsor == *newSponsor)) + adjustReserve = newCount - oldCount; + else + adjustReserve = newCount; } else { @@ -155,7 +169,7 @@ SetOracle::preclaim(PreclaimContext const& ctx) if (!ctx.tx.isFieldPresent(sfProvider) || !ctx.tx.isFieldPresent(sfAssetClass)) return temMALFORMED; - adjustReserve = pairs.size() > 5 ? 2 : 1; + adjustReserve = calculateOracleReserve(pairs.size()); } if (pairs.empty()) @@ -237,7 +251,7 @@ SetOracle::doApply() sfQuoteAsset, entry.getFieldCurrency(sfQuoteAsset)); pairs.emplace(tokenPairKey(entry), std::move(priceData)); } - auto const oldCount = pairs.size() > 5 ? 2 : 1; + auto const oldCount = calculateOracleReserve(pairs.size()); // update/add/delete pairs for (auto const& entry : ctx_.tx.getFieldArray(sfPriceDataSeries)) { @@ -276,7 +290,7 @@ SetOracle::doApply() (*sle)[sfOracleDocumentID] = ctx_.tx[sfOracleDocumentID]; } - auto const newCount = pairs.size() > 5 ? 2 : 1; + auto const newCount = calculateOracleReserve(pairs.size()); auto const adjust = newCount - oldCount; if (adjust > 0) @@ -353,7 +367,7 @@ SetOracle::doApply() (*sle)[sfOwnerNode] = *page; - auto const count = series.size() > 5 ? 2 : 1; + auto const count = calculateOracleReserve(series.size()); auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); if (!adjustOwnerCount(ctx_, sponsor, count)) return tefINTERNAL; // LCOV_EXCL_LINE diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index d866fa08475..483d0af329f 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -403,7 +403,12 @@ SetTrust::doApply() txSponsorSle = view().peek(keylet::account(*txSponsorAcc)); std::uint32_t const uOwnerCount = ownerCount(txSponsorSle.value_or(sle)); - bool const freeTrustLine = uOwnerCount < 2; + + bool const isSponsoredAndPreFunded = + txSponsorSle && !isSponsorReserveCoSigning(ctx_.tx); + // If PreFunded Sponsor, it must be checked whether sufficient + // ReserveCount exists. + bool const freeTrustLine = uOwnerCount < 2 && !isSponsoredAndPreFunded; std::uint32_t uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0); std::uint32_t uQualityOut( @@ -639,6 +644,17 @@ SetTrust::doApply() if (bLowReserveSet && !bLowReserved) { + // should be checked PreFunded Sponsor before adjustOwnerCount() + if (auto const ret = checkInsufficientReserve( + view(), + ctx_.tx, + sleLowAccount, + mPriorBalance, + txSponsorSle, + 1); + isSponsoredAndPreFunded && !isTesSuccess(ret)) + return tecINSUF_RESERVE_LINE; + // Set reserve for low account. adjustOwnerCount( view(), ctx_.tx, sleLowAccount, txSponsorSle, 1, viewJ); @@ -663,6 +679,17 @@ SetTrust::doApply() if (bHighReserveSet && !bHighReserved) { + // should be checked PreFunded Sponsor before adjustOwnerCount() + if (auto const ret = checkInsufficientReserve( + view(), + ctx_.tx, + sleHighAccount, + mPriorBalance, + txSponsorSle, + 1); + isSponsoredAndPreFunded && !isTesSuccess(ret)) + return tecINSUF_RESERVE_LINE; + // Set reserve for high account. adjustOwnerCount( view(), ctx_.tx, sleHighAccount, txSponsorSle, 1, viewJ); diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index ab9c37a681e..08e8f08165e 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -311,7 +311,17 @@ SponsorshipSet::doApply() { // transfer feeAmount to ledger entry (*sponsorAccSle)[sfBalance] -= *feeAmount; - (*sponsorObjSle)[sfFeeAmount] += *feeAmount; + if ((*sponsorObjSle).isFieldPresent(sfFeeAmount)) + { + auto const oldFeeAmount = + (*sponsorObjSle).getFieldAmount(sfFeeAmount); + auto const newFeeAmount = oldFeeAmount + *feeAmount; + (*sponsorObjSle).setFieldAmount(sfFeeAmount, newFeeAmount); + } + else + { + (*sponsorObjSle).setFieldAmount(sfFeeAmount, *feeAmount); + } } if (maxFee) @@ -321,7 +331,7 @@ SponsorshipSet::doApply() if (reserveCount) (*sponsorObjSle)[sfReserveCount] = - (*sponsorObjSle)[sfReserveCount] + *reserveCount; + (*sponsorObjSle).getFieldU32(sfReserveCount) + *reserveCount; // update Flags auto flags = sponsorObjSle->getFieldU32(sfFlags); diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/xrpld/app/tx/detail/VaultCreate.cpp index 151e4e369ad..baa9df1f7f1 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/xrpld/app/tx/detail/VaultCreate.cpp @@ -162,12 +162,25 @@ VaultCreate::doApply() if (auto ter = dirLink(view(), account_, vault)) return ter; auto const sponsor = getTxReserveSponsor(view(), tx); - adjustOwnerCount(view(), tx, owner, sponsor, 1, j_); - addSponsorToLedgerEntry(vault, sponsor); - if (auto const ret = checkInsufficientReserve( - view(), tx, owner, mPriorBalance, sponsor, 0); - !isTesSuccess(ret)) - return ret; + if (!ctx_.view().rules().enabled(featureSponsor)) + { + adjustOwnerCount(view(), tx, owner, sponsor, 1, j_); + addSponsorToLedgerEntry(vault, sponsor); + if (auto const ret = checkInsufficientReserve( + view(), tx, owner, mPriorBalance, sponsor, 0); + !isTesSuccess(ret)) + return ret; + } + else + { + // after Sponsor Amendment, check insufficient reserve first + if (auto const ret = checkInsufficientReserve( + view(), tx, owner, mPriorBalance, sponsor, 1); + !isTesSuccess(ret)) + return ret; + adjustOwnerCount(view(), tx, owner, sponsor, 1, j_); + addSponsorToLedgerEntry(vault, sponsor); + } auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID); if (!maybePseudo) From 2337d340e57c7ba4e313b943a98d44854cfc80cd Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 17 Oct 2025 18:56:46 +0900 Subject: [PATCH 066/249] Allow delegation for Sponsorship transactions --- include/xrpl/protocol/detail/transactions.macro | 4 ++-- src/test/app/Delegate_test.cpp | 5 ++++- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 47a774e7b90..2ddd871074b 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -949,7 +949,7 @@ TRANSACTION(ttBATCH, 71, Batch, # include #endif TRANSACTION(ttSPONSORSHIP_TRANSFER, 72, SponsorshipTransfer, - Delegation::notDelegatable, + Delegation::delegatable, featureSponsor, noPriv, ({ @@ -961,7 +961,7 @@ TRANSACTION(ttSPONSORSHIP_TRANSFER, 72, SponsorshipTransfer, # include #endif TRANSACTION(ttSPONSORSHIP_SET, 73, SponsorshipSet, - Delegation::notDelegatable, + Delegation::delegatable, featureSponsor, noPriv, ({ diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index ea5e073a558..d328bdd2799 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -1718,7 +1718,10 @@ class Delegate_test : public beast::unit_test::suite {"VaultDelete", featureSingleAssetVault}, {"VaultDeposit", featureSingleAssetVault}, {"VaultWithdraw", featureSingleAssetVault}, - {"VaultClawback", featureSingleAssetVault}}; + {"VaultClawback", featureSingleAssetVault}, + {"SponsorshipTransfer", featureSponsor}, + {"SponsorshipSet", featureSponsor}, + }; // fixDelegateV1_1 post-amendment: can not delegate tx if any // required feature disabled. diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 08e8f08165e..f89e2893194 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -95,14 +95,11 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (maxFee.xrp().drops() <= 0) return temBAD_AMOUNT; - - // TODO: check maxFee > basefee } if (ctx.tx.isFieldPresent(sfReserveCount)) { auto const reserveCount = ctx.tx.getFieldU32(sfReserveCount); - // TODO: max reserveCount? if (reserveCount < 1) return temMALFORMED; } From d0bcca6bf1963b4056879d6bcaaa4a2aaa752db1 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 28 Oct 2025 17:18:38 +0900 Subject: [PATCH 067/249] Fix the behavior of co-sign + pre-fund --- include/xrpl/ledger/View.h | 17 +---- src/libxrpl/ledger/CredentialHelpers.cpp | 2 +- src/libxrpl/ledger/View.cpp | 44 ++++++++----- src/test/app/Sponsor_test.cpp | 65 +++++++++++++++++++ src/xrpld/app/tx/detail/CancelCheck.cpp | 2 +- src/xrpld/app/tx/detail/CashCheck.cpp | 2 +- src/xrpld/app/tx/detail/Change.cpp | 4 +- src/xrpld/app/tx/detail/Credentials.cpp | 2 +- src/xrpld/app/tx/detail/DID.cpp | 2 +- src/xrpld/app/tx/detail/DelegateSet.cpp | 2 +- src/xrpld/app/tx/detail/DeleteOracle.cpp | 2 +- src/xrpld/app/tx/detail/DepositPreauth.cpp | 2 +- src/xrpld/app/tx/detail/Escrow.cpp | 4 +- .../app/tx/detail/MPTokenIssuanceDestroy.cpp | 2 +- src/xrpld/app/tx/detail/NFTokenUtils.cpp | 12 ++-- src/xrpld/app/tx/detail/PayChan.cpp | 2 +- .../tx/detail/PermissionedDomainDelete.cpp | 2 +- src/xrpld/app/tx/detail/SetOracle.cpp | 2 +- src/xrpld/app/tx/detail/SetSignerList.cpp | 2 +- src/xrpld/app/tx/detail/SetTrust.cpp | 4 +- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 4 +- src/xrpld/app/tx/detail/Transactor.cpp | 34 ++++++---- src/xrpld/app/tx/detail/Transactor.h | 2 +- src/xrpld/app/tx/detail/VaultDelete.cpp | 4 +- src/xrpld/app/tx/detail/XChainBridge.cpp | 2 +- 25 files changed, 143 insertions(+), 79 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 8fa0d6ce665..8e9b0bd2b17 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -520,7 +520,6 @@ adjustOwnerCount( std::shared_ptr const& accountSle, std::optional> const& sponsorSle, std::int32_t amount, - bool const isSponsorCoSigning, beast::Journal j); inline void @@ -532,8 +531,7 @@ adjustOwnerCount( std::int32_t amount, beast::Journal j) { - auto const isCoSigning = isSponsorReserveCoSigning(tx); - adjustOwnerCount(view, accountSle, sponsorSle, amount, isCoSigning, j); + adjustOwnerCount(view, accountSle, sponsorSle, amount, j); } inline void @@ -555,19 +553,6 @@ adjustOwnerCount( j); } -inline void -reduceOwnerCount( - ApplyView& view, - std::shared_ptr const& accountSle, - std::optional> const& sponsorSle, - std::int32_t amount, - beast::Journal j) -{ - XRPL_ASSERT( - amount <= 0, "ripple::reduceOwnerCount : amount must be negative"); - adjustOwnerCount(view, accountSle, sponsorSle, amount, true, j); -} - /** @{ */ /** Returns the first entry in the directory, advancing the index diff --git a/src/libxrpl/ledger/CredentialHelpers.cpp b/src/libxrpl/ledger/CredentialHelpers.cpp index 5a14bd952a9..fd3092495c4 100644 --- a/src/libxrpl/ledger/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/CredentialHelpers.cpp @@ -99,7 +99,7 @@ deleteSLE( { auto const sponsor = getLedgerEntryReserveSponsor(view, sleCredential); - reduceOwnerCount(view, sleAccount, sponsor, -1, j); + adjustOwnerCount(view, sleAccount, sponsor, -1, j); } return tesSUCCESS; diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 586f1d3a94f..bbfef0a85c6 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1073,8 +1073,21 @@ checkInsufficientReserve( { auto const isCoSigning = isSponsorReserveCoSigning(tx); + auto const sle = view.read(keylet::sponsor( + (*sponsorSle)->getAccountID(sfAccount), + accSle->getAccountID(sfAccount))); + if (isCoSigning) { + if (sle) + { + auto const reserveCountAllowed = + sle->getFieldU32(sfReserveCount); + if (reserveCountAllowed < ownerCountDelta) + return tecINSUFFICIENT_RESERVE; + + return tesSUCCESS; + } auto const sponsorBalance = (*sponsorSle)->getFieldAmount(sfBalance); STAmount const sponsorReserve{view.fees().accountReserve( @@ -1092,17 +1105,12 @@ checkInsufficientReserve( else { // pre funded - auto const sle = view.read(keylet::sponsor( - (*sponsorSle)->getAccountID(sfAccount), - accSle->getAccountID(sfAccount))); if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE auto const reserveCountAllowed = sle->getFieldU32(sfReserveCount); if (reserveCountAllowed < ownerCountDelta) - { return tecINSUFFICIENT_RESERVE; - } } } else @@ -1225,7 +1233,6 @@ adjustOwnerCount( std::shared_ptr const& accountSle, std::optional> const& sponsorSle, std::int32_t amount, - bool const isSponsorCoSigning, beast::Journal j) { if (!accountSle) @@ -1264,13 +1271,14 @@ adjustOwnerCount( view.update(accountSle); } - if (!isSponsorCoSigning && amount > 0) + auto sle = view.peek(keylet::sponsor( + (*sponsorSle)->getAccountID(sfAccount), + accountSle->getAccountID(sfAccount))); + + if (sle && amount > 0) { // pre funded // modify sponsor's ReserveCount - auto sle = view.peek(keylet::sponsor( - (*sponsorSle)->getAccountID(sfAccount), - accountSle->getAccountID(sfAccount))); XRPL_ASSERT( sle, "ripple::adjustOwnerCount : co-signing sponsor not found"); @@ -1572,7 +1580,7 @@ authorizeMPToken( return tecINTERNAL; // LCOV_EXCL_LINE auto const sponsor = getLedgerEntryReserveSponsor(view, sleMpt); - reduceOwnerCount(view, sleAcct, sponsor, -1, journal); + adjustOwnerCount(view, sleAcct, sponsor, -1, journal); view.erase(sleMpt); return tesSUCCESS; @@ -1766,7 +1774,7 @@ trustCreate( sponsorSle = view.peek(keylet::account(*sponsorAccountID)); sleRippleState->setFieldU32(sfFlags, uFlags); - adjustOwnerCount(view, sleAccount, sponsorSle, 1, isSponsorCoSigning, j); + adjustOwnerCount(view, sleAccount, sponsorSle, 1, j); addSponsorToLedgerEntry( sleRippleState, @@ -1820,7 +1828,7 @@ removeEmptyHolding( if (!sleLowAccount) return tecINTERNAL; // LCOV_EXCL_LINE - reduceOwnerCount(view, sleLowAccount, std::nullopt, -1, journal); + adjustOwnerCount(view, sleLowAccount, std::nullopt, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. @@ -1835,7 +1843,7 @@ removeEmptyHolding( if (!sleHighAccount) return tecINTERNAL; // LCOV_EXCL_LINE - reduceOwnerCount(view, sleHighAccount, std::nullopt, -1, journal); + adjustOwnerCount(view, sleHighAccount, std::nullopt, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. @@ -1970,7 +1978,7 @@ offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) } auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - reduceOwnerCount(view, view.peek(keylet::account(owner)), sponsor, -1, j); + adjustOwnerCount(view, view.peek(keylet::account(owner)), sponsor, -1, j); view.erase(sle); @@ -2069,7 +2077,7 @@ rippleCreditIOU( view, sleRippleState, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); - reduceOwnerCount( + adjustOwnerCount( view, view.peek(keylet::account(uSenderID)), currentSponsor, @@ -2587,7 +2595,7 @@ updateTrustLine( view, state, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); - reduceOwnerCount(view, sle, currentSponsor, -1, j); + adjustOwnerCount(view, sle, currentSponsor, -1, j); // Clear reserve flag. state->setFieldU32( @@ -3196,7 +3204,7 @@ deleteAMMTrustLine( auto const sponsorSle = getLedgerEntryReserveSponsor( view, sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount); - reduceOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, -1, j); + adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, -1, j); removeSponsorFromLedgerEntry( sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 4ba271a412a..d57e5fccb66 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -26,6 +26,8 @@ #include #include +#include "test/jtx/txflags.h" + namespace ripple { namespace test { @@ -426,6 +428,67 @@ class Sponsor_test : public beast::unit_test::suite } } + void + testPreFundAndCosign() + { + testcase("PreFund and Cosign"); + using namespace test::jtx; + Account const alice("alice"); + Account const sponsor("sponsor"); + + { + // both pre-funded and co-signed,pre-funded value is used + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, sponsor); + env.close(); + + env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + env(ticket::create(alice, 1), + sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), + sig(sfSponsorSignature, sponsor), + fee(XRP(1)), + ter(tesSUCCESS)); + env.close(); + + auto const sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfReserveCount) == 99); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99)); + } + + { + // if pre-funded value is not enough, error + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, sponsor); + env.close(); + + env(sponsor::set(sponsor, 0, 10, XRP(10), XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + // fee insufficient + env(ticket::create(alice, 1), + sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), + sig(sfSponsorSignature, sponsor), + fee(XRP(11)), + ter(terINSUF_FEE_B)); + env.close(); + + // reserve insufficient + env(ticket::create(alice, 11), + sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), + sig(sfSponsorSignature, sponsor), + fee(XRP(1)), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } + } + void testTransferSponsor() { @@ -4260,6 +4323,8 @@ class Sponsor_test : public beast::unit_test::suite testSimpleSponsorshipSet(); + testPreFundAndCosign(); + testTransferSponsor(); testSponsorFee(); testSponsorAccount(); diff --git a/src/xrpld/app/tx/detail/CancelCheck.cpp b/src/xrpld/app/tx/detail/CancelCheck.cpp index da71577b052..b9bf0b6c8aa 100644 --- a/src/xrpld/app/tx/detail/CancelCheck.cpp +++ b/src/xrpld/app/tx/detail/CancelCheck.cpp @@ -114,7 +114,7 @@ CancelCheck::doApply() // If we succeeded, update the check owner's reserve. auto const sleSrc = view().peek(keylet::account(srcId)); auto const sponsor = getLedgerEntryReserveSponsor(view(), sleCheck); - reduceOwnerCount(view(), sleSrc, sponsor, -1, viewJ); + adjustOwnerCount(view(), sleSrc, sponsor, -1, viewJ); // Remove check from ledger. view().erase(sleCheck); diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index ab3fae7c18d..41dc133e2c8 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -517,7 +517,7 @@ CashCheck::doApply() // If we succeeded, update the check owner's reserve. auto const sponsorSle = getLedgerEntryReserveSponsor(psb, sleCheck); - reduceOwnerCount( + adjustOwnerCount( psb, psb.peek(keylet::account(srcId)), sponsorSle, -1, viewJ); // Remove check from ledger. diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index 606f0677d93..ecc54d77598 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -215,7 +215,7 @@ Change::activateTrustLinesToSelfFix() } if (tl->getFlags() & lsfLowReserve) - reduceOwnerCount( + adjustOwnerCount( sb, sb.peek(keylet::account(lo.getIssuer())), std::nullopt, @@ -223,7 +223,7 @@ Change::activateTrustLinesToSelfFix() j_); if (tl->getFlags() & lsfHighReserve) - reduceOwnerCount( + adjustOwnerCount( sb, sb.peek(keylet::account(hi.getIssuer())), std::nullopt, diff --git a/src/xrpld/app/tx/detail/Credentials.cpp b/src/xrpld/app/tx/detail/Credentials.cpp index c1bd6cff09f..85b85139523 100644 --- a/src/xrpld/app/tx/detail/Credentials.cpp +++ b/src/xrpld/app/tx/detail/Credentials.cpp @@ -366,7 +366,7 @@ CredentialAccept::doApply() sleCred->setFieldU32(sfFlags, lsfAccepted); view().update(sleCred); - reduceOwnerCount(view(), sleIssuer, currentSponsor, -1, j_); + adjustOwnerCount(view(), sleIssuer, currentSponsor, -1, j_); removeSponsorFromLedgerEntry(sleCred); adjustOwnerCount(view(), ctx_.tx, sleSubject, newSponsor, 1, j_); addSponsorToLedgerEntry(sleCred, newSponsor); diff --git a/src/xrpld/app/tx/detail/DID.cpp b/src/xrpld/app/tx/detail/DID.cpp index 4cabc8bd20b..9ead4035879 100644 --- a/src/xrpld/app/tx/detail/DID.cpp +++ b/src/xrpld/app/tx/detail/DID.cpp @@ -199,7 +199,7 @@ DIDDelete::deleteSLE( return tecINTERNAL; // LCOV_EXCL_LINE auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - reduceOwnerCount(view, sleOwner, sponsor, -1, j); + adjustOwnerCount(view, sleOwner, sponsor, -1, j); view.update(sleOwner); // Remove object from ledger diff --git a/src/xrpld/app/tx/detail/DelegateSet.cpp b/src/xrpld/app/tx/detail/DelegateSet.cpp index 5acfc2f184b..b79866238d5 100644 --- a/src/xrpld/app/tx/detail/DelegateSet.cpp +++ b/src/xrpld/app/tx/detail/DelegateSet.cpp @@ -165,7 +165,7 @@ DelegateSet::deleteDelegate( return tecINTERNAL; // LCOV_EXCL_LINE auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - reduceOwnerCount(view, sleOwner, sponsor, -1, j); + adjustOwnerCount(view, sleOwner, sponsor, -1, j); view.erase(sle); diff --git a/src/xrpld/app/tx/detail/DeleteOracle.cpp b/src/xrpld/app/tx/detail/DeleteOracle.cpp index 705b2ae6398..e84cc2c333f 100644 --- a/src/xrpld/app/tx/detail/DeleteOracle.cpp +++ b/src/xrpld/app/tx/detail/DeleteOracle.cpp @@ -83,7 +83,7 @@ DeleteOracle::deleteOracle( sle->getFieldArray(sfPriceDataSeries).size() > 5 ? -2 : -1; auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - reduceOwnerCount(view, sleOwner, sponsor, count, j); + adjustOwnerCount(view, sleOwner, sponsor, count, j); view.erase(sle); diff --git a/src/xrpld/app/tx/detail/DepositPreauth.cpp b/src/xrpld/app/tx/detail/DepositPreauth.cpp index 62fa073f163..be4c9b00f3f 100644 --- a/src/xrpld/app/tx/detail/DepositPreauth.cpp +++ b/src/xrpld/app/tx/detail/DepositPreauth.cpp @@ -309,7 +309,7 @@ DepositPreauth::removeFromLedger( return tefINTERNAL; // LCOV_EXCL_LINE auto const sponsor = getLedgerEntryReserveSponsor(view, slePreauth); - reduceOwnerCount(view, sleOwner, sponsor, -1, j); + adjustOwnerCount(view, sleOwner, sponsor, -1, j); // Remove DepositPreauth from ledger. view.erase(slePreauth); diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index fbeb8eca7ac..18fdbc13d88 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -1237,7 +1237,7 @@ EscrowFinish::doApply() // Adjust source owner count auto const sle = ctx_.view().peek(keylet::account(account)); auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep); - reduceOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); + adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); ctx_.view().update(sle); // Remove escrow from ledger @@ -1461,7 +1461,7 @@ EscrowCancel::doApply() } auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep); - reduceOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); + adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); ctx_.view().update(sle); // Remove escrow from ledger diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp index 097fbb185e6..af17d6c5f5e 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp @@ -75,7 +75,7 @@ MPTokenIssuanceDestroy::doApply() view().erase(mpt); auto const sponsor = getLedgerEntryReserveSponsor(view(), mpt); - reduceOwnerCount( + adjustOwnerCount( view(), view().peek(keylet::account(account_)), sponsor, -1, j_); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.cpp b/src/xrpld/app/tx/detail/NFTokenUtils.cpp index ee2c0e9f6b9..7b54d54eeaa 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.cpp +++ b/src/xrpld/app/tx/detail/NFTokenUtils.cpp @@ -495,7 +495,7 @@ removeToken( if (prev && mergePages(view, prev, curr)) { auto const sponsor = getLedgerEntryReserveSponsor(view, prev); - reduceOwnerCount( + adjustOwnerCount( view, view.peek(keylet::account(owner)), sponsor, @@ -506,7 +506,7 @@ removeToken( if (next && mergePages(view, curr, next)) { auto const sponsor = getLedgerEntryReserveSponsor(view, curr); - reduceOwnerCount( + adjustOwnerCount( view, view.peek(keylet::account(owner)), sponsor, @@ -547,7 +547,7 @@ removeToken( } auto const sponsor = getLedgerEntryReserveSponsor(view, prev); - reduceOwnerCount( + adjustOwnerCount( view, view.peek(keylet::account(owner)), sponsor, @@ -581,7 +581,7 @@ removeToken( } auto const sponsor = getLedgerEntryReserveSponsor(view, curr); - reduceOwnerCount( + adjustOwnerCount( view, view.peek(keylet::account(owner)), getLedgerEntryReserveSponsor(view, curr), @@ -604,7 +604,7 @@ removeToken( view.peek(Keylet(ltNFTOKEN_PAGE, prev->key())), view.peek(Keylet(ltNFTOKEN_PAGE, next->key())))) { - reduceOwnerCount( + adjustOwnerCount( view, view.peek(keylet::account(owner)), getLedgerEntryReserveSponsor(view, prev), @@ -763,7 +763,7 @@ deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer) return false; auto const sponsor = getLedgerEntryReserveSponsor(view, offer); - reduceOwnerCount( + adjustOwnerCount( view, view.peek(keylet::account(owner)), sponsor, diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index 2bbf118e524..4b466b70d16 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -160,7 +160,7 @@ closeChannel( (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; auto const sponsor = getLedgerEntryReserveSponsor(view, slep); - reduceOwnerCount(view, sle, sponsor, -1, j); + adjustOwnerCount(view, sle, sponsor, -1, j); view.update(sle); // Remove PayChan from ledger diff --git a/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp b/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp index ee7e6068629..8fc904caead 100644 --- a/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp +++ b/src/xrpld/app/tx/detail/PermissionedDomainDelete.cpp @@ -78,7 +78,7 @@ PermissionedDomainDelete::doApply() ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0, "ripple::PermissionedDomainDelete::doApply : nonzero owner count"); auto const sponsor = getLedgerEntryReserveSponsor(view(), slePd); - reduceOwnerCount(view(), ownerSle, sponsor, -1, ctx_.journal); + adjustOwnerCount(view(), ownerSle, sponsor, -1, ctx_.journal); view().erase(slePd); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index f3264891089..cd4cdb331f2 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -200,7 +200,7 @@ adjustOwnerCount( adjustOwnerCount( ctx.view(), ctx.tx, sleAccount, sponsor, count, ctx.journal); else - reduceOwnerCount( + adjustOwnerCount( ctx.view(), sleAccount, sponsor, count, ctx.journal); return true; } diff --git a/src/xrpld/app/tx/detail/SetSignerList.cpp b/src/xrpld/app/tx/detail/SetSignerList.cpp index 2150a40a5b1..47cc6f8a0f1 100644 --- a/src/xrpld/app/tx/detail/SetSignerList.cpp +++ b/src/xrpld/app/tx/detail/SetSignerList.cpp @@ -233,7 +233,7 @@ removeSignersFromLedger( } auto const sponsor = getLedgerEntryReserveSponsor(view, signers); - reduceOwnerCount( + adjustOwnerCount( view, view.peek(accountKeylet), sponsor, diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 3fa8637dc35..ced0db8a2ba 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -670,7 +670,7 @@ SetTrust::doApply() if (bLowReserveClear && bLowReserved) { // Clear reserve for low account. - reduceOwnerCount( + adjustOwnerCount( view(), sleLowAccount, currentLowSponsor, -1, viewJ); uFlagsOut &= ~lsfLowReserve; @@ -705,7 +705,7 @@ SetTrust::doApply() if (bHighReserveClear && bHighReserved) { // Clear reserve for high account. - reduceOwnerCount( + adjustOwnerCount( view(), sleHighAccount, currentHighSponsor, -1, viewJ); uFlagsOut &= ~lsfHighReserve; diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index f89e2893194..8c507ead81b 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -217,7 +217,7 @@ SponsorshipSet::doApply() auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), sponsorObjSle); - reduceOwnerCount(ctx_.view(), sponsorAccSle, sponsor, -1, ctx_.journal); + adjustOwnerCount(ctx_.view(), sponsorAccSle, sponsor, -1, ctx_.journal); ctx_.view().dirRemove( keylet::ownerDir(sponsorAcc), @@ -370,7 +370,7 @@ SponsorshipSet::deleteSponsorship( (*sponsorAccSle)[sfBalance] += feeAmount; auto const reserveSponsor = getLedgerEntryReserveSponsor(view, sle); - reduceOwnerCount(view, sponsorAccSle, reserveSponsor, -1, j); + adjustOwnerCount(view, sponsorAccSle, reserveSponsor, -1, j); view.update(sponsorAccSle); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index d4c824b5425..469e708170e 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -444,7 +444,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) std::optional availableBalance; - auto const result = getFeePayer(ctx.tx); + auto const result = getFeePayer(ctx.view, ctx.tx); if (result.type == FeePayerType::SponsorPreFunded) { @@ -511,7 +511,7 @@ Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); - auto const result = getFeePayer(ctx_.tx); + auto const result = getFeePayer(view(), ctx_.tx); auto const sle = view().peek(result.keylet); @@ -711,7 +711,7 @@ Transactor::ticketDelete( // Update the Ticket owner's reserve. auto const sponsor = getLedgerEntryReserveSponsor(view, sleTicket); - reduceOwnerCount(view, sleAccount, sponsor, -1, j); + adjustOwnerCount(view, sleAccount, sponsor, -1, j); // Remove Ticket from ledger. view.erase(sleTicket); @@ -1226,7 +1226,7 @@ Transactor::reset(XRPAmount fee) if (!txnAcct) return {tefINTERNAL, beast::zero}; - auto const result = getFeePayer(ctx_.tx); + auto const result = getFeePayer(view(), ctx_.tx); auto const payerSle = view().peek(result.keylet); @@ -1269,25 +1269,31 @@ Transactor::reset(XRPAmount fee) } FeePayer -Transactor::getFeePayer(STTx const& tx) +Transactor::getFeePayer(ReadView const& view, STTx const& tx) { if (tx.isFieldPresent(sfSponsor) && tx.getFieldObject(sfSponsor).isFlag(tfSponsorFee)) { auto const sponsor = tx.getFieldObject(sfSponsor); auto const hasSignature = tx.isFieldPresent(sfSponsorSignature); + auto const keylet = keylet::sponsor( + sponsor.getAccountID(sfAccount), tx.getAccountID(sfAccount)); - if (!hasSignature) + if (hasSignature) { - // pre funded - auto const keylet = keylet::sponsor( - sponsor.getAccountID(sfAccount), tx.getAccountID(sfAccount)); - return FeePayer{ - keylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; + // if pre-funded sponsorship exists, prefer it + if (view.exists(keylet)) + return FeePayer{ + keylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; + + // co-signed + auto const keylet = + keylet::account(sponsor.getAccountID(sfAccount)); + return FeePayer{keylet, sfBalance, FeePayerType::SponsorCoSigned}; } - // co-signed - auto const keylet = keylet::account(sponsor.getAccountID(sfAccount)); - return FeePayer{keylet, sfBalance, FeePayerType::SponsorCoSigned}; + + // pre funded + return FeePayer{keylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; } if (tx.isFieldPresent(sfDelegate)) diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index 04ef354a6eb..03857cb116c 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -337,7 +337,7 @@ class Transactor reset(XRPAmount fee); static FeePayer - getFeePayer(STTx const& tx); + getFeePayer(ReadView const& view, STTx const& tx); TER consumeSeqProxy(SLE::pointer const& sleAccount); diff --git a/src/xrpld/app/tx/detail/VaultDelete.cpp b/src/xrpld/app/tx/detail/VaultDelete.cpp index fdacc185f83..656c3710654 100644 --- a/src/xrpld/app/tx/detail/VaultDelete.cpp +++ b/src/xrpld/app/tx/detail/VaultDelete.cpp @@ -157,7 +157,7 @@ VaultDelete::doApply() return tefBAD_LEDGER; // LCOV_EXCL_STOP } - reduceOwnerCount(view(), pseudoAcct, std::nullopt, -1, j_); + adjustOwnerCount(view(), pseudoAcct, std::nullopt, -1, j_); view().erase(mpt); @@ -191,7 +191,7 @@ VaultDelete::doApply() // LCOV_EXCL_STOP } auto const vaultSponsor = getLedgerEntryReserveSponsor(view(), vault); - reduceOwnerCount(view(), owner, vaultSponsor, -1, j_); + adjustOwnerCount(view(), owner, vaultSponsor, -1, j_); // Destroy the vault. view().erase(vault); diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index f4106c260be..787e75c74d2 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -769,7 +769,7 @@ finalizeClaimHelper( auto const sponsor = getLedgerEntryReserveSponsor(outerSb, sleClaimID); - reduceOwnerCount(outerSb, sleOwner, sponsor, -1, j); + adjustOwnerCount(outerSb, sleOwner, sponsor, -1, j); } } From 965ecd3f27dfe1b1be4c697a6cf5619dedb48983 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 28 Oct 2025 21:07:20 +0900 Subject: [PATCH 068/249] Add sponsored outer batch --- src/test/app/Sponsor_test.cpp | 79 +++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index d57e5fccb66..df798a8c090 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -4312,6 +4312,84 @@ class Sponsor_test : public beast::unit_test::suite } } + void + testBatch() + { + testcase("Batch"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + // + // Outer transaction + // + { + // test outer transaction with co-signing sponsor + Env env{*this, testable_amendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::inner(noop(alice), seq + 1), + batch::inner(ticket::create(alice, 1), seq + 2), + sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), + sig(sfSponsorSignature, sponsor), + ter(tesSUCCESS)); + env.close(); + + // does not affect reserve + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // fee is paid by sponsor + BEAST_EXPECT(env.balance(alice) == XRP(1000)); + BEAST_EXPECT(env.balance(sponsor) == XRP(1000 - 1)); + } + { + // test outer transaction with prefunded sponsor + Env env{*this, testable_amendments()}; + env.fund(XRP(1000), alice, bob); + env.fund(XRP(1001), sponsor); + env.close(); + + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tesSUCCESS)); + env.close(); + + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::inner(noop(alice), seq + 1), + batch::inner(ticket::create(alice, 1), seq + 2), + sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), + ter(tesSUCCESS)); + env.close(); + + // does not affect reserve + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // fee is paid by sponsor object + BEAST_EXPECT(env.balance(alice) == XRP(1000)); + BEAST_EXPECT(env.balance(sponsor) == XRP(900)); + + auto const sponsorshipSle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sponsorshipSle); + BEAST_EXPECT(sponsorshipSle->at(sfFeeAmount) == XRP(100 - 1)); + BEAST_EXPECT(sponsorshipSle->at(sfReserveCount) == 100); + } + + // + // Inner transaction + // + // TEQU: TODO + } + void run() override { @@ -4334,6 +4412,7 @@ class Sponsor_test : public beast::unit_test::suite testAccountDelete(); testDelegatePermission(); + testBatch(); } }; From b4188a887a2d9ab715838a55a8ab207c173f77e2 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 6 Nov 2025 17:53:07 +0900 Subject: [PATCH 069/249] address reviews --- include/xrpl/protocol/TxFlags.h | 2 +- include/xrpl/protocol/detail/features.macro | 2 +- src/libxrpl/protocol/Indexes.cpp | 2 +- src/xrpld/app/tx/detail/Transactor.cpp | 9 ++++----- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 23f1758f7f9..6d1d52d340c 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -65,7 +65,7 @@ constexpr std::uint32_t tfUniversalMask = ~tfUniversal; // Sponsor flags (Global): constexpr std::uint32_t tfSponsorFee = 0x00000001; constexpr std::uint32_t tfSponsorReserve = 0x00000002; -constexpr std::uint32_t tfSponsorMask = tfSponsorFee | tfSponsorReserve; +constexpr std::uint32_t tfSponsorMask = tfSponsorFee | tfSponsorReserve; // AccountSet flags: constexpr std::uint32_t tfRequireDestTag = 0x00010000; diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 94209aae8d9..97d64015a7f 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -30,7 +30,7 @@ // Add new amendments to the top of this list. // Keep it sorted in reverse chronological order. -XRPL_FEATURE(Sponsor, Supported::yes, VoteBehavior::DefaultNo) +XRPL_FEATURE(Sponsor, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (DirectoryLimit, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (IncludeKeyletFields, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo) diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 969b737c0c5..7b9a02a6486 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -96,7 +96,7 @@ enum class LedgerNameSpace : std::uint16_t { PERMISSIONED_DOMAIN = 'm', DELEGATE = 'E', VAULT = 'V', - SPONSORSHIP = 'N', + SPONSORSHIP = '>', // No longer used or supported. Left here to reserve the space // to avoid accidental reuse. diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 468c2f79f97..241b2db397f 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -317,15 +317,14 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) if (!tx.isFieldPresent(sfSponsor)) return tesSUCCESS; - auto const txSponsor = tx.getFieldObject(sfSponsor); - - auto const sponsorAcc = txSponsor.getAccountID(sfAccount); - auto const sponseeAcc = tx.getAccountID(sfAccount); - auto const hasSponsorSignature = tx.isFieldPresent(sfSponsorSignature); if (!hasSponsorSignature) { + auto const txSponsor = tx.getFieldObject(sfSponsor); + + auto const sponsorAcc = txSponsor.getAccountID(sfAccount); + auto const sponseeAcc = tx.getAccountID(sfAccount); auto const sponsorSle = view.read(keylet::sponsor(sponsorAcc, sponseeAcc)); From 13145f6db29f892e99eb8733832aaf223d914f20 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 6 Nov 2025 18:34:24 +0900 Subject: [PATCH 070/249] fix header name --- src/xrpld/app/tx/detail/SponsorshipSet.h | 4 ++-- src/xrpld/app/tx/detail/SponsorshipTransfer.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.h b/src/xrpld/app/tx/detail/SponsorshipSet.h index 68940426bc2..2dab830b89f 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.h +++ b/src/xrpld/app/tx/detail/SponsorshipSet.h @@ -17,8 +17,8 @@ */ //============================================================================== -#ifndef RIPPLE_TX_SPONSORSHIPSET_H_INCLUDED -#define RIPPLE_TX_SPONSORSHIPSET_H_INCLUDED +#ifndef XRPL_TX_SPONSORSHIPSET_H_INCLUDED +#define XRPL_TX_SPONSORSHIPSET_H_INCLUDED #include diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.h b/src/xrpld/app/tx/detail/SponsorshipTransfer.h index 452c4b06b2c..05921b19b3b 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.h +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.h @@ -17,8 +17,8 @@ */ //============================================================================== -#ifndef RIPPLE_TX_SponsorshipTransfer_H_INCLUDED -#define RIPPLE_TX_SponsorshipTransfer_H_INCLUDED +#ifndef XRPL_TX_SPONSORSHIPTRANSFER_H_INCLUDED +#define XRPL_TX_SPONSORSHIPTRANSFER_H_INCLUDED #include From 297083e27b144e5e6718e79c146df9f48ad5a33b Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 6 Nov 2025 18:36:17 +0900 Subject: [PATCH 071/249] fix: specify type for adjust variable in SetOracle transaction processing --- src/xrpld/app/tx/detail/SetOracle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index 5cb34088f70..92633f607f0 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -272,7 +272,7 @@ SetOracle::doApply() } auto const newCount = calculateOracleReserve(pairs.size()); - auto const adjust = newCount - oldCount; + int32_t const adjust = newCount - oldCount; if (adjust > 0) { From cd62f7f4cf5b4e3e583d3d87e935c54140aca8c6 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 6 Nov 2025 21:08:51 +0900 Subject: [PATCH 072/249] remove copyright --- include/xrpl/protocol/Sponsor.h | 17 ----------------- src/test/app/Sponsor_test.cpp | 19 ------------------- src/test/jtx/impl/sponsor.cpp | 19 ------------------- src/test/jtx/sponsor.h | 19 ------------------- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 19 ------------------- src/xrpld/app/tx/detail/SponsorshipSet.h | 19 ------------------- .../app/tx/detail/SponsorshipTransfer.cpp | 19 ------------------- src/xrpld/app/tx/detail/SponsorshipTransfer.h | 19 ------------------- 8 files changed, 150 deletions(-) diff --git a/include/xrpl/protocol/Sponsor.h b/include/xrpl/protocol/Sponsor.h index 0533ed496ad..f729b6b0957 100644 --- a/include/xrpl/protocol/Sponsor.h +++ b/include/xrpl/protocol/Sponsor.h @@ -1,20 +1,3 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - #include #include #include diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 6b270738b6a..ab8e17eebc1 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1,22 +1,3 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - #include #include #include diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index e5729130c34..94cf074ec3c 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -1,22 +1,3 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - #include #include diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index 64e09a96c17..e9993e2a2f0 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -1,22 +1,3 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - #pragma once #include diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 37df1e5d0f8..c2d19276d86 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -1,22 +1,3 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - #include #include diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.h b/src/xrpld/app/tx/detail/SponsorshipSet.h index 2dab830b89f..a444c894cfe 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.h +++ b/src/xrpld/app/tx/detail/SponsorshipSet.h @@ -1,22 +1,3 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - #ifndef XRPL_TX_SPONSORSHIPSET_H_INCLUDED #define XRPL_TX_SPONSORSHIPSET_H_INCLUDED diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index a7f3ccf1743..9486e25de6a 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -1,22 +1,3 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - #include #include diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.h b/src/xrpld/app/tx/detail/SponsorshipTransfer.h index 05921b19b3b..6d818c02c74 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.h +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.h @@ -1,22 +1,3 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - #ifndef XRPL_TX_SPONSORSHIPTRANSFER_H_INCLUDED #define XRPL_TX_SPONSORSHIPTRANSFER_H_INCLUDED From e03c7a9c9615e35e7023186ccade107c727ac3b2 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 10 Nov 2025 00:40:15 +0900 Subject: [PATCH 073/249] add sponsor transfer test --- src/test/app/Sponsor_test.cpp | 156 +++++++++++++++++- .../app/tx/detail/SponsorshipTransfer.cpp | 8 +- 2 files changed, 153 insertions(+), 11 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index ab8e17eebc1..33e99806ab2 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -7,8 +7,6 @@ #include #include -#include "test/jtx/txflags.h" - namespace ripple { namespace test { @@ -480,9 +478,10 @@ class Sponsor_test : public beast::unit_test::suite // sponsor account Env env{*this, testable_amendments()}; Account const alice("alice"); + Account const bob("bob"); Account const sponsor1("sponsor1"); Account const sponsor2("sponsor2"); - env.fund(XRP(10000), alice); + env.fund(XRP(10000), alice, bob); env.fund(env.current()->fees().reserve * 2 - 1, sponsor1, sponsor2); env.close(); @@ -552,6 +551,10 @@ class Sponsor_test : public beast::unit_test::suite env(pay(sponsor2, alice, XRP(1))); env.close(); + // not sponsored + env(sponsor::transfer(bob), ter(tecNO_PERMISSION)); + env.close(); + env(sponsor::transfer(alice)); env.close(); @@ -680,6 +683,42 @@ class Sponsor_test : public beast::unit_test::suite auto const sle3 = env.le(keylet::unchecked(checkId)); BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); } + + { + // invalid transfer + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // create owner dir + env(ticket::create(alice, 1)); + env.close(); + + // AccountRoot + // Amendments + // LedgerHashes + // FeeSettings + // NegativeUNL + // DirNode + auto const keylets = { + keylet::account(alice), + // keylet::amendments(), + keylet::skip(), + keylet::fees(), + // keylet::negativeUNL(), + keylet::ownerDir(alice), + }; + for (auto const& keylet : keylets) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor), + ter(tecNO_PERMISSION)); + } + } } void @@ -1296,6 +1335,27 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::amm(USD.issue(), EUR.issue())) ->isFieldPresent(sfSponsorAccount)); }); + + auto const ammKeylet = keylet::amm(USD.issue(), EUR.issue()); + if (cosigning) + { + env(sponsor::transfer(alice, ammKeylet.key), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor), + ter(tecNO_PERMISSION)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 1), + sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, 0, 1), + sponsor::sponseeAcc(alice)); + env(sponsor::transfer(alice, ammKeylet.key), + sponsor::as(sponsor, tfSponsorReserve), + ter(tecNO_PERMISSION)); + env.close(); + } } { // AMMDeposit @@ -2045,6 +2105,25 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + // transfer accepted credential + if (cosigning) + { + env(sponsor::transfer(subject, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(subject)); + env.close(); + + env(sponsor::transfer(subject, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } + // CredentialsDelete env(credentials::deleteCred(subject, subject, issuer, credType)); env.close(); @@ -2654,7 +2733,7 @@ class Sponsor_test : public beast::unit_test::suite { Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor); + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); // NFTokenMint @@ -2672,6 +2751,24 @@ class Sponsor_test : public beast::unit_test::suite submit(token::mint(alice)); }); + // transfer sponsor + auto const keylet = keylet::nftpage_max(alice); + if (cosigning) + { + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + } // NFTokenBurn env(token::burn(alice, nftId)); env.close(); @@ -2679,6 +2776,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); // NFTokenMintOffer adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); @@ -3466,7 +3564,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); // SignerListSet @@ -3485,6 +3583,20 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor if (cosigning) { + // invalid signer list owner 1 + // account doesn't have signer list but specified signer list exists + env(sponsor::transfer(bob, keylet::signers(alice).key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2), + ter(tecNO_PERMISSION)); + // invalid signer list owner 2 + // account has signer list and specified signer list exists + env(signers(bob, 1, {{alice, 1}})); + env.close(); + env(sponsor::transfer(alice, keylet::signers(bob).key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2), + ter(tecNO_PERMISSION)); env(sponsor::transfer(alice, keylet::signers(alice).key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); @@ -3522,6 +3634,7 @@ class Sponsor_test : public beast::unit_test::suite using namespace test::jtx; Account const alice("alice"); Account const bob("bob"); + Account const charlie("charlie"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); @@ -3547,7 +3660,7 @@ class Sponsor_test : public beast::unit_test::suite for (bool isIssuerHigh : {false, true}) { Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.fund(XRP(1000000), alice, bob, charlie, sponsor, sponsor2); env.close(); auto const& issuer = isIssuerHigh ? highAcc : lowAcc; @@ -3577,6 +3690,35 @@ class Sponsor_test : public beast::unit_test::suite submit(trust(user, USD(100))); }); + auto const keylet = keylet::line(user, issuer, currency); + + if (cosigning) + { + // invalid owner + env(sponsor::transfer(charlie, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2), + ter(tecNO_PERMISSION)); + // invalid reserve owner + env(sponsor::transfer(issuer, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2), + ter(tecNO_PERMISSION)); + env(sponsor::transfer(user, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(user)); + env.close(); + env(sponsor::transfer(user, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } + // delete TrustLine env(trust(user, USD(0))); env.close(); @@ -3585,7 +3727,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(!env.le(keylet::line(user, issuer, currency))); + BEAST_EXPECT(!env.le(keylet)); } // update diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 9486e25de6a..d1f9fddb965 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -51,8 +51,7 @@ getLedgerEntryOwner( auto const signerList = view.read(keylet::signers(account)); if (!signerList) return std::nullopt; - if (signerList->getFieldH256(sfLedgerIndex) == - sle->getFieldH256(sfLedgerIndex)) + if (signerList->key() == sle->key()) return account; return std::nullopt; } @@ -155,7 +154,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) auto const accSle = ctx.view.read(keylet::account(ctx.tx[sfAccount])); if (!accSle) - return tecINTERNAL; + return tecINTERNAL; // LCOV_EXCL_LINE if (isObjectSponsor) { @@ -210,7 +209,8 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) // check not same account if ((*newSponsor)->getAccountID(sfAccount) == accSle->getAccountID(sfAccount)) - return tecNO_PERMISSION; + // already checked in Transactor::preflight1() + return tecINTERNAL; // LCOV_EXCL_LINE } } else From fbf403aaa45e3b74ca1868a8f4a712730dc3c386 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 10 Nov 2025 00:55:39 +0900 Subject: [PATCH 074/249] add tests for sponsor field --- src/test/app/Sponsor_test.cpp | 37 ++++++++++++++++++++++++++ src/xrpld/app/tx/detail/Transactor.cpp | 6 ++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 33e99806ab2..5640f8c0ce2 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -276,6 +276,41 @@ class Sponsor_test : public beast::unit_test::suite ter(tesSUCCESS)); } + void + testInvalidSponsorField() + { + testcase("Invalid Sponsor Field"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const noFunded("noFunded"); + env.fund(XRP(10000), alice, sponsor); + env.close(); + + // Invalid Sponsor Account (Account = Sponsor.Account) + env(noop(alice), sponsor::as(alice), ter(temMALFORMED)); + + // Invalid Sponsor Account + // (SponsorSignature is specified but Sponsor.Account is not specified) + env(noop(alice), sig(sfSponsorSignature, sponsor), ter(temMALFORMED)); + + // Invalid Sponsor Account (Sponsor.Account doesn't exist) + env(noop(alice), + sponsor::as(noFunded, tfSponsorReserve), + ter(terNO_SPONSORSHIP)); + env(noop(alice), + sponsor::as(noFunded, tfSponsorReserve), + sig(sfSponsorSignature, noFunded), + ter(terNO_ACCOUNT)); + + // Invalid Flags + env(noop(alice), sponsor::as(sponsor, 4), ter(temINVALID_FLAG)); + env(noop(alice), + sponsor::as(sponsor, ~tfSponsorMask), + ter(temINVALID_FLAG)); + } + void testSimpleSponsorshipSet() { @@ -4513,6 +4548,8 @@ class Sponsor_test : public beast::unit_test::suite testSingleSigning(); testMultiSigning(); + testInvalidSponsorField(); + testSimpleSponsorshipSet(); testPreFundAndCosign(); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 64fb2d49af7..5f95e6afc5d 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -211,13 +211,13 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) auto const sponsor = ctx.tx.getFieldObject(sfSponsor); if (sponsor[sfAccount] == ctx.tx[sfAccount]) { - JLOG(ctx.j.fatal()) << "preflight1: invalid sponsor account"; + JLOG(ctx.j.debug()) << "preflight1: invalid sponsor account"; return temMALFORMED; } if (!(sponsor.getFlags() & tfSponsorMask)) { - JLOG(ctx.j.fatal()) << "preflight1: invalid sponsor flags"; - return temMALFORMED; + JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; + return temINVALID_FLAG; } } From a49cb91c732becb25b5c5f2aec4ce9634c9d4f4c Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 10 Nov 2025 12:11:42 +0900 Subject: [PATCH 075/249] add tests --- src/test/app/Sponsor_test.cpp | 116 +++++++++++++++--- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 8 +- .../app/tx/detail/SponsorshipTransfer.cpp | 5 +- 3 files changed, 107 insertions(+), 22 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 5640f8c0ce2..e293cafad94 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -138,6 +138,13 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sponseeAcc(alice), ter(temBAD_AMOUNT)); } + // Invalid MaxFee + for (auto amt : {XRP(-1), XRP(0), USD(1)}) + { + env(sponsor::set_fee(sponsor, 0, XRP(1), amt), + sponsor::sponseeAcc(alice), + ter(temBAD_AMOUNT)); + } // Invalid reserveCount env(sponsor::set_reserve(sponsor, 0, 0), @@ -153,6 +160,14 @@ class Sponsor_test : public beast::unit_test::suite ter(temMALFORMED)); // TODO: test MaxFee with tfDeleteObject + // Invalid SponsorAccount with non-Delete operation + env(sponsor::set_reserve(sponsor, 0, 100), + sponsor::sponsorAcc(alice), + ter(temMALFORMED)); + env(sponsor::set_fee(sponsor, 0, XRP(1), XRP(1)), + sponsor::sponsorAcc(alice), + ter(temMALFORMED)); + // // preclaim // @@ -162,14 +177,21 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sponseeAcc(noFunded), ter(tecNO_DST)); - // Invalid Delete operation (not found) + // Invalid Delete operation (sponsorship not found) env(sponsor::set(sponsor, tfDeleteObject), sponsor::sponseeAcc(alice), ter(tecNO_ENTRY)); // DisallowIncomingSponsor: tested in other testcase + // insufficent reserve to create sponsorship + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tecUNFUNDED)); + // create sponsor to use above tests + adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); @@ -612,14 +634,12 @@ class Sponsor_test : public beast::unit_test::suite Account const bob("bob"); Account const sponsor1("sponsor1"); Account const sponsor2("sponsor2"); - env.fund(XRP(10000), alice, bob); - env.fund( - env.current()->fees().reserve + - env.current()->fees().increment - drops(1), - sponsor1, - sponsor2); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); env.close(); + adjustAccountXRPBalance(env, sponsor1, reserve(env, 1) - drops(1)); + adjustAccountXRPBalance(env, sponsor2, reserve(env, 1) - drops(1)); + auto const seq = env.seq(alice); env(check::create(alice, bob, XRP(1))); env.close(); @@ -636,6 +656,13 @@ class Sponsor_test : public beast::unit_test::suite env(pay(alice, sponsor1, drops(1))); env.close(); + // Invalid ObjectID (not found) + env(sponsor::transfer(alice, keylet::check(alice, 0).key), + sponsor::as(sponsor1, tfSponsorReserve), + sig(sfSponsorSignature, sponsor1), + ter(tecNO_ENTRY)); + env.close(); + // Invalid Owner env(sponsor::transfer(bob, checkId), sponsor::as(sponsor1, tfSponsorReserve), @@ -688,21 +715,27 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); // dissolve sponsor - env(pay(alice, - sponsor2, - (env.balance(alice).value() - - env.current()->fees().reserve - - env.current()->fees().increment - XRP(1) + drops(1))), - fee(XRP(1))); - env.close(); + adjustAccountXRPBalance(env, alice, reserve(env, 1) - drops(1)); env(sponsor::transfer(alice, checkId), ter(tecINSUFFICIENT_RESERVE)); env.close(); - env(pay(sponsor2, alice, XRP(1))); + adjustAccountXRPBalance(env, alice, reserve(env, 1)); + + // object doesn't sponsored + auto const ticketSeq = env.seq(alice); + env(ticket::create(alice, 1)); + env.close(); + auto ticketId = keylet::ticket(alice, ticketSeq + 1).key; + BEAST_EXPECT(env.le(keylet::unchecked(ticketId))); + env(sponsor::transfer(alice, ticketId), ter(tecNO_PERMISSION)); + env.close(); + env(noop(alice), ticket::use(ticketSeq + 1)); env.close(); + adjustAccountXRPBalance(env, alice, reserve(env, 1)); + env(sponsor::transfer(alice, checkId)); env.close(); @@ -4351,6 +4384,59 @@ class Sponsor_test : public beast::unit_test::suite Account const bob("bob"); Account const carol("carol"); + // + // SponsorshipTransfer + // + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, carol); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const keylet = keylet::check(alice, seq); + + env(sponsor::transfer(alice, keylet.key), + sponsor::as(bob, tfSponsorReserve), + sig(sfSponsorSignature, bob), + delegate::as(carol), + ter(terNO_DELEGATE_PERMISSION)); + + env(delegate::set(alice, carol, {"SponsorshipTransfer"})); + env.close(); + + env(sponsor::transfer(alice, keylet.key), + sponsor::as(bob, tfSponsorReserve), + sig(sfSponsorSignature, bob), + delegate::as(carol), + ter(tesSUCCESS)); + env.close(); + } + // + // SponsorshipSet + // + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, carol); + env.close(); + + env(sponsor::set(alice, 0, 100, XRP(100)), + sponsor::sponseeAcc(bob), + delegate::as(carol), + ter(terNO_DELEGATE_PERMISSION)); + + env(delegate::set(alice, carol, {"SponsorshipSet"})); + env.close(); + + env(sponsor::set(alice, 0, 100, XRP(100)), + sponsor::sponseeAcc(bob), + delegate::as(carol), + ter(tesSUCCESS)); + env.close(); + } + // // Permission SponsorFee // diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index c2d19276d86..5aef0cb8997 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -43,10 +43,6 @@ SponsorshipSet::preflight(PreflightContext const& ctx) !ctx.tx.isFieldPresent(sfSponsee))) return temMALFORMED; - // if (ctx.tx.isFieldPresent(sfSponsorAccount) && - // !ctx.tx.isFlag(tfDeleteObject)) - // return temMALFORMED; - auto const sponsor = ctx.tx.isFieldPresent(sfSponsorAccount) ? ctx.tx.getAccountID(sfSponsorAccount) : ctx.tx.getAccountID(sfAccount); @@ -186,7 +182,7 @@ SponsorshipSet::doApply() auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorAcc)); if (!sponsorAccSle) - return tecINTERNAL; + return tecINTERNAL; // LCOV_EXCL_LINE auto const sponsorObjSle = ctx_.view().peek(keylet); @@ -345,7 +341,7 @@ SponsorshipSet::deleteSponsorship( // adjust balance auto const sponsorAccSle = view.peek(keylet::account(sponsor)); if (!sponsorAccSle) - return tecINTERNAL; + return tecINTERNAL; // LCOV_EXCL_LINE auto const feeAmount = sle->getFieldAmount(sfFeeAmount); (*sponsorAccSle)[sfBalance] += feeAmount; diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index d1f9fddb965..a379c575c00 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -134,10 +134,12 @@ getLedgerEntrySponsorField(T const& sle, AccountID const& owner) if (lowAccount == owner) return sfLowSponsorAccount; } + // LCOV_EXCL_START XRPL_ASSERT( false, "Should not happen. Owner should be checked before calling " "this function."); + // LCOV_EXCL_END } default: return sfSponsorAccount; @@ -178,7 +180,8 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) // transfer sponsor // check if the object owner isn't the same as the new sponsor if ((*newSponsor)->getAccountID(sfAccount) == owner) - return tecNO_PERMISSION; + // checked in above + return tecINTERNAL; // LCOV_EXCL_LINE } } else From 3f9673510a10be4acef092e5df6366c610f5224b Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 10 Nov 2025 21:50:31 +0900 Subject: [PATCH 076/249] fix comment for LCOV --- src/xrpld/app/tx/detail/SponsorshipTransfer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index a379c575c00..6b59f3860a1 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -139,7 +139,7 @@ getLedgerEntrySponsorField(T const& sle, AccountID const& owner) false, "Should not happen. Owner should be checked before calling " "this function."); - // LCOV_EXCL_END + // LCOV_EXCL_STOP } default: return sfSponsorAccount; From af9d91bc72fb4881c40e411349508acd5b3a486a Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 10 Nov 2025 22:22:32 +0900 Subject: [PATCH 077/249] add tests fro SponsorshipTransfer --- src/test/app/Sponsor_test.cpp | 75 +++++++++++++------ .../app/tx/detail/SponsorshipTransfer.cpp | 41 ++++++---- 2 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e293cafad94..e464ff2c70d 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -10,6 +10,12 @@ namespace ripple { namespace test { +static STAmount +accountReserve(jtx::Env& env, std::uint32_t count = 1) +{ + return env.current()->fees().reserve * count; +} + static STAmount reserve(jtx::Env& env, std::uint32_t count) { @@ -538,18 +544,19 @@ class Sponsor_test : public beast::unit_test::suite Account const bob("bob"); Account const sponsor1("sponsor1"); Account const sponsor2("sponsor2"); - env.fund(XRP(10000), alice, bob); - env.fund(env.current()->fees().reserve * 2 - 1, sponsor1, sponsor2); - env.close(); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + + adjustAccountXRPBalance( + env, sponsor1, accountReserve(env, 2) - drops(1)); env(sponsor::transfer(alice), sponsor::as(sponsor1, tfSponsorReserve), sig(sfSponsorSignature, sponsor1), ter(tecINSUFFICIENT_RESERVE)); - - env(pay(alice, sponsor1, drops(1))); env.close(); + adjustAccountXRPBalance(env, sponsor1, accountReserve(env, 2)); + env(sponsor::transfer(alice), sponsor::as(sponsor1, tfSponsorReserve), sig(sfSponsorSignature, sponsor1)); @@ -566,14 +573,17 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); // transfer sponsor + adjustAccountXRPBalance( + env, sponsor2, accountReserve(env, 2) - drops(1)); + env(sponsor::transfer(alice), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2), ter(tecINSUFFICIENT_RESERVE)); - - env(pay(alice, sponsor2, drops(1))); env.close(); + adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 2)); + env(sponsor::transfer(alice), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); @@ -588,29 +598,27 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); + BEAST_EXPECT(!env.le(keylet::account(sponsor1)) + ->isFieldPresent(sfSponsoringAccountCount)); auto const sle2 = env.le(keylet::account(alice)); BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); - // dissolve sponsor - env(pay(alice, - sponsor2, - (env.balance(alice).value() - - env.current()->fees().reserve - XRP(1) + drops(1))), - fee(XRP(1))); + // sponsor 2 accounts + adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 3)); + env(sponsor::transfer(bob), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); env.close(); - BEAST_EXPECT( - env.balance(alice) == env.current()->fees().reserve - drops(1)); - env(sponsor::transfer(alice), ter(tecINSUFFICIENT_RESERVE)); - env.close(); + // dissolve sponsors + adjustAccountXRPBalance( + env, alice, accountReserve(env, 1) - drops(1)); - env(pay(sponsor2, alice, XRP(1))); + env(sponsor::transfer(alice), ter(tecINSUFFICIENT_RESERVE)); env.close(); - // not sponsored - env(sponsor::transfer(bob), ter(tecNO_PERMISSION)); - env.close(); + adjustAccountXRPBalance(env, alice, accountReserve(env, 1)); env(sponsor::transfer(alice)); env.close(); @@ -623,9 +631,30 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); - BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); auto const sle3 = env.le(keylet::account(alice)); BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); + + env(sponsor::transfer(bob)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, bob) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + BEAST_EXPECT(!env.le(keylet::account(sponsor2)) + ->isFieldPresent(sfSponsoringAccountCount)); + auto const sle4 = env.le(keylet::account(bob)); + BEAST_EXPECT(!sle4->isFieldPresent(sfSponsorAccount)); + + // not sponsored + env(sponsor::transfer(bob), ter(tecNO_PERMISSION)); + env.close(); } { // sponsor object @@ -748,6 +777,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + BEAST_EXPECT(!env.le(keylet::account(sponsor2)) + ->isFieldPresent(sfSponsoringOwnerCount)); auto const sle3 = env.le(keylet::unchecked(checkId)); BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); } diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 6b59f3860a1..adde9890937 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -294,18 +294,18 @@ SponsorshipTransfer::doApply() else { // update owner's sponsored count - ownerSle->setFieldU32( - sfSponsoredOwnerCount, + auto const newCount = ownerSle->getFieldU32(sfSponsoredOwnerCount) + - ownerCountDelta); + ownerCountDelta; + ownerSle->setFieldU32(sfSponsoredOwnerCount, newCount); view().update(ownerSle); } // increment new sponsoring count auto const newSponsorSle = view().peek(keylet::account(newSponsor)); - newSponsorSle->setFieldU32( - sfSponsoringOwnerCount, + auto const newCount = newSponsorSle->getFieldU32(sfSponsoringOwnerCount) + - ownerCountDelta); + ownerCountDelta; + newSponsorSle->setFieldU32(sfSponsoringOwnerCount, newCount); view().update(newSponsorSle); objSle->setAccountID(sponsorField, newSponsor); @@ -328,10 +328,14 @@ SponsorshipTransfer::doApply() if (auto const oldSponsorSle = view().peek(keylet::account(oldSponsor))) { - oldSponsorSle->setFieldU32( - sfSponsoringOwnerCount, + auto const newCount = oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - - ownerCountDelta); + ownerCountDelta; + if (newCount == 0) + oldSponsorSle->makeFieldAbsent(sfSponsoringOwnerCount); + else + oldSponsorSle->setFieldU32( + sfSponsoringOwnerCount, newCount); view().update(oldSponsorSle); } @@ -360,9 +364,13 @@ SponsorshipTransfer::doApply() auto const oldSponsor = accSle->getAccountID(sfSponsorAccount); auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); - oldSponsorSle->setFieldU32( - sfSponsoringAccountCount, - oldSponsorSle->getFieldU32(sfSponsoringAccountCount) - 1); + auto const newCount = + oldSponsorSle->getFieldU32(sfSponsoringAccountCount) - 1; + if (newCount == 0) + oldSponsorSle->makeFieldAbsent(sfSponsoringAccountCount); + else + oldSponsorSle->setFieldU32( + sfSponsoringAccountCount, newCount); view().update(oldSponsorSle); } accSle->setAccountID(sfSponsorAccount, newSponsor); @@ -375,9 +383,12 @@ SponsorshipTransfer::doApply() accSle->makeFieldAbsent(sfSponsorAccount); // decrement account sponsoring count auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); - oldSponsorSle->setFieldU32( - sfSponsoringAccountCount, - oldSponsorSle->getFieldU32(sfSponsoringAccountCount) - 1); + auto const newCount = + oldSponsorSle->getFieldU32(sfSponsoringAccountCount) - 1; + if (newCount == 0) + oldSponsorSle->makeFieldAbsent(sfSponsoringAccountCount); + else + oldSponsorSle->setFieldU32(sfSponsoringAccountCount, newCount); view().update(oldSponsorSle); } } From b5db59644a1e1cb35b2c0c1f5484bc18e47c67a5 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 17 Nov 2025 18:25:02 +0900 Subject: [PATCH 078/249] update Sponsored VaultCreate test --- src/test/app/Sponsor_test.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e464ff2c70d..5703f61e4b6 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -3918,8 +3918,8 @@ class Sponsor_test : public beast::unit_test::suite cosigning, sponsor, alice, - 2, - 2, + 3, // Vault, PseudoAccount, MPToken(Share Token) + 3, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { auto result = @@ -4120,9 +4120,11 @@ class Sponsor_test : public beast::unit_test::suite sig(sfSponsorSignature, sponsor)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // Vault, MPToken(share) - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + BEAST_EXPECT( + ownerCount(env, alice) == + 3); // Vault, PseudoAccount, MPToken(share) + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 3); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 3); env(vault.del({.owner = alice, .id = keylet.key})); env.close(); From a9629ae325147120ee7a8f4aec089ff1635ac2b0 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 21 Nov 2025 17:44:05 +0900 Subject: [PATCH 079/249] address reviews --- include/xrpl/protocol/TxFlags.h | 2 +- src/test/app/Sponsor_test.cpp | 14 +++++++- src/test/jtx/sponsor.h | 5 ++- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 41 +++++++++++----------- src/xrpld/app/tx/detail/Transactor.cpp | 4 +-- 5 files changed, 39 insertions(+), 27 deletions(-) diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 2a54aa55da8..a664cc5e940 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -46,7 +46,7 @@ constexpr std::uint32_t tfUniversalMask = ~tfUniversal; // Sponsor flags (Global): constexpr std::uint32_t tfSponsorFee = 0x00000001; constexpr std::uint32_t tfSponsorReserve = 0x00000002; -constexpr std::uint32_t tfSponsorMask = tfSponsorFee | tfSponsorReserve; +constexpr std::uint32_t tfSponsorMask = ~(tfSponsorFee | tfSponsorReserve); // AccountSet flags: constexpr std::uint32_t tfRequireDestTag = 0x00010000; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 5703f61e4b6..8aca80c9df8 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -335,7 +335,7 @@ class Sponsor_test : public beast::unit_test::suite // Invalid Flags env(noop(alice), sponsor::as(sponsor, 4), ter(temINVALID_FLAG)); env(noop(alice), - sponsor::as(sponsor, ~tfSponsorMask), + sponsor::as(sponsor, tfSponsorMask), ter(temINVALID_FLAG)); } @@ -4511,6 +4511,12 @@ class Sponsor_test : public beast::unit_test::suite env.close(); testFeePermission(tesSUCCESS); + + // test with SponsorReserve (should failed) + env(sponsor::set(alice, 0, 100, XRP(100)), + sponsor::sponseeAcc(bob), + delegate::as(carol), + ter(terNO_DELEGATE_PERMISSION)); } // @@ -4549,6 +4555,12 @@ class Sponsor_test : public beast::unit_test::suite env.close(); testReservePermission(tesSUCCESS); + + // test with SponsorFee (should failed) + env(sponsor::set(alice, 0, 100, XRP(100)), + sponsor::sponseeAcc(bob), + delegate::as(carol), + ter(terNO_DELEGATE_PERMISSION)); } } diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index e9993e2a2f0..04ccc92fb3d 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef XRPL_TEST_JTX_SPONSOR_H_INCLUDED +#define XRPL_TEST_JTX_SPONSOR_H_INCLUDED #include #include @@ -92,3 +93,5 @@ ledgerEntry( } // namespace jtx } // namespace test } // namespace ripple + +#endif diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 5aef0cb8997..2444be98745 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -53,26 +53,27 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (sponsee == sponsor) return temMALFORMED; - if (ctx.tx.isFieldPresent(sfFeeAmount)) - { - auto const feeAmount = ctx.tx.getFieldAmount(sfFeeAmount); + auto const checkOptionalAmountField = [&](SField const& field) -> NotTEC { + if (!ctx.tx.isFieldPresent(field)) + return tesSUCCESS; - if (!isXRP(feeAmount)) - return temBAD_AMOUNT; + auto const amount = ctx.tx.getFieldAmount(field); - if (feeAmount.xrp().drops() <= 0) + if (!isXRP(amount)) return temBAD_AMOUNT; - } - if (ctx.tx.isFieldPresent(sfMaxFee)) - { - auto const maxFee = ctx.tx.getFieldAmount(sfMaxFee); - if (!isXRP(maxFee)) + if (amount.xrp().drops() <= 0) return temBAD_AMOUNT; - if (maxFee.xrp().drops() <= 0) - return temBAD_AMOUNT; - } + return tesSUCCESS; + }; + + if (auto const ret = checkOptionalAmountField(sfFeeAmount); + !isTesSuccess(ret)) + return ret; + + if (auto const ret = checkOptionalAmountField(sfMaxFee); !isTesSuccess(ret)) + return ret; if (ctx.tx.isFieldPresent(sfReserveCount)) { @@ -122,15 +123,13 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) auto const sponsoringReserve = tx.isFieldPresent(sfReserveCount) || tx.isFlag(tfSponsorshipSetRequireSignForReserve); - if (granularPermissions.contains(SponsorFee) && sponsoringFee) - return tesSUCCESS; - - if (granularPermissions.contains(SponsorReserve) && sponsoringReserve) - return tesSUCCESS; + if (sponsoringFee && !granularPermissions.contains(SponsorFee)) + return terNO_DELEGATE_PERMISSION; - // TODO: needs to check permission to delete sponsorship? + if (sponsoringReserve && !granularPermissions.contains(SponsorReserve)) + return terNO_DELEGATE_PERMISSION; - return terNO_DELEGATE_PERMISSION; + return tesSUCCESS; } TER diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 1c706875eae..5c82d580667 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -154,9 +154,7 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) } if (ctx.tx.isFieldPresent(sfSponsor) && !ctx.rules.enabled(featureSponsor)) - { return temDISABLED; - } if (auto const ret = preflight0(ctx, flagMask)) return ret; @@ -206,7 +204,7 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) JLOG(ctx.j.debug()) << "preflight1: invalid sponsor account"; return temMALFORMED; } - if (!(sponsor.getFlags() & tfSponsorMask)) + if (sponsor.getFlags() & tfSponsorMask) { JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; return temINVALID_FLAG; From e49d338cd8d06ee0e859f8f235bbdaee24affff3 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 26 Nov 2025 17:48:35 +0900 Subject: [PATCH 080/249] address SponsorshipSet reviews --- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 119 +++++++++++++-------- 1 file changed, 72 insertions(+), 47 deletions(-) diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 2444be98745..ea92947d09d 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -17,18 +17,19 @@ SponsorshipSet::preflight(PreflightContext const& ctx) { // check Flags { - if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForFee) && - ctx.tx.isFlag(tfSponsorshipClearRequireSignForFee)) + auto const flags = ctx.tx.getFlags(); + if ((flags & tfSponsorshipSetRequireSignForFee) && + (flags & tfSponsorshipClearRequireSignForFee)) return temINVALID_FLAG; - if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForReserve) && - ctx.tx.isFlag(tfSponsorshipClearRequireSignForReserve)) + if ((flags & tfSponsorshipSetRequireSignForReserve) && + (flags & tfSponsorshipClearRequireSignForReserve)) return temINVALID_FLAG; - if (ctx.tx.isFlag(tfDeleteObject)) + if (flags & tfDeleteObject) { // check Flags - if (ctx.tx.getFlags() & + if (flags & (tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForFee | @@ -37,53 +38,34 @@ SponsorshipSet::preflight(PreflightContext const& ctx) } } - if ((ctx.tx.isFieldPresent(sfSponsorAccount) && - ctx.tx.isFieldPresent(sfSponsee)) || - (!ctx.tx.isFieldPresent(sfSponsorAccount) && - !ctx.tx.isFieldPresent(sfSponsee))) - return temMALFORMED; - - auto const sponsor = ctx.tx.isFieldPresent(sfSponsorAccount) - ? ctx.tx.getAccountID(sfSponsorAccount) - : ctx.tx.getAccountID(sfAccount); - auto const sponsee = ctx.tx.isFieldPresent(sfSponsee) - ? ctx.tx.getAccountID(sfSponsee) - : ctx.tx.getAccountID(sfAccount); + auto const account = ctx.tx.getAccountID(sfAccount); + bool hasSponsor = ctx.tx.isFieldPresent(sfSponsorAccount); + bool hasSponsee = ctx.tx.isFieldPresent(sfSponsee); - if (sponsee == sponsor) + // The transaction must specify either Sponsor or Sponsee, but not both. + if (hasSponsor == hasSponsee) return temMALFORMED; - auto const checkOptionalAmountField = [&](SField const& field) -> NotTEC { - if (!ctx.tx.isFieldPresent(field)) - return tesSUCCESS; + auto const sponsor = ctx.tx[~sfSponsorAccount].value_or(account); + auto const sponsee = ctx.tx[~sfSponsee].value_or(account); - auto const amount = ctx.tx.getFieldAmount(field); - - if (!isXRP(amount)) - return temBAD_AMOUNT; - - if (amount.xrp().drops() <= 0) - return temBAD_AMOUNT; - - return tesSUCCESS; - }; - - if (auto const ret = checkOptionalAmountField(sfFeeAmount); - !isTesSuccess(ret)) - return ret; - - if (auto const ret = checkOptionalAmountField(sfMaxFee); !isTesSuccess(ret)) - return ret; + if (sponsor == sponsee) + return temMALFORMED; - if (ctx.tx.isFieldPresent(sfReserveCount)) + auto const flags = ctx.tx.getFlags(); + if (flags & tfDeleteObject) { - auto const reserveCount = ctx.tx.getFieldU32(sfReserveCount); - if (reserveCount < 1) - return temMALFORMED; - } + // can not combine with any modification flags when deleting + constexpr std::uint32_t modifyFlags = + tfSponsorshipSetRequireSignForFee | + tfSponsorshipSetRequireSignForReserve | + tfSponsorshipClearRequireSignForFee | + tfSponsorshipClearRequireSignForReserve; + + if (flags & modifyFlags) + return temINVALID_FLAG; - if (ctx.tx.isFlag(tfDeleteObject)) - { + // can not include these fields when deleting if (ctx.tx.isFieldPresent(sfFeeAmount) || ctx.tx.isFieldPresent(sfReserveCount) || ctx.tx.isFieldPresent(sfMaxFee)) @@ -91,8 +73,51 @@ SponsorshipSet::preflight(PreflightContext const& ctx) } else { - if (!ctx.tx.isFieldPresent(sfSponsee)) + // although both Sponsor and Sponsee can delete, + // only the Sponsor can create or update sponsorship. + if (account != sponsor) return temMALFORMED; + + if ((flags & tfSponsorshipSetRequireSignForFee) && + (flags & tfSponsorshipClearRequireSignForFee)) + return temINVALID_FLAG; + + if ((flags & tfSponsorshipSetRequireSignForReserve) && + (flags & tfSponsorshipClearRequireSignForReserve)) + return temINVALID_FLAG; + + // Check FeeAmount and MaxFee + auto const checkOptionalAmountField = + [&](SField const& field) -> NotTEC { + if (!ctx.tx.isFieldPresent(field)) + return tesSUCCESS; + + auto const amount = ctx.tx.getFieldAmount(field); + + if (!isXRP(amount)) + return temBAD_AMOUNT; + + if (amount.xrp().drops() <= 0) + return temBAD_AMOUNT; + + return tesSUCCESS; + }; + + if (auto const ret = checkOptionalAmountField(sfFeeAmount); + !isTesSuccess(ret)) + return ret; + + if (auto const ret = checkOptionalAmountField(sfMaxFee); + !isTesSuccess(ret)) + return ret; + + // Check ReserveCount + if (ctx.tx.isFieldPresent(sfReserveCount)) + { + auto const reserveCount = ctx.tx.getFieldU32(sfReserveCount); + if (reserveCount < 1) + return temMALFORMED; + } } return tesSUCCESS; From f5e2195bbb82c4a5c9a297a984375b86e92cb84a Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 11 Dec 2025 10:31:30 +0900 Subject: [PATCH 081/249] address reviews --- include/xrpl/protocol/TxFlags.h | 1 + src/libxrpl/ledger/View.cpp | 31 ++++++------ src/test/app/Sponsor_test.cpp | 5 ++ src/xrpld/app/tx/detail/SponsorshipSet.cpp | 58 ++++++++++++---------- 4 files changed, 52 insertions(+), 43 deletions(-) diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index a664cc5e940..2b2afb40963 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -280,6 +280,7 @@ constexpr std::uint32_t tfSponsorshipSetRequireSignForReserve = 0 constexpr std::uint32_t tfSponsorshipClearRequireSignForReserve = 0x00080000; constexpr std::uint32_t tfDeleteObject = 0x00100000; constexpr std::uint32_t tfSponsorshipSetMask = ~(tfUniversal | tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee | tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve | tfDeleteObject); +constexpr std::uint32_t tfSponsorshipSetPermissionMask = ~(tfUniversal | tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve ); // clang-format on diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 0ec470611f3..9217b41f64b 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1222,54 +1222,51 @@ adjustOwnerCount( if (sponsorSle) { - XRPL_ASSERT(sponsorSle, "ripple::adjustOwnerCount : sponsor not found"); + XRPL_ASSERT(sponsorSle, "ripple::adjustOwnerCount : sponsor exists"); + auto const account = accountSle->getAccountID(sfAccount); + auto const sponsorAcc = (*sponsorSle)->getAccountID(sfAccount); { // modify sponsor's SponsoringOwnerCount std::uint32_t const current{ (*sponsorSle)->getFieldU32(sfSponsoringOwnerCount)}; - AccountID const id = (*sponsorSle)->getAccountID(sfAccount); std::uint32_t const adjusted = - confineOwnerCount(current, amount, id, j); - view.adjustOwnerCountHook(id, current, adjusted); + confineOwnerCount(current, amount, sponsorAcc, j); + view.adjustOwnerCountHook(sponsorAcc, current, adjusted); if (adjusted == 0) (*sponsorSle)->makeFieldAbsent(sfSponsoringOwnerCount); else - (*sponsorSle)->at(sfSponsoringOwnerCount) = adjusted; + (*sponsorSle)->setFieldU32(sfSponsoringOwnerCount, adjusted); view.update(*sponsorSle); } { // modify account's SponsoredOwnerCount std::uint32_t const current{ accountSle->getFieldU32(sfSponsoredOwnerCount)}; - AccountID const id = (*accountSle)[sfAccount]; std::uint32_t const adjusted = - confineOwnerCount(current, amount, id, j); - view.adjustOwnerCountHook(id, current, adjusted); + confineOwnerCount(current, amount, account, j); + view.adjustOwnerCountHook(account, current, adjusted); if (adjusted == 0) accountSle->makeFieldAbsent(sfSponsoredOwnerCount); else - accountSle->at(sfSponsoredOwnerCount) = adjusted; + accountSle->setFieldU32(sfSponsoredOwnerCount, adjusted); view.update(accountSle); } - auto sle = view.peek(keylet::sponsor( - (*sponsorSle)->getAccountID(sfAccount), - accountSle->getAccountID(sfAccount))); + auto sle = view.peek(keylet::sponsor(sponsorAcc, account)); if (sle && amount > 0) { // pre funded - // modify sponsor's ReserveCount - + // update the pre-funded ReserveCount on Sponsorship ledger object XRPL_ASSERT( - sle, "ripple::adjustOwnerCount : co-signing sponsor not found"); + sle, "ripple::adjustOwnerCount : co-signing sponsor exists"); auto const currentReserveCount = sle->getFieldU32(sfReserveCount); XRPL_ASSERT( currentReserveCount >= amount, - "ripple::adjustOwnerCount : reserve count not enough"); + "ripple::adjustOwnerCount : enough reserve count"); - sle->at(sfReserveCount) = currentReserveCount - amount; + sle->setFieldU32(sfReserveCount, currentReserveCount - amount); view.update(sle); } } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 8aca80c9df8..88143ed43d2 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -183,6 +183,11 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sponseeAcc(noFunded), ter(tecNO_DST)); + // Invalid Sponsor + env(sponsor::set(sponsor, tfDeleteObject), + sponsor::sponsorAcc(noFunded), + ter(tecNO_DST)); + // Invalid Delete operation (sponsorship not found) env(sponsor::set(sponsor, tfDeleteObject), sponsor::sponseeAcc(alice), diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index ea92947d09d..b0908b2b811 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -15,9 +15,8 @@ SponsorshipSet::getFlagsMask(PreflightContext const& ctx) NotTEC SponsorshipSet::preflight(PreflightContext const& ctx) { - // check Flags + auto const flags = ctx.tx.getFlags(); { - auto const flags = ctx.tx.getFlags(); if ((flags & tfSponsorshipSetRequireSignForFee) && (flags & tfSponsorshipClearRequireSignForFee)) return temINVALID_FLAG; @@ -39,8 +38,8 @@ SponsorshipSet::preflight(PreflightContext const& ctx) } auto const account = ctx.tx.getAccountID(sfAccount); - bool hasSponsor = ctx.tx.isFieldPresent(sfSponsorAccount); - bool hasSponsee = ctx.tx.isFieldPresent(sfSponsee); + bool const hasSponsor = ctx.tx.isFieldPresent(sfSponsorAccount); + bool const hasSponsee = ctx.tx.isFieldPresent(sfSponsee); // The transaction must specify either Sponsor or Sponsee, but not both. if (hasSponsor == hasSponsee) @@ -52,7 +51,6 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (sponsor == sponsee) return temMALFORMED; - auto const flags = ctx.tx.getFlags(); if (flags & tfDeleteObject) { // can not combine with any modification flags when deleting @@ -139,14 +137,21 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) if (checkTxPermission(sle, tx) == tesSUCCESS) return tesSUCCESS; + auto const txFlags = tx.getFlags(); + + // this is added in case more flags will be added for SponsorshipSet + // in the future. Currently unreachable. + if (txFlags & tfSponsorshipSetPermissionMask) + return terNO_DELEGATE_PERMISSION; + std::unordered_set granularPermissions; loadGranularPermission(sle, ttSPONSORSHIP_SET, granularPermissions); auto const sponsoringFee = tx.isFieldPresent(sfFeeAmount) || tx.isFieldPresent(sfMaxFee) || - tx.isFlag(tfSponsorshipSetRequireSignForFee); + txFlags & tfSponsorshipSetRequireSignForFee; auto const sponsoringReserve = tx.isFieldPresent(sfReserveCount) || - tx.isFlag(tfSponsorshipSetRequireSignForReserve); + txFlags & tfSponsorshipSetRequireSignForReserve; if (sponsoringFee && !granularPermissions.contains(SponsorFee)) return terNO_DELEGATE_PERMISSION; @@ -160,16 +165,16 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) TER SponsorshipSet::preclaim(PreclaimContext const& ctx) { - auto const sponsor = ctx.tx.isFieldPresent(sfSponsorAccount) - ? ctx.tx.getAccountID(sfSponsorAccount) - : ctx.tx.getAccountID(sfAccount); - auto const sponsee = ctx.tx.isFieldPresent(sfSponsee) - ? ctx.tx.getAccountID(sfSponsee) - : ctx.tx.getAccountID(sfAccount); + auto const sponsor = ctx.tx[~sfSponsorAccount].value_or(ctx.tx[sfAccount]); + auto const sponsee = ctx.tx[~sfSponsee].value_or(ctx.tx[sfAccount]); if (sponsee == sponsor) return tecINTERNAL; // LCOV_EXCL_LINE + // check Sponsor + if (!ctx.view.exists(keylet::account(sponsor))) + return tecNO_DST; + // check Sponsee auto const sponseeSle = ctx.view.read(keylet::account(sponsee)); if (!sponseeSle) @@ -191,24 +196,21 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) TER SponsorshipSet::doApply() { - auto const sponseeAcc = ctx_.tx.isFieldPresent(sfSponsee) - ? ctx_.tx.getAccountID(sfSponsee) - : ctx_.tx.getAccountID(sfAccount); - - auto const sponsorAcc = ctx_.tx.isFieldPresent(sfSponsorAccount) - ? ctx_.tx.getAccountID(sfSponsorAccount) - : account_; + auto const sponsorAcc = ctx_.tx[~sfSponsorAccount].value_or(account_); + auto const sponseeAcc = ctx_.tx[~sfSponsee].value_or(account_); if (sponseeAcc == sponsorAcc) return tecINTERNAL; // LCOV_EXCL_LINE - auto const keylet = keylet::sponsor(sponsorAcc, sponseeAcc); - auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorAcc)); if (!sponsorAccSle) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsorObjSle = ctx_.view().peek(keylet); + if (!ctx_.view().exists(keylet::account(sponseeAcc))) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const sponsorKeylet = keylet::sponsor(sponsorAcc, sponseeAcc); + auto const sponsorObjSle = ctx_.view().peek(sponsorKeylet); if (ctx_.tx.isFlag(tfDeleteObject)) { @@ -249,7 +251,7 @@ SponsorshipSet::doApply() if (!sponsorObjSle) { // Create - auto newSle = std::make_shared(keylet); + auto newSle = std::make_shared(sponsorKeylet); if (auto const ret = checkInsufficientReserve( ctx_.view(), @@ -287,11 +289,15 @@ SponsorshipSet::doApply() (*newSle)[sfFlags] = flags; auto const sponsorPage = view().dirInsert( - keylet::ownerDir(sponsorAcc), keylet, describeOwnerDir(sponsorAcc)); + keylet::ownerDir(sponsorAcc), + sponsorKeylet, + describeOwnerDir(sponsorAcc)); (*newSle)[sfOwnerNode] = *sponsorPage; auto const sponseePage = view().dirInsert( - keylet::ownerDir(sponseeAcc), keylet, describeOwnerDir(sponseeAcc)); + keylet::ownerDir(sponseeAcc), + sponsorKeylet, + describeOwnerDir(sponseeAcc)); (*newSle)[sfSponseeNode] = *sponseePage; auto viewJ = ctx_.app.journal("View"); From 198f3f83db2db65bf8155c4f12353921315c7538 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 7 Jan 2026 11:14:01 +0900 Subject: [PATCH 082/249] remove unused insSponsorCoSigning args --- include/xrpl/ledger/View.h | 3 - src/libxrpl/ledger/View.cpp | 57 ++----------------- src/xrpld/app/paths/AMMOffer.h | 5 +- src/xrpld/app/tx/detail/AMMCreate.cpp | 12 +--- src/xrpld/app/tx/detail/AMMDeposit.cpp | 6 +- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 4 -- src/xrpld/app/tx/detail/CashCheck.cpp | 4 -- src/xrpld/app/tx/detail/Escrow.cpp | 3 - .../app/tx/detail/LoanBrokerCoverClawback.cpp | 1 - .../app/tx/detail/LoanBrokerCoverDeposit.cpp | 1 - src/xrpld/app/tx/detail/LoanBrokerDelete.cpp | 1 - src/xrpld/app/tx/detail/LoanManage.cpp | 1 - src/xrpld/app/tx/detail/LoanPay.cpp | 2 - src/xrpld/app/tx/detail/LoanSet.cpp | 2 - src/xrpld/app/tx/detail/SetTrust.cpp | 3 - src/xrpld/app/tx/detail/VaultClawback.cpp | 2 - src/xrpld/app/tx/detail/VaultDeposit.cpp | 3 - src/xrpld/app/tx/detail/VaultWithdraw.cpp | 2 - 18 files changed, 8 insertions(+), 104 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index cc143ac5fd3..53407a4dfb7 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -926,7 +926,6 @@ trustCreate( std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, std::optional const& sponsorAccountID, - bool const isSponsorCoSigning, beast::Journal j); [[nodiscard]] TER @@ -1029,7 +1028,6 @@ accountSend( STAmount const& saAmount, beast::Journal j, std::optional const& sponsorAcc = std::nullopt, - bool isSponsorCoSigning = false, WaiveTransferFee waiveFee = WaiveTransferFee::No); using MultiplePaymentDestinations = std::vector>; @@ -1047,7 +1045,6 @@ accountSendMulti( MultiplePaymentDestinations const& receivers, beast::Journal j, std::optional const& sponsorAccount, - bool isSponsorCoSigning, WaiveTransferFee waiveFee = WaiveTransferFee::No); [[nodiscard]] TER diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 23b266876cb..802aeb9a4b9 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1681,7 +1681,6 @@ doWithdraw( } auto const sponsorAcc = getTxReserveSponsorAccountID(tx); - auto const isSponsorCoSigning = isSponsorReserveCoSigning(tx); // Move the funds directly from the broker's pseudo-account to the // dstAcct @@ -1692,7 +1691,6 @@ doWithdraw( amount, j, sponsorAcc, - isSponsorCoSigning, WaiveTransferFee::Yes); } @@ -1730,7 +1728,6 @@ addEmptyHolding( auto const& sponsorAccountID = !isPseudoAccount(sleDst) ? getTxReserveSponsorAccountID(tx) : std::nullopt; - auto const isSponsorCoSigning = tx.isFieldPresent(sfSponsorSignature); // Can the account cover the trust line reserve ? if (auto const ret = checkInsufficientReserve( @@ -1761,7 +1758,6 @@ addEmptyHolding( /*qualityIn=*/0, /*qualityOut=*/0, sponsorAccountID, - isSponsorCoSigning, journal); } @@ -1938,7 +1934,6 @@ trustCreate( std::uint32_t uQualityIn, std::uint32_t uQualityOut, std::optional const& sponsorAccountID, - bool const isSponsorCoSigning, beast::Journal j) { JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", " @@ -2274,7 +2269,6 @@ rippleCreditIOU( STAmount const& saAmount, bool bCheckIssuer, std::optional const& sponsorAccount, - bool isSponsorCoSigning, beast::Journal j) { AccountID const& issuer = saAmount.getIssuer(); @@ -2429,7 +2423,6 @@ rippleCreditIOU( 0, 0, sponsorAccount, - isSponsorCoSigning, j); } @@ -2445,7 +2438,6 @@ rippleSendIOU( STAmount& saActual, beast::Journal j, std::optional const& sponsorAccount, - bool isSponsorCoSigning, WaiveTransferFee waiveFee) { auto const& issuer = saAmount.getIssuer(); @@ -2461,14 +2453,7 @@ rippleSendIOU( { // Direct send: redeeming IOUs and/or sending own IOUs. auto const ter = rippleCreditIOU( - view, - uSenderID, - uReceiverID, - saAmount, - false, - sponsorAccount, - isSponsorCoSigning, - j); + view, uSenderID, uReceiverID, saAmount, false, sponsorAccount, j); if (ter != tesSUCCESS) return ter; saActual = saAmount; @@ -2489,25 +2474,11 @@ rippleSendIOU( << " cost=" << saActual.getFullText(); TER terResult = rippleCreditIOU( - view, - issuer, - uReceiverID, - saAmount, - true, - sponsorAccount, - isSponsorCoSigning, - j); + view, issuer, uReceiverID, saAmount, true, sponsorAccount, j); if (tesSUCCESS == terResult) terResult = rippleCreditIOU( - view, - uSenderID, - issuer, - saActual, - true, - sponsorAccount, - isSponsorCoSigning, - j); + view, uSenderID, issuer, saActual, true, sponsorAccount, j); return terResult; } @@ -2524,7 +2495,6 @@ rippleSendMultiIOU( STAmount& actual, beast::Journal j, std::optional const& sponsorAccount, - bool isSponsorCoSigning, WaiveTransferFee waiveFee) { auto const& issuer = issue.getIssuer(); @@ -2562,7 +2532,6 @@ rippleSendMultiIOU( amount, false, sponsorAccount, - isSponsorCoSigning, j)) return ter; actual += amount; @@ -2588,14 +2557,7 @@ rippleSendMultiIOU( << " cost=" << actual.getFullText(); if (TER const terResult = rippleCreditIOU( - view, - issuer, - receiverID, - amount, - true, - sponsorAccount, - isSponsorCoSigning, - j)) + view, issuer, receiverID, amount, true, sponsorAccount, j)) return terResult; } @@ -2608,7 +2570,6 @@ rippleSendMultiIOU( takeFromSender, true, sponsorAccount, - isSponsorCoSigning, j)) return terResult; } @@ -2624,7 +2585,6 @@ accountSendIOU( STAmount const& saAmount, beast::Journal j, std::optional const& sponsorAccount, - bool isSponsorCoSigning, WaiveTransferFee waiveFee) { if (view.rules().enabled(fixAMMv1_1)) @@ -2665,7 +2625,6 @@ accountSendIOU( saActual, j, sponsorAccount, - isSponsorCoSigning, waiveFee); } @@ -2758,7 +2717,6 @@ accountSendMultiIOU( MultiplePaymentDestinations const& receivers, beast::Journal j, std::optional const& sponsorAccount, - bool isSponsorCoSigning, WaiveTransferFee waiveFee) { XRPL_ASSERT_PARTS( @@ -2780,7 +2738,6 @@ accountSendMultiIOU( actual, j, sponsorAccount, - isSponsorCoSigning, waiveFee); } @@ -3170,7 +3127,6 @@ accountSend( STAmount const& saAmount, beast::Journal j, std::optional const& sponsorAcc, - bool isSponsorCoSigning, WaiveTransferFee waiveFee) { return std::visit( @@ -3183,7 +3139,6 @@ accountSend( saAmount, j, sponsorAcc, - isSponsorCoSigning, waiveFee); else return accountSendMPT( @@ -3200,7 +3155,6 @@ accountSendMulti( MultiplePaymentDestinations const& receivers, beast::Journal j, std::optional const& sponsorAccount, - bool isSponsorCoSigning, WaiveTransferFee waiveFee) { XRPL_ASSERT_PARTS( @@ -3217,7 +3171,6 @@ accountSendMulti( receivers, j, sponsorAccount, - isSponsorCoSigning, waiveFee); else return accountSendMultiMPT( @@ -3384,7 +3337,6 @@ issueIOU( 0, 0, std::nullopt, - false, j); } @@ -3951,7 +3903,6 @@ rippleCredit( saAmount, bCheckIssuer, std::nullopt, - false, j); } else diff --git a/src/xrpld/app/paths/AMMOffer.h b/src/xrpld/app/paths/AMMOffer.h index 7b9f7ff95fe..fd89db0015c 100644 --- a/src/xrpld/app/paths/AMMOffer.h +++ b/src/xrpld/app/paths/AMMOffer.h @@ -104,10 +104,7 @@ class AMMOffer send(Args&&... args) { return accountSend( - std::forward(args)..., - std::nullopt, - false, - WaiveTransferFee::Yes); + std::forward(args)..., std::nullopt, WaiveTransferFee::Yes); } bool diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 73bfcfccc43..7ba2f590ac2 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -262,15 +262,8 @@ applyCreate( // Send LPT to LP. auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); - auto const isSponsorCoSigning = isSponsorReserveCoSigning(ctx_.tx); - auto res = accountSend( - sb, - accountId, - account_, - lpTokens, - ctx_.journal, - sponsor, - isSponsorCoSigning); + auto res = + accountSend(sb, accountId, account_, lpTokens, ctx_.journal, sponsor); if (res != tesSUCCESS) { JLOG(j_.debug()) << "AMM Instance: failed to send LPT " << lpTokens; @@ -285,7 +278,6 @@ applyCreate( amount, ctx_.journal, std::nullopt, // don't sponsor for AMM Trustline - false, WaiveTransferFee::Yes)) return res; // Set AMM flag on AMM trustline diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index e0d2ea15d9f..b0ffae3e663 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -510,7 +510,6 @@ AMMDeposit::deposit( std::uint16_t tfee) { auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); - auto const isSponsorCoSigning = isSponsorReserveCoSigning(ctx_.tx); // Check account has sufficient funds. // Return true if it does, false otherwise. @@ -585,7 +584,6 @@ AMMDeposit::deposit( amountDepositActual, ctx_.journal, std::nullopt, // don't sponsor for AMM Trustline - false, WaiveTransferFee::Yes); if (res != tesSUCCESS) { @@ -613,7 +611,6 @@ AMMDeposit::deposit( *amount2DepositActual, ctx_.journal, std::nullopt, // don't sponsor for AMM Trustline - false, WaiveTransferFee::Yes); if (res != tesSUCCESS) { @@ -630,8 +627,7 @@ AMMDeposit::deposit( account_, lpTokensDepositActual, ctx_.journal, - sponsor, - isSponsorCoSigning); + sponsor); if (res != tesSUCCESS) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens"; diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index d3ac6dcdb5a..9aba06ce0ec 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -610,8 +610,6 @@ AMMWithdraw::withdraw( if (auto const err = sufficientReserve(amountWithdrawActual.issue())) return {err, STAmount{}, STAmount{}, STAmount{}}; - auto const isSponsorCoSigning = isSponsorReserveCoSigning(tx); - // Withdraw amountWithdraw auto res = accountSend( view, @@ -620,7 +618,6 @@ AMMWithdraw::withdraw( amountWithdrawActual, journal, sponsor, - isSponsorCoSigning, WaiveTransferFee::Yes); if (res != tesSUCCESS) { @@ -645,7 +642,6 @@ AMMWithdraw::withdraw( *amount2WithdrawActual, journal, sponsor, - isSponsorCoSigning, WaiveTransferFee::Yes); if (res != tesSUCCESS) { diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 65db9450fc9..6cc35fecfe8 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -348,9 +348,6 @@ CashCheck::doApply() STAmount initialBalance(flowDeliver.issue()); initialBalance.setIssuer(noAccount()); - auto const isSponsorCoSigning = - isSponsorReserveCoSigning(ctx_.tx); - // clang-format off if (TER const ter = trustCreate( psb, // payment sandbox @@ -368,7 +365,6 @@ CashCheck::doApply() 0, // quality in 0, // quality out sponsorAcc, // sponsor - isSponsorCoSigning, // is sponsor co-signing viewJ); // journal !isTesSuccess(ter)) { diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 3dc379c92d4..0b4fe8db811 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -797,8 +797,6 @@ escrowUnlockApplyHelper( STAmount initialBalance(amount.issue()); initialBalance.setIssuer(noAccount()); - auto const isSponsorCoSigning = isSponsorReserveCoSigning(tx); - // clang-format off if (TER const ter = trustCreate( view, // payment sandbox @@ -816,7 +814,6 @@ escrowUnlockApplyHelper( 0, // quality in 0, // quality out sponeorAcc, // sponsor - isSponsorCoSigning, // is sponsor co-signing journal); // journal !isTesSuccess(ter)) { diff --git a/src/xrpld/app/tx/detail/LoanBrokerCoverClawback.cpp b/src/xrpld/app/tx/detail/LoanBrokerCoverClawback.cpp index b9d4898d77e..9f4ecb4a972 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerCoverClawback.cpp +++ b/src/xrpld/app/tx/detail/LoanBrokerCoverClawback.cpp @@ -338,7 +338,6 @@ LoanBrokerCoverClawback::doApply() clawAmount, j_, {}, - false, WaiveTransferFee::Yes); } diff --git a/src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.cpp b/src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.cpp index cae4b2f0541..5446df07869 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.cpp +++ b/src/xrpld/app/tx/detail/LoanBrokerCoverDeposit.cpp @@ -109,7 +109,6 @@ LoanBrokerCoverDeposit::doApply() amount, j_, {}, - false, WaiveTransferFee::Yes)) return ter; diff --git a/src/xrpld/app/tx/detail/LoanBrokerDelete.cpp b/src/xrpld/app/tx/detail/LoanBrokerDelete.cpp index 22e31846a1e..61d49fbaa0e 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerDelete.cpp +++ b/src/xrpld/app/tx/detail/LoanBrokerDelete.cpp @@ -145,7 +145,6 @@ LoanBrokerDelete::doApply() coverAvailable, j_, {}, - false, WaiveTransferFee::Yes)) return ter; } diff --git a/src/xrpld/app/tx/detail/LoanManage.cpp b/src/xrpld/app/tx/detail/LoanManage.cpp index cce169c9e3e..5fce2213ba1 100644 --- a/src/xrpld/app/tx/detail/LoanManage.cpp +++ b/src/xrpld/app/tx/detail/LoanManage.cpp @@ -290,7 +290,6 @@ LoanManage::defaultLoan( STAmount{vaultAsset, defaultCovered}, j, {}, - false, WaiveTransferFee::Yes); } diff --git a/src/xrpld/app/tx/detail/LoanPay.cpp b/src/xrpld/app/tx/detail/LoanPay.cpp index 7f1b2e2cc4a..4a0352e47d8 100644 --- a/src/xrpld/app/tx/detail/LoanPay.cpp +++ b/src/xrpld/app/tx/detail/LoanPay.cpp @@ -521,7 +521,6 @@ LoanPay::doApply() } auto const sponsorAccount = getTxReserveSponsorAccountID(tx); - auto const isSponsorCoSigning = isSponsorReserveCoSigning(tx); if (auto const ter = accountSendMulti( view, @@ -531,7 +530,6 @@ LoanPay::doApply() {brokerPayee, totalPaidToBroker}}, j_, sponsorAccount, - isSponsorCoSigning, WaiveTransferFee::Yes)) return ter; diff --git a/src/xrpld/app/tx/detail/LoanSet.cpp b/src/xrpld/app/tx/detail/LoanSet.cpp index e251a4e27fd..310a13fa3d1 100644 --- a/src/xrpld/app/tx/detail/LoanSet.cpp +++ b/src/xrpld/app/tx/detail/LoanSet.cpp @@ -545,7 +545,6 @@ LoanSet::doApply() } auto const sponsorAccount = getTxReserveSponsorAccountID(tx); - auto const isSponsorCoSigning = isSponsorReserveCoSigning(tx); if (auto const ter = accountSendMulti( view, @@ -554,7 +553,6 @@ LoanSet::doApply() {{borrower, loanAssetsToBorrower}, {brokerOwner, originationFee}}, j_, sponsorAccount, - isSponsorCoSigning, WaiveTransferFee::Yes)) return ter; diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index f93680ebf8c..1c58f6a7929 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -722,8 +722,6 @@ SetTrust::doApply() JLOG(j_.trace()) << "doTrustSet: Creating ripple line: " << to_string(k.key); - auto const isSponsorCoSigning = isSponsorReserveCoSigning(ctx_.tx); - // Create a new ripple line. terResult = trustCreate( view(), @@ -741,7 +739,6 @@ SetTrust::doApply() uQualityIn, uQualityOut, txSponsorAcc, - isSponsorCoSigning, viewJ); } diff --git a/src/xrpld/app/tx/detail/VaultClawback.cpp b/src/xrpld/app/tx/detail/VaultClawback.cpp index 6710889e0c7..44c974fdc50 100644 --- a/src/xrpld/app/tx/detail/VaultClawback.cpp +++ b/src/xrpld/app/tx/detail/VaultClawback.cpp @@ -251,7 +251,6 @@ VaultClawback::doApply() sharesDestroyed, j_, std::nullopt, - false, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -292,7 +291,6 @@ VaultClawback::doApply() assetsRecovered, j_, std::nullopt, - false, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/app/tx/detail/VaultDeposit.cpp b/src/xrpld/app/tx/detail/VaultDeposit.cpp index 389aa0c3b36..ae14f6800cf 100644 --- a/src/xrpld/app/tx/detail/VaultDeposit.cpp +++ b/src/xrpld/app/tx/detail/VaultDeposit.cpp @@ -262,7 +262,6 @@ VaultDeposit::doApply() assetsDeposited, j_, std::nullopt, - false, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -283,7 +282,6 @@ VaultDeposit::doApply() } auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); - auto const isSponsorCoSigning = ctx_.tx.isFieldPresent(sfSponsorSignature); // Transfer shares from vault to depositor. if (auto const ter = accountSend( @@ -293,7 +291,6 @@ VaultDeposit::doApply() sharesCreated, j_, sponsor, - isSponsorCoSigning, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/xrpld/app/tx/detail/VaultWithdraw.cpp b/src/xrpld/app/tx/detail/VaultWithdraw.cpp index 6f263438ab2..94a04e8b8ce 100644 --- a/src/xrpld/app/tx/detail/VaultWithdraw.cpp +++ b/src/xrpld/app/tx/detail/VaultWithdraw.cpp @@ -199,7 +199,6 @@ VaultWithdraw::doApply() auto const& vaultAccount = vault->at(sfAccount); auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); - auto const isSponsorCoSigning = isSponsorReserveCoSigning(ctx_.tx); // Transfer shares from depositor to vault. if (auto const ter = accountSend( @@ -209,7 +208,6 @@ VaultWithdraw::doApply() sharesRedeemed, j_, sponsor, - isSponsorCoSigning, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; From 163a2acbf5552892c76e73f23c973d864b8f48ac Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 7 Jan 2026 11:27:10 +0900 Subject: [PATCH 083/249] add `sfPreviousTxnID` and `sfPreviousTxnLgrSeq` to ltSponsorship --- include/xrpl/protocol/detail/ledger_entries.macro | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 2867867dd16..75b9aa49e3d 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -612,6 +612,8 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({ \sa keylet::sponsor */ LEDGER_ENTRY(ltSPONSORSHIP, 0x0090, Sponsorship, sponsorship, ({ + {sfPreviousTxnID, soeREQUIRED}, + {sfPreviousTxnLgrSeq, soeREQUIRED}, {sfOwner, soeREQUIRED}, {sfSponsee, soeREQUIRED}, {sfFeeAmount, soeOPTIONAL}, From 2813fea2947428c6585cbb63ac7b9ff7e845fd5d Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 7 Jan 2026 11:39:56 +0900 Subject: [PATCH 084/249] address review --- include/xrpl/protocol/Sponsor.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/xrpl/protocol/Sponsor.h b/include/xrpl/protocol/Sponsor.h index 1068ddd60b0..f5eecc8d647 100644 --- a/include/xrpl/protocol/Sponsor.h +++ b/include/xrpl/protocol/Sponsor.h @@ -1,3 +1,6 @@ +#ifndef XRPL_PROTOCOL_SPONSOR_H_INCLUDED +#define XRPL_PROTOCOL_SPONSOR_H_INCLUDED + #include #include #include @@ -16,3 +19,5 @@ addSerializeSponsorData( } } // namespace xrpl + +#endif From cf03c4cb8f5c8c4944b82d90640a49fbe9c5b255 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 7 Jan 2026 12:31:01 +0900 Subject: [PATCH 085/249] change Sponsor related fields to `soeDEFAULT` --- .../xrpl/protocol/detail/ledger_entries.macro | 10 ++++----- src/libxrpl/ledger/View.cpp | 5 ++++- src/test/app/Sponsor_test.cpp | 21 ++++++++++++++++++- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 4 ---- src/xrpld/app/tx/detail/Transactor.cpp | 10 +++++++-- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 75b9aa49e3d..20cff0e41bb 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -150,9 +150,9 @@ LEDGER_ENTRY(ltACCOUNT_ROOT, 0x0061, AccountRoot, account, ({ {sfAMMID, soeOPTIONAL}, // pseudo-account designator {sfVaultID, soeOPTIONAL}, // pseudo-account designator {sfLoanBrokerID, soeOPTIONAL}, // pseudo-account designator - {sfSponsoredOwnerCount, soeOPTIONAL}, - {sfSponsoringOwnerCount, soeOPTIONAL}, - {sfSponsoringAccountCount,soeOPTIONAL}, + {sfSponsoredOwnerCount, soeDEFAULT}, + {sfSponsoringOwnerCount, soeDEFAULT}, + {sfSponsoringAccountCount,soeDEFAULT}, })) /** A ledger object which contains a list of object identifiers. @@ -616,9 +616,9 @@ LEDGER_ENTRY(ltSPONSORSHIP, 0x0090, Sponsorship, sponsorship, ({ {sfPreviousTxnLgrSeq, soeREQUIRED}, {sfOwner, soeREQUIRED}, {sfSponsee, soeREQUIRED}, - {sfFeeAmount, soeOPTIONAL}, + {sfFeeAmount, soeDEFAULT}, {sfMaxFee, soeOPTIONAL}, - {sfReserveCount, soeOPTIONAL}, + {sfReserveCount, soeDEFAULT}, {sfOwnerNode, soeREQUIRED}, {sfSponseeNode, soeREQUIRED}, })) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 802aeb9a4b9..e4238b199bb 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1387,7 +1387,10 @@ adjustOwnerCount( currentReserveCount >= amount, "xrpl::adjustOwnerCount : enough reserve count"); - sle->setFieldU32(sfReserveCount, currentReserveCount - amount); + if (currentReserveCount - amount > 0) + sle->setFieldU32(sfReserveCount, currentReserveCount - amount); + else + sle->makeFieldAbsent(sfReserveCount); view.update(sle); } } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 6c642af5cde..45c8f30d195 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -990,7 +990,7 @@ class Sponsor_test : public beast::unit_test::suite auto sponsorFee = sponsorFeeBalance(sponsor, alice); env(pay(alice, bob, XRP(100)), - fee(XRP(100) + drops(1)), + fee(XRP(90) + drops(1)), sponsor::as(sponsor, tfSponsorFee), ter(terINSUF_FEE_B)); env.close(); @@ -1001,6 +1001,25 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT( sponsorFeeBalance(sponsor, alice) == sponsorFee); } + // use all FeeAmount + { + // = FeeAmount + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + env(pay(alice, bob, XRP(100)), + fee(XRP(90)), + sponsor::as(sponsor, tfSponsorFee), + ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100)); + BEAST_EXPECT(env.balance(bob) == bobBalance + XRP(100)); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice)) + ->isFieldPresent(sfFeeAmount)); + } // reset FeeAmount and MaxFee env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index cdb84292ac3..8f61f86b448 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -271,13 +271,9 @@ SponsorshipSet::doApply() (*newSle)[sfFeeAmount] = *feeAmount; } if (maxFee) - { (*newSle)[sfMaxFee] = *maxFee; - } if (reserveCount) - { (*newSle)[sfReserveCount] = *reserveCount; - } auto flags = 0; if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForFee)) diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 0398c152844..34c6c48fab6 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -501,8 +501,14 @@ Transactor::payFee() if (!sle) return tefINTERNAL; // LCOV_EXCL_LINE - sle->setFieldAmount( - result.field, sle->getFieldAmount(result.field) - feePaid); + auto const feeAmountAfter = sle->getFieldAmount(result.field) - feePaid; + + if (feeAmountAfter == beast::zero && + result.field.fieldMeta == SField::sMD_Default) + // for ltSponsorship.sfFeeAmount + sle->makeFieldAbsent(result.field); + else + sle->setFieldAmount(result.field, feeAmountAfter); view().update(sle); From edddf3fe4b2918247fd19e5c16fa60cfcdadecfb Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 7 Jan 2026 12:34:51 +0900 Subject: [PATCH 086/249] address review --- src/libxrpl/ledger/View.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index e4238b199bb..1e646c1eb43 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1373,25 +1373,28 @@ adjustOwnerCount( view.update(accountSle); } - auto sle = view.peek(keylet::sponsor(sponsorAcc, account)); + auto sponsorObjSle = view.peek(keylet::sponsor(sponsorAcc, account)); - if (sle && amount > 0) + if (sponsorObjSle && amount > 0) { // pre funded // update the pre-funded ReserveCount on Sponsorship ledger object XRPL_ASSERT( - sle, "xrpl::adjustOwnerCount : co-signing sponsor exists"); + sponsorObjSle, + "xrpl::adjustOwnerCount : co-signing sponsor exists"); - auto const currentReserveCount = sle->getFieldU32(sfReserveCount); + auto const currentReserveCount = + sponsorObjSle->getFieldU32(sfReserveCount); XRPL_ASSERT( currentReserveCount >= amount, "xrpl::adjustOwnerCount : enough reserve count"); if (currentReserveCount - amount > 0) - sle->setFieldU32(sfReserveCount, currentReserveCount - amount); + sponsorObjSle->setFieldU32( + sfReserveCount, currentReserveCount - amount); else - sle->makeFieldAbsent(sfReserveCount); - view.update(sle); + sponsorObjSle->makeFieldAbsent(sfReserveCount); + view.update(sponsorObjSle); } } std::uint32_t const current{accountSle->getFieldU32(sfOwnerCount)}; From 0791ca164a83feaf37daf0a1f57a3d44d772d008 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 7 Jan 2026 16:42:18 +0900 Subject: [PATCH 087/249] fix template exception --- src/test/app/NFToken_test.cpp | 27 ++++++++++++++++--- src/test/rpc/AccountSet_test.cpp | 23 ++++++++++++++-- .../app/tx/detail/SponsorshipTransfer.cpp | 5 +++- src/xrpld/app/tx/detail/Transactor.cpp | 7 ++--- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/test/app/NFToken_test.cpp b/src/test/app/NFToken_test.cpp index 75bf59e70dd..26731deb259 100644 --- a/src/test/app/NFToken_test.cpp +++ b/src/test/app/NFToken_test.cpp @@ -395,15 +395,17 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite using namespace test::jtx; Account const alice{"alice"}; + Account const bob{"bob"}; Env env{*this, features}; - env.fund(XRP(1000), alice); + env.fund(XRP(1000), alice, bob); env.close(); // We're going to hack the ledger in order to avoid generating // 4 billion or so NFTs. Because we're hacking the ledger we - // need alice's account to have non-zero sfMintedNFTokens and - // sfBurnedNFTokens fields. This prevents an exception when the - // AccountRoot template is applied. + // need alice's account to have non-zero sfMintedNFTokens, + // sfBurnedNFTokens, sfSponsoredOwnerCount, sfSponsoringOwnerCount, + // sfSponsoringAccountCount fields. This prevents an exception when + // the AccountRoot template is applied. { uint256 const nftId0{token::getNextID(env, alice, 0u)}; env(token::mint(alice, 0u)); @@ -411,6 +413,23 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(token::burn(alice, nftId0)); env.close(); + + env(did::set(alice), + did::uri("uri"), + sponsor::as(bob, tfSponsorReserve), + sig(sfSponsorSignature, bob)); + env.close(); + + env(did::set(bob), + did::uri("uri"), + sponsor::as(alice, tfSponsorReserve), + sig(sfSponsorSignature, alice)); + env.close(); + + env(sponsor::transfer(bob), + sponsor::as(alice, tfSponsorReserve), + sig(sfSponsorSignature, alice)); + env.close(); } // Note that we're bypassing almost all of the ledger's safety diff --git a/src/test/rpc/AccountSet_test.cpp b/src/test/rpc/AccountSet_test.cpp index 38ac44b6edf..95ca4296fdc 100644 --- a/src/test/rpc/AccountSet_test.cpp +++ b/src/test/rpc/AccountSet_test.cpp @@ -399,8 +399,10 @@ class AccountSet_test : public beast::unit_test::suite env.close(); // Because we're hacking the ledger we need the account to have - // non-zero sfMintedNFTokens and sfBurnedNFTokens fields. This - // prevents an exception when the AccountRoot template is applied. + // non-zero sfMintedNFTokens, sfBurnedNFTokens, + // sfSponsoredOwnerCount, sfSponsoringOwnerCount, + // sfSponsoringAccountCount fields. This prevents an exception when + // the AccountRoot template is applied. { uint256 const nftId0{token::getNextID(env, gw, 0u)}; env(token::mint(gw, 0u)); @@ -408,6 +410,23 @@ class AccountSet_test : public beast::unit_test::suite env(token::burn(gw, nftId0)); env.close(); + + env(did::set(gw), + did::uri("uri"), + sponsor::as(alice, tfSponsorReserve), + sig(sfSponsorSignature, alice)); + env.close(); + + env(did::set(alice), + did::uri("uri"), + sponsor::as(gw, tfSponsorReserve), + sig(sfSponsorSignature, gw)); + env.close(); + + env(sponsor::transfer(alice), + sponsor::as(gw, tfSponsorReserve), + sig(sfSponsorSignature, gw)); + env.close(); } // Note that we're bypassing almost all of the ledger's safety diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 9196b77a467..36dbf54cdbd 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -297,7 +297,10 @@ SponsorshipTransfer::doApply() auto const newCount = ownerSle->getFieldU32(sfSponsoredOwnerCount) + ownerCountDelta; - ownerSle->setFieldU32(sfSponsoredOwnerCount, newCount); + if (newCount == 0) + ownerSle->makeFieldAbsent(sfSponsoredOwnerCount); + else + ownerSle->setFieldU32(sfSponsoredOwnerCount, newCount); view().update(ownerSle); } // increment new sponsoring count diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 34c6c48fab6..97d4d40add9 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -503,9 +503,10 @@ Transactor::payFee() auto const feeAmountAfter = sle->getFieldAmount(result.field) - feePaid; - if (feeAmountAfter == beast::zero && - result.field.fieldMeta == SField::sMD_Default) - // for ltSponsorship.sfFeeAmount + if (feeAmountAfter == beast::zero && result.field == sfFeeAmount) + // Because ltSponsorship.sfFeeAmount is soeDEFAULT + // TODO: Use whether the field is soeDEFAULT instead of sfFeeAmount in + // the condition. sle->makeFieldAbsent(result.field); else sle->setFieldAmount(result.field, feeAmountAfter); From 82b146bf0320c21d2b4df3cdd03ad23b644edadb Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 7 Jan 2026 17:16:50 +0900 Subject: [PATCH 088/249] fix ripple -> xrpl --- src/test/app/Sponsor_test.cpp | 2 +- src/xrpld/app/tx/detail/Transactor.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 45c8f30d195..1719e72f479 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -4722,7 +4722,7 @@ class Sponsor_test : public beast::unit_test::suite } }; -BEAST_DEFINE_TESTSUITE(Sponsor, app, ripple); +BEAST_DEFINE_TESTSUITE(Sponsor, app, xrpl); } // namespace test } // namespace xrpl diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 97d4d40add9..6ff2383cbd0 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -437,7 +437,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) { XRPL_ASSERT( sponsorSle->getType() == ltSPONSORSHIP, - "ripple::Transactor::checkFee : could not get sponsorship"); + "xrpl::Transactor::checkFee : could not get sponsorship"); XRPAmount feeAmount = sponsorSle->isFieldPresent(result.field) ? sponsorSle->getFieldAmount(result.field).xrp() @@ -460,14 +460,14 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) XRPL_ASSERT( sle->getType() == ltACCOUNT_ROOT, - "ripple::Transactor::checkFee : could not get account"); + "xrpl::Transactor::checkFee : could not get account"); availableBalance = (*sle)[result.field].xrp(); } XRPL_ASSERT( availableBalance, - "ripple::Transactor::checkFee : could not get balance for fee"); + "xrpl::Transactor::checkFee : could not get balance for fee"); if (*availableBalance < feePaid) { From 1e8f6ce1b579e3694b900340590222ed18c74a18 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 9 Jan 2026 16:31:22 +0900 Subject: [PATCH 089/249] address reviews --- src/test/app/Sponsor_test.cpp | 10 +- src/xrpld/app/tx/detail/Transactor.cpp | 176 +++++++++++++------------ src/xrpld/app/tx/detail/Transactor.h | 4 +- 3 files changed, 101 insertions(+), 89 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 1719e72f479..94e4027729e 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -302,8 +302,16 @@ class Sponsor_test : public beast::unit_test::suite env(signers(sponsor, 2, {{signer1, 1}, {signer2, 1}})); env.close(); + // test calculateBaseFee for multisigned sponsor + auto const baseFee = env.current()->fees().base; env(noop(alice), - fee(XRP(1)), + fee(baseFee + 2 * baseFee - 1), + sponsor::as(sponsor, tfSponsorReserve), + msig(sfSponsorSignature, {signer1, signer2}), + ter(telINSUF_FEE_P)); + + env(noop(alice), + fee(baseFee + 2 * baseFee), sponsor::as(sponsor, tfSponsorReserve), msig(sfSponsorSignature, {signer1, signer2}), ter(tesSUCCESS)); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 6ff2383cbd0..62a38016cba 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -153,9 +153,19 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return temBAD_SIGNER; } - if (ctx.tx.isFieldPresent(sfSponsor) && !ctx.rules.enabled(featureSponsor)) + bool const hasSponsor = ctx.tx.isFieldPresent(sfSponsor); + bool const hasSponsorSig = ctx.tx.isFieldPresent(sfSponsorSignature); + + if ((hasSponsor || hasSponsorSig) && !ctx.rules.enabled(featureSponsor)) return temDISABLED; + if (hasSponsorSig && !hasSponsor) + { + JLOG(ctx.j.debug()) + << "preflight1: sponsor signature without sponsor definition"; + return temMALFORMED; + } + if (auto const ret = preflight0(ctx, flagMask)) return ret; @@ -196,17 +206,18 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) "Inner batch transaction must have a parent batch ID."); // Sponsor checks - if (ctx.tx.isFieldPresent(sfSponsor)) + if (hasSponsor) { auto const sponsor = ctx.tx.getFieldObject(sfSponsor); if (sponsor[sfAccount] == ctx.tx[sfAccount]) { - JLOG(ctx.j.debug()) << "preflight1: invalid sponsor account"; + JLOG(ctx.j.debug()) << "preflight1: Sponsor account cannot be the " + "same as the transaction originator"; return temMALFORMED; } if (sponsor.getFlags() & tfSponsorMask) { - JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; + JLOG(ctx.j.debug()) << "preflight1: Invalid sponsor flags"; return temINVALID_FLAG; } } @@ -301,27 +312,26 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) auto const hasSponsorSignature = tx.isFieldPresent(sfSponsorSignature); - if (!hasSponsorSignature) - { - auto const txSponsor = tx.getFieldObject(sfSponsor); + if (hasSponsorSignature) + return tesSUCCESS; - auto const sponsorAcc = txSponsor.getAccountID(sfAccount); - auto const sponseeAcc = tx.getAccountID(sfAccount); - auto const sponsorSle = - view.read(keylet::sponsor(sponsorAcc, sponseeAcc)); + auto const txSponsor = tx.getFieldObject(sfSponsor); - // pre funded - if (!sponsorSle) - return terNO_SPONSORSHIP; + auto const sponsorAcc = txSponsor.getAccountID(sfAccount); + auto const sponseeAcc = tx.getAccountID(sfAccount); + auto const sponsorSle = view.read(keylet::sponsor(sponsorAcc, sponseeAcc)); - if (txSponsor.isFlag(tfSponsorFee) && - sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) - return terNO_SPONSORSHIP; + // sponsorship object missing for pre-funded tx + if (!sponsorSle) + return terNO_SPONSORSHIP; - if (txSponsor.isFlag(tfSponsorReserve) && - sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) - return terNO_SPONSORSHIP; - } + if (txSponsor.isFlag(tfSponsorFee) && + sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) + return terNO_SPONSORSHIP; + + if (txSponsor.isFlag(tfSponsorReserve) && + sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) + return terNO_SPONSORSHIP; return tesSUCCESS; } @@ -343,9 +353,9 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) tx.isFieldPresent(sfSigners) ? tx.getFieldArray(sfSigners).size() : 0; std::size_t sponsorSignerCount = 0; - if (tx.isFieldPresent(sfSponsor)) + if (tx.isFieldPresent(sfSponsorSignature)) { - auto const sponsorObj = tx.getFieldObject(sfSponsor); + auto const sponsorObj = tx.getFieldObject(sfSponsorSignature); sponsorSignerCount += sponsorObj.isFieldPresent(sfSigners) ? sponsorObj.getFieldArray(sfSigners).size() : 0; @@ -423,59 +433,49 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (feePaid == beast::zero) return tesSUCCESS; - std::optional availableBalance; + auto const payer = getFeePayer(ctx.view, ctx.tx); + auto const payerSle = ctx.view.read(payer.entry); + + if (!payerSle) + { + if (payer.type == FeePayerType::SponsorPreFunded) + // Sanity check: already checked in checkSponsor + return tefINTERNAL; // LCOV_EXCL_LINE + + return terNO_ACCOUNT; + } - auto const result = getFeePayer(ctx.view, ctx.tx); + XRPAmount maxSpendable = beast::zero; - if (result.type == FeePayerType::SponsorPreFunded) + if (payer.type == FeePayerType::SponsorPreFunded) { - // use prefunded fee sponsor - auto const sponsorSle = ctx.view.read(result.keylet); - if (!sponsorSle) - return terNO_SPONSORSHIP; - else + if (payerSle->getType() != ltSPONSORSHIP) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (payerSle->isFieldPresent(payer.balanceField)) + maxSpendable = payerSle->getFieldAmount(payer.balanceField).xrp(); + + if (payerSle->isFieldPresent(sfMaxFee)) { - XRPL_ASSERT( - sponsorSle->getType() == ltSPONSORSHIP, - "xrpl::Transactor::checkFee : could not get sponsorship"); - - XRPAmount feeAmount = sponsorSle->isFieldPresent(result.field) - ? sponsorSle->getFieldAmount(result.field).xrp() - : XRPAmount(0); - - if (sponsorSle->isFieldPresent(sfMaxFee)) - feeAmount = std::min( - feeAmount, sponsorSle->getFieldAmount(sfMaxFee).xrp()); - availableBalance = feeAmount; + auto const cap = payerSle->getFieldAmount(sfMaxFee).xrp(); + maxSpendable = std::min(maxSpendable, cap); } } else { - auto const keylet = result.keylet; - JLOG(ctx.j.trace()) << "Fee payer: " + to_string(keylet.key); - auto const sle = ctx.view.read(keylet); - - if (!sle) - return terNO_ACCOUNT; + if (payerSle->getType() != ltACCOUNT_ROOT) + return tefINTERNAL; // LCOV_EXCL_LINE - XRPL_ASSERT( - sle->getType() == ltACCOUNT_ROOT, - "xrpl::Transactor::checkFee : could not get account"); - - availableBalance = (*sle)[result.field].xrp(); + maxSpendable = payerSle->getFieldAmount(payer.balanceField).xrp(); } - XRPL_ASSERT( - availableBalance, - "xrpl::Transactor::checkFee : could not get balance for fee"); - - if (*availableBalance < feePaid) + if (maxSpendable < feePaid) { JLOG(ctx.j.trace()) - << "Insufficient balance:" << " balance=" - << to_string(*availableBalance) << " paid=" << to_string(feePaid); + << "Insufficient balance:" << " balance=" << to_string(maxSpendable) + << " paid=" << to_string(feePaid); - if ((*availableBalance > beast::zero) && !ctx.view.open()) + if ((maxSpendable > beast::zero) && !ctx.view.open()) { // Closed ledger, non-zero balance, less than fee return tecINSUFF_FEE; @@ -492,28 +492,29 @@ Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); - auto const result = getFeePayer(view(), ctx_.tx); + auto const payer = getFeePayer(view(), ctx_.tx); - auto const sle = view().peek(result.keylet); + auto const sle = view().peek(payer.entry); - JLOG(j_.trace()) << "Fee payer: " + to_string(result.keylet.key); + JLOG(j_.trace()) << "Fee payer: " + to_string(payer.entry.key); if (!sle) return tefINTERNAL; // LCOV_EXCL_LINE - auto const feeAmountAfter = sle->getFieldAmount(result.field) - feePaid; + auto const feeAmountAfter = + sle->getFieldAmount(payer.balanceField) - feePaid; - if (feeAmountAfter == beast::zero && result.field == sfFeeAmount) + if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) // Because ltSponsorship.sfFeeAmount is soeDEFAULT // TODO: Use whether the field is soeDEFAULT instead of sfFeeAmount in // the condition. - sle->makeFieldAbsent(result.field); + sle->makeFieldAbsent(payer.balanceField); else - sle->setFieldAmount(result.field, feeAmountAfter); + sle->setFieldAmount(payer.balanceField, feeAmountAfter); view().update(sle); - if (result.type == FeePayerType::Account) + if (payer.type == FeePayerType::Account) // Deduct the fee, so it's not available during the transaction. // Will only write the account back if the transaction succeeds. mSourceBalance -= feePaid; @@ -794,8 +795,9 @@ Transactor::checkSign( if (sigObject.isFieldPresent(sfSponsorSignature)) { + // Sanity check: already checked in preflight1 if (!sigObject.isFieldPresent(sfSponsor)) - return temMALFORMED; + return tefINTERNAL; // LCOV_EXCL_LINE auto const sponsorObj = sigObject.getFieldObject(sfSponsor); auto const isCoSigned = sigObject.isFieldPresent(sfSponsorSignature); @@ -1197,14 +1199,13 @@ Transactor::reset(XRPAmount fee) if (!txnAcct) return {tefINTERNAL, beast::zero}; - auto const result = getFeePayer(view(), ctx_.tx); - - auto const payerSle = view().peek(result.keylet); + auto const payer = getFeePayer(view(), ctx_.tx); + auto const payerSle = view().peek(payer.entry); if (!payerSle) return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE - auto const balance = payerSle->getFieldAmount(result.field).xrp(); + auto const balance = payerSle->getFieldAmount(payer.balanceField).xrp(); // balance should have already been checked in checkFee / preFlight. XRPL_ASSERT( @@ -1223,7 +1224,7 @@ Transactor::reset(XRPAmount fee) // If for some reason we are unable to consume the ticket or sequence // then the ledger is corrupted. Rather than make things worse we // reject the transaction. - payerSle->setFieldAmount(result.field, balance - fee); + payerSle->setFieldAmount(payer.balanceField, balance - fee); TER const ter{consumeSeqProxy(txnAcct)}; XRPL_ASSERT( @@ -1247,34 +1248,37 @@ Transactor::getFeePayer(ReadView const& view, STTx const& tx) { auto const sponsor = tx.getFieldObject(sfSponsor); auto const hasSignature = tx.isFieldPresent(sfSponsorSignature); - auto const keylet = keylet::sponsor( + auto const sponsorKeylet = keylet::sponsor( sponsor.getAccountID(sfAccount), tx.getAccountID(sfAccount)); if (hasSignature) { // if pre-funded sponsorship exists, prefer it - if (view.exists(keylet)) + if (view.exists(sponsorKeylet)) return FeePayer{ - keylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; + sponsorKeylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; // co-signed - auto const keylet = + auto const sponsorAccountKeylet = keylet::account(sponsor.getAccountID(sfAccount)); - return FeePayer{keylet, sfBalance, FeePayerType::SponsorCoSigned}; + return FeePayer{ + sponsorAccountKeylet, sfBalance, FeePayerType::SponsorCoSigned}; } // pre funded - return FeePayer{keylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; + return FeePayer{ + sponsorKeylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; } if (tx.isFieldPresent(sfDelegate)) { - auto const keylet = keylet::account(tx.getAccountID(sfDelegate)); - return FeePayer{keylet, sfBalance, FeePayerType::Delegate}; + auto const delegatorKeylet = + keylet::account(tx.getAccountID(sfDelegate)); + return FeePayer{delegatorKeylet, sfBalance, FeePayerType::Delegate}; } - auto const keylet = keylet::account(tx.getAccountID(sfAccount)); - return FeePayer{keylet, sfBalance, FeePayerType::Account}; + auto const accountKeylet = keylet::account(tx.getAccountID(sfAccount)); + return FeePayer{accountKeylet, sfBalance, FeePayerType::Account}; } // The sole purpose of this function is to provide a convenient, named diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index 8d619e8c354..b602d82570e 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -127,8 +127,8 @@ enum class FeePayerType { struct FeePayer { - Keylet keylet; - SF_AMOUNT const& field; + Keylet entry; + SF_AMOUNT const& balanceField; FeePayerType type; }; From f88e964dac52eefb630d3033687276f041b397a4 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 9 Jan 2026 16:40:15 +0900 Subject: [PATCH 090/249] allow `sponsee` on cspell --- .config/cspell.config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/cspell.config.yaml b/.config/cspell.config.yaml index 0cac82807df..5b573467ecf 100644 --- a/.config/cspell.config.yaml +++ b/.config/cspell.config.yaml @@ -203,6 +203,7 @@ words: - sles - soci - socidb + - sponsee - sslws - statsd - STATSDCOLLECTOR From ec056134722cdbd49a12c7aabf7ee81a297567fe Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 9 Jan 2026 16:45:12 +0900 Subject: [PATCH 091/249] fix typo --- src/xrpld/app/tx/detail/Escrow.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 0b4fe8db811..b71d5a903a2 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -779,10 +779,10 @@ escrowUnlockApplyHelper( if (!view.exists(trustLineKey) && createAsset && !receiverIssuer) { // Can the account cover the trust line's reserve? - auto const sponeorAcc = getTxReserveSponsorAccountID(tx); + auto const sponsorAcc = getTxReserveSponsorAccountID(tx); std::optional> sponsorSle = std::nullopt; - if (sponeorAcc) - sponsorSle = view.peek(keylet::account(*sponeorAcc)); + if (sponsorAcc) + sponsorSle = view.peek(keylet::account(*sponsorAcc)); if (auto const ret = checkInsufficientReserve( view, tx, sleDest, xrpBalance, sponsorSle, 1); !isTesSuccess(ret)) @@ -813,7 +813,7 @@ escrowUnlockApplyHelper( Issue(currency, receiver), // limit of zero 0, // quality in 0, // quality out - sponeorAcc, // sponsor + sponsorAcc, // sponsor journal); // journal !isTesSuccess(ter)) { From 1bc5a1bb437b1dd6dcd9dae7e73107961221e50c Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 14 Jan 2026 12:49:49 +0900 Subject: [PATCH 092/249] address spec changes (payment flags/ iou amount) --- .../xrpl/protocol/detail/transactions.macro | 4 +- src/test/app/Sponsor_test.cpp | 50 ++++++++++++++++--- src/xrpld/app/tx/detail/Payment.cpp | 6 ++- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index ef3fdf99d2d..3b9e71b8af1 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -1063,7 +1063,7 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay, # include #endif TRANSACTION(ttSPONSORSHIP_TRANSFER, 85, SponsorshipTransfer, - Delegation::delegatable, + Delegation::delegable, featureSponsor, noPriv, ({ @@ -1075,7 +1075,7 @@ TRANSACTION(ttSPONSORSHIP_TRANSFER, 85, SponsorshipTransfer, # include #endif TRANSACTION(ttSPONSORSHIP_SET, 86, SponsorshipSet, - Delegation::delegatable, + Delegation::delegable, featureSponsor, noPriv, ({ diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 94e4027729e..13edeb53244 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1120,19 +1120,55 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Sponsor Account"); using namespace test::jtx; - Env env{*this, testable_amendments()}; + Env env{*this, testable_amendments() - featureSponsor}; Account const alice("alice"); Account const sponsor("sponsor"); Account const bob("bob"); Account const charlie("charlie"); + Account const gw("gw"); + auto const USD = gw["USD"]; env.fund(XRP(10000), alice, sponsor); + // Disabled + env(pay(alice, bob, XRP(100)), + txflags(tfSponsorCreatedAccount), + ter(temDISABLED)); + env.close(); + + env.enableFeature(featureSponsor); + env.close(); + + // Invalid flags + for (auto flag : { + tfNoRippleDirect, + tfPartialPayment, + tfLimitQuality, + }) + { + env(pay(alice, bob, XRP(100)), + txflags(tfSponsorCreatedAccount | flag), + ter(temINVALID_FLAG)); + env.close(); + } + + // Invalid amount(iou) + env(pay(alice, bob, USD(100)), + txflags(tfSponsorCreatedAccount), + ter(temBAD_AMOUNT)); + env.close(); + + // Insufficient reserve + env(pay(alice, + bob, + (env.current()->fees().accountReserve(0) - drops(1))), + txflags(tfSponsorCreatedAccount), + ter(tecNO_DST_INSUF_XRP)); + env.close(); + // Account is not sponsored by normal Sponsor specification { - env(pay(alice, - bob, - STAmount(env.current()->fees().accountReserve(0))), + env(pay(alice, bob, drops(env.current()->fees().accountReserve(0))), sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor)); env.close(); @@ -1145,17 +1181,17 @@ class Sponsor_test : public beast::unit_test::suite // Use tfSponsorCreatedAccount to sponsor an account { - // to funded accoutn + // to funded account env(pay(sponsor, bob, - STAmount(env.current()->fees().accountReserve(0))), + drops(env.current()->fees().accountReserve(0))), txflags(tfSponsorCreatedAccount), ter(tecNO_SPONSOR_PERMISSION)); // to non-funded account env(pay(sponsor, charlie, - STAmount(env.current()->fees().accountReserve(0))), + drops(env.current()->fees().accountReserve(0))), txflags(tfSponsorCreatedAccount)); env.close(); diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 88b8765ba6a..19413617cdc 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -87,10 +87,14 @@ Payment::preflight(PreflightContext const& ctx) if (txFlags & tfSponsorCreatedAccount) { if (!ctx.rules.enabled(featureSponsor)) + return temDISABLED; + + if (txFlags & tfNoRippleDirect || txFlags & tfPartialPayment || + txFlags & tfLimitQuality) return temINVALID_FLAG; if (!dstAmount.native()) - return temMALFORMED; + return temBAD_AMOUNT; } if (mptDirect && ctx.tx.isFieldPresent(sfPaths)) From f885f02ededfb9f4373c1ca9e75c06063d19648e Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 14 Jan 2026 19:28:10 +0900 Subject: [PATCH 093/249] address spec changes (sfReserveCount, sfFeeAmount) --- src/test/app/Sponsor_test.cpp | 82 +++++++++++++++------- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 22 +++--- 2 files changed, 66 insertions(+), 38 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 13edeb53244..b0178cc909a 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -371,6 +371,7 @@ class Sponsor_test : public beast::unit_test::suite 100, XRP(100), XRP(1)), + fee(XRP(1)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); env.close(); @@ -382,25 +383,51 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1)); BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForFee)); BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForReserve)); + BEAST_EXPECT( + env.balance(sponsor) == + XRP(10000) - sle->at(sfFeeAmount) - XRP(1)); - // update sponsorship - env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(2)), + // update sponsorship (decrement) + env(sponsor::set(sponsor, 0, 50, XRP(50), XRP(0.5)), sponsor::sponseeAcc(alice), + fee(XRP(1)), ter(tesSUCCESS)); env.close(); - auto sle2 = env.le(keylet::sponsor(sponsor, alice)); - BEAST_EXPECT(sle2); - BEAST_EXPECT(sle2->at(sfReserveCount) == 200); // add 100 - BEAST_EXPECT(sle2->at(sfFeeAmount) == XRP(200)); // add 100 - BEAST_EXPECT(sle2->at(sfMaxFee) == XRP(2)); // update to 2 + sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfReserveCount) == 50); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(50)); + BEAST_EXPECT(sle->at(sfMaxFee) == XRP(0.5)); + BEAST_EXPECT( + env.balance(sponsor) == + XRP(10000) - sle->at(sfFeeAmount) - XRP(2)); + + // update sponsorship (increment) + env(sponsor::set(sponsor, 0, 200, XRP(200), XRP(2)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tesSUCCESS)); + env.close(); + + sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfReserveCount) == 200); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(200)); + BEAST_EXPECT(sle->at(sfMaxFee) == XRP(2)); + BEAST_EXPECT( + env.balance(sponsor) == + XRP(10000) - sle->at(sfFeeAmount) - XRP(3)); // delete from sponsor env(sponsor::del(sponsor), sponsor::sponseeAcc(alice), + fee(XRP(1)), ter(tesSUCCESS)); env.close(); + BEAST_EXPECT(env.balance(sponsor) == XRP(10000) - XRP(4)); + env(sponsor::set( sponsor, tfSponsorshipSetRequireSignForFee | @@ -1101,17 +1128,19 @@ class Sponsor_test : public beast::unit_test::suite ter(terNO_SPONSORSHIP)); env.close(); + BEAST_EXPECT( + env.le(keylet::sponsor(sponsor, alice)) + ->getFieldAmount(sfFeeAmount) == XRP(10)); + // clear flag env(sponsor::set_fee( sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), sponsor::sponseeAcc(alice)); env.close(); - env(pay(alice, bob, XRP(100)), - fee(XRP(10)), - sponsor::as(sponsor, tfSponsorFee), - ter(tesSUCCESS)); - env.close(); + // Payment is re-applied + BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice)) + ->isFieldPresent(sfFeeAmount)); } } @@ -1228,7 +1257,10 @@ class Sponsor_test : public beast::unit_test::suite fee(XRP(10)), sponsor::as(sponsor, tfSponsorReserve), ter(terNO_SPONSORSHIP)); - env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); // clear flag env(sponsor::set_reserve( @@ -1236,11 +1268,10 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sponseeAcc(alice)); env.close(); - env(check::create(alice, bob, XRP(100)), - fee(XRP(10)), - sponsor::as(sponsor, tfSponsorReserve), - ter(tesSUCCESS)); - env.close(); + // CheckCreate is re-applied + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); } { @@ -1263,7 +1294,11 @@ class Sponsor_test : public beast::unit_test::suite fee(XRP(10)), sponsor::as(sponsor, tfSponsorFee), ter(terNO_SPONSORSHIP)); - env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT( + env.le(keylet::sponsor(sponsor, alice)) + ->getFieldAmount(sfFeeAmount) == XRP(10)); // clear flag env(sponsor::set_fee( @@ -1271,11 +1306,10 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sponseeAcc(alice)); env.close(); - env(check::create(alice, bob, XRP(100)), - fee(XRP(10)), - sponsor::as(sponsor, tfSponsorFee), - ter(tesSUCCESS)); - env.close(); + // CheckCreate is re-applied + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice)) + ->isFieldPresent(sfFeeAmount)); } } diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 8f61f86b448..544f2bbd562 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -309,29 +309,23 @@ SponsorshipSet::doApply() // Update if (feeAmount) { + auto const currentFeeAmount = + (*sponsorObjSle)[~sfFeeAmount].value_or(XRPAmount(0)); + auto feeAmountDelta = XRPAmount(*feeAmount - currentFeeAmount); + // transfer feeAmount to ledger entry - (*sponsorAccSle)[sfBalance] -= *feeAmount; - if ((*sponsorObjSle).isFieldPresent(sfFeeAmount)) - { - auto const oldFeeAmount = - (*sponsorObjSle).getFieldAmount(sfFeeAmount); - auto const newFeeAmount = oldFeeAmount + *feeAmount; - (*sponsorObjSle).setFieldAmount(sfFeeAmount, newFeeAmount); - } - else + if (feeAmountDelta != beast::zero) { + (*sponsorAccSle)[sfBalance] -= feeAmountDelta; (*sponsorObjSle).setFieldAmount(sfFeeAmount, *feeAmount); } } if (maxFee) - { (*sponsorObjSle)[sfMaxFee] = *maxFee; - } if (reserveCount) - (*sponsorObjSle)[sfReserveCount] = - (*sponsorObjSle).getFieldU32(sfReserveCount) + *reserveCount; + (*sponsorObjSle)[sfReserveCount] = *reserveCount; // update Flags auto flags = sponsorObjSle->getFieldU32(sfFlags); @@ -350,7 +344,7 @@ SponsorshipSet::doApply() if (flags != (*sponsorObjSle)[sfFlags]) (*sponsorObjSle)[sfFlags] = flags; - ctx_.view().update(sponsorObjSle); + view().update(sponsorObjSle); return tesSUCCESS; } From 5b91d815d18e5ae70965037d551f188c90d1415e Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 15 Jan 2026 14:29:37 +0900 Subject: [PATCH 094/249] address revew (std::optional + std::shared_ptr) --- include/xrpl/ledger/View.h | 25 ++++--- src/libxrpl/ledger/View.cpp | 72 +++++++++---------- src/test/app/Sponsor_test.cpp | 1 + src/xrpld/app/tx/detail/AMMWithdraw.cpp | 4 +- src/xrpld/app/tx/detail/CashCheck.cpp | 2 +- src/xrpld/app/tx/detail/Escrow.cpp | 4 +- .../app/tx/detail/MPTokenIssuanceCreate.cpp | 5 +- .../app/tx/detail/NFTokenAcceptOffer.cpp | 2 +- src/xrpld/app/tx/detail/NFTokenUtils.cpp | 4 +- src/xrpld/app/tx/detail/PayChan.cpp | 7 +- src/xrpld/app/tx/detail/SetOracle.cpp | 2 +- src/xrpld/app/tx/detail/SetTrust.cpp | 5 +- .../app/tx/detail/SponsorshipTransfer.cpp | 4 +- src/xrpld/app/tx/detail/VaultDelete.cpp | 2 +- src/xrpld/app/tx/detail/XChainBridge.cpp | 5 +- 15 files changed, 69 insertions(+), 75 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 53407a4dfb7..0e0aea4da08 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -565,40 +565,40 @@ checkInsufficientReserve( STTx const& tx, std::shared_ptr accSle, STAmount const& accBalance, - std::optional> const& sponsorSle, + std::shared_ptr const& sponsorSle, std::int32_t ownerCountDelta, std::int32_t accountCountDelta = 0); std::optional getTxReserveSponsorAccountID(STTx const& tx); -std::optional> +std::shared_ptr getTxReserveSponsor(ApplyView& view, STTx const& tx); -std::optional> +std::shared_ptr const getTxReserveSponsor(ReadView const& view, STTx const& tx); std::optional getLedgerEntryReserveSponsorAccountID( - std::shared_ptr sle, + std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsorAccount); -std::optional> +std::shared_ptr getLedgerEntryReserveSponsor( ApplyView& view, - std::shared_ptr sle, + std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsorAccount); -std::optional> +std::shared_ptr const getLedgerEntryReserveSponsor( ReadView const& view, - std::shared_ptr sle, + std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsorAccount); void addSponsorToLedgerEntry( std::shared_ptr const& sle, - std::optional> const& sponsorSle, + std::shared_ptr const& sponsorSle, SF_ACCOUNT const& field = sfSponsorAccount); void @@ -617,7 +617,7 @@ void adjustOwnerCount( ApplyView& view, std::shared_ptr const& accountSle, - std::optional> const& sponsorSle, + std::shared_ptr const& sponsorSle, std::int32_t amount, beast::Journal j); @@ -626,7 +626,7 @@ adjustOwnerCount( ApplyView& view, STTx const& tx, std::shared_ptr const& accountSle, - std::optional> const& sponsorSle, + std::shared_ptr const& sponsorSle, std::int32_t amount, beast::Journal j) { @@ -646,8 +646,7 @@ adjustOwnerCount( view, tx, view.peek(keylet::account(account)), - sponsor ? view.peek(keylet::account(*sponsor)) - : std::optional>(), + sponsor ? view.peek(keylet::account(*sponsor)) : std::shared_ptr(), amount, j); } diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index a18d0230bc6..a24090454c2 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1167,7 +1167,7 @@ checkInsufficientReserve( STTx const& tx, std::shared_ptr accSle, STAmount const& accBalance, - std::optional> const& sponsorSle, + std::shared_ptr const& sponsorSle, std::int32_t ownerCountDelta, std::int32_t accountCountDelta) { @@ -1176,7 +1176,7 @@ checkInsufficientReserve( auto const isCoSigning = isSponsorReserveCoSigning(tx); auto const sle = view.read(keylet::sponsor( - (*sponsorSle)->getAccountID(sfAccount), + sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount))); if (isCoSigning) @@ -1190,15 +1190,14 @@ checkInsufficientReserve( return tesSUCCESS; } - auto const sponsorBalance = - (*sponsorSle)->getFieldAmount(sfBalance); + auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance); STAmount const sponsorReserve{view.fees().accountReserve( - (*sponsorSle)->getFieldU32(sfOwnerCount), - (*sponsorSle)->getFieldU32(sfSponsoredOwnerCount), - (*sponsorSle)->getFieldU32(sfSponsoringOwnerCount) + + sponsorSle->getFieldU32(sfOwnerCount), + sponsorSle->getFieldU32(sfSponsoredOwnerCount), + sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ownerCountDelta, - (*sponsorSle)->isFieldPresent(sfSponsorAccount), - (*sponsorSle)->getFieldU32(sfSponsoringAccountCount) + + sponsorSle->isFieldPresent(sfSponsorAccount), + sponsorSle->getFieldU32(sfSponsoringAccountCount) + accountCountDelta)}; if (sponsorBalance < sponsorReserve) @@ -1239,65 +1238,65 @@ getTxReserveSponsorAccountID(STTx const& tx) if (sponsorObj.isFlag(tfSponsorReserve)) return sponsorObj.getAccountID(sfAccount); } - return std::nullopt; + return {}; } -std::optional> +std::shared_ptr getTxReserveSponsor(ApplyView& view, STTx const& tx) { auto const sponsorID = getTxReserveSponsorAccountID(tx); if (sponsorID) return view.peek(keylet::account(*sponsorID)); - return std::nullopt; + return {}; } -std::optional> +std::shared_ptr const getTxReserveSponsor(ReadView const& view, STTx const& tx) { auto const sponsorID = getTxReserveSponsorAccountID(tx); if (sponsorID) return view.read(keylet::account(*sponsorID)); - return std::nullopt; + return {}; } std::optional getLedgerEntryReserveSponsorAccountID( - std::shared_ptr sle, + std::shared_ptr const& sle, SF_ACCOUNT const& field) { if (sle->isFieldPresent(field)) return sle->getAccountID(field); - return std::nullopt; + return {}; } -std::optional> +std::shared_ptr getLedgerEntryReserveSponsor( ApplyView& view, - std::shared_ptr sle, + std::shared_ptr const& sle, SF_ACCOUNT const& field) { auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); if (sponsorID) return view.peek(keylet::account(*sponsorID)); - return std::nullopt; + return {}; } -std::optional> +std::shared_ptr const getLedgerEntryReserveSponsor( ReadView const& view, - std::shared_ptr sle, + std::shared_ptr const& sle, SF_ACCOUNT const& field) { auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); if (sponsorID) return view.read(keylet::account(*sponsorID)); - return std::nullopt; + return {}; } void addSponsorToLedgerEntry( std::shared_ptr const& sle, - std::optional> const& sponsorSle, + std::shared_ptr const& sponsorSle, SF_ACCOUNT const& field) { XRPL_ASSERT( @@ -1306,7 +1305,7 @@ addSponsorToLedgerEntry( (sle->getType() != ltRIPPLE_STATE && field == sfSponsorAccount), "addSponsorToLedgerEntry : Invalid field to the LedgerEntry"); if (sponsorSle) - sle->setAccountID(field, (*sponsorSle)->getAccountID(sfAccount)); + sle->setAccountID(field, sponsorSle->getAccountID(sfAccount)); } void @@ -1333,7 +1332,7 @@ void adjustOwnerCount( ApplyView& view, std::shared_ptr const& accountSle, - std::optional> const& sponsorSle, + std::shared_ptr const& sponsorSle, std::int32_t amount, beast::Journal j) { @@ -1345,19 +1344,19 @@ adjustOwnerCount( { XRPL_ASSERT(sponsorSle, "xrpl::adjustOwnerCount : sponsor exists"); auto const account = accountSle->getAccountID(sfAccount); - auto const sponsorAcc = (*sponsorSle)->getAccountID(sfAccount); + auto const sponsorAcc = (sponsorSle)->getAccountID(sfAccount); { // modify sponsor's SponsoringOwnerCount std::uint32_t const current{ - (*sponsorSle)->getFieldU32(sfSponsoringOwnerCount)}; + (sponsorSle)->getFieldU32(sfSponsoringOwnerCount)}; std::uint32_t const adjusted = confineOwnerCount(current, amount, sponsorAcc, j); view.adjustOwnerCountHook(sponsorAcc, current, adjusted); if (adjusted == 0) - (*sponsorSle)->makeFieldAbsent(sfSponsoringOwnerCount); + (sponsorSle)->makeFieldAbsent(sfSponsoringOwnerCount); else - (*sponsorSle)->setFieldU32(sfSponsoringOwnerCount, adjusted); - view.update(*sponsorSle); + (sponsorSle)->setFieldU32(sfSponsoringOwnerCount, adjusted); + view.update(sponsorSle); } { // modify account's SponsoredOwnerCount @@ -1741,9 +1740,8 @@ addEmptyHolding( tx, sleDst, priorBalance, - sponsorAccountID - ? view.peek(keylet::account(*sponsorAccountID)) - : std::optional>(std::nullopt), + sponsorAccountID ? view.peek(keylet::account(*sponsorAccountID)) + : std::shared_ptr(), 1); !isTesSuccess(ret)) return tecNO_LINE_INSUF_RESERVE; @@ -1850,7 +1848,7 @@ authorizeMPToken( // items. This is similar to the reserve requirements of trust lines. // If PreFunded Sponsor, it must be checked whether sufficient // ReserveCount exists. - if (ownerCount(sponsor.value_or(sleAcct)) >= 2 || + if (ownerCount(sponsor ? sponsor : sleAcct) >= 2 || isSponsoredAndPreFunded) { if (auto const ret = checkInsufficientReserve( @@ -2036,7 +2034,7 @@ trustCreate( uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); } - std::optional> sponsorSle = std::nullopt; + std::shared_ptr sponsorSle = {}; if (sponsorAccountID) sponsorSle = view.peek(keylet::account(*sponsorAccountID)); @@ -2098,7 +2096,7 @@ removeEmptyHolding( if (!sleLowAccount) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleLowAccount, std::nullopt, -1, journal); + adjustOwnerCount(view, sleLowAccount, {}, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. @@ -2113,7 +2111,7 @@ removeEmptyHolding( if (!sleHighAccount) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleHighAccount, std::nullopt, -1, journal); + adjustOwnerCount(view, sleHighAccount, {}, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index b0178cc909a..06256b72b60 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -298,6 +298,7 @@ class Sponsor_test : public beast::unit_test::suite sponsor::as(sponsor, tfSponsorReserve), msig(sfSponsorSignature, {signer1}), ter(tesSUCCESS)); + env.close(); env(signers(sponsor, 2, {{signer1, 1}, {signer2, 1}})); env.close(); diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index 9aba06ce0ec..5192865877b 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -589,7 +589,7 @@ AMMWithdraw::withdraw( return tecINTERNAL; // LCOV_EXCL_LINE auto const balance = (*sleAccount)[sfBalance].xrp(); std::uint32_t const count = - ownerCount(sponsorSle ? *sponsorSle : sleAccount); + ownerCount(sponsorSle ? sponsorSle : sleAccount); if (count >= 2) { if (auto const ret = checkInsufficientReserve( @@ -598,7 +598,7 @@ AMMWithdraw::withdraw( sleAccount, std::max(priorBalance, balance), sponsor ? view.read(keylet::account(*sponsor)) - : std::optional>{}, + : std::shared_ptr(), 1); !isTesSuccess(ret)) return ret; diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 6cc35fecfe8..5cf66fe9c89 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -329,7 +329,7 @@ CashCheck::doApply() auto const sleDst = psb.peek(keylet::account(account_)); auto const sponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); - std::optional> sponsorSle = std::nullopt; + std::shared_ptr sponsorSle = {}; if (sponsorAcc) sponsorSle = psb.peek(keylet::account(*sponsorAcc)); diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index b71d5a903a2..2852a015299 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -444,7 +444,7 @@ EscrowCreate::doApply() ctx_.tx, sle, mSourceBalance - STAmount(amount).xrp(), - std::optional>(), + {}, 1); !isTesSuccess(ret)) return tecUNFUNDED; @@ -780,7 +780,7 @@ escrowUnlockApplyHelper( { // Can the account cover the trust line's reserve? auto const sponsorAcc = getTxReserveSponsorAccountID(tx); - std::optional> sponsorSle = std::nullopt; + std::shared_ptr sponsorSle = {}; if (sponsorAcc) sponsorSle = view.peek(keylet::account(*sponsorAcc)); if (auto const ret = checkInsufficientReserve( diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp index 5ea408e3e4c..7f79c2caaa9 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp @@ -88,8 +88,9 @@ MPTokenIssuanceCreate::create( if (!acct) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE - auto const sponsor = - !isPseudoAccount((acct)) ? getTxReserveSponsor(view, tx) : std::nullopt; + auto const sponsor = !isPseudoAccount((acct)) + ? getTxReserveSponsor(view, tx) + : std::shared_ptr(); if (args.priorBalance) { if (auto const ret = checkInsufficientReserve( diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp index 103856708f8..c70c05da1ce 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp @@ -407,7 +407,7 @@ NFTokenAcceptOffer::transferNFToken( { auto const sponsor = account_ == buyer ? getTxReserveSponsor(ctx_.view(), ctx_.tx) - : std::optional>(); + : std::shared_ptr(); if (auto const ret = checkInsufficientReserve( ctx_.view(), ctx_.tx, sleBuyer, buyerBalance, sponsor, 0); !isTesSuccess(ret)) diff --git a/src/xrpld/app/tx/detail/NFTokenUtils.cpp b/src/xrpld/app/tx/detail/NFTokenUtils.cpp index 6548f00300b..f06814d2592 100644 --- a/src/xrpld/app/tx/detail/NFTokenUtils.cpp +++ b/src/xrpld/app/tx/detail/NFTokenUtils.cpp @@ -280,9 +280,9 @@ insertToken( std::shared_ptr const& newPage, AccountID const& owner, std::optional const& sponsor) -> TER { - std::optional> const sponsorSle = sponsor + std::shared_ptr const sponsorSle = sponsor ? view.peek(keylet::account(*sponsor)) - : std::optional>{std::nullopt}; + : std::shared_ptr(); if (isReserveSponsored(tx)) { diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index 6e5ad5f1066..ba806122705 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -369,12 +369,7 @@ PayChanFund::doApply() return ret; if (auto const ret = checkInsufficientReserve( - ctx_.view(), - ctx_.tx, - sle, - balance - ctx_.tx[sfAmount], - std::optional>(), - 0); + ctx_.view(), ctx_.tx, sle, balance - ctx_.tx[sfAmount], {}, 0); !isTesSuccess(ret)) return tecUNFUNDED; } diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index 6afd8b5dc3f..96ffca0ee08 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -171,7 +171,7 @@ SetOracle::preclaim(PreclaimContext const& ctx) static bool adjustOwnerCount( ApplyContext& ctx, - std::optional> const& sponsor, + std::shared_ptr const& sponsor, int count) { if (auto const sleAccount = diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 1c58f6a7929..01bd34f3488 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -354,11 +354,12 @@ SetTrust::doApply() // could use the extra XRP for their own purposes. auto const txSponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); - std::optional> txSponsorSle = std::nullopt; + std::shared_ptr txSponsorSle = {}; if (txSponsorAcc) txSponsorSle = view().peek(keylet::account(*txSponsorAcc)); - std::uint32_t const uOwnerCount = ownerCount(txSponsorSle.value_or(sle)); + std::uint32_t const uOwnerCount = + ownerCount(txSponsorSle ? txSponsorSle : sle); bool const isSponsoredAndPreFunded = txSponsorSle && !isSponsorReserveCoSigning(ctx_.tx); diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 36dbf54cdbd..900b658d0ca 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -179,7 +179,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { // transfer sponsor // check if the object owner isn't the same as the new sponsor - if ((*newSponsor)->getAccountID(sfAccount) == owner) + if (newSponsor->getAccountID(sfAccount) == owner) // checked in above return tecINTERNAL; // LCOV_EXCL_LINE } @@ -210,7 +210,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (accSle->isFieldPresent(sfSponsorAccount)) { // check not same account - if ((*newSponsor)->getAccountID(sfAccount) == + if (newSponsor->getAccountID(sfAccount) == accSle->getAccountID(sfAccount)) // already checked in Transactor::preflight1() return tecINTERNAL; // LCOV_EXCL_LINE diff --git a/src/xrpld/app/tx/detail/VaultDelete.cpp b/src/xrpld/app/tx/detail/VaultDelete.cpp index 3e87bca3119..d7234efd44c 100644 --- a/src/xrpld/app/tx/detail/VaultDelete.cpp +++ b/src/xrpld/app/tx/detail/VaultDelete.cpp @@ -138,7 +138,7 @@ VaultDelete::doApply() return tefBAD_LEDGER; // LCOV_EXCL_STOP } - adjustOwnerCount(view(), pseudoAcct, std::nullopt, -1, j_); + adjustOwnerCount(view(), pseudoAcct, {}, -1, j_); view().erase(mpt); diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index 49c87ddbb35..12f05140323 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -1058,9 +1058,8 @@ applyCreateAccountAttestations( // Check reserve auto const balance = (*sleDoor)[sfBalance]; - auto const sponsor = std::optional>(); - if (auto const ret = checkInsufficientReserve( - psb, tx, sleDoor, balance, sponsor, 1); + if (auto const ret = + checkInsufficientReserve(psb, tx, sleDoor, balance, {}, 1); !isTesSuccess(ret)) return Unexpected(ret); // tecINSUFFICIENT_RESERVE } From 9e65dba253b4b9897ab4eb566a7c4f0b005db01a Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 15 Jan 2026 14:32:47 +0900 Subject: [PATCH 095/249] fix redundant co-signed sponsor check --- src/xrpld/app/tx/detail/Transactor.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 27c90c65aaa..241a8e2ea83 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -795,22 +795,20 @@ Transactor::checkSign( if (sigObject.isFieldPresent(sfSponsorSignature)) { + // Co-signed sponsorship + // Sanity check: already checked in preflight1 if (!sigObject.isFieldPresent(sfSponsor)) return tefINTERNAL; // LCOV_EXCL_LINE auto const sponsorObj = sigObject.getFieldObject(sfSponsor); - auto const isCoSigned = sigObject.isFieldPresent(sfSponsorSignature); - if (isCoSigned) - { - auto const sponsorAcc = sponsorObj.getAccountID(sfAccount); - auto const sponsorSignature = - sigObject.getFieldObject(sfSponsorSignature); - if (auto const ret = - checkSign(view, flags, {}, sponsorAcc, sponsorSignature, j); - !isTesSuccess(ret)) - return ret; - } + auto const sponsorAcc = sponsorObj.getAccountID(sfAccount); + auto const sponsorSignature = + sigObject.getFieldObject(sfSponsorSignature); + if (auto const ret = + checkSign(view, flags, {}, sponsorAcc, sponsorSignature, j); + !isTesSuccess(ret)) + return ret; } // If the pk is empty and not simulate or simulate and signers, From c321e1070df2e0127f4b0e4fd42a73248659f59a Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 15 Jan 2026 14:51:05 +0900 Subject: [PATCH 096/249] address reviews --- include/xrpl/protocol/Fees.h | 12 ++++++++---- src/libxrpl/protocol/TER.cpp | 2 +- src/xrpld/app/tx/detail/Payment.cpp | 6 ++++-- src/xrpld/app/tx/detail/Transactor.cpp | 4 ++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 95f9d147b23..d86ea6e8a1c 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -34,10 +34,14 @@ struct Fees bool isAccountSponsored = false, std::size_t sponsoringAccountCount = 0) const { - return (isAccountSponsored ? XRPAmount(0) : reserve) + - increment * - (ownerCount + sponsoringOwnerCount - sponsoredOwnerCount) + - reserve * sponsoringAccountCount; + auto const accountReserveUnits = + (isAccountSponsored ? 0 : 1) + sponsoringAccountCount; + + auto const ownerReserveUnits = + (ownerCount - sponsoredOwnerCount) + sponsoringOwnerCount; + + return (reserve * accountReserveUnits) + + (increment * ownerReserveUnits); } }; diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index b6303efef2b..d839b5f6075 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -109,7 +109,7 @@ transResults() MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."), MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."), MAKE_ERROR(tecNO_DELEGATE_PERMISSION, "Delegated account lacks permission to perform this transaction."), - MAKE_ERROR(tecNO_SPONSOR_PERMISSION, "Does not have permission to sponsored this transaction."), + MAKE_ERROR(tecNO_SPONSOR_PERMISSION, "Sponsor has not authorized this transaction."), MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."), MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."), diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 19413617cdc..4c1bdf02a80 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -337,6 +337,9 @@ Payment::preclaim(PreclaimContext const& ctx) } else { + // The tfSponsorCreatedAccount flag is specific to account creation via + // sponsorship. If the destination account already exists, applying this + // flag is invalid. if (txFlags & tfSponsorCreatedAccount) return tecNO_SPONSOR_PERMISSION; @@ -425,12 +428,11 @@ Payment::doApply() { auto const sponsor = view().peek(keylet::account(account_)); if (!sponsor) - return tecINTERNAL; // LCOV_EXCL_LINE + return tefINTERNAL; // LCOV_EXCL_LINE auto const currentSponsoringAccountCount = sponsor->getFieldU32(sfSponsoringAccountCount); sponsor->setFieldU32( sfSponsoringAccountCount, currentSponsoringAccountCount + 1); - view().update(sponsor); addSponsorToLedgerEntry(sleDst, sponsor); view().update(sponsor); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 241a8e2ea83..31a38ec1aab 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -805,8 +805,8 @@ Transactor::checkSign( auto const sponsorAcc = sponsorObj.getAccountID(sfAccount); auto const sponsorSignature = sigObject.getFieldObject(sfSponsorSignature); - if (auto const ret = - checkSign(view, flags, {}, sponsorAcc, sponsorSignature, j); + if (auto const ret = checkSign( + view, flags, std::nullopt, sponsorAcc, sponsorSignature, j); !isTesSuccess(ret)) return ret; } From 3568df43c482335e6ef9f7ee4de0912584763bcf Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 15 Jan 2026 15:01:55 +0900 Subject: [PATCH 097/249] add `calculateReserve` helper --- include/xrpl/ledger/View.h | 3 +++ src/libxrpl/ledger/View.cpp | 31 ++++++++++++++---------- src/xrpld/app/tx/detail/Payment.cpp | 21 +++------------- src/xrpld/app/tx/detail/XChainBridge.cpp | 15 +----------- 4 files changed, 25 insertions(+), 45 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 0e0aea4da08..51a46f20ee5 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -553,6 +553,9 @@ areCompatible( uint32_t ownerCount(std::shared_ptr const& sponsorSle); +XRPAmount +calculateReserve(std::shared_ptr const& sle, Fees const& fees); + bool isReserveSponsored(STTx const& tx); diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index a24090454c2..256241f5694 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -732,20 +732,10 @@ xrpLiquid( std::uint32_t const ownerCount = confineOwnerCount( view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); - std::size_t sponsoredOwnerCount = sle->getFieldU32(sfSponsoredOwnerCount); - std::size_t sponsoringOwnerCount = sle->getFieldU32(sfSponsoringOwnerCount); - bool isAccountSponsored = sle->isFieldPresent(sfSponsorAccount); - std::size_t sponsoringAccountCount = - sle->getFieldU32(sfSponsoringAccountCount); - // Pseudo-accounts have no reserve requirement - auto const reserve = isPseudoAccount(sle) ? XRPAmount{0} - : view.fees().accountReserve( - ownerCount, - sponsoredOwnerCount, - sponsoringOwnerCount, - isAccountSponsored, - sponsoringAccountCount); + auto const reserve = isPseudoAccount(sle) + ? XRPAmount{0} + : calculateReserve(sle, view.fees()); auto const fullBalance = sle->getFieldAmount(sfBalance); @@ -1146,6 +1136,21 @@ ownerCount(std::shared_ptr const& sponsorSle) return ownerCount + sponsoringOwnerCount - sponsoredOwnerCount; } +XRPAmount +calculateReserve(std::shared_ptr const& sle, Fees const& fees) +{ + XRPL_ASSERT( + sle->getType() == ltACCOUNT_ROOT, + "xrpl::calculateReserve : valid sle type"); + + return fees.accountReserve( + sle->getFieldU32(sfOwnerCount), + sle->getFieldU32(sfSponsoredOwnerCount), + sle->getFieldU32(sfSponsoringOwnerCount), + sle->isFieldPresent(sfSponsorAccount), + sle->getFieldU32(sfSponsoringAccountCount)); +} + bool isReserveSponsored(STTx const& tx) { diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 4c1bdf02a80..f85840269cf 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -612,24 +612,9 @@ Payment::doApply() if (!sleSrc) return tefINTERNAL; // LCOV_EXCL_LINE - // ownerCount is the number of entries in this ledger for this - // account that require a reserve. - auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount); - - // This is the total reserve in drops. - std::size_t sponsoredOwnerCount = - sleSrc->getFieldU32(sfSponsoredOwnerCount); - std::size_t sponsoringOwnerCount = - sleSrc->getFieldU32(sfSponsoringOwnerCount); - bool isAccountSponsored = sleSrc->isFieldPresent(sfSponsorAccount); - std::size_t sponsoringAccountCount = - sleSrc->getFieldU32(sfSponsoringAccountCount); - auto const reserve = view().fees().accountReserve( - ownerCount, - sponsoredOwnerCount, - sponsoringOwnerCount, - isAccountSponsored, - sponsoringAccountCount); + // the number of reserves in this ledger for this account that require a + // reserve. + auto const reserve = calculateReserve(sleSrc, view().fees()); // mPriorBalance is the balance on the sending account BEFORE the // fees were charged. We want to make sure we have enough reserve diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index 12f05140323..de7f21252fb 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -428,20 +428,7 @@ transferHelper( return tecINTERNAL; // LCOV_EXCL_LINE { - auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount); - std::size_t sponsoredOwnerCount = - sleSrc->getFieldU32(sfSponsoredOwnerCount); - std::size_t sponsoringOwnerCount = - sleSrc->getFieldU32(sfSponsoringOwnerCount); - bool isAccountSponsored = sleSrc->isFieldPresent(sfSponsorAccount); - std::size_t sponsoringAccountCount = - sleSrc->getFieldU32(sfSponsoringAccountCount); - auto const reserve = psb.fees().accountReserve( - ownerCount, - sponsoredOwnerCount, - sponsoringOwnerCount, - isAccountSponsored, - sponsoringAccountCount); + auto const reserve = calculateReserve(sleSrc, psb.fees()); auto const availableBalance = [&]() -> STAmount { STAmount const curBal = (*sleSrc)[sfBalance]; From c3e5bcfafd6f856a92dd3afc5eaca1b7216bd57a Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 15 Jan 2026 15:53:29 +0900 Subject: [PATCH 098/249] address review (DeleteAccount) --- src/xrpld/app/tx/detail/DeleteAccount.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index c39c57a8e84..1f51c97c5e9 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -407,6 +407,9 @@ DeleteAccount::doApply() if (ter != tesSUCCESS) return ter; + if (src->isFieldPresent(sfSponsoredOwnerCount)) + return tefINTERNAL; // LCOV_EXCL_LINE + // Transfer any XRP remaining after the fee is paid to the destination: (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance; (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance; @@ -417,6 +420,10 @@ DeleteAccount::doApply() auto const sponsorAcc = src->getAccountID(sfSponsorAccount); auto sponsorSle = view().peek(keylet::account(sponsorAcc)); + if (!sponsorSle || + !sponsorSle->isFieldPresent(sfSponsoringAccountCount)) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const sponsoringAccountCount = sponsorSle->getFieldU32(sfSponsoringAccountCount); @@ -427,6 +434,9 @@ DeleteAccount::doApply() sfSponsoringAccountCount, sponsoringAccountCount - 1); view().update(sponsorSle); + // Following line might look redundant, but without it, sfSponsorAccount + // would end up remaining in after-ltAccountRoot during the + // InvariantCheck. (*src).makeFieldAbsent(sfSponsorAccount); } From 204138fb0efe317dff6a4a25454299906806549b Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 15 Jan 2026 18:48:06 +0900 Subject: [PATCH 099/249] Revert the use of calculateReserve in xrpLiquid --- src/libxrpl/ledger/View.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 5e63c78ebf9..b1a8e7c15c7 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -688,10 +688,20 @@ xrpLiquid( std::uint32_t const ownerCount = confineOwnerCount( view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); + std::size_t sponsoredOwnerCount = sle->getFieldU32(sfSponsoredOwnerCount); + std::size_t sponsoringOwnerCount = sle->getFieldU32(sfSponsoringOwnerCount); + bool isAccountSponsored = sle->isFieldPresent(sfSponsorAccount); + std::size_t sponsoringAccountCount = + sle->getFieldU32(sfSponsoringAccountCount); + // Pseudo-accounts have no reserve requirement - auto const reserve = isPseudoAccount(sle) - ? XRPAmount{0} - : calculateReserve(sle, view.fees()); + auto const reserve = isPseudoAccount(sle) ? XRPAmount{0} + : view.fees().accountReserve( + ownerCount, + sponsoredOwnerCount, + sponsoringOwnerCount, + isAccountSponsored, + sponsoringAccountCount); auto const fullBalance = sle->getFieldAmount(sfBalance); From f333dd1b2e5acd4cc3c0563f538e42e5a4e7c953 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 27 Jan 2026 10:33:52 +0900 Subject: [PATCH 100/249] add signature existence check, consume ReserveCount if pre-funded sponsoring --- src/test/app/Sponsor_test.cpp | 138 ++++++++++++++++- .../app/tx/detail/SponsorshipTransfer.cpp | 139 +++++++++++------- 2 files changed, 222 insertions(+), 55 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 06256b72b60..5992cc48dc7 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -7,6 +7,8 @@ #include #include +#include "test/jtx/sponsor.h" + namespace xrpl { namespace test { @@ -587,6 +589,12 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + // sfSponsor provided but sfSponsorSignature not provided + env(sponsor::transfer(alice), + sponsor::as(sponsor1, tfSponsorReserve), + ter(temMALFORMED)); + env.close(); + adjustAccountXRPBalance( env, sponsor1, accountReserve(env, 2) - drops(1)); @@ -698,7 +706,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); } { - // sponsor object + // sponsor object (co-signing) Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); @@ -823,6 +831,134 @@ class Sponsor_test : public beast::unit_test::suite auto const sle3 = env.le(keylet::unchecked(checkId)); BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); } + { + // sponsor object (pre-funded + no ltSponsorship entry) + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor1, tfSponsorReserve), + ter(terNO_SPONSORSHIP)); + env.close(); + + env(sponsor::set_reserve(sponsor2, 0, 1), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor1, tfSponsorReserve), + ter(terNO_SPONSORSHIP)); + env.close(); + } + { + // sponsor object (pre-funded) + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + // insufficient reserve count + env(sponsor::set_fee(sponsor1, 0, XRP(100)), + sponsor::sponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor1, tfSponsorReserve), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + env(sponsor::set_reserve(sponsor1, 0, 100), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor1, tfSponsorReserve)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + auto const sle1 = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(sle1->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); + auto const sponsorSle = env.le(keylet::sponsor(sponsor1, alice)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfReserveCount) == 99); + + // transfer sponsor + env(sponsor::set_reserve(sponsor2, 0, 100), + sponsor::sponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, checkId), + sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + auto const sle2 = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); + auto const sponsorSle2 = env.le(keylet::sponsor(sponsor2, alice)); + BEAST_EXPECT(sponsorSle2->getFieldU32(sfReserveCount) == 99); + + // dissolve sponsor + adjustAccountXRPBalance(env, alice, reserve(env, 1)); + env(sponsor::transfer(alice, checkId)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + BEAST_EXPECT(!env.le(keylet::account(sponsor2)) + ->isFieldPresent(sfSponsoringOwnerCount)); + auto const sle3 = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); + auto const sponsorSle3 = env.le(keylet::sponsor(sponsor2, alice)); + BEAST_EXPECT( + sponsorSle3->getFieldU32(sfReserveCount) == 99); // no change + } { // invalid transfer diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 900b658d0ca..cb68fa0a5a8 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -15,6 +15,20 @@ namespace xrpl { NotTEC SponsorshipTransfer::preflight(PreflightContext const& ctx) { + // When an account sponsoring, sfSponsorSignature must be provided + auto const newSponsor = getTxReserveSponsorAccountID(ctx.tx); + bool const isObjectSponsor = ctx.tx.isFieldPresent(sfObjectID); + + // both sfSponsor and sfObjectID are provided + bool const isNewAccountSponsor = newSponsor && !isObjectSponsor; + + if (isNewAccountSponsor && !ctx.tx.isFieldPresent(sfSponsorSignature)) + { + JLOG(ctx.j.debug()) + << "preflight: sponsoring an account needs co-signing sponsor"; + return temMALFORMED; + } + return tesSUCCESS; } @@ -154,7 +168,9 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) bool const isObjectSponsor = index != std::nullopt; - auto const accSle = ctx.view.read(keylet::account(ctx.tx[sfAccount])); + auto const account = ctx.tx[sfAccount]; + + auto const accSle = ctx.view.read(keylet::account(account)); if (!accSle) return tecINTERNAL; // LCOV_EXCL_LINE @@ -166,9 +182,8 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) auto const ownerCountDelta = getLedgerEntryOwnerCount(sle); - auto const owner = - getLedgerEntryOwner(ctx.view, sle, ctx.tx[sfAccount]); - if (!owner || owner != ctx.tx[sfAccount]) + auto const owner = getLedgerEntryOwner(ctx.view, sle, account); + if (!owner || owner != account) return tecNO_PERMISSION; auto const& sponsorField = getLedgerEntrySponsorField(sle, *owner); @@ -240,6 +255,37 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } +TER +adjustReserveCount( + ApplyView& view, + AccountID const& account, + AccountID const& sponsor, + int32_t delta) +{ + if (delta == 0) + return tesSUCCESS; + if (delta > 0) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const sponsorKeylet = keylet::sponsor(sponsor, account); + auto const sponsorSle = view.peek(sponsorKeylet); + if (!sponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const reserveCount = sponsorSle->getFieldU32(sfReserveCount); + int32_t const afterReserveCount = reserveCount + delta; + + if (afterReserveCount < 0) + // already checked in preclaim() + return tefINTERNAL; // LCOV_EXCL_LINE + + if (afterReserveCount == 0) + sponsorSle->makeFieldAbsent(sfReserveCount); + else + sponsorSle->setFieldU32(sfReserveCount, afterReserveCount); + view.update(sponsorSle); + return tesSUCCESS; +} + TER SponsorshipTransfer::doApply() { @@ -252,8 +298,19 @@ SponsorshipTransfer::doApply() if (!accSle) return tefINTERNAL; // LCOV_EXCL_LINE + auto const setSponsorFieldU32 = + [](auto const& sle, auto const& field, auto const& delta) { + auto const newValue = sle->getFieldU32(field) + delta; + if (newValue == 0) + sle->makeFieldAbsent(field); + else + sle->setFieldU32(field, newValue); + }; + if (isObjectSponsor) { + auto const hasSignature = tx.isFieldPresent(sfSponsorSignature); + // transfer object sponsor auto const objSle = view().peek(keylet::unchecked(*index)); if (!objSle) @@ -280,65 +337,51 @@ SponsorshipTransfer::doApply() if (auto const oldSponsorSle = view().peek(keylet::account(oldSponsor))) { - auto const newCount = - oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - - ownerCountDelta; - if (newCount == 0) - oldSponsorSle->makeFieldAbsent(sfSponsoringOwnerCount); - else - oldSponsorSle->setFieldU32( - sfSponsoringOwnerCount, newCount); - + setSponsorFieldU32( + oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); view().update(oldSponsorSle); } else { // update owner's sponsored count - auto const newCount = - ownerSle->getFieldU32(sfSponsoredOwnerCount) + - ownerCountDelta; - if (newCount == 0) - ownerSle->makeFieldAbsent(sfSponsoredOwnerCount); - else - ownerSle->setFieldU32(sfSponsoredOwnerCount, newCount); + setSponsorFieldU32( + ownerSle, sfSponsoredOwnerCount, ownerCountDelta); view().update(ownerSle); } + // increment new sponsoring count auto const newSponsorSle = view().peek(keylet::account(newSponsor)); - auto const newCount = - newSponsorSle->getFieldU32(sfSponsoringOwnerCount) + - ownerCountDelta; - newSponsorSle->setFieldU32(sfSponsoringOwnerCount, newCount); + + setSponsorFieldU32( + newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); view().update(newSponsorSle); objSle->setAccountID(sponsorField, newSponsor); view().update(objSle); + + if (!hasSignature) + { + // pre-funded sponsor + if (auto const ter = adjustReserveCount( + view(), account_, newSponsor, -ownerCountDelta); + !isTesSuccess(ter)) + return ter; + } } else { // dissolve object sponsor auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); // decrement sponsored count - auto const newCount = - accSle->getFieldU32(sfSponsoredOwnerCount) - ownerCountDelta; - if (newCount == 0) - accSle->makeFieldAbsent(sfSponsoredOwnerCount); - else - accSle->setFieldU32(sfSponsoredOwnerCount, newCount); + setSponsorFieldU32(accSle, sfSponsoredOwnerCount, -ownerCountDelta); view().update(accSle); // decrement old sponsoring count if (auto const oldSponsorSle = view().peek(keylet::account(oldSponsor))) { - auto const newCount = - oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - - ownerCountDelta; - if (newCount == 0) - oldSponsorSle->makeFieldAbsent(sfSponsoringOwnerCount); - else - oldSponsorSle->setFieldU32( - sfSponsoringOwnerCount, newCount); + setSponsorFieldU32( + oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); view().update(oldSponsorSle); } @@ -357,9 +400,8 @@ SponsorshipTransfer::doApply() // increment new sponsoring count auto const newSponsor = sponsorObj[sfAccount]; auto const newSponsorSle = view().peek(keylet::account(newSponsor)); - newSponsorSle->setFieldU32( - sfSponsoringAccountCount, - newSponsorSle->getFieldU32(sfSponsoringAccountCount) + 1); + setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); + view().update(newSponsorSle); // decrement old sponsoring count if (accSle->isFieldPresent(sfSponsorAccount)) @@ -367,13 +409,7 @@ SponsorshipTransfer::doApply() auto const oldSponsor = accSle->getAccountID(sfSponsorAccount); auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); - auto const newCount = - oldSponsorSle->getFieldU32(sfSponsoringAccountCount) - 1; - if (newCount == 0) - oldSponsorSle->makeFieldAbsent(sfSponsoringAccountCount); - else - oldSponsorSle->setFieldU32( - sfSponsoringAccountCount, newCount); + setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); view().update(oldSponsorSle); } accSle->setAccountID(sfSponsorAccount, newSponsor); @@ -386,12 +422,7 @@ SponsorshipTransfer::doApply() accSle->makeFieldAbsent(sfSponsorAccount); // decrement account sponsoring count auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); - auto const newCount = - oldSponsorSle->getFieldU32(sfSponsoringAccountCount) - 1; - if (newCount == 0) - oldSponsorSle->makeFieldAbsent(sfSponsoringAccountCount); - else - oldSponsorSle->setFieldU32(sfSponsoringAccountCount, newCount); + setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); view().update(oldSponsorSle); } } From 1823d70b21f9bd2b226608d9737672a85bc30162 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 27 Jan 2026 10:34:15 +0900 Subject: [PATCH 101/249] refactor Oracle Reserve calculation --- src/xrpld/app/tx/detail/SetOracle.cpp | 2 +- src/xrpld/app/tx/detail/SetOracle.h | 3 +++ src/xrpld/app/tx/detail/SponsorshipTransfer.cpp | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index 96ffca0ee08..a6f82afbe75 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -40,7 +40,7 @@ SetOracle::preflight(PreflightContext const& ctx) } uint32_t -calculateOracleReserve(std::size_t count) +SetOracle::calculateOracleReserve(std::size_t count) { return count > 5 ? 2 : 1; } diff --git a/src/xrpld/app/tx/detail/SetOracle.h b/src/xrpld/app/tx/detail/SetOracle.h index c1c9a3d3fb6..ca27a76d017 100644 --- a/src/xrpld/app/tx/detail/SetOracle.h +++ b/src/xrpld/app/tx/detail/SetOracle.h @@ -23,6 +23,9 @@ class SetOracle : public Transactor { } + static uint32_t + calculateOracleReserve(std::size_t count); + static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index cb68fa0a5a8..4044291c551 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -120,7 +121,8 @@ getLedgerEntryOwnerCount(T const& sle) switch (sle->getType()) { case ltORACLE: { - return sle->getFieldArray(sfPriceDataSeries).size() > 5 ? 2 : 1; + return SetOracle::calculateOracleReserve( + sle->getFieldArray(sfPriceDataSeries).size()); } default: return 1; From ce8049a17f49292c7c82d7c30a3b441140432fd8 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 27 Jan 2026 11:34:08 +0900 Subject: [PATCH 102/249] fix parseSponsorship --- src/xrpld/rpc/handlers/LedgerEntry.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 7a573705854..21694129eae 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -795,7 +795,10 @@ parseVault( } static Expected -parseSponsorship(Json::Value const& params, Json::StaticString const fieldName) +parseSponsorship( + Json::Value const& params, + Json::StaticString const fieldName, + [[maybe_unused]] unsigned const apiVersion) { if (!params.isObject()) { From 88e870b1c64e58674a65e04ed2a620e256a13085 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 27 Jan 2026 12:38:01 +0900 Subject: [PATCH 103/249] change sfSponsor to STAccount, Sponsor flags as global flags --- include/xrpl/protocol/TxFlags.h | 6 +-- include/xrpl/protocol/detail/sfields.macro | 6 +-- src/libxrpl/ledger/View.cpp | 9 ++-- src/libxrpl/protocol/InnerObjectFormats.cpp | 7 --- src/test/app/Sponsor_test.cpp | 13 +++-- src/test/jtx/impl/sponsor.cpp | 4 +- src/test/rpc/Simulate_test.cpp | 4 +- .../app/tx/detail/SponsorshipTransfer.cpp | 6 +-- src/xrpld/app/tx/detail/Transactor.cpp | 53 ++++++++----------- 9 files changed, 47 insertions(+), 61 deletions(-) diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 50563036ce3..d241abd46ae 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -40,12 +40,12 @@ namespace xrpl { // Universal Transaction flags: constexpr std::uint32_t tfFullyCanonicalSig = 0x80000000; constexpr std::uint32_t tfInnerBatchTxn = 0x40000000; -constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; +constexpr std::uint32_t tfSponsorFee = 0x20000000; +constexpr std::uint32_t tfSponsorReserve = 0x10000000; +constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn | tfSponsorFee | tfSponsorReserve; constexpr std::uint32_t tfUniversalMask = ~tfUniversal; // Sponsor flags (Global): -constexpr std::uint32_t tfSponsorFee = 0x00000001; -constexpr std::uint32_t tfSponsorReserve = 0x00000002; constexpr std::uint32_t tfSponsorMask = ~(tfSponsorFee | tfSponsorReserve); // AccountSet flags: diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 69216e05ef4..dd4aa7c71a8 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -334,7 +334,8 @@ TYPED_SFIELD(sfCounterparty, ACCOUNT, 26) TYPED_SFIELD(sfSponsorAccount, ACCOUNT, 27) TYPED_SFIELD(sfHighSponsorAccount, ACCOUNT, 28) TYPED_SFIELD(sfLowSponsorAccount, ACCOUNT, 29) -TYPED_SFIELD(sfSponsee, ACCOUNT, 30) +TYPED_SFIELD(sfSponsor, ACCOUNT, 30) +TYPED_SFIELD(sfSponsee, ACCOUNT, 31) // vector of 256-bit TYPED_SFIELD(sfIndexes, VECTOR256, 1, SField::sMD_Never) @@ -399,8 +400,7 @@ UNTYPED_SFIELD(sfRawTransaction, OBJECT, 34) UNTYPED_SFIELD(sfBatchSigner, OBJECT, 35) UNTYPED_SFIELD(sfBook, OBJECT, 36) UNTYPED_SFIELD(sfCounterpartySignature, OBJECT, 37, SField::sMD_Default, SField::notSigning) -UNTYPED_SFIELD(sfSponsor, OBJECT, 38) -UNTYPED_SFIELD(sfSponsorSignature, OBJECT, 39, SField::sMD_Default, SField::notSigning) +UNTYPED_SFIELD(sfSponsorSignature, OBJECT, 38, SField::sMD_Default, SField::notSigning) // array of objects (common) // ARRAY/1 is reserved for end of array diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 608c0e3ab73..ce75d80a6dd 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1118,8 +1118,7 @@ calculateReserve(std::shared_ptr const& sle, Fees const& fees) bool isReserveSponsored(STTx const& tx) { - auto const sponsor = tx.getFieldObject(sfSponsor); - return sponsor.isFlag(tfSponsorReserve); + return tx.isFlag(tfSponsorReserve); } bool @@ -1201,11 +1200,9 @@ checkInsufficientReserve( std::optional getTxReserveSponsorAccountID(STTx const& tx) { - if (tx.isFieldPresent(sfSponsor)) + if (tx.isFieldPresent(sfSponsor) && tx.isFlag(tfSponsorReserve)) { - auto const sponsorObj = tx.getFieldObject(sfSponsor); - if (sponsorObj.isFlag(tfSponsorReserve)) - return sponsorObj.getAccountID(sfAccount); + return tx.getAccountID(sfSponsor); } return {}; } diff --git a/src/libxrpl/protocol/InnerObjectFormats.cpp b/src/libxrpl/protocol/InnerObjectFormats.cpp index 071f6b53f58..dabe7a47cdb 100644 --- a/src/libxrpl/protocol/InnerObjectFormats.cpp +++ b/src/libxrpl/protocol/InnerObjectFormats.cpp @@ -162,13 +162,6 @@ InnerObjectFormats::InnerObjectFormats() {sfSigners, soeOPTIONAL}, }); - add(sfSponsor.jsonName.c_str(), - sfSponsor.getCode(), - { - {sfAccount, soeREQUIRED}, - {sfFlags, soeREQUIRED}, - }); - add(sfSponsorSignature.jsonName.c_str(), sfSponsorSignature.getCode(), { diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 5992cc48dc7..5a63dcb70c9 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -7,6 +7,7 @@ #include #include +#include "test/jtx/envconfig.h" #include "test/jtx/sponsor.h" namespace xrpl { @@ -62,12 +63,19 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); env.fund(XRP(10000), alice, sponsor); + // check Sponsor field env(noop(alice), fee(XRP(1)), sponsor::as(sponsor), sig(sfSponsorSignature, sponsor), ter(temDISABLED)); + // check Sponsor flags + for (auto flag : + {tfSponsorFee, tfSponsorReserve, tfSponsorFee | tfSponsorReserve}) + env(noop(alice), fee(XRP(1)), txflags(flag), ter(temINVALID_FLAG)); + + // check Sponsor transactions env(sponsor::transfer(alice), ter(temDISABLED)); env(sponsor::set(sponsor, 0), ter(temDISABLED)); } @@ -226,7 +234,7 @@ class Sponsor_test : public beast::unit_test::suite // Signature doesn't exist auto tx = noop(alice); - tx[sfSponsor.jsonName][sfAccount.jsonName] = sponsor.human(); + tx[sfSponsor.jsonName] = sponsor.human(); tx[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = strHex(sponsor.pk().slice()); @@ -350,9 +358,6 @@ class Sponsor_test : public beast::unit_test::suite // Invalid Flags env(noop(alice), sponsor::as(sponsor, 4), ter(temINVALID_FLAG)); - env(noop(alice), - sponsor::as(sponsor, tfSponsorMask), - ter(temINVALID_FLAG)); } void diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index bbf42198a7a..d5931e32e8b 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -96,8 +96,8 @@ sponseeAcc::operator()(Env& env, JTx& jt) const void as::operator()(Env& env, JTx& jt) const { - jt.jv[sfSponsor.jsonName][sfAccount.jsonName] = sponsor_.human(); - jt.jv[sfSponsor.jsonName][sfFlags.jsonName] = flags; + jt.jv[sfSponsor.jsonName] = sponsor_.human(); + jt.jv[sfFlags.jsonName] = jt.jv[sfFlags.jsonName].asUInt() | flags; } Json::Value diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 258c8790f49..9a5c194abe5 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -651,8 +651,8 @@ class Simulate_test : public beast::unit_test::suite tx[jss::Account] = env.master.human(); tx[jss::TransactionType] = jss::AccountSet; tx[sfDomain] = newDomain; - tx[sfSponsor.jsonName][sfAccount.jsonName] = sponsor.human(); - tx[sfSponsor.jsonName][sfFlags.jsonName] = tfSponsorFee; + tx[sfSponsor.jsonName] = sponsor.human(); + tx[sfFlags.jsonName] = tfSponsorFee; tx[sfSponsorSignature.jsonName] = Json::objectValue; // test with autofill diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 4044291c551..dbed4dfcaff 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -332,9 +332,8 @@ SponsorshipTransfer::doApply() if (tx.isFieldPresent(sfSponsor)) { - auto const sponsorObj = tx.getFieldObject(sfSponsor); auto const oldSponsor = objSle->getAccountID(sponsorField); - auto const newSponsor = sponsorObj[sfAccount]; + auto const newSponsor = tx.getAccountID(sfSponsor); // decrement old sponsoring count if exists if (auto const oldSponsorSle = view().peek(keylet::account(oldSponsor))) @@ -398,9 +397,8 @@ SponsorshipTransfer::doApply() if (tx.isFieldPresent(sfSponsor)) { // transfer account sponsor - auto const sponsorObj = tx.getFieldObject(sfSponsor); // increment new sponsoring count - auto const newSponsor = sponsorObj[sfAccount]; + auto const newSponsor = tx.getAccountID(sfSponsor); auto const newSponsorSle = view().peek(keylet::account(newSponsor)); setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 90032470e4a..e03f85949be 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -73,6 +73,14 @@ preflight0(PreflightContext const& ctx, std::uint32_t flagMask) return temINVALID_FLAG; } + if (!ctx.rules.enabled(featureSponsor) && + ctx.tx.getFlags() & ~tfSponsorMask) + { + JLOG(ctx.j.debug()) << "preflight0: Sponsor flags set without Sponsor " + "amendment enabled"; + return temINVALID_FLAG; + } + return tesSUCCESS; } @@ -206,20 +214,11 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) "Inner batch transaction must have a parent batch ID."); // Sponsor checks - if (hasSponsor) + if (hasSponsor && ctx.tx.getAccountID(sfSponsor) == id) { - auto const sponsor = ctx.tx.getFieldObject(sfSponsor); - if (sponsor[sfAccount] == ctx.tx[sfAccount]) - { - JLOG(ctx.j.debug()) << "preflight1: Sponsor account cannot be the " - "same as the transaction originator"; - return temMALFORMED; - } - if (sponsor.getFlags() & tfSponsorMask) - { - JLOG(ctx.j.debug()) << "preflight1: Invalid sponsor flags"; - return temINVALID_FLAG; - } + JLOG(ctx.j.debug()) << "preflight1: Sponsor account cannot be the " + "same as the transaction originator"; + return temMALFORMED; } return tesSUCCESS; @@ -321,21 +320,18 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) if (hasSponsorSignature) return tesSUCCESS; - auto const txSponsor = tx.getFieldObject(sfSponsor); - - auto const sponsorAcc = txSponsor.getAccountID(sfAccount); - auto const sponseeAcc = tx.getAccountID(sfAccount); - auto const sponsorSle = view.read(keylet::sponsor(sponsorAcc, sponseeAcc)); + auto const sponsorSle = view.read(keylet::sponsor( + tx.getAccountID(sfSponsor), tx.getAccountID(sfAccount))); // sponsorship object missing for pre-funded tx if (!sponsorSle) return terNO_SPONSORSHIP; - if (txSponsor.isFlag(tfSponsorFee) && + if (tx.isFlag(tfSponsorFee) && sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) return terNO_SPONSORSHIP; - if (txSponsor.isFlag(tfSponsorReserve) && + if (tx.isFlag(tfSponsorReserve) && sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) return terNO_SPONSORSHIP; @@ -807,8 +803,7 @@ Transactor::checkSign( if (!sigObject.isFieldPresent(sfSponsor)) return tefINTERNAL; // LCOV_EXCL_LINE - auto const sponsorObj = sigObject.getFieldObject(sfSponsor); - auto const sponsorAcc = sponsorObj.getAccountID(sfAccount); + auto const sponsorAcc = sigObject.getAccountID(sfSponsor); auto const sponsorSignature = sigObject.getFieldObject(sfSponsorSignature); if (auto const ret = checkSign( @@ -1247,13 +1242,12 @@ Transactor::reset(XRPAmount fee) FeePayer Transactor::getFeePayer(ReadView const& view, STTx const& tx) { - if (tx.isFieldPresent(sfSponsor) && - tx.getFieldObject(sfSponsor).isFlag(tfSponsorFee)) + auto const id = tx.getAccountID(sfAccount); + if (tx.isFieldPresent(sfSponsor) && tx.isFlag(tfSponsorFee)) { - auto const sponsor = tx.getFieldObject(sfSponsor); + auto const sponsor = tx.getAccountID(sfSponsor); auto const hasSignature = tx.isFieldPresent(sfSponsorSignature); - auto const sponsorKeylet = keylet::sponsor( - sponsor.getAccountID(sfAccount), tx.getAccountID(sfAccount)); + auto const sponsorKeylet = keylet::sponsor(sponsor, id); if (hasSignature) { @@ -1263,8 +1257,7 @@ Transactor::getFeePayer(ReadView const& view, STTx const& tx) sponsorKeylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; // co-signed - auto const sponsorAccountKeylet = - keylet::account(sponsor.getAccountID(sfAccount)); + auto const sponsorAccountKeylet = keylet::account(sponsor); return FeePayer{ sponsorAccountKeylet, sfBalance, FeePayerType::SponsorCoSigned}; } @@ -1281,7 +1274,7 @@ Transactor::getFeePayer(ReadView const& view, STTx const& tx) return FeePayer{delegatorKeylet, sfBalance, FeePayerType::Delegate}; } - auto const accountKeylet = keylet::account(tx.getAccountID(sfAccount)); + auto const accountKeylet = keylet::account(id); return FeePayer{accountKeylet, sfBalance, FeePayerType::Account}; } From 87718bdb3bb3e4bf91ed60f0a16511a43a5c6875 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 29 Jan 2026 10:58:06 +0900 Subject: [PATCH 104/249] remove unused HashPrefix --- include/xrpl/protocol/HashPrefix.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/xrpl/protocol/HashPrefix.h b/include/xrpl/protocol/HashPrefix.h index 016f7cb66ba..39e03bf94fe 100644 --- a/include/xrpl/protocol/HashPrefix.h +++ b/include/xrpl/protocol/HashPrefix.h @@ -69,9 +69,6 @@ enum class HashPrefix : std::uint32_t { /** Batch */ batch = detail::make_hash_prefix('B', 'C', 'H'), - - /** Sponsor */ - sponsor = detail::make_hash_prefix('S', 'P', 'N'), }; template From ab1de456ccd462a3c263b264c1091eb7f8985c39 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 29 Jan 2026 13:52:19 +0900 Subject: [PATCH 105/249] add comment --- src/test/jtx/impl/sig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/jtx/impl/sig.cpp b/src/test/jtx/impl/sig.cpp index 3ea7f669a74..2211ae1f242 100644 --- a/src/test/jtx/impl/sig.cpp +++ b/src/test/jtx/impl/sig.cpp @@ -17,7 +17,7 @@ sig::operator()(Env&, JTx& jt) const // VFALCO Inefficient pre-C++14 auto const account = *account_; auto callback = [subField = subField_, account](Env&, JTx& jtx) { - // Where to put the signature. Supports sfCounterPartySignature. + // Where to put the signature. Supports sfCounterPartySignature and sfSponsorSignature. auto& sigObject = subField ? jtx[*subField] : jtx.jv; jtx::sign(jtx.jv, account, sigObject); From 5f385e0f9b2b62b01d6d7406e54804c0e6dc2323 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 10:36:48 +0900 Subject: [PATCH 106/249] update to use sfSponsorFlags --- include/xrpl/protocol/TxFlags.h | 6 +++--- include/xrpl/protocol/detail/sfields.macro | 1 + src/libxrpl/ledger/View.cpp | 4 ++-- src/libxrpl/protocol/TxFormats.cpp | 1 + src/test/app/Sponsor_test.cpp | 16 +++++++++------ src/test/jtx/impl/sponsor.cpp | 2 +- src/xrpld/app/tx/detail/Transactor.cpp | 24 ++++++++++++---------- 7 files changed, 31 insertions(+), 23 deletions(-) diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index d241abd46ae..50563036ce3 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -40,12 +40,12 @@ namespace xrpl { // Universal Transaction flags: constexpr std::uint32_t tfFullyCanonicalSig = 0x80000000; constexpr std::uint32_t tfInnerBatchTxn = 0x40000000; -constexpr std::uint32_t tfSponsorFee = 0x20000000; -constexpr std::uint32_t tfSponsorReserve = 0x10000000; -constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn | tfSponsorFee | tfSponsorReserve; +constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; constexpr std::uint32_t tfUniversalMask = ~tfUniversal; // Sponsor flags (Global): +constexpr std::uint32_t tfSponsorFee = 0x00000001; +constexpr std::uint32_t tfSponsorReserve = 0x00000002; constexpr std::uint32_t tfSponsorMask = ~(tfSponsorFee | tfSponsorReserve); // AccountSet flags: diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index dd4aa7c71a8..03a1c18a5e0 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -118,6 +118,7 @@ TYPED_SFIELD(sfSponsoredOwnerCount, UINT32, 69) TYPED_SFIELD(sfSponsoringOwnerCount, UINT32, 70) TYPED_SFIELD(sfSponsoringAccountCount, UINT32, 71) TYPED_SFIELD(sfReserveCount, UINT32, 72) +TYPED_SFIELD(sfSponsorFlags, UINT32, 73) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 119913548e4..e95b793d22a 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -965,7 +965,7 @@ calculateReserve(std::shared_ptr const& sle, Fees const& fees) bool isReserveSponsored(STTx const& tx) { - return tx.isFlag(tfSponsorReserve); + return tx.getFieldU32(sfSponsorFlags) & tfSponsorReserve; } bool @@ -1043,7 +1043,7 @@ checkInsufficientReserve( std::optional getTxReserveSponsorAccountID(STTx const& tx) { - if (tx.isFieldPresent(sfSponsor) && tx.isFlag(tfSponsorReserve)) + if (tx.isFieldPresent(sfSponsor) && tx.getFieldU32(sfSponsorFlags) & tfSponsorReserve) { return tx.getAccountID(sfSponsor); } diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 462b1af3201..655b3bcf147 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -29,6 +29,7 @@ TxFormats::TxFormats() {sfNetworkID, soeOPTIONAL}, {sfDelegate, soeOPTIONAL}, {sfSponsor, soeOPTIONAL}, + {sfSponsorFlags, soeOPTIONAL}, {sfSponsorSignature, soeOPTIONAL}, }; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 51699b30231..24360b3af35 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -59,12 +59,16 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); env.fund(XRP(10000), alice, sponsor); - // check Sponsor field - env(noop(alice), fee(XRP(1)), sponsor::as(sponsor), sig(sfSponsorSignature, sponsor), ter(temDISABLED)); + // check Sponsor fields + auto const jt = noop(alice); + auto jt1 = jt; + jt1[sfSponsor.jsonName] = sponsor.human(); + env(jt1, ter(temDISABLED)); + env(jt, sig(sfSponsorSignature, sponsor), ter(temDISABLED)); - // check Sponsor flags - for (auto flag : {tfSponsorFee, tfSponsorReserve, tfSponsorFee | tfSponsorReserve}) - env(noop(alice), fee(XRP(1)), txflags(flag), ter(temINVALID_FLAG)); + auto jt2 = jt; + jt2[sfSponsorFlags.jsonName] = tfSponsorFee | tfSponsorReserve; + env(jt2, ter(temDISABLED)); // check Sponsor transactions env(sponsor::transfer(alice), ter(temDISABLED)); @@ -296,7 +300,7 @@ class Sponsor_test : public beast::unit_test::suite ter(terNO_ACCOUNT)); // Invalid Flags - env(noop(alice), sponsor::as(sponsor, 4), ter(temINVALID_FLAG)); + env(noop(alice), sponsor::as(sponsor, (tfSponsorFee | tfSponsorReserve) + 1), ter(temINVALID_FLAG)); } void diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 61344093a9d..942fd317a0f 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -93,7 +93,7 @@ void as::operator()(Env& env, JTx& jt) const { jt.jv[sfSponsor.jsonName] = sponsor_.human(); - jt.jv[sfFlags.jsonName] = jt.jv[sfFlags.jsonName].asUInt() | flags; + jt.jv[sfSponsorFlags.jsonName] = flags; } Json::Value diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 2e8775ce77f..018c0f9416d 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -70,13 +70,6 @@ preflight0(PreflightContext const& ctx, std::uint32_t flagMask) return temINVALID_FLAG; } - if (!ctx.rules.enabled(featureSponsor) && ctx.tx.getFlags() & ~tfSponsorMask) - { - JLOG(ctx.j.debug()) << "preflight0: Sponsor flags set without Sponsor " - "amendment enabled"; - return temINVALID_FLAG; - } - return tesSUCCESS; } @@ -153,11 +146,18 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) } bool const hasSponsor = ctx.tx.isFieldPresent(sfSponsor); + bool const hasSponsorFlags = ctx.tx.isFieldPresent(sfSponsorFlags); bool const hasSponsorSig = ctx.tx.isFieldPresent(sfSponsorSignature); - if ((hasSponsor || hasSponsorSig) && !ctx.rules.enabled(featureSponsor)) + if ((hasSponsor || hasSponsorFlags || hasSponsorSig) && !ctx.rules.enabled(featureSponsor)) return temDISABLED; + if (hasSponsorFlags && ctx.tx.getFieldU32(sfSponsorFlags) & tfSponsorMask) + { + JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; + return temINVALID_FLAG; + } + if (hasSponsorSig && !hasSponsor) { JLOG(ctx.j.debug()) << "preflight1: sponsor signature without sponsor definition"; @@ -310,10 +310,12 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) if (!sponsorSle) return terNO_SPONSORSHIP; - if (tx.isFlag(tfSponsorFee) && sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) + auto const sponsorFlags = tx.getFieldU32(sfSponsorFlags); + + if (sponsorFlags & tfSponsorFee && sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) return terNO_SPONSORSHIP; - if (tx.isFlag(tfSponsorReserve) && sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) + if (sponsorFlags & tfSponsorReserve && sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) return terNO_SPONSORSHIP; return tesSUCCESS; @@ -1143,7 +1145,7 @@ FeePayer Transactor::getFeePayer(ReadView const& view, STTx const& tx) { auto const id = tx.getAccountID(sfAccount); - if (tx.isFieldPresent(sfSponsor) && tx.isFlag(tfSponsorFee)) + if (tx.isFieldPresent(sfSponsor) && tx.getFieldU32(sfSponsorFlags) & tfSponsorFee) { auto const sponsor = tx.getAccountID(sfSponsor); auto const hasSignature = tx.isFieldPresent(sfSponsorSignature); From 65768237cddf8f3aa6365a0d75767e0e4996773a Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 11:36:56 +0900 Subject: [PATCH 107/249] Payback ReserveCount --- src/libxrpl/ledger/View.cpp | 7 ++- src/test/app/Sponsor_test.cpp | 43 ++++++++++++------- .../app/tx/detail/SponsorshipTransfer.cpp | 26 ++++++++--- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index e95b793d22a..2e175d52524 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1167,16 +1167,19 @@ adjustOwnerCount( auto sponsorObjSle = view.peek(keylet::sponsor(sponsorAcc, account)); - if (sponsorObjSle && amount > 0) + if (sponsorObjSle) { // pre funded // update the pre-funded ReserveCount on Sponsorship ledger object XRPL_ASSERT(sponsorObjSle, "xrpl::adjustOwnerCount : co-signing sponsor exists"); auto const currentReserveCount = sponsorObjSle->getFieldU32(sfReserveCount); - XRPL_ASSERT(currentReserveCount >= amount, "xrpl::adjustOwnerCount : enough reserve count"); + if (amount > 0) + XRPL_ASSERT(currentReserveCount >= amount, "xrpl::adjustOwnerCount : enough reserve count"); if (currentReserveCount - amount > 0) + // if amount > 0, reduce the reserve count + // if amount < 0, payback the reserve count sponsorObjSle->setFieldU32(sfReserveCount, currentReserveCount - amount); else sponsorObjSle->makeFieldAbsent(sfReserveCount); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 24360b3af35..0d120d698a6 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -445,17 +445,26 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); env.close(); - env(ticket::create(alice, 1), + env(did::set(alice), + did::uri("uri"), sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), sig(sfSponsorSignature, sponsor), fee(XRP(1)), ter(tesSUCCESS)); env.close(); - auto const sle = env.le(keylet::sponsor(sponsor, alice)); + auto sle = env.le(keylet::sponsor(sponsor, alice)); BEAST_EXPECT(sle); BEAST_EXPECT(sle->at(sfReserveCount) == 99); BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99)); + + env(did::del(alice), ter(tesSUCCESS)); + env.close(); + + sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfReserveCount) == 100); // paybacked + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99)); } { @@ -794,11 +803,11 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); - auto const sle1 = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(sle1->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); - auto const sponsorSle = env.le(keylet::sponsor(sponsor1, alice)); - BEAST_EXPECT(sponsorSle->getFieldU32(sfReserveCount) == 99); + auto checkSle = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(checkSle->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(checkSle->getAccountID(sfSponsorAccount) == sponsor1.id()); + auto sponsor1Sle = env.le(keylet::sponsor(sponsor1, alice)); + BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 99); // transfer sponsor env(sponsor::set_reserve(sponsor2, 0, 100), sponsor::sponseeAcc(alice)); @@ -816,11 +825,13 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); - auto const sle2 = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); - auto const sponsorSle2 = env.le(keylet::sponsor(sponsor2, alice)); - BEAST_EXPECT(sponsorSle2->getFieldU32(sfReserveCount) == 99); + checkSle = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(checkSle->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(checkSle->getAccountID(sfSponsorAccount) == sponsor2.id()); + sponsor1Sle = env.le(keylet::sponsor(sponsor1, alice)); + BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 100); // paybacked + auto sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice)); + BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 99); // dissolve sponsor adjustAccountXRPBalance(env, alice, reserve(env, 1)); @@ -837,10 +848,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); BEAST_EXPECT(!env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); - auto const sle3 = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); - auto const sponsorSle3 = env.le(keylet::sponsor(sponsor2, alice)); - BEAST_EXPECT(sponsorSle3->getFieldU32(sfReserveCount) == 99); // no change + checkSle = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(!checkSle->isFieldPresent(sfSponsorAccount)); + sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice)); + BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 100); // paybacked } { diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 681dddb8ec9..35d4eb71d71 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -241,8 +241,6 @@ adjustReserveCount(ApplyView& view, AccountID const& account, AccountID const& s { if (delta == 0) return tesSUCCESS; - if (delta > 0) - return tefINTERNAL; // LCOV_EXCL_LINE auto const sponsorKeylet = keylet::sponsor(sponsor, account); auto const sponsorSle = view.peek(sponsorKeylet); if (!sponsorSle) @@ -337,21 +335,35 @@ SponsorshipTransfer::doApply() !isTesSuccess(ter)) return ter; } + + // payback the reserve count if ltSponsorship exists + if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); sponsorSle) + if (auto const ter = adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); + !isTesSuccess(ter)) + return ter; } else { // dissolve object sponsor auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); + auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); + if (!oldSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + // decrement sponsored count setSponsorFieldU32(accSle, sfSponsoredOwnerCount, -ownerCountDelta); view().update(accSle); + // decrement old sponsoring count - if (auto const oldSponsorSle = view().peek(keylet::account(oldSponsor))) - { - setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); - view().update(oldSponsorSle); - } + setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); + view().update(oldSponsorSle); + + // payback the reserve count if ltSponsorship exists + if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); sponsorSle) + if (auto const ter = adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); + !isTesSuccess(ter)) + return ter; // remove sponsor from object objSle->makeFieldAbsent(sfSponsorAccount); From d7ab1b48e1501d9104d73e3ba57efbf9554f9db5 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 11:43:45 +0900 Subject: [PATCH 108/249] Separate test suite --- src/test/app/Sponsor_test.cpp | 91 +++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 0d120d698a6..e11b6b206b4 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -4111,34 +4111,6 @@ class Sponsor_test : public beast::unit_test::suite } } - void - testSponsorReserve() - { - testRequireFlag(); - for (auto cosigning : {false, true}) - { - testAMM(cosigning); - testCheck(cosigning); - testOffer(cosigning); - testTicket(cosigning); - testCredentials(cosigning); - testDelegate(cosigning); - testDepositPreauth(cosigning); - testDID(cosigning); - testEscrow(cosigning); - testMPToken(cosigning); - testNFToken(cosigning); - testNFTokenOffer(cosigning); - testPayChan(cosigning); - testPermissionedDomain(cosigning); - testOracle(cosigning); - testSignerList(cosigning); - testTrustSet(cosigning); - testVault(cosigning); - testXChain(cosigning); - } - } - void testBatch() { @@ -4215,7 +4187,33 @@ class Sponsor_test : public beast::unit_test::suite } void - run() override + testSponsorReserve(bool cosigning) + { + testRequireFlag(); + testAMM(cosigning); + testCheck(cosigning); + testOffer(cosigning); + testTicket(cosigning); + testCredentials(cosigning); + testDelegate(cosigning); + testDepositPreauth(cosigning); + testDID(cosigning); + testEscrow(cosigning); + testMPToken(cosigning); + testNFToken(cosigning); + testNFTokenOffer(cosigning); + testPayChan(cosigning); + testPermissionedDomain(cosigning); + testOracle(cosigning); + testSignerList(cosigning); + testTrustSet(cosigning); + testVault(cosigning); + testXChain(cosigning); + } + +protected: + void + testSponsor() { testDisabled(); testInvalidSponsorshipSet(); @@ -4232,7 +4230,7 @@ class Sponsor_test : public beast::unit_test::suite testTransferSponsor(); testSponsorFee(); testSponsorAccount(); - testSponsorReserve(); + testDisallowIncoming(); testAccountDelete(); @@ -4240,9 +4238,42 @@ class Sponsor_test : public beast::unit_test::suite testDelegatePermission(); testBatch(); } + + void + testTxSponsor(bool cosigning) + { + testSponsorReserve(cosigning); + } + +public: + void + run() override + { + testSponsor(); + } +}; + +class SponsorTxCosigning_test : public Sponsor_test +{ + void + run() override + { + testTxSponsor(true); + } +}; + +class SponsorTxPrefunded_test : public Sponsor_test +{ + void + run() override + { + testTxSponsor(false); + } }; BEAST_DEFINE_TESTSUITE(Sponsor, app, xrpl); +BEAST_DEFINE_TESTSUITE(SponsorTxCosigning, app, xrpl); +BEAST_DEFINE_TESTSUITE(SponsorTxPrefunded, app, xrpl); } // namespace test } // namespace xrpl From c0de722cfa7ca29d4854a20c43e5dfb59bed0137 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 12:32:48 +0900 Subject: [PATCH 109/249] `sfHigh/LowSponsorAccount` -> `High/LowSponsor` --- .../xrpl/protocol/detail/ledger_entries.macro | 4 +-- include/xrpl/protocol/detail/sfields.macro | 4 +-- src/libxrpl/ledger/View.cpp | 25 +++++++++---------- src/test/app/Sponsor_test.cpp | 8 +++--- src/xrpld/app/tx/detail/SetTrust.cpp | 12 ++++----- .../app/tx/detail/SponsorshipTransfer.cpp | 4 +-- 6 files changed, 27 insertions(+), 30 deletions(-) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index d846c6129b6..b7333b2f0cb 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -287,8 +287,8 @@ LEDGER_ENTRY(ltRIPPLE_STATE, 0x0072, RippleState, state, ({ {sfHighNode, soeOPTIONAL}, {sfHighQualityIn, soeOPTIONAL}, {sfHighQualityOut, soeOPTIONAL}, - {sfHighSponsorAccount, soeOPTIONAL}, - {sfLowSponsorAccount, soeOPTIONAL}, + {sfHighSponsor, soeOPTIONAL}, + {sfLowSponsor, soeOPTIONAL}, })) /** The ledger object which lists the network's fee settings. diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 03a1c18a5e0..94c6be1ab4d 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -333,8 +333,8 @@ TYPED_SFIELD(sfSubject, ACCOUNT, 24) TYPED_SFIELD(sfBorrower, ACCOUNT, 25) TYPED_SFIELD(sfCounterparty, ACCOUNT, 26) TYPED_SFIELD(sfSponsorAccount, ACCOUNT, 27) -TYPED_SFIELD(sfHighSponsorAccount, ACCOUNT, 28) -TYPED_SFIELD(sfLowSponsorAccount, ACCOUNT, 29) +TYPED_SFIELD(sfHighSponsor, ACCOUNT, 28) +TYPED_SFIELD(sfLowSponsor, ACCOUNT, 29) TYPED_SFIELD(sfSponsor, ACCOUNT, 30) TYPED_SFIELD(sfSponsee, ACCOUNT, 31) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 2e175d52524..c9604b435ac 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1101,7 +1101,7 @@ addSponsorToLedgerEntry( SF_ACCOUNT const& field) { XRPL_ASSERT( - (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsorAccount || field == sfLowSponsorAccount)) || + (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || (sle->getType() != ltRIPPLE_STATE && field == sfSponsorAccount), "addSponsorToLedgerEntry : Invalid field to the LedgerEntry"); if (sponsorSle) @@ -1112,7 +1112,7 @@ void removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field) { XRPL_ASSERT( - (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsorAccount || field == sfLowSponsorAccount)) || + (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || (sle->getType() != ltRIPPLE_STATE && field == sfSponsorAccount), "removeSponsorFromLedgerEntry : Invalid field to the LedgerEntry"); if (sle->isFieldPresent(field)) @@ -1811,7 +1811,7 @@ trustCreate( sleRippleState->setFieldU32(sfFlags, uFlags); adjustOwnerCount(view, sleAccount, sponsorSle, 1, j); - addSponsorToLedgerEntry(sleRippleState, sponsorSle, bSetHigh ? sfHighSponsorAccount : sfLowSponsorAccount); + addSponsorToLedgerEntry(sleRippleState, sponsorSle, bSetHigh ? sfHighSponsor : sfLowSponsor); // ONLY: Create ripple balance. sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); @@ -1944,8 +1944,8 @@ trustDelete( return tefBAD_LEDGER; // LCOV_EXCL_LINE } - removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsorAccount); - removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsorAccount); + removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsor); + removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsor); JLOG(j.trace()) << "trustDelete: Deleting ripple line: state"; view.erase(sleRippleState); @@ -2074,11 +2074,11 @@ rippleCreditIOU( // Sender quality out is 0. { // Clear the reserve of the sender, possibly delete the line! - auto const currentSponsor = getLedgerEntryReserveSponsor( - view, sleRippleState, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); + auto const currentSponsor = + getLedgerEntryReserveSponsor(view, sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); adjustOwnerCount(view, view.peek(keylet::account(uSenderID)), currentSponsor, -1, j); - removeSponsorFromLedgerEntry(sleRippleState, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); + removeSponsorFromLedgerEntry(sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); // Clear reserve flag. sleRippleState->setFieldU32(sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); @@ -2817,13 +2817,13 @@ updateTrustLine( // VFALCO Where is the line being deleted? // Clear the reserve of the sender, possibly delete the line! auto const currentSponsor = - getLedgerEntryReserveSponsor(view, state, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); + getLedgerEntryReserveSponsor(view, state, !bSenderHigh ? sfLowSponsor : sfHighSponsor); adjustOwnerCount(view, sle, currentSponsor, -1, j); // Clear reserve flag. state->setFieldU32(sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); - removeSponsorFromLedgerEntry(state, !bSenderHigh ? sfLowSponsorAccount : sfHighSponsorAccount); + removeSponsorFromLedgerEntry(state, !bSenderHigh ? sfLowSponsor : sfHighSponsor); // Balance is zero, receiver reserve is clear. if (!after // Balance is zero. @@ -3379,10 +3379,9 @@ deleteAMMTrustLine( if (!(sleState->getFlags() & uFlags)) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsorSle = - getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount); + auto const sponsorSle = getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor); adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, -1, j); - removeSponsorFromLedgerEntry(sleState, !ammLow ? sfLowSponsorAccount : sfHighSponsorAccount); + removeSponsorFromLedgerEntry(sleState, !ammLow ? sfLowSponsor : sfHighSponsor); return tesSUCCESS; } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e11b6b206b4..8b7a7f96269 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2465,8 +2465,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT( - env.le(keylet::line(bob, gw, USD.currency))->getAccountID(sfHighSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(env.le(keylet::line(bob, gw, USD.currency))->getAccountID(sfHighSponsor) == sponsor2.id()); } } @@ -3415,9 +3414,8 @@ class Sponsor_test : public beast::unit_test::suite { auto const validateSponsoredTrustline = [&](std::shared_ptr const& sle, bool isIssuerHigh, Account const& sponsor) { - BEAST_EXPECT( - sle->getAccountID(isIssuerHigh ? sfLowSponsorAccount : sfHighSponsorAccount) == sponsor.id()); - BEAST_EXPECT(!sle->isFieldPresent(isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); + BEAST_EXPECT(sle->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == sponsor.id()); + BEAST_EXPECT(!sle->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); }; auto const& highAcc = alice > bob ? alice : bob; diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 366099d9595..b953e21826a 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -519,8 +519,8 @@ SetTrust::doApply() bool bReserveIncrease = false; - auto const currentHighSponsor = getLedgerEntryReserveSponsor(view(), sleRippleState, sfHighSponsorAccount); - auto const currentLowSponsor = getLedgerEntryReserveSponsor(view(), sleRippleState, sfLowSponsorAccount); + auto const currentHighSponsor = getLedgerEntryReserveSponsor(view(), sleRippleState, sfHighSponsor); + auto const currentLowSponsor = getLedgerEntryReserveSponsor(view(), sleRippleState, sfLowSponsor); if (bSetAuth) { @@ -539,7 +539,7 @@ SetTrust::doApply() adjustOwnerCount(view(), ctx_.tx, sleLowAccount, txSponsorSle, 1, viewJ); uFlagsOut |= lsfLowReserve; - addSponsorToLedgerEntry(sleRippleState, txSponsorSle, sfLowSponsorAccount); + addSponsorToLedgerEntry(sleRippleState, txSponsorSle, sfLowSponsor); if (!bHigh) bReserveIncrease = true; @@ -551,7 +551,7 @@ SetTrust::doApply() adjustOwnerCount(view(), sleLowAccount, currentLowSponsor, -1, viewJ); uFlagsOut &= ~lsfLowReserve; - removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsorAccount); + removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsor); } if (bHighReserveSet && !bHighReserved) @@ -566,7 +566,7 @@ SetTrust::doApply() adjustOwnerCount(view(), ctx_.tx, sleHighAccount, txSponsorSle, 1, viewJ); uFlagsOut |= lsfHighReserve; - addSponsorToLedgerEntry(sleRippleState, txSponsorSle, sfHighSponsorAccount); + addSponsorToLedgerEntry(sleRippleState, txSponsorSle, sfHighSponsor); if (bHigh) bReserveIncrease = true; @@ -578,7 +578,7 @@ SetTrust::doApply() adjustOwnerCount(view(), sleHighAccount, currentHighSponsor, -1, viewJ); uFlagsOut &= ~lsfHighReserve; - removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsorAccount); + removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsor); } if (uFlagsIn != uFlagsOut) diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 35d4eb71d71..a292e7511c8 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -133,13 +133,13 @@ getLedgerEntrySponsorField(T const& sle, AccountID const& owner) { auto const highAccount = sle->getFieldAmount(sfHighLimit).getIssuer(); if (highAccount == owner) - return sfHighSponsorAccount; + return sfHighSponsor; } if (sle->isFlag(lsfLowReserve)) { auto const lowAccount = sle->getFieldAmount(sfLowLimit).getIssuer(); if (lowAccount == owner) - return sfLowSponsorAccount; + return sfLowSponsor; } // LCOV_EXCL_START XRPL_ASSERT( From 5155a94015b6e5536665c388898c8faf25125101 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 17:20:15 +0900 Subject: [PATCH 110/249] Add sponsor tests for Batch innerTxn --- src/test/app/Sponsor_test.cpp | 55 +++++++++++++++++++++++++++++-- src/xrpld/app/tx/detail/Batch.cpp | 8 +++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 8b7a7f96269..8ba30372ced 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -4177,11 +4177,62 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsorshipSle->at(sfFeeAmount) == XRP(100 - 1)); BEAST_EXPECT(sponsorshipSle->at(sfReserveCount) == 100); } - // // Inner transaction // - // TEQU: TODO + { + // test inner transaction with co-signing sponsor + Env env{*this, testable_amendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + auto jt = env.jtnofill( + noop(alice), sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), sig(sfSponsorSignature, sponsor)); + + auto const seq = env.seq(alice); + // should fail because inner transaction cannot include SponsorSignature + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::inner(jt.jv, seq + 1), + batch::inner(ticket::create(alice, 1), seq + 2), + ter(temBAD_SIGNATURE)); + } + { + // test outer transaction with prefunded sponsor + Env env{*this, testable_amendments()}; + env.fund(XRP(1000), alice, bob); + env.fund(XRP(1001), sponsor); + env.close(); + + env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), fee(XRP(1)), ter(tesSUCCESS)); + env.close(); + BEAST_EXPECT(env.balance(sponsor) == XRP(900)); + + auto jt = env.jtnofill(ticket::create(alice, 1), sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee)); + // remove txn signature since it is filled by env.jtnofill() + jt.jv.removeMember(jss::TxnSignature); + + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::inner(noop(alice), seq + 1), + batch::inner(jt.jv, seq + 2), + ter(tesSUCCESS)); + env.close(); + + // affect sponsor reserve + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // fee is paid by outer transaction originator (alice) + BEAST_EXPECT(env.balance(alice) == XRP(999)); + BEAST_EXPECT(env.balance(sponsor) == XRP(900)); + + // reserve count is decreased + auto const sponsorshipSle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sponsorshipSle); + BEAST_EXPECT(sponsorshipSle->at(sfFeeAmount) == XRP(100)); + BEAST_EXPECT(sponsorshipSle->at(sfReserveCount) == 99); + } } void diff --git a/src/xrpld/app/tx/detail/Batch.cpp b/src/xrpld/app/tx/detail/Batch.cpp index 34b08beb2f2..f0732b46ff2 100644 --- a/src/xrpld/app/tx/detail/Batch.cpp +++ b/src/xrpld/app/tx/detail/Batch.cpp @@ -284,6 +284,14 @@ Batch::preflight(PreflightContext const& ctx) return ret; } } + if (stx.isFieldPresent(sfSponsorSignature)) + { + auto const sponsorSignature = stx.getFieldObject(sfSponsorSignature); + if (auto const ret = checkSignatureFields(sponsorSignature, hash, "sponsor signature ")) + { + return ret; + } + } // Check that the Fee is native asset (XRP) and zero if (auto const fee = stx.getFieldAmount(sfFee); !fee.native() || fee.xrp() != beast::zero) From 4cd8d0d1784c32ddc9ceb8cc3754c2ff0b65a933 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 19:51:07 +0900 Subject: [PATCH 111/249] fix simulate test --- src/test/rpc/Simulate_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index e5e62ce1e23..6e9bc0fef73 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -554,7 +554,7 @@ class Simulate_test : public beast::unit_test::suite tx[jss::TransactionType] = jss::AccountSet; tx[sfDomain] = newDomain; tx[sfSponsor.jsonName] = sponsor.human(); - tx[sfFlags.jsonName] = tfSponsorFee; + tx[sfSponsorFlags.jsonName] = tfSponsorFee; tx[sfSponsorSignature.jsonName] = Json::objectValue; // test with autofill From 175a2dfd28f321692e3caf3e63b4edd9dbc193a3 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 20:03:24 +0900 Subject: [PATCH 112/249] chang `sfFeeAmount` to `soeOPTIONAL` --- include/xrpl/protocol/detail/ledger_entries.macro | 2 +- src/xrpld/app/tx/detail/Transactor.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index b7333b2f0cb..0a9d86c287a 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -616,7 +616,7 @@ LEDGER_ENTRY(ltSPONSORSHIP, 0x0090, Sponsorship, sponsorship, ({ {sfPreviousTxnLgrSeq, soeREQUIRED}, {sfOwner, soeREQUIRED}, {sfSponsee, soeREQUIRED}, - {sfFeeAmount, soeDEFAULT}, + {sfFeeAmount, soeOPTIONAL}, {sfMaxFee, soeOPTIONAL}, {sfReserveCount, soeDEFAULT}, {sfOwnerNode, soeREQUIRED}, diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 018c0f9416d..5ec1c2ac2fd 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -478,9 +478,7 @@ Transactor::payFee() auto const feeAmountAfter = sle->getFieldAmount(payer.balanceField) - feePaid; if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) - // Because ltSponsorship.sfFeeAmount is soeDEFAULT - // TODO: Use whether the field is soeDEFAULT instead of sfFeeAmount in - // the condition. + // Because ltSponsorship.sfFeeAmount is soeOptional sle->makeFieldAbsent(payer.balanceField); else sle->setFieldAmount(payer.balanceField, feeAmountAfter); From 7cdaa24e63e72991363b0cb77a4d06a9b0b95338 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 20:27:46 +0900 Subject: [PATCH 113/249] Fixed the minimum amount for creating a Sponsored Account. --- src/test/app/Sponsor_test.cpp | 30 +++++++++++++---------------- src/xrpld/app/tx/detail/Payment.cpp | 5 +++++ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 8ba30372ced..5e1bbea2dc3 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1155,7 +1155,6 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Sponsor Account"); using namespace test::jtx; - Env env{*this, testable_amendments() - featureSponsor}; Account const alice("alice"); Account const sponsor("sponsor"); @@ -1163,13 +1162,18 @@ class Sponsor_test : public beast::unit_test::suite Account const charlie("charlie"); Account const gw("gw"); auto const USD = gw["USD"]; - env.fund(XRP(10000), alice, sponsor); - // Disabled - env(pay(alice, bob, XRP(100)), txflags(tfSponsorCreatedAccount), ter(temDISABLED)); - env.close(); + { + // Disabled + Env env{*this, testable_amendments() - featureSponsor}; + env.fund(XRP(10000), alice, sponsor); + env.close(); + env(pay(alice, bob, XRP(100)), txflags(tfSponsorCreatedAccount), ter(temDISABLED)); + env.close(); + } - env.enableFeature(featureSponsor); + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, sponsor); env.close(); // Invalid flags @@ -1187,12 +1191,6 @@ class Sponsor_test : public beast::unit_test::suite env(pay(alice, bob, USD(100)), txflags(tfSponsorCreatedAccount), ter(temBAD_AMOUNT)); env.close(); - // Insufficient reserve - env(pay(alice, bob, (env.current()->fees().accountReserve(0) - drops(1))), - txflags(tfSponsorCreatedAccount), - ter(tecNO_DST_INSUF_XRP)); - env.close(); - // Account is not sponsored by normal Sponsor specification { env(pay(alice, bob, drops(env.current()->fees().accountReserve(0))), @@ -1209,13 +1207,11 @@ class Sponsor_test : public beast::unit_test::suite // Use tfSponsorCreatedAccount to sponsor an account { // to funded account - env(pay(sponsor, bob, drops(env.current()->fees().accountReserve(0))), - txflags(tfSponsorCreatedAccount), - ter(tecNO_SPONSOR_PERMISSION)); + env(pay(sponsor, bob, drops(1)), txflags(tfSponsorCreatedAccount), ter(tecNO_SPONSOR_PERMISSION)); + env.close(); // to non-funded account - env(pay(sponsor, charlie, drops(env.current()->fees().accountReserve(0))), - txflags(tfSponsorCreatedAccount)); + env(pay(sponsor, charlie, drops(1)), txflags(tfSponsorCreatedAccount)); env.close(); auto const charlieSle = env.le(keylet::account(charlie)); diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index e57415ac430..9980cc3d1ea 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -299,6 +299,11 @@ Payment::preclaim(PreclaimContext const& ctx) // transaction would succeed. return telNO_DST_PARTIAL; } + else if (txFlags & tfSponsorCreatedAccount) + { + // The minimum amount when creating a Sponsored Account is 1 drop. + // Since the reserve is covered by the sponsor, you don't need to hold the 1-increment reserve yourself. + } else if (dstAmount < STAmount(ctx.view.fees().reserve)) { // accountReserve is the minimum amount that an account can have. From 40cf5993c171d74d9ee298ea9aaa6540076bccf9 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 20:36:43 +0900 Subject: [PATCH 114/249] Add sanity check for sponsoringAccountCount = 0 --- src/xrpld/app/tx/detail/DeleteAccount.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index 36deee9b865..9bdb783d3f6 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -410,6 +410,11 @@ DeleteAccount::doApply() auto const sponsoringAccountCount = sponsorSle->getFieldU32(sfSponsoringAccountCount); + if (sponsoringAccountCount == 0) + // sanity check + // Since sfSponsoringAccountCount is set to soeDEFAULT, the field will not be populated with a value of 0. + return tefINTERNAL; // LCOV_EXCL_LINE + if (sponsoringAccountCount == 1) sponsorSle->makeFieldAbsent(sfSponsoringAccountCount); else From 8f8fc03800006255d09df916d186e0e4a5c4a8ae Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 21:21:38 +0900 Subject: [PATCH 115/249] Add InvariantChecks for if OwnerCount < SponsoredOwnerCount --- src/test/app/Invariants_test.cpp | 23 +++++++++++++++++++++- src/xrpld/app/tx/detail/InvariantCheck.cpp | 20 +++++++++++++++++++ src/xrpld/app/tx/detail/InvariantCheck.h | 2 ++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 9af5c551b58..f67c65df812 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -101,7 +101,8 @@ class Invariants_test : public beast::unit_test::suite if (messages.find(m) == std::string::npos) { // uncomment if you want to log the invariant failure - // std::cerr << " --> " << m << std::endl; + // std::cerr << " expected --> " << m << std::endl; + // std::cerr << " actual --> " << messages << std::endl; fail(); } } @@ -3516,6 +3517,26 @@ class Invariants_test : public beast::unit_test::suite }); } + { + auto const expect_message = "OwnerCount must be greater than or equal to SponsoredOwnerCount."; + + doInvariantCheck({{expect_message}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(A1.id())); + if (!sle) + return false; + sle->setFieldU32(sfOwnerCount, 0); + sle->setFieldU32(sfSponsoredOwnerCount, 1); + ac.view().update(sle); + + auto const sle2 = ac.view().peek(keylet::account(A2.id())); + if (!sle2) + return false; + sle2->setFieldU32(sfSponsoringOwnerCount, 1); + ac.view().update(sle2); + return true; + }); + } + { auto const expect_message = "Invariant failed: Net delta of SponsoringAccountCount does " diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index 86b8c5ede65..c3966fa5172 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -3287,6 +3287,17 @@ SponsorshipOwnerCountsMatch::visitEntry( return 0; }; + auto getOwnerCount = [](std::shared_ptr const& sle) -> std::uint32_t { + if (sle && sle->getType() == ltACCOUNT_ROOT) + return sle->getFieldU32(sfOwnerCount); + return 0; + }; + auto getSponsoredOwnerCount = [](std::shared_ptr const& sle) -> std::uint32_t { + if (sle && sle->getType() == ltACCOUNT_ROOT) + return sle->getFieldU32(sfSponsoredOwnerCount); + return 0; + }; + std::int64_t const beforeSponsored = getSponsored(before); std::int64_t const afterSponsored = getSponsored(after); std::int64_t const beforeSponsoring = getSponsoring(before); @@ -3294,6 +3305,9 @@ SponsorshipOwnerCountsMatch::visitEntry( deltaSponsoredOwnerCount_ += (afterSponsored - beforeSponsored); deltaSponsoringOwnerCount_ += (afterSponsoring - beforeSponsoring); + + if (getOwnerCount(after) < getSponsoredOwnerCount(after)) + invalidOwnerCountLessThanSponsoredOwnerCount_ += 1; } bool @@ -3306,6 +3320,12 @@ SponsorshipOwnerCountsMatch::finalize(STTx const&, TER const, XRPAmount const, R return false; } + if (invalidOwnerCountLessThanSponsoredOwnerCount_ > 0) + { + JLOG(j.fatal()) << "Invariant failed: OwnerCount must be greater than or equal to SponsoredOwnerCount."; + return false; + } + return true; } diff --git a/src/xrpld/app/tx/detail/InvariantCheck.h b/src/xrpld/app/tx/detail/InvariantCheck.h index b4ccd6a5f42..45d18581fd3 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.h +++ b/src/xrpld/app/tx/detail/InvariantCheck.h @@ -401,11 +401,13 @@ class NFTokenCountTracking * The following check is made for every transaction: * - The sum of all per-account deltas of `sfSponsoredOwnerCount` equals * the sum of all per-account deltas of `sfSponsoringOwnerCount`. + * - Account OwnerCount must be greater than or equal to SponsoredOwnerCount. */ class SponsorshipOwnerCountsMatch { std::int64_t deltaSponsoredOwnerCount_ = 0; std::int64_t deltaSponsoringOwnerCount_ = 0; + std::uint64_t invalidOwnerCountLessThanSponsoredOwnerCount_ = 0; public: void From 0a410024a5bfb45b9edd5b142978b5dbac528ac1 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 22:22:09 +0900 Subject: [PATCH 116/249] refactor SponsorshipSet flag checks --- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index d57de6c4ddd..2c6e6f6e364 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -16,22 +16,11 @@ NotTEC SponsorshipSet::preflight(PreflightContext const& ctx) { auto const flags = ctx.tx.getFlags(); - { - if ((flags & tfSponsorshipSetRequireSignForFee) && (flags & tfSponsorshipClearRequireSignForFee)) - return temINVALID_FLAG; - if ((flags & tfSponsorshipSetRequireSignForReserve) && (flags & tfSponsorshipClearRequireSignForReserve)) - return temINVALID_FLAG; - - if (flags & tfDeleteObject) - { - // check Flags - if (flags & - (tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve | - tfSponsorshipClearRequireSignForFee | tfSponsorshipClearRequireSignForReserve)) - return temINVALID_FLAG; - } - } + if ((flags & tfSponsorshipSetRequireSignForFee) && (flags & tfSponsorshipClearRequireSignForFee)) + return temINVALID_FLAG; + if ((flags & tfSponsorshipSetRequireSignForReserve) && (flags & tfSponsorshipClearRequireSignForReserve)) + return temINVALID_FLAG; auto const account = ctx.tx.getAccountID(sfAccount); bool const hasSponsor = ctx.tx.isFieldPresent(sfSponsorAccount); From 4a205eb9d88f31c0cda1a83f257468524c9f0606 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 22:23:45 +0900 Subject: [PATCH 117/249] add checks to accountReserve and fix sponsored owner count for xrpLiquid --- include/xrpl/protocol/Fees.h | 13 +++++++------ src/libxrpl/ledger/View.cpp | 2 +- src/xrpld/app/tx/detail/AMMDeposit.cpp | 5 +++-- src/xrpld/app/tx/detail/CashCheck.cpp | 5 +++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index d86ea6e8a1c..9ccffc3cb38 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -34,14 +34,15 @@ struct Fees bool isAccountSponsored = false, std::size_t sponsoringAccountCount = 0) const { - auto const accountReserveUnits = - (isAccountSponsored ? 0 : 1) + sponsoringAccountCount; + auto const accountReserveUnits = (isAccountSponsored ? 0 : 1) + sponsoringAccountCount; - auto const ownerReserveUnits = - (ownerCount - sponsoredOwnerCount) + sponsoringOwnerCount; + XRPL_ASSERT( + ownerCount >= sponsoredOwnerCount, + "xrpl::Fees::accountReserve : OwnerCount must be greater than or equal to SponsoredOwnerCount"); - return (reserve * accountReserveUnits) + - (increment * ownerReserveUnits); + auto const ownerReserveUnits = (ownerCount - sponsoredOwnerCount) + sponsoringOwnerCount; + + return (reserve * accountReserveUnits) + (increment * ownerReserveUnits); } }; diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index c9604b435ac..73c2779136a 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1043,7 +1043,7 @@ checkInsufficientReserve( std::optional getTxReserveSponsorAccountID(STTx const& tx) { - if (tx.isFieldPresent(sfSponsor) && tx.getFieldU32(sfSponsorFlags) & tfSponsorReserve) + if (tx.isFieldPresent(sfSponsor) && isReserveSponsored(tx)) { return tx.getAccountID(sfSponsor); } diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index 930bdb8764c..9adb2a94888 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -444,8 +444,9 @@ AMMDeposit::deposit( { auto const& lpIssue = lpTokensDeposit.issue(); // Adjust the reserve if LP doesn't have LPToken trustline - auto const sle = view.read(keylet::line(account_, lpIssue.account, lpIssue.currency)); - if (xrpLiquid(view, sponsor.value_or(account_), !sle, j_) >= depositAmount) + auto const trustlineExists = view.exists(keylet::line(account_, lpIssue.account, lpIssue.currency)); + auto const ownerCountAdj = trustlineExists ? 0 : 1; + if (xrpLiquid(view, sponsor.value_or(account_), sponsor ? ownerCountAdj : 0, j_) >= depositAmount) return tesSUCCESS; } else if ( diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 42589eed13d..712e9402bc5 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -218,6 +218,8 @@ CashCheck::doApply() // LCOV_EXCL_STOP } + auto const sponsorSle = getLedgerEntryReserveSponsor(psb, sleCheck); + // Preclaim already checked that source has at least the requested // funds. // @@ -246,7 +248,7 @@ CashCheck::doApply() // from src's directory, we allow them to send that additional // incremental reserve amount in the transfer. Hence the -1 // argument. - STAmount const srcLiquid{xrpLiquid(psb, srcId, -1, viewJ)}; + STAmount const srcLiquid{xrpLiquid(psb, srcId, sponsorSle ? 0 : -1, viewJ)}; // Now, how much do they need in order to be successful? STAmount const xrpDeliver{ @@ -436,7 +438,6 @@ CashCheck::doApply() // If we succeeded, update the check owner's reserve. - auto const sponsorSle = getLedgerEntryReserveSponsor(psb, sleCheck); adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), sponsorSle, -1, viewJ); // Remove check from ledger. From 527d7bbedeab29ba84b420937dbd462f5b6256fa Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 23:24:50 +0900 Subject: [PATCH 118/249] fix to return error if FeeAmount > Balance on SponsorshipSet --- src/test/app/Sponsor_test.cpp | 18 ++++++++++++++++-- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 6 +++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 5e1bbea2dc3..05923635c02 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -156,22 +156,36 @@ class Sponsor_test : public beast::unit_test::suite // Invalid Sponsee env(sponsor::set(sponsor, 0), sponsor::sponseeAcc(noFunded), ter(tecNO_DST)); + env.close(); // Invalid Sponsor env(sponsor::set(sponsor, tfDeleteObject), sponsor::sponsorAcc(noFunded), ter(tecNO_DST)); + env.close(); // Invalid Delete operation (sponsorship not found) env(sponsor::set(sponsor, tfDeleteObject), sponsor::sponseeAcc(alice), ter(tecNO_ENTRY)); + env.close(); // DisallowIncomingSponsor: tested in other testcase // insufficent reserve to create sponsorship adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tecUNFUNDED)); + env.close(); + + // FeeAmount + Fee > Balance + /// Balance = 1000XRP, FeeAmount = 1001XRP + adjustAccountXRPBalance(env, sponsor, XRP(1000)); + env(sponsor::set_fee(sponsor, 0, XRP(1001)), sponsor::sponseeAcc(alice), fee(XRP(1)), ter(tecUNFUNDED)); + env.close(); + /// Balance = 1000XRP, FeeAmount = 999XRP, Fee=2XRP + adjustAccountXRPBalance(env, sponsor, XRP(1000)); + env(sponsor::set_fee(sponsor, 0, XRP(999)), sponsor::sponseeAcc(alice), fee(XRP(2)), ter(tecUNFUNDED)); + env.close(); // create sponsor to use above tests - adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + adjustAccountXRPBalance(env, sponsor, XRP(1001)); + env(sponsor::set(sponsor, 0, 100, XRP(1000)), sponsor::sponseeAcc(alice), fee(XRP(1)), ter(tesSUCCESS)); env.close(); } diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 2c6e6f6e364..2ae7dda8d96 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -147,7 +147,8 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) return tecINTERNAL; // LCOV_EXCL_LINE // check Sponsor - if (!ctx.view.exists(keylet::account(sponsor))) + auto const sponsorAccSle = ctx.view.read(keylet::account(sponsor)); + if (!sponsorAccSle) return tecNO_DST; // check Sponsee @@ -215,6 +216,9 @@ SponsorshipSet::doApply() auto reserveSponsorAccSle = getTxReserveSponsor(view(), ctx_.tx); + if (feeAmount && (*feeAmount).xrp() > (*sponsorAccSle)[sfBalance]) + return tecUNFUNDED; + if (!sponsorObjSle) { // Create From 4e87de7303e6a90d5d57f804a854428a5e57669c Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 31 Jan 2026 01:31:37 +0900 Subject: [PATCH 119/249] add test for Sponsored trustline --- src/test/app/Sponsor_test.cpp | 232 ++++++++++++++++++++-------------- 1 file changed, 137 insertions(+), 95 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 05923635c02..9794fae3229 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -3421,128 +3421,170 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); + auto const validateSponsoredTrustline = + [&](std::shared_ptr const& sle, bool isIssuerHigh, Account const& sponsor) { + BEAST_EXPECT(sle->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == sponsor.id()); + BEAST_EXPECT(!sle->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); + }; + + auto const& highAcc = alice > bob ? alice : bob; + auto const& lowAcc = alice > bob ? bob : alice; + + // create and delete + for (bool isIssuerHigh : {false, true}) { - auto const validateSponsoredTrustline = - [&](std::shared_ptr const& sle, bool isIssuerHigh, Account const& sponsor) { - BEAST_EXPECT(sle->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == sponsor.id()); - BEAST_EXPECT(!sle->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); - }; + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, charlie, sponsor, sponsor2); + env.close(); + + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; - auto const& highAcc = alice > bob ? alice : bob; - auto const& lowAcc = alice > bob ? bob : alice; + auto const USD = issuer["USD"]; + auto const currency = USD.currency; - // create and delete - for (bool isIssuerHigh : {false, true}) + // create TrustLine + if (cosigning) { - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, charlie, sponsor, sponsor2); + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(ticket::create(sponsor, 2)); // adjust for free trustline env.close(); + } - auto const& issuer = isIssuerHigh ? highAcc : lowAcc; - auto const& user = isIssuerHigh ? lowAcc : highAcc; + testEachSponsorship( + env, cosigning, sponsor, user, 1, 1, tecNO_LINE_INSUF_RESERVE, [&](Env& env, auto const& submit) { + submit(trust(user, USD(100))); + }); - auto const USD = issuer["USD"]; - auto const currency = USD.currency; + auto const keylet = keylet::line(user, issuer, currency); - // create TrustLine - if (cosigning) - { - adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); - env(ticket::create(sponsor, 2)); // adjust for free trustline - env.close(); - } + if (cosigning) + { + // invalid owner + env(sponsor::transfer(charlie, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2), + ter(tecNO_PERMISSION)); + // invalid reserve owner + env(sponsor::transfer(issuer, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2), + ter(tecNO_PERMISSION)); + env(sponsor::transfer(user, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(user)); + env.close(); + env(sponsor::transfer(user, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env.close(); + } - testEachSponsorship( - env, cosigning, sponsor, user, 1, 1, tecNO_LINE_INSUF_RESERVE, [&](Env& env, auto const& submit) { - submit(trust(user, USD(100))); - }); + // delete TrustLine + env(trust(user, USD(0))); + env.close(); - auto const keylet = keylet::line(user, issuer, currency); + BEAST_EXPECT(ownerCount(env, user) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - if (cosigning) - { - // invalid owner - env(sponsor::transfer(charlie, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2), - ter(tecNO_PERMISSION)); - // invalid reserve owner - env(sponsor::transfer(issuer, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2), - ter(tecNO_PERMISSION)); - env(sponsor::transfer(user, keylet.key), - sponsor::as(sponsor2, tfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); - env.close(); - } - else - { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(user)); - env.close(); - env(sponsor::transfer(user, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); - env.close(); - } + BEAST_EXPECT(!env.le(keylet)); + } - // delete TrustLine - env(trust(user, USD(0))); - env.close(); + // update + for (bool isIssuerHigh : {false, true}) + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); - BEAST_EXPECT(ownerCount(env, user) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; - BEAST_EXPECT(!env.le(keylet)); - } + auto const USD = issuer["USD"]; + auto const currency = USD.currency; + + // create TrustLine from issuer + env(trust(issuer, user["USD"](100))); + env.close(); - // update - for (bool isIssuerHigh : {false, true}) + BEAST_EXPECT(env.le(keylet::line(user, issuer, currency))); + + if (cosigning) { - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(ticket::create(sponsor, 2)); // adjust for free trustline env.close(); + } - auto const& issuer = isIssuerHigh ? highAcc : lowAcc; - auto const& user = isIssuerHigh ? lowAcc : highAcc; + testEachSponsorship( + env, cosigning, sponsor, user, 1, 1, tecINSUF_RESERVE_LINE, [&](Env& env, auto const& submit) { + submit(trust(user, USD(100))); + }); - auto const USD = issuer["USD"]; - auto const currency = USD.currency; + auto const line = env.le(keylet::line(user, issuer, currency)); + validateSponsoredTrustline(line, isIssuerHigh, sponsor); - // create TrustLine from issuer - env(trust(issuer, user["USD"](100))); - env.close(); + // update TrustLine from user to clear reserve + env(trust(user, USD(0))); + env.close(); - BEAST_EXPECT(env.le(keylet::line(user, issuer, currency))); + BEAST_EXPECT(ownerCount(env, user) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(env.le(keylet::line(user, issuer, currency))); - if (cosigning) - { - adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); - env(ticket::create(sponsor, 2)); // adjust for free trustline - env.close(); - } + // remove TrustLine from issuer + env(trust(issuer, user["USD"](0))); + env.close(); + BEAST_EXPECT(!env.le(keylet::line(user, issuer, currency))); + } - testEachSponsorship( - env, cosigning, sponsor, user, 1, 1, tecINSUF_RESERVE_LINE, [&](Env& env, auto const& submit) { - submit(trust(user, USD(100))); - }); + // both High and Low sponsored + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); - auto const line = env.le(keylet::line(user, issuer, currency)); - validateSponsoredTrustline(line, isIssuerHigh, sponsor); + // create TrustLines + env(trust(alice, bob["USD"](100)), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + env(trust(bob, alice["USD"](100)), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); - // update TrustLine from user to clear reserve - env(trust(user, USD(0))); - env.close(); + auto sle = env.le(keylet::line(alice, bob, alice["USD"].currency)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->isFlag(lsfHighReserve)); + BEAST_EXPECT(sle->isFlag(lsfLowReserve)); + BEAST_EXPECT(sle->getAccountID(sfHighSponsor) == sponsor.id()); + BEAST_EXPECT(sle->getAccountID(sfLowSponsor) == sponsor.id()); - BEAST_EXPECT(ownerCount(env, user) == 0); - BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(env.le(keylet::line(user, issuer, currency))); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); - // remove TrustLine from issuer - env(trust(issuer, user["USD"](0))); - env.close(); - BEAST_EXPECT(!env.le(keylet::line(user, issuer, currency))); - } + // clear TrustLines + env(trust(alice, bob["USD"](0))); + env.close(); + env(trust(bob, alice["USD"](0))); + env.close(); + + sle = env.le(keylet::line(alice, bob, alice["USD"].currency)); + BEAST_EXPECT(!sle); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } } From d390bc7a87d8a700e1d63fff6728ee206a46cf29 Mon Sep 17 00:00:00 2001 From: tequ Date: Sat, 31 Jan 2026 01:33:29 +0900 Subject: [PATCH 120/249] clang-format --- src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h index 14dd47e65b0..ba5d9d6d9bf 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h @@ -44,11 +44,7 @@ class MPTokenIssuanceCreate : public Transactor doApply() override; static Expected - create( - ApplyView& view, - STTx const& tx, - beast::Journal journal, - MPTCreateArgs const& args); + create(ApplyView& view, STTx const& tx, beast::Journal journal, MPTCreateArgs const& args); }; } // namespace xrpl From f8209087a36bd76097aa3fa36949c55e0da01579 Mon Sep 17 00:00:00 2001 From: tequ Date: Sun, 1 Feb 2026 20:38:37 +0900 Subject: [PATCH 121/249] Add tests for LoanBroker transactions --- src/test/app/Sponsor_test.cpp | 131 ++++++++++++++++++++++ src/xrpld/app/tx/detail/LoanBrokerSet.cpp | 2 + 2 files changed, 133 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 9794fae3229..cfd7b23d4c4 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -3905,6 +3905,134 @@ class Sponsor_test : public beast::unit_test::suite } } + void + testLending(bool cosigning) + { + testcase("Lending"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const issuer("issuer"); + Account const sponsor("sponsor"); + + // LoanBrokerSet / LoanBrokerDelete + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + PrettyAsset const asset{xrpIssue(), 1'000'000}; + + Vault vault{env}; + auto const [tx, keylet] = vault.create({.owner = alice, .asset = asset}); + env(tx, THISLINE); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 3); // Vault, PseudoAccount(Vault), MPToken(Vault) + + // LoanBrokerSet + testEachSponsorship( + // Both the Pseudo-account and LoanBroker objects are created, but only the LoanBroker is sponsored. + env, + cosigning, + sponsor, + alice, + 2, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(loanBroker::set(alice, keylet.key, 0)); }); + + BEAST_EXPECT( + ownerCount(env, alice) == + 5); // LoanBroker, PseudoAccount(LB), (Vault, PseudoAccount(Vault), MPToken(Vault)) + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // LoanBrokerDelete + auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice) - 1); + env(loanBroker::del(alice, brokerKeylet.key, 0)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 3); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + // LoanBrokerConverDeposit/Withdraw/Clawback + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000), alice, bob, issuer, sponsor); + env.close(); + + MPTTester mptt{env, issuer, mptInitNoFund}; + mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); + env.close(); + PrettyAsset const asset = mptt["MPT"]; + mptt.authorize({.account = alice}); + env.close(); + + env(pay(issuer, alice, asset(100))); + env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 1); + + Vault vault{env}; + auto const [tx, keylet] = vault.create({.owner = alice, .asset = asset}); + env(tx, THISLINE); + env.close(); + + env(loanBroker::set(alice, keylet.key, 0)); + env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 6); // LoanBroker, PseudoAccount(LB), (Vault, PseudoAccount(Vault), + // MPToken(Vault), MPToken(issuer)) + + auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice) - 1); + // LoanBrokerCoverDeposit + // doesn't sponsor anything + env(loanBroker::coverDeposit(alice, brokerKeylet.key, asset(100)), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 6); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // remove MPToken(issuer) + mptt.authorize({.account = alice, .flags = tfMPTUnauthorize}); + env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 5); + + env(ticket::create(sponsor, 2)); // for avoid free MPToken + env.close(); + + // LoanBrokerCoverWithdraw + testEachSponsorship( + env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + submit(loanBroker::coverWithdraw(alice, brokerKeylet.key, asset(10))); + }); + + BEAST_EXPECT(ownerCount(env, alice) == 6); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // LoanBrokerCoverClawback + // doesn't sponsor anything + env(loanBroker::coverClawback(issuer), + loanBroker::loanBrokerID(brokerKeylet.key), + amount(asset(1)), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 6); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } + // LoanSet + // LoanDelete + // LoanManage + // LoanPay + } + void testDisallowIncoming() { @@ -4290,6 +4418,8 @@ class Sponsor_test : public beast::unit_test::suite void testSponsorReserve(bool cosigning) { + testLending(cosigning); + return; testRequireFlag(); testAMM(cosigning); testCheck(cosigning); @@ -4310,6 +4440,7 @@ class Sponsor_test : public beast::unit_test::suite testTrustSet(cosigning); testVault(cosigning); testXChain(cosigning); + testLending(cosigning); } protected: diff --git a/src/xrpld/app/tx/detail/LoanBrokerSet.cpp b/src/xrpld/app/tx/detail/LoanBrokerSet.cpp index 8b0cd9013bb..8784935ed56 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerSet.cpp +++ b/src/xrpld/app/tx/detail/LoanBrokerSet.cpp @@ -263,6 +263,8 @@ LoanBrokerSet::doApply() if (auto const coverLiq = tx[~sfCoverRateLiquidation]) broker->at(sfCoverRateLiquidation) = *coverLiq; + addSponsorToLedgerEntry(broker, sponsor); + view.insert(broker); associateAsset(*broker, vaultAsset); From 37f4ea94cbc160b81967f405862a3c4199fc51c3 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 2 Feb 2026 12:25:56 +0900 Subject: [PATCH 122/249] Add tests for Loan --- src/test/app/Sponsor_test.cpp | 102 +++++++++++++++++++++++++--- src/xrpld/app/tx/detail/LoanPay.cpp | 4 +- src/xrpld/app/tx/detail/LoanSet.cpp | 5 +- 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index cfd7b23d4c4..5f516d61cf7 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -7,9 +7,6 @@ #include #include -#include "test/jtx/envconfig.h" -#include "test/jtx/sponsor.h" - namespace xrpl { namespace test { @@ -4028,9 +4025,100 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); } // LoanSet - // LoanDelete - // LoanManage - // LoanPay + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, issuer, sponsor); + env.close(); + + MPTTester mptt{env, issuer, mptInitNoFund}; + mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); + env.close(); + PrettyAsset const asset = mptt["MPT"]; + mptt.authorize({.account = alice}); + mptt.authorize({.account = bob}); + env.close(); + + env(pay(issuer, alice, asset(1000))); + env(pay(issuer, bob, asset(1000))); + env.close(); + + Vault vault{env}; + auto const [tx, keylet] = vault.create({.owner = bob, .asset = asset}); + env(tx, THISLINE); + env.close(); + env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}), THISLINE); + env.close(); + + auto const brokerKeylet = keylet::loanbroker(bob.id(), env.seq(bob)); + env(loanBroker::set(bob, keylet.key, 0)); + env.close(); + env(loanBroker::coverDeposit(bob, brokerKeylet.key, asset(100))); + env.close(); + + auto broker = env.le(brokerKeylet); + BEAST_EXPECT(broker->getFieldU32(sfOwnerCount) == 0); + BEAST_EXPECT(!broker->isFieldPresent(sfSponsoredOwnerCount)); + BEAST_EXPECT(!broker->isFieldPresent(sfSponsoringOwnerCount)); + + auto const loanSeq = broker->getFieldU32(sfLoanSequence); + testEachSponsorship( + env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + submit(loan::set(alice, brokerKeylet.key, 10), sig(sfCounterpartySignature, bob), fee(XRP(1))); + }); + broker = env.le(brokerKeylet); + // broker'object doesn't sponsored + BEAST_EXPECT(broker->getFieldU32(sfOwnerCount) == 1); + BEAST_EXPECT(!broker->isFieldPresent(sfSponsoredOwnerCount)); + BEAST_EXPECT(!broker->isFieldPresent(sfSponsoringOwnerCount)); + + auto const loanKeylet = keylet::loan(brokerKeylet.key, loanSeq); + + auto sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfOwnerCount) == 0); + BEAST_EXPECT(!sponsorSle->isFieldPresent(sfSponsoredOwnerCount)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringOwnerCount) == 1); + + // LoanManage + env(loan::manage(bob, loanKeylet.key, lsfLoanImpaired), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + // doesn't sponsor anything + sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfOwnerCount) == 0); + BEAST_EXPECT(!sponsorSle->isFieldPresent(sfSponsoredOwnerCount)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringOwnerCount) == 1); + + // LoanPay + env(loan::pay(alice, loanKeylet.key, asset(10)), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + // doesn't sponsor anything + sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfOwnerCount) == 0); + BEAST_EXPECT(!sponsorSle->isFieldPresent(sfSponsoredOwnerCount)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringOwnerCount) == 1); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + + // LoanDelete + env(loan::del(alice, loanKeylet.key), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + // Sponsored ltLoan is deleted + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + // Sponsor for ltLoan object is deleted + sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfOwnerCount) == 0); + BEAST_EXPECT(!sponsorSle->isFieldPresent(sfSponsoredOwnerCount)); + } } void @@ -4418,8 +4506,6 @@ class Sponsor_test : public beast::unit_test::suite void testSponsorReserve(bool cosigning) { - testLending(cosigning); - return; testRequireFlag(); testAMM(cosigning); testCheck(cosigning); diff --git a/src/xrpld/app/tx/detail/LoanPay.cpp b/src/xrpld/app/tx/detail/LoanPay.cpp index 0f078b3dafc..bb4b9d04c40 100644 --- a/src/xrpld/app/tx/detail/LoanPay.cpp +++ b/src/xrpld/app/tx/detail/LoanPay.cpp @@ -489,15 +489,13 @@ LoanPay::doApply() return ter; } - auto const sponsorAccount = getTxReserveSponsorAccountID(tx); - if (auto const ter = accountSendMulti( view, account_, asset, {{vaultPseudoAccount, totalPaidToVaultRounded}, {brokerPayee, totalPaidToBroker}}, j_, - sponsorAccount, + {}, // Vault and Broker cannot be sponsored WaiveTransferFee::Yes)) return ter; diff --git a/src/xrpld/app/tx/detail/LoanSet.cpp b/src/xrpld/app/tx/detail/LoanSet.cpp index 36b141e71d6..60391e78bf6 100644 --- a/src/xrpld/app/tx/detail/LoanSet.cpp +++ b/src/xrpld/app/tx/detail/LoanSet.cpp @@ -494,15 +494,13 @@ LoanSet::doApply() if (auto const ter = requireAuth(view, vaultAsset, brokerOwner, AuthType::StrongAuth)) return ter; - auto const sponsorAccount = getTxReserveSponsorAccountID(tx); - if (auto const ter = accountSendMulti( view, vaultPseudo, vaultAsset, {{borrower, loanAssetsToBorrower}, {brokerOwner, originationFee}}, j_, - sponsorAccount, + {}, // Vault and Broker cannot be sponsored WaiveTransferFee::Yes)) return ter; @@ -548,6 +546,7 @@ LoanSet::doApply() loan->at(sfPreviousPaymentDueDate) = 0; loan->at(sfNextPaymentDueDate) = startDate + paymentInterval; loan->at(sfPaymentRemaining) = paymentTotal; + addSponsorToLedgerEntry(loan, sponsorSle); view.insert(loan); // Update the balances in the vault From 49c52695cb886c6e15d16857acba7ee033120a68 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 2 Feb 2026 12:51:35 +0900 Subject: [PATCH 123/249] `sfSponsorAccount` -> `sfSponsor`/`sfCounterpartySponsor` --- include/xrpl/ledger/View.h | 15 ++-- include/xrpl/protocol/detail/sfields.macro | 4 +- .../xrpl/protocol/detail/transactions.macro | 2 +- src/libxrpl/ledger/View.cpp | 12 +-- src/libxrpl/protocol/LedgerFormats.cpp | 2 +- src/test/app/Invariants_test.cpp | 6 +- src/test/app/Sponsor_test.cpp | 85 ++++++++++--------- src/test/jtx/impl/sponsor.cpp | 4 +- src/test/jtx/sponsor.h | 4 +- src/xrpld/app/tx/detail/DeleteAccount.cpp | 12 +-- src/xrpld/app/tx/detail/InvariantCheck.cpp | 6 +- src/xrpld/app/tx/detail/InvariantCheck.h | 2 +- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 8 +- .../app/tx/detail/SponsorshipTransfer.cpp | 20 ++--- src/xrpld/rpc/handlers/AccountObjects.cpp | 4 +- 15 files changed, 92 insertions(+), 94 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 839b2922015..131ad7e6e64 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -450,30 +450,25 @@ std::shared_ptr const getTxReserveSponsor(ReadView const& view, STTx const& tx); std::optional -getLedgerEntryReserveSponsorAccountID( - std::shared_ptr const& sle, - SF_ACCOUNT const& field = sfSponsorAccount); +getLedgerEntryReserveSponsorAccountID(std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsor); std::shared_ptr -getLedgerEntryReserveSponsor( - ApplyView& view, - std::shared_ptr const& sle, - SF_ACCOUNT const& field = sfSponsorAccount); +getLedgerEntryReserveSponsor(ApplyView& view, std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsor); std::shared_ptr const getLedgerEntryReserveSponsor( ReadView const& view, std::shared_ptr const& sle, - SF_ACCOUNT const& field = sfSponsorAccount); + SF_ACCOUNT const& field = sfSponsor); void addSponsorToLedgerEntry( std::shared_ptr const& sle, std::shared_ptr const& sponsorSle, - SF_ACCOUNT const& field = sfSponsorAccount); + SF_ACCOUNT const& field = sfSponsor); void -removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsorAccount); +removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsor); //------------------------------------------------------------------------------ // diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 94c6be1ab4d..c0f8752463c 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -332,10 +332,10 @@ TYPED_SFIELD(sfIssuingChainDoor, ACCOUNT, 23) TYPED_SFIELD(sfSubject, ACCOUNT, 24) TYPED_SFIELD(sfBorrower, ACCOUNT, 25) TYPED_SFIELD(sfCounterparty, ACCOUNT, 26) -TYPED_SFIELD(sfSponsorAccount, ACCOUNT, 27) +TYPED_SFIELD(sfSponsor, ACCOUNT, 27) TYPED_SFIELD(sfHighSponsor, ACCOUNT, 28) TYPED_SFIELD(sfLowSponsor, ACCOUNT, 29) -TYPED_SFIELD(sfSponsor, ACCOUNT, 30) +TYPED_SFIELD(sfCounterpartySponsor, ACCOUNT, 30) TYPED_SFIELD(sfSponsee, ACCOUNT, 31) // vector of 256-bit diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 3b9e71b8af1..c960142ddba 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -1079,7 +1079,7 @@ TRANSACTION(ttSPONSORSHIP_SET, 86, SponsorshipSet, featureSponsor, noPriv, ({ - {sfSponsorAccount, soeOPTIONAL}, + {sfCounterpartySponsor, soeOPTIONAL}, {sfSponsee, soeOPTIONAL}, {sfFeeAmount, soeOPTIONAL}, {sfMaxFee, soeOPTIONAL}, diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 73c2779136a..b99975c5d44 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -580,7 +580,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, std::size_t sponsoredOwnerCount = sle->getFieldU32(sfSponsoredOwnerCount); std::size_t sponsoringOwnerCount = sle->getFieldU32(sfSponsoringOwnerCount); - bool isAccountSponsored = sle->isFieldPresent(sfSponsorAccount); + bool isAccountSponsored = sle->isFieldPresent(sfSponsor); std::size_t sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount); // Pseudo-accounts have no reserve requirement @@ -958,7 +958,7 @@ calculateReserve(std::shared_ptr const& sle, Fees const& fees) sle->getFieldU32(sfOwnerCount), sle->getFieldU32(sfSponsoredOwnerCount), sle->getFieldU32(sfSponsoringOwnerCount), - sle->isFieldPresent(sfSponsorAccount), + sle->isFieldPresent(sfSponsor), sle->getFieldU32(sfSponsoringAccountCount)); } @@ -1008,7 +1008,7 @@ checkInsufficientReserve( sponsorSle->getFieldU32(sfOwnerCount), sponsorSle->getFieldU32(sfSponsoredOwnerCount), sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ownerCountDelta, - sponsorSle->isFieldPresent(sfSponsorAccount), + sponsorSle->isFieldPresent(sfSponsor), sponsorSle->getFieldU32(sfSponsoringAccountCount) + accountCountDelta)}; if (sponsorBalance < sponsorReserve) @@ -1031,7 +1031,7 @@ checkInsufficientReserve( accSle->getFieldU32(sfOwnerCount) + ownerCountDelta, accSle->getFieldU32(sfSponsoredOwnerCount), accSle->getFieldU32(sfSponsoringOwnerCount), - accSle->isFieldPresent(sfSponsorAccount), + accSle->isFieldPresent(sfSponsor), accSle->getFieldU32(sfSponsoringAccountCount) + accountCountDelta)}; if (accBalance < reserve) @@ -1102,7 +1102,7 @@ addSponsorToLedgerEntry( { XRPL_ASSERT( (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || - (sle->getType() != ltRIPPLE_STATE && field == sfSponsorAccount), + (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), "addSponsorToLedgerEntry : Invalid field to the LedgerEntry"); if (sponsorSle) sle->setAccountID(field, sponsorSle->getAccountID(sfAccount)); @@ -1113,7 +1113,7 @@ removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& { XRPL_ASSERT( (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || - (sle->getType() != ltRIPPLE_STATE && field == sfSponsorAccount), + (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), "removeSponsorFromLedgerEntry : Invalid field to the LedgerEntry"); if (sle->isFieldPresent(field)) sle->makeFieldAbsent(field); diff --git a/src/libxrpl/protocol/LedgerFormats.cpp b/src/libxrpl/protocol/LedgerFormats.cpp index f13befdee8a..223e0b4bad2 100644 --- a/src/libxrpl/protocol/LedgerFormats.cpp +++ b/src/libxrpl/protocol/LedgerFormats.cpp @@ -14,7 +14,7 @@ LedgerFormats::LedgerFormats() {sfLedgerIndex, soeOPTIONAL}, {sfLedgerEntryType, soeREQUIRED}, {sfFlags, soeREQUIRED}, - {sfSponsorAccount, soeOPTIONAL}, + {sfSponsor, soeOPTIONAL}, }; #pragma push_macro("UNWRAP") diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index f67c65df812..65b7aa6c285 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -1429,7 +1429,7 @@ class Invariants_test : public beast::unit_test::suite }, { "pseudo-account has a sponsorship field", - [](SLE::pointer& sle) { sle->at(sfSponsorAccount) = Account("sponsor").id(); }, + [](SLE::pointer& sle) { sle->at(sfSponsor) = Account("sponsor").id(); }, }, }); @@ -3540,7 +3540,7 @@ class Invariants_test : public beast::unit_test::suite { auto const expect_message = "Invariant failed: Net delta of SponsoringAccountCount does " - "not match net delta of sfSponsorAccount presence."; + "not match net delta of sfSponsor presence."; doInvariantCheck({{expect_message}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { auto const sle = ac.view().peek(keylet::account(A1.id())); @@ -3555,7 +3555,7 @@ class Invariants_test : public beast::unit_test::suite auto const sle = ac.view().peek(keylet::account(A1.id())); if (!sle) return false; - sle->setAccountID(sfSponsorAccount, A2.id()); + sle->setAccountID(sfSponsor, A2.id()); ac.view().update(sle); return true; }); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 5f516d61cf7..0b57f4bfd8a 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -118,11 +118,14 @@ class Sponsor_test : public beast::unit_test::suite // invalid SponsorAccount / Sponsee // Account = Sponsor - env(sponsor::set(alice, tfDeleteObject), sponsor::sponsorAcc(alice), ter(temMALFORMED)); + env(sponsor::set(alice, tfDeleteObject), sponsor::counterpartySponsor(alice), ter(temMALFORMED)); // Account = Sponsee env(sponsor::set(alice, tfDeleteObject), sponsor::sponseeAcc(alice), ter(temMALFORMED)); // Both Sponsor and Sponsee are specified - env(sponsor::set(alice, 0), sponsor::sponsorAcc(sponsor), sponsor::sponseeAcc(alice), ter(temMALFORMED)); + env(sponsor::set(alice, 0), + sponsor::counterpartySponsor(sponsor), + sponsor::sponseeAcc(alice), + ter(temMALFORMED)); // Invalid feeAmount for (auto amt : {XRP(-1), XRP(0), USD(1)}) @@ -144,8 +147,8 @@ class Sponsor_test : public beast::unit_test::suite // TODO: test MaxFee with tfDeleteObject // Invalid SponsorAccount with non-Delete operation - env(sponsor::set_reserve(sponsor, 0, 100), sponsor::sponsorAcc(alice), ter(temMALFORMED)); - env(sponsor::set_fee(sponsor, 0, XRP(1), XRP(1)), sponsor::sponsorAcc(alice), ter(temMALFORMED)); + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::counterpartySponsor(alice), ter(temMALFORMED)); + env(sponsor::set_fee(sponsor, 0, XRP(1), XRP(1)), sponsor::counterpartySponsor(alice), ter(temMALFORMED)); // // preclaim @@ -156,7 +159,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Invalid Sponsor - env(sponsor::set(sponsor, tfDeleteObject), sponsor::sponsorAcc(noFunded), ter(tecNO_DST)); + env(sponsor::set(sponsor, tfDeleteObject), sponsor::counterpartySponsor(noFunded), ter(tecNO_DST)); env.close(); // Invalid Delete operation (sponsorship not found) @@ -391,7 +394,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // delete from sponsee - env(sponsor::del(alice), sponsor::sponsorAcc(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); env.close(); } @@ -405,7 +408,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); env.close(); - env(sponsor::del(alice), sponsor::sponsorAcc(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); env.close(); } { @@ -418,7 +421,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor, 0, 100), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); env.close(); - env(sponsor::del(alice), sponsor::sponsorAcc(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); env.close(); } { @@ -426,7 +429,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); env.close(); - env(sponsor::del(alice), sponsor::sponsorAcc(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); env.close(); } { @@ -434,7 +437,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor, 0, 100), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); env.close(); - env(sponsor::del(alice), sponsor::sponsorAcc(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); env.close(); } } @@ -544,8 +547,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 1); auto const sle1 = env.le(keylet::account(alice)); - BEAST_EXPECT(sle1->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); + BEAST_EXPECT(sle1->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sle1->getAccountID(sfSponsor) == sponsor1.id()); // transfer sponsor adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 2) - drops(1)); @@ -572,8 +575,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); BEAST_EXPECT(!env.le(keylet::account(sponsor1))->isFieldPresent(sfSponsoringAccountCount)); auto const sle2 = env.le(keylet::account(alice)); - BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(sle2->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sle2->getAccountID(sfSponsor) == sponsor2.id()); // sponsor 2 accounts adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 3)); @@ -601,7 +604,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); auto const sle3 = env.le(keylet::account(alice)); - BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(!sle3->isFieldPresent(sfSponsor)); env(sponsor::transfer(bob)); env.close(); @@ -617,7 +620,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); BEAST_EXPECT(!env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringAccountCount)); auto const sle4 = env.le(keylet::account(bob)); - BEAST_EXPECT(!sle4->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(!sle4->isFieldPresent(sfSponsor)); // not sponsored env(sponsor::transfer(bob), ter(tecNO_PERMISSION)); @@ -680,8 +683,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); auto const sle1 = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(sle1->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); + BEAST_EXPECT(sle1->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sle1->getAccountID(sfSponsor) == sponsor1.id()); // transfer sponsor env(sponsor::transfer(alice, checkId), @@ -707,8 +710,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); auto const sle2 = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(sle2->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sle2->getAccountID(sfSponsor) == sponsor2.id()); // dissolve sponsor adjustAccountXRPBalance(env, alice, reserve(env, 1) - drops(1)); @@ -745,7 +748,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); BEAST_EXPECT(!env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); auto const sle3 = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(!sle3->isFieldPresent(sfSponsor)); } { // sponsor object (pre-funded + no ltSponsorship entry) @@ -815,8 +818,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); auto checkSle = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(checkSle->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(checkSle->getAccountID(sfSponsorAccount) == sponsor1.id()); + BEAST_EXPECT(checkSle->isFieldPresent(sfSponsor)); + BEAST_EXPECT(checkSle->getAccountID(sfSponsor) == sponsor1.id()); auto sponsor1Sle = env.le(keylet::sponsor(sponsor1, alice)); BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 99); @@ -837,8 +840,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); checkSle = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(checkSle->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(checkSle->getAccountID(sfSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(checkSle->isFieldPresent(sfSponsor)); + BEAST_EXPECT(checkSle->getAccountID(sfSponsor) == sponsor2.id()); sponsor1Sle = env.le(keylet::sponsor(sponsor1, alice)); BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 100); // paybacked auto sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice)); @@ -860,7 +863,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); BEAST_EXPECT(!env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); checkSle = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(!checkSle->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(!checkSle->isFieldPresent(sfSponsor)); sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice)); BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 100); // paybacked } @@ -1210,7 +1213,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); auto const bobSle = env.le(keylet::account(bob)); - BEAST_EXPECT(!bobSle->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(!bobSle->isFieldPresent(sfSponsor)); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 0); } @@ -1226,8 +1229,8 @@ class Sponsor_test : public beast::unit_test::suite env.close(); auto const charlieSle = env.le(keylet::account(charlie)); - BEAST_EXPECT(charlieSle->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(charlieSle->getAccountID(sfSponsorAccount) == sponsor.id()); + BEAST_EXPECT(charlieSle->isFieldPresent(sfSponsor)); + BEAST_EXPECT(charlieSle->getAccountID(sfSponsor) == sponsor.id()); BEAST_EXPECT(sponsoredOwnerCount(env, charlie) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1); } @@ -1469,7 +1472,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); // LPToken BEAST_EXPECT(sponsoredOwnerCount(env, ammAccount) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken - BEAST_EXPECT(!env.le(keylet::amm(USD.issue(), EUR.issue()))->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(!env.le(keylet::amm(USD.issue(), EUR.issue()))->isFieldPresent(sfSponsor)); }); auto const ammKeylet = keylet::amm(USD.issue(), EUR.issue()); @@ -1712,7 +1715,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); auto const keylet = keylet::check(alice, seq); - BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id()); if (cosigning) { @@ -1738,7 +1741,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor2.id()); // CheckCancel env(check::cancel(alice, keylet.key)); @@ -1801,7 +1804,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); auto const keylet = keylet::check(alice, seq2); - BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id()); // CheckCash testEachSponsorship( @@ -1874,7 +1877,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor2.id()); // OfferCancel env(offer_cancel(alice, seq)); @@ -2037,7 +2040,7 @@ class Sponsor_test : public beast::unit_test::suite }); auto const keylet = keylet::ticket(alice, ticketSeq); - BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id()); // transfer sponsor if (cosigning) @@ -2061,7 +2064,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 249); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor2.id()); // use a Ticket env(noop(alice), ticket::use(ticketSeq)); @@ -2188,7 +2191,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(!env.le(keylet::credential(subject, issuer, credTypeSlice))->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(!env.le(keylet::credential(subject, issuer, credTypeSlice))->isFieldPresent(sfSponsor)); env(credentials::deleteCred(subject, subject, issuer, credType)); env.close(); @@ -2386,7 +2389,7 @@ class Sponsor_test : public beast::unit_test::suite escrow::condition(escrow::cb1), escrow::cancel_time(env.now() + 100s)); }); - BEAST_EXPECT(env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsorAccount) == sponsor.id()); + BEAST_EXPECT(env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); // transfer sponsor if (cosigning) @@ -2410,7 +2413,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT(env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor2.id()); // EscrowFinish env(escrow::finish(bob, alice, seq), @@ -2456,7 +2459,7 @@ class Sponsor_test : public beast::unit_test::suite escrow::cancel_time(env.now() + 100s)); }); - BEAST_EXPECT(env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsorAccount) == sponsor.id()); + BEAST_EXPECT(env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); // EscrowFinish testEachSponsorship( @@ -3623,7 +3626,7 @@ class Sponsor_test : public beast::unit_test::suite submit(std::get<0>(result)); keylet = std::get<1>(result); }); - BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsorAccount) == sponsor.id()); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id()); } // VaultDeposit { diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 942fd317a0f..d9b73dabedb 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -78,9 +78,9 @@ transfer(jtx::Account const& account, std::optional const& index) } void -sponsorAcc::operator()(Env& env, JTx& jt) const +counterpartySponsor::operator()(Env& env, JTx& jt) const { - jt.jv[sfSponsorAccount.jsonName] = sponsor_.human(); + jt.jv[sfCounterpartySponsor.jsonName] = sponsor_.human(); } void diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index d077933f008..b118bd4356c 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -34,13 +34,13 @@ del(jtx::Account const& account); Json::Value transfer(jtx::Account const& account, std::optional const& index = std::nullopt); -struct sponsorAcc +struct counterpartySponsor { private: jtx::Account sponsor_; public: - sponsorAcc(jtx::Account const& account) : sponsor_(account) + counterpartySponsor(jtx::Account const& account) : sponsor_(account) { } diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index 9bdb783d3f6..b3ebd0a249e 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -266,9 +266,9 @@ DeleteAccount::preclaim(PreclaimContext const& ctx) if (cp) return tecHAS_OBLIGATIONS; - if (sleAccount->isFieldPresent(sfSponsorAccount)) + if (sleAccount->isFieldPresent(sfSponsor)) { - if (dst != sleAccount->getAccountID(sfSponsorAccount)) + if (dst != sleAccount->getAccountID(sfSponsor)) return tecNO_SPONSOR_PERMISSION; } if (sleAccount->isFieldPresent(sfSponsoringOwnerCount) || sleAccount->isFieldPresent(sfSponsoringAccountCount)) @@ -400,9 +400,9 @@ DeleteAccount::doApply() (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance; ctx_.deliver(mSourceBalance); - if (src->isFieldPresent(sfSponsorAccount)) + if (src->isFieldPresent(sfSponsor)) { - auto const sponsorAcc = src->getAccountID(sfSponsorAccount); + auto const sponsorAcc = src->getAccountID(sfSponsor); auto sponsorSle = view().peek(keylet::account(sponsorAcc)); if (!sponsorSle || !sponsorSle->isFieldPresent(sfSponsoringAccountCount)) @@ -421,10 +421,10 @@ DeleteAccount::doApply() sponsorSle->setFieldU32(sfSponsoringAccountCount, sponsoringAccountCount - 1); view().update(sponsorSle); - // Following line might look redundant, but without it, sfSponsorAccount + // Following line might look redundant, but without it, sfSponsor // would end up remaining in after-ltAccountRoot during the // InvariantCheck. - (*src).makeFieldAbsent(sfSponsorAccount); + (*src).makeFieldAbsent(sfSponsor); } XRPL_ASSERT((*src)[sfBalance] == XRPAmount(0), "xrpl::DeleteAccount::doApply : source balance is zero"); diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index c3966fa5172..003acad9cf8 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -1668,7 +1668,7 @@ ValidPseudoAccounts::visitEntry( errors_.emplace_back("pseudo-account has a regular key"); } if (after->isFieldPresent(sfSponsoredOwnerCount) || after->isFieldPresent(sfSponsoringOwnerCount) || - after->isFieldPresent(sfSponsorAccount)) + after->isFieldPresent(sfSponsor)) { errors_.emplace_back("pseudo-account has a sponsorship field"); } @@ -3342,7 +3342,7 @@ SponsorshipAccountCountMatchesField::visitEntry( }; auto hasSponsorField = [](std::shared_ptr const& sle) -> bool { - return sle && sle->getType() == ltACCOUNT_ROOT && sle->isFieldPresent(sfSponsorAccount); + return sle && sle->getType() == ltACCOUNT_ROOT && sle->isFieldPresent(sfSponsor); }; std::int64_t const beforeCount = getSponsoringAccountCount(before); @@ -3365,7 +3365,7 @@ SponsorshipAccountCountMatchesField::finalize( if (deltaSponsoringAccountCount_ != deltaSponsorFieldPresence_) { JLOG(j.fatal()) << "Invariant failed: Net delta of SponsoringAccountCount does not " - "match net delta of sfSponsorAccount presence."; + "match net delta of sfSponsor presence."; return false; } diff --git a/src/xrpld/app/tx/detail/InvariantCheck.h b/src/xrpld/app/tx/detail/InvariantCheck.h index 45d18581fd3..4bda7a28171 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.h +++ b/src/xrpld/app/tx/detail/InvariantCheck.h @@ -423,7 +423,7 @@ class SponsorshipOwnerCountsMatch * The following check is made for every transaction: * - The net delta of `sfSponsoringAccountCount` across all accounts equals * the net delta of the count of ltACCOUNT_ROOT entries having - * `sfSponsorAccount` present (presence transitions only: add/remove). + * `sfSponsor` present (presence transitions only: add/remove). */ class SponsorshipAccountCountMatchesField { diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 2ae7dda8d96..4b536d2cf97 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -23,14 +23,14 @@ SponsorshipSet::preflight(PreflightContext const& ctx) return temINVALID_FLAG; auto const account = ctx.tx.getAccountID(sfAccount); - bool const hasSponsor = ctx.tx.isFieldPresent(sfSponsorAccount); + bool const hasSponsor = ctx.tx.isFieldPresent(sfCounterpartySponsor); bool const hasSponsee = ctx.tx.isFieldPresent(sfSponsee); // The transaction must specify either Sponsor or Sponsee, but not both. if (hasSponsor == hasSponsee) return temMALFORMED; - auto const sponsor = ctx.tx[~sfSponsorAccount].value_or(account); + auto const sponsor = ctx.tx[~sfCounterpartySponsor].value_or(account); auto const sponsee = ctx.tx[~sfSponsee].value_or(account); if (sponsor == sponsee) @@ -140,7 +140,7 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) TER SponsorshipSet::preclaim(PreclaimContext const& ctx) { - auto const sponsor = ctx.tx[~sfSponsorAccount].value_or(ctx.tx[sfAccount]); + auto const sponsor = ctx.tx[~sfCounterpartySponsor].value_or(ctx.tx[sfAccount]); auto const sponsee = ctx.tx[~sfSponsee].value_or(ctx.tx[sfAccount]); if (sponsee == sponsor) @@ -172,7 +172,7 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) TER SponsorshipSet::doApply() { - auto const sponsorAcc = ctx_.tx[~sfSponsorAccount].value_or(account_); + auto const sponsorAcc = ctx_.tx[~sfCounterpartySponsor].value_or(account_); auto const sponseeAcc = ctx_.tx[~sfSponsee].value_or(account_); if (sponseeAcc == sponsorAcc) diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index a292e7511c8..7ebbdc41802 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -149,7 +149,7 @@ getLedgerEntrySponsorField(T const& sle, AccountID const& owner) // LCOV_EXCL_STOP } default: - return sfSponsorAccount; + return sfSponsor; } }; @@ -210,7 +210,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { if (newSponsor) { - if (accSle->isFieldPresent(sfSponsorAccount)) + if (accSle->isFieldPresent(sfSponsor)) { // check not same account if (newSponsor->getAccountID(sfAccount) == accSle->getAccountID(sfAccount)) @@ -222,7 +222,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { // dissolve sponsor // check account is sponsored - if (!accSle->isFieldPresent(sfSponsorAccount)) + if (!accSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; } @@ -345,7 +345,7 @@ SponsorshipTransfer::doApply() else { // dissolve object sponsor - auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); + auto const oldSponsor = objSle->getAccountID(sfSponsor); auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE @@ -366,7 +366,7 @@ SponsorshipTransfer::doApply() return ter; // remove sponsor from object - objSle->makeFieldAbsent(sfSponsorAccount); + objSle->makeFieldAbsent(sfSponsor); view().update(objSle); } } @@ -383,21 +383,21 @@ SponsorshipTransfer::doApply() view().update(newSponsorSle); // decrement old sponsoring count - if (accSle->isFieldPresent(sfSponsorAccount)) + if (accSle->isFieldPresent(sfSponsor)) { - auto const oldSponsor = accSle->getAccountID(sfSponsorAccount); + auto const oldSponsor = accSle->getAccountID(sfSponsor); auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); view().update(oldSponsorSle); } - accSle->setAccountID(sfSponsorAccount, newSponsor); + accSle->setAccountID(sfSponsor, newSponsor); view().update(accSle); } else { // dissolve account sponsor - auto const oldSponsor = accSle->getAccountID(sfSponsorAccount); - accSle->makeFieldAbsent(sfSponsorAccount); + auto const oldSponsor = accSle->getAccountID(sfSponsor); + accSle->makeFieldAbsent(sfSponsor); // decrement account sponsoring count auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); diff --git a/src/xrpld/rpc/handlers/AccountObjects.cpp b/src/xrpld/rpc/handlers/AccountObjects.cpp index bc25e11ba33..e6aa2d5f965 100644 --- a/src/xrpld/rpc/handlers/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/AccountObjects.cpp @@ -320,8 +320,8 @@ getAccountObjects( if (typeFilter.has_value() && !typeMatchesFilter(typeFilter.value(), sleNode->getType())) canAppend = false; - std::optional const sponsor = sleNode->isFieldPresent(sfSponsorAccount) - ? sleNode->getAccountID(sfSponsorAccount) + std::optional const sponsor = sleNode->isFieldPresent(sfSponsor) + ? sleNode->getAccountID(sfSponsor) : std::optional(std::nullopt); if (sponsored.has_value() && !sponsoredMatchesFilter(sponsored.value(), sponsor)) From fe075470f88df22a007936de2db96ec9c8963ccf Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 2 Feb 2026 18:57:04 +0900 Subject: [PATCH 124/249] address review --- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 36 +++++++++++++--------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index 4b536d2cf97..b9dba52abbd 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -22,6 +22,26 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if ((flags & tfSponsorshipSetRequireSignForReserve) && (flags & tfSponsorshipClearRequireSignForReserve)) return temINVALID_FLAG; + if (flags & tfDeleteObject) + { + // can not combine with any modification flags when deleting + constexpr std::uint32_t modifyFlags = tfSponsorshipSetRequireSignForFee | + tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForFee | + tfSponsorshipClearRequireSignForReserve; + + if (flags & modifyFlags) + return temINVALID_FLAG; + } + else + { + // can not combine set flag with clear flag + if ((flags & tfSponsorshipSetRequireSignForFee) && (flags & tfSponsorshipClearRequireSignForFee)) + return temINVALID_FLAG; + + if ((flags & tfSponsorshipSetRequireSignForReserve) && (flags & tfSponsorshipClearRequireSignForReserve)) + return temINVALID_FLAG; + } + auto const account = ctx.tx.getAccountID(sfAccount); bool const hasSponsor = ctx.tx.isFieldPresent(sfCounterpartySponsor); bool const hasSponsee = ctx.tx.isFieldPresent(sfSponsee); @@ -38,14 +58,6 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (flags & tfDeleteObject) { - // can not combine with any modification flags when deleting - constexpr std::uint32_t modifyFlags = tfSponsorshipSetRequireSignForFee | - tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForFee | - tfSponsorshipClearRequireSignForReserve; - - if (flags & modifyFlags) - return temINVALID_FLAG; - // can not include these fields when deleting if (ctx.tx.isFieldPresent(sfFeeAmount) || ctx.tx.isFieldPresent(sfReserveCount) || ctx.tx.isFieldPresent(sfMaxFee)) @@ -58,12 +70,6 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (account != sponsor) return temMALFORMED; - if ((flags & tfSponsorshipSetRequireSignForFee) && (flags & tfSponsorshipClearRequireSignForFee)) - return temINVALID_FLAG; - - if ((flags & tfSponsorshipSetRequireSignForReserve) && (flags & tfSponsorshipClearRequireSignForReserve)) - return temINVALID_FLAG; - // Check FeeAmount and MaxFee auto const checkOptionalAmountField = [&](SField const& field) -> NotTEC { if (!ctx.tx.isFieldPresent(field)) @@ -74,7 +80,7 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (!isXRP(amount)) return temBAD_AMOUNT; - if (amount.xrp().drops() <= 0) + if (amount.xrp() <= XRPAmount{0}) return temBAD_AMOUNT; return tesSUCCESS; From e1aee43359be003019997e6ded3bedaa934faba7 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 3 Feb 2026 14:45:55 +0900 Subject: [PATCH 125/249] address review --- src/xrpld/app/tx/detail/SponsorshipSet.cpp | 28 +++++++--------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/xrpld/app/tx/detail/SponsorshipSet.cpp b/src/xrpld/app/tx/detail/SponsorshipSet.cpp index b9dba52abbd..47839e5801e 100644 --- a/src/xrpld/app/tx/detail/SponsorshipSet.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipSet.cpp @@ -22,26 +22,6 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if ((flags & tfSponsorshipSetRequireSignForReserve) && (flags & tfSponsorshipClearRequireSignForReserve)) return temINVALID_FLAG; - if (flags & tfDeleteObject) - { - // can not combine with any modification flags when deleting - constexpr std::uint32_t modifyFlags = tfSponsorshipSetRequireSignForFee | - tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForFee | - tfSponsorshipClearRequireSignForReserve; - - if (flags & modifyFlags) - return temINVALID_FLAG; - } - else - { - // can not combine set flag with clear flag - if ((flags & tfSponsorshipSetRequireSignForFee) && (flags & tfSponsorshipClearRequireSignForFee)) - return temINVALID_FLAG; - - if ((flags & tfSponsorshipSetRequireSignForReserve) && (flags & tfSponsorshipClearRequireSignForReserve)) - return temINVALID_FLAG; - } - auto const account = ctx.tx.getAccountID(sfAccount); bool const hasSponsor = ctx.tx.isFieldPresent(sfCounterpartySponsor); bool const hasSponsee = ctx.tx.isFieldPresent(sfSponsee); @@ -58,6 +38,14 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (flags & tfDeleteObject) { + // can not combine with any modification flags when deleting + constexpr std::uint32_t modifyFlags = tfSponsorshipSetRequireSignForFee | + tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForFee | + tfSponsorshipClearRequireSignForReserve; + + if (flags & modifyFlags) + return temINVALID_FLAG; + // can not include these fields when deleting if (ctx.tx.isFieldPresent(sfFeeAmount) || ctx.tx.isFieldPresent(sfReserveCount) || ctx.tx.isFieldPresent(sfMaxFee)) From 392a913631af081818db3ecbbd44bfec30d57e4b Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 14:09:03 +0900 Subject: [PATCH 126/249] audit 3 --- src/libxrpl/ledger/View.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index b99975c5d44..f235a5cc13d 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1139,7 +1139,6 @@ adjustOwnerCount( if (sponsorSle) { - XRPL_ASSERT(sponsorSle, "xrpl::adjustOwnerCount : sponsor exists"); auto const account = accountSle->getAccountID(sfAccount); auto const sponsorAcc = (sponsorSle)->getAccountID(sfAccount); { From d1d613da27bdf467b5825f99b327a7287c3c9370 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 14:19:40 +0900 Subject: [PATCH 127/249] audit 4 --- src/libxrpl/tx/Transactor.cpp | 8 +++++++- src/test/app/Sponsor_test.cpp | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 5e557ea0fd5..20090923c09 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -157,7 +157,13 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return temINVALID_FLAG; } - if (hasSponsorSig && !hasSponsor) + if (!hasSponsor && hasSponsorFlags) + { + JLOG(ctx.j.debug()) << "preflight1: sponsor flags without sponsor definition"; + return temINVALID_FLAG; + } + + if (!hasSponsor && hasSponsorSig) { JLOG(ctx.j.debug()) << "preflight1: sponsor signature without sponsor definition"; return temMALFORMED; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 0b57f4bfd8a..6a619830f7d 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -315,6 +315,11 @@ class Sponsor_test : public beast::unit_test::suite // Invalid Flags env(noop(alice), sponsor::as(sponsor, (tfSponsorFee | tfSponsorReserve) + 1), ter(temINVALID_FLAG)); + + // Invalid Flags without sponsor + auto tx = noop(alice); + tx[sfSponsorFlags.jsonName] = tfSponsorFee | tfSponsorReserve; + env(tx, ter(temINVALID_FLAG)); } void From c1e1be510fdf25a501221dcf53984bae8f5bf75e Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 14:39:27 +0900 Subject: [PATCH 128/249] audit 5 --- src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 946a9621ef9..2d13c1c1f88 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -226,6 +226,8 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } // check account have sufficient balance + // In the case of removing an account sponsor, accSle should have no sfSponsor set (AccountReserve = 0). + // However, by setting accountCountDelta = 1 here, we are able to calculate the actual required Account Reserve. if (auto const ter = checkInsufficientReserve(ctx.view, ctx.tx, accSle, accSle->getFieldAmount(sfBalance), newSponsor, 0, 1); !isTesSuccess(ter)) From 686f9459098d767f728f92d1294fcd76492bbafb Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 14:46:03 +0900 Subject: [PATCH 129/249] audit 6 --- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index c893e7ac465..114f6e530e4 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -195,8 +195,11 @@ SponsorshipSet::doApply() keylet::ownerDir(sponseeAcc), (*sponsorObjSle)[sfSponseeNode], sponsorObjSle->key(), false); // transfer feeAmount from ledger entry - auto const feeAmount = sponsorObjSle->getFieldAmount(sfFeeAmount); - (*sponsorAccSle)[sfBalance] += feeAmount; + if (sponsorObjSle->isFieldPresent(sfFeeAmount)) + { + auto const feeAmount = sponsorObjSle->getFieldAmount(sfFeeAmount); + (*sponsorAccSle)[sfBalance] += feeAmount; + } ctx_.view().erase(sponsorObjSle); @@ -313,8 +316,11 @@ SponsorshipSet::deleteSponsorship(ApplyView& view, std::shared_ptr const& s if (!sponsorAccSle) return tecINTERNAL; // LCOV_EXCL_LINE - auto const feeAmount = sle->getFieldAmount(sfFeeAmount); - (*sponsorAccSle)[sfBalance] += feeAmount; + if (sle->isFieldPresent(sfFeeAmount)) + { + auto const feeAmount = sle->getFieldAmount(sfFeeAmount); + (*sponsorAccSle)[sfBalance] += feeAmount; + } auto const reserveSponsor = getLedgerEntryReserveSponsor(view, sle); adjustOwnerCount(view, sponsorAccSle, reserveSponsor, -1, j); From bf4fa37937ec9e17f75fc42bb6622bb86bcbb497 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 14:48:51 +0900 Subject: [PATCH 130/249] audit 7 --- src/libxrpl/tx/transactors/NFT/NFTokenUtils.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenUtils.cpp b/src/libxrpl/tx/transactors/NFT/NFTokenUtils.cpp index 5c5cbf2d63d..136bb5657bb 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenUtils.cpp +++ b/src/libxrpl/tx/transactors/NFT/NFTokenUtils.cpp @@ -487,11 +487,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, s auto const sponsor = getLedgerEntryReserveSponsor(view, curr); adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - getLedgerEntryReserveSponsor(view, curr), - -1, - beast::Journal{beast::Journal::getNullSink()}); + view, view.peek(keylet::account(owner)), sponsor, -1, beast::Journal{beast::Journal::getNullSink()}); view.erase(curr); From f6d79f74d680a57c2243a3150c983a71e649b0ef Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 14:53:21 +0900 Subject: [PATCH 131/249] audit 8 --- include/xrpl/ledger/View.h | 4 ++-- src/libxrpl/ledger/View.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 3e83c74b78a..71f3aaa1133 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -445,7 +445,7 @@ getTxReserveSponsorAccountID(STTx const& tx); std::shared_ptr getTxReserveSponsor(ApplyView& view, STTx const& tx); -std::shared_ptr const +std::shared_ptr getTxReserveSponsor(ReadView const& view, STTx const& tx); std::optional @@ -454,7 +454,7 @@ getLedgerEntryReserveSponsorAccountID(std::shared_ptr const& sle, SF_ std::shared_ptr getLedgerEntryReserveSponsor(ApplyView& view, std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsor); -std::shared_ptr const +std::shared_ptr getLedgerEntryReserveSponsor( ReadView const& view, std::shared_ptr const& sle, diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index f235a5cc13d..75f16275192 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1059,7 +1059,7 @@ getTxReserveSponsor(ApplyView& view, STTx const& tx) return {}; } -std::shared_ptr const +std::shared_ptr getTxReserveSponsor(ReadView const& view, STTx const& tx) { auto const sponsorID = getTxReserveSponsorAccountID(tx); @@ -1085,7 +1085,7 @@ getLedgerEntryReserveSponsor(ApplyView& view, std::shared_ptr const& sle, S return {}; } -std::shared_ptr const +std::shared_ptr getLedgerEntryReserveSponsor(ReadView const& view, std::shared_ptr const& sle, SF_ACCOUNT const& field) { auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); From 9859ba85a8cf355c52ff33be8b028b4dcf5a5a3f Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 15:01:35 +0900 Subject: [PATCH 132/249] audit 9 --- src/libxrpl/ledger/View.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 75f16275192..daebea38b28 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1170,18 +1170,14 @@ adjustOwnerCount( { // pre funded // update the pre-funded ReserveCount on Sponsorship ledger object - XRPL_ASSERT(sponsorObjSle, "xrpl::adjustOwnerCount : co-signing sponsor exists"); - auto const currentReserveCount = sponsorObjSle->getFieldU32(sfReserveCount); - if (amount > 0) - XRPL_ASSERT(currentReserveCount >= amount, "xrpl::adjustOwnerCount : enough reserve count"); - - if (currentReserveCount - amount > 0) - // if amount > 0, reduce the reserve count - // if amount < 0, payback the reserve count - sponsorObjSle->setFieldU32(sfReserveCount, currentReserveCount - amount); - else + std::uint32_t const currentReserveCount = sponsorObjSle->getFieldU32(sfReserveCount); + // Reserve count moves opposite to amount: +amount => consume reserve (-), -amount => payback (+) + std::uint32_t const adjusted = confineOwnerCount(currentReserveCount, -amount, sponsorAcc, j); + if (adjusted == 0) sponsorObjSle->makeFieldAbsent(sfReserveCount); + else + sponsorObjSle->setFieldU32(sfReserveCount, adjusted); view.update(sponsorObjSle); } } From 9cde57d284f17250ab34bad37f4aff2a80f00f7d Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 15:09:12 +0900 Subject: [PATCH 133/249] audit 10 --- src/libxrpl/ledger/View.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index daebea38b28..9689360e07a 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1855,11 +1855,14 @@ removeEmptyHolding( if (!sleLowAccount) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleLowAccount, {}, -1, journal); + auto const currentLowSponsor = getLedgerEntryReserveSponsor(view, line, sfLowSponsor); + + adjustOwnerCount(view, sleLowAccount, currentLowSponsor, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. line->clearFlag(lsfLowReserve); + removeSponsorFromLedgerEntry(line, sfLowSponsor); } if (line->isFlag(lsfHighReserve)) @@ -1869,11 +1872,14 @@ removeEmptyHolding( if (!sleHighAccount) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleHighAccount, {}, -1, journal); + auto const currentHighSponsor = getLedgerEntryReserveSponsor(view, line, sfHighSponsor); + + adjustOwnerCount(view, sleHighAccount, currentHighSponsor, -1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. line->clearFlag(lsfHighReserve); + removeSponsorFromLedgerEntry(line, sfHighSponsor); } return trustDelete(view, line, line->at(sfLowLimit)->getIssuer(), line->at(sfHighLimit)->getIssuer(), journal); From fe5fab00edc829b1f79c7a8c18284c7a9dfd4e8e Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 15:18:17 +0900 Subject: [PATCH 134/249] audit 11 --- src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp index 081344ce81a..775fc7da0d2 100644 --- a/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/NFT/NFTokenAcceptOffer.cpp @@ -364,9 +364,9 @@ NFTokenAcceptOffer::transferNFToken(AccountID const& buyer, AccountID const& sel auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount); if (buyerOwnerCountAfter > buyerOwnerCountBefore) { - auto const sponsor = + auto const sponsorSle = account_ == buyer ? getTxReserveSponsor(ctx_.view(), ctx_.tx) : std::shared_ptr(); - if (auto const ret = checkInsufficientReserve(ctx_.view(), ctx_.tx, sleBuyer, buyerBalance, sponsor, 0); + if (auto const ret = checkInsufficientReserve(ctx_.view(), ctx_.tx, sleBuyer, buyerBalance, sponsorSle, 0); !isTesSuccess(ret)) return ret; } From 2fd8e1be3c81ed816f3b808314b626c707a32d47 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 15:55:10 +0900 Subject: [PATCH 135/249] audit 12 --- .../Sponsor/SponsorshipTransfer.cpp | 4 +- src/test/app/Sponsor_test.cpp | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 2d13c1c1f88..a11891d8c80 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -346,7 +346,7 @@ SponsorshipTransfer::doApply() else { // dissolve object sponsor - auto const oldSponsor = objSle->getAccountID(sfSponsor); + auto const oldSponsor = objSle->getAccountID(sponsorField); auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE @@ -367,7 +367,7 @@ SponsorshipTransfer::doApply() return ter; // remove sponsor from object - objSle->makeFieldAbsent(sfSponsor); + objSle->makeFieldAbsent(sponsorField); view().update(objSle); } } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 6a619830f7d..a40a56436f3 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -873,6 +873,55 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 100); // paybacked } + { + // sponsor trustline + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + auto const& highAcc = alice > bob ? alice : bob; + auto const& lowAcc = alice > bob ? bob : alice; + + for (bool isIssuerHigh : {false, true}) + { + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; + + auto const USD = issuer["USD"]; + auto const currency = USD.currency; + + env(trust(user, issuer["USD"](100))); + env.close(); + + auto const trustId = keylet::line(user, issuer, currency); + BEAST_EXPECT(env.le(trustId)); + + // transfer sponsor + env(sponsor::transfer(user, trustId.key), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + BEAST_EXPECT(env.le(trustId)); + + BEAST_EXPECT( + env.le(trustId)->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == sponsor.id()); + BEAST_EXPECT(!env.le(trustId)->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); + + // dissolve sponsor + env(sponsor::transfer(user, trustId.key)); + env.close(); + + BEAST_EXPECT(env.le(trustId)); + BEAST_EXPECT(!env.le(trustId)->isFieldPresent(isIssuerHigh ? sfLowSponsor : sfHighSponsor)); + BEAST_EXPECT(!env.le(trustId)->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); + } + } + { // invalid transfer Env env{*this, testable_amendments()}; From dcfcb1f3fd4919e8627aa3c7444fccc72120d628 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 16:33:32 +0900 Subject: [PATCH 136/249] audit 13 --- src/libxrpl/tx/transactors/Escrow.cpp | 8 ++++-- src/test/app/Sponsor_test.cpp | 41 ++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/libxrpl/tx/transactors/Escrow.cpp b/src/libxrpl/tx/transactors/Escrow.cpp index a4ef7a81bac..2f36eb06193 100644 --- a/src/libxrpl/tx/transactors/Escrow.cpp +++ b/src/libxrpl/tx/transactors/Escrow.cpp @@ -834,7 +834,8 @@ escrowUnlockApplyHelper( auto const mptID = amount.get().getMptID(); auto const issuanceKey = keylet::mptIssuance(mptID); - if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer) + auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver); + if (!view.exists(mptKeylet) && createAsset && !receiverIssuer) { auto const sponsor = getTxReserveSponsor(view, tx); if (auto const ret = checkInsufficientReserve(view, tx, sleDest, xrpBalance, sponsor, 1); !isTesSuccess(ret)) @@ -847,10 +848,11 @@ escrowUnlockApplyHelper( // update owner count. adjustOwnerCount(view, tx, sleDest, sponsor, 1, journal); - addSponsorToLedgerEntry(sleDest, sponsor); + auto mptSle = view.peek(mptKeylet); + addSponsorToLedgerEntry(mptSle, sponsor); } - if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && !receiverIssuer) + if (!view.exists(mptKeylet) && !receiverIssuer) return tecNO_PERMISSION; auto const xferRate = transferRate(view, amount); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index a40a56436f3..e841d51de3b 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2531,6 +2531,45 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet::line(bob, gw, USD.currency))->getAccountID(sfHighSponsor) == sponsor2.id()); } + { + // MPT Escrow + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), bob, sponsor); + env.close(); + + MPTTester mptGw(env, gw, {.holders = {alice}}); + mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.authorize({.account = alice}); + auto const MPT = mptGw["MPT"]; + env(pay(gw, alice, MPT(10'000))); + env.close(); + return; + + // create Escrow from alice to bob + auto const seq = env.seq(alice); + env(escrow::create(alice, bob, MPT(100)), + escrow::condition(escrow::cb1), + escrow::cancel_time(env.now() + 100s)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // finish Escrow + env(escrow::finish(bob, alice, seq), + escrow::condition(escrow::cb1), + escrow::fulfillment(escrow::fb1), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor), + fee(XRP(1))); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } } void @@ -3384,7 +3423,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == ocount); - // disolve sponsor + // dissolve sponsor env(sponsor::transfer(alice, keylet::oracle(alice, 1).key)); env.close(); From 293394fbc6ae00edfcae2a84eceacb3b520a5c13 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Feb 2026 17:22:23 +0900 Subject: [PATCH 137/249] audit 14 --- src/libxrpl/tx/transactors/Payment.cpp | 3 ++- src/test/app/Sponsor_test.cpp | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/libxrpl/tx/transactors/Payment.cpp b/src/libxrpl/tx/transactors/Payment.cpp index 973252c25e1..96c01410396 100644 --- a/src/libxrpl/tx/transactors/Payment.cpp +++ b/src/libxrpl/tx/transactors/Payment.cpp @@ -562,7 +562,8 @@ Payment::doApply() // the number of reserves in this ledger for this account that require a // reserve. - auto const reserve = calculateReserve(sleSrc, view().fees()); + auto const reserve = calculateReserve(sleSrc, view().fees()) + + ((txFlags & tfSponsorCreatedAccount) ? view().fees().reserve : beast::zero); // mPriorBalance is the balance on the sending account BEFORE the // fees were charged. We want to make sure we have enough reserve diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e841d51de3b..7d0afbeec99 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1226,6 +1226,7 @@ class Sponsor_test : public beast::unit_test::suite Account const alice("alice"); Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); Account const bob("bob"); Account const charlie("charlie"); Account const gw("gw"); @@ -1241,7 +1242,7 @@ class Sponsor_test : public beast::unit_test::suite } Env env{*this, testable_amendments()}; - env.fund(XRP(10000), alice, sponsor); + env.fund(XRP(10000), alice, sponsor, sponsor2); env.close(); // Invalid flags @@ -1275,18 +1276,29 @@ class Sponsor_test : public beast::unit_test::suite // Use tfSponsorCreatedAccount to sponsor an account { // to funded account - env(pay(sponsor, bob, drops(1)), txflags(tfSponsorCreatedAccount), ter(tecNO_SPONSOR_PERMISSION)); + env(pay(sponsor2, bob, drops(1)), + txflags(tfSponsorCreatedAccount), + fee(XRP(1)), + ter(tecNO_SPONSOR_PERMISSION)); + env.close(); + + BEAST_EXPECT(env.balance(sponsor2) == XRP(9999)); + + // to non-funded account / insufficient balance for reserve + env(pay(sponsor2, charlie, XRP(9999) - env.current()->fees().reserve + drops(1)), + txflags(tfSponsorCreatedAccount), + ter(tecUNFUNDED_PAYMENT)); env.close(); // to non-funded account - env(pay(sponsor, charlie, drops(1)), txflags(tfSponsorCreatedAccount)); + env(pay(sponsor2, charlie, drops(1)), txflags(tfSponsorCreatedAccount), fee(XRP(1))); env.close(); auto const charlieSle = env.le(keylet::account(charlie)); BEAST_EXPECT(charlieSle->isFieldPresent(sfSponsor)); - BEAST_EXPECT(charlieSle->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT(charlieSle->getAccountID(sfSponsor) == sponsor2.id()); BEAST_EXPECT(sponsoredOwnerCount(env, charlie) == 0); - BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); } } From 178bb8f7e9bcae79282d9c7d9645f6ce0a888cff Mon Sep 17 00:00:00 2001 From: tequ Date: Sun, 22 Feb 2026 00:01:56 +0900 Subject: [PATCH 138/249] Allow zero value for ReserveCount, FeeAmount, MaxFee --- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 32 +++++++++++---- src/test/app/Sponsor_test.cpp | 41 ++++++++++++++++--- src/test/jtx/impl/sponsor.cpp | 11 +++++ src/test/jtx/sponsor.h | 3 ++ 4 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 114f6e530e4..cefdbf8090d 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -67,7 +67,7 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (!isXRP(amount)) return temBAD_AMOUNT; - if (amount.xrp() <= XRPAmount{0}) + if (amount.xrp() < XRPAmount{0}) return temBAD_AMOUNT; return tesSUCCESS; @@ -83,8 +83,8 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (ctx.tx.isFieldPresent(sfReserveCount)) { auto const reserveCount = ctx.tx.getFieldU32(sfReserveCount); - if (reserveCount < 1) - return temMALFORMED; + if (reserveCount < 0) + return temMALFORMED; // LCOV_EXCL_LINE } } @@ -227,14 +227,14 @@ SponsorshipSet::doApply() (*newSle)[sfOwner] = sponsorAcc; (*newSle)[sfSponsee] = sponseeAcc; - if (feeAmount) + if (feeAmount && *feeAmount > XRPAmount(0)) { (*sponsorAccSle)[sfBalance] -= *feeAmount; (*newSle)[sfFeeAmount] = *feeAmount; } - if (maxFee) + if (maxFee && *maxFee > XRPAmount(0)) (*newSle)[sfMaxFee] = *maxFee; - if (reserveCount) + if (reserveCount && *reserveCount > 0) (*newSle)[sfReserveCount] = *reserveCount; auto flags = 0; @@ -273,15 +273,29 @@ SponsorshipSet::doApply() if (feeAmountDelta != beast::zero) { (*sponsorAccSle)[sfBalance] -= feeAmountDelta; - (*sponsorObjSle).setFieldAmount(sfFeeAmount, *feeAmount); + + if (*feeAmount == XRPAmount(0)) + (*sponsorObjSle).makeFieldAbsent(sfFeeAmount); + else + (*sponsorObjSle).setFieldAmount(sfFeeAmount, *feeAmount); } } if (maxFee) - (*sponsorObjSle)[sfMaxFee] = *maxFee; + { + if (*maxFee == XRPAmount(0)) + (*sponsorObjSle).makeFieldAbsent(sfMaxFee); + else + (*sponsorObjSle)[sfMaxFee] = *maxFee; + } if (reserveCount) - (*sponsorObjSle)[sfReserveCount] = *reserveCount; + { + if (*reserveCount == 0) + (*sponsorObjSle).makeFieldAbsent(sfReserveCount); + else + (*sponsorObjSle)[sfReserveCount] = *reserveCount; + } // update Flags auto flags = sponsorObjSle->getFieldU32(sfFlags); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 7d0afbeec99..f90eda8cee8 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -128,23 +128,20 @@ class Sponsor_test : public beast::unit_test::suite ter(temMALFORMED)); // Invalid feeAmount - for (auto amt : {XRP(-1), XRP(0), USD(1)}) + for (auto amt : {XRP(-1), USD(1)}) { env(sponsor::set_fee(sponsor, 0, amt), sponsor::sponseeAcc(alice), ter(temBAD_AMOUNT)); } // Invalid MaxFee - for (auto amt : {XRP(-1), XRP(0), USD(1)}) + for (auto amt : {XRP(-1), USD(1)}) { env(sponsor::set_fee(sponsor, 0, XRP(1), amt), sponsor::sponseeAcc(alice), ter(temBAD_AMOUNT)); } - // Invalid reserveCount - env(sponsor::set_reserve(sponsor, 0, 0), sponsor::sponseeAcc(alice), ter(temMALFORMED)); - // Invalid Delete operation env(sponsor::set_reserve(sponsor, tfDeleteObject, 1), sponsor::sponseeAcc(alice), ter(temMALFORMED)); env(sponsor::set_fee(sponsor, tfDeleteObject, XRP(1)), sponsor::sponseeAcc(alice), ter(temMALFORMED)); - // TODO: test MaxFee with tfDeleteObject + env(sponsor::set_max_fee(sponsor, tfDeleteObject, XRP(1)), sponsor::sponseeAcc(alice), ter(temMALFORMED)); // Invalid SponsorAccount with non-Delete operation env(sponsor::set_reserve(sponsor, 0, 100), sponsor::counterpartySponsor(alice), ter(temMALFORMED)); @@ -334,6 +331,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); { + // create sponsorship env(sponsor::set( sponsor, tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve, @@ -401,6 +399,37 @@ class Sponsor_test : public beast::unit_test::suite // delete from sponsee env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); env.close(); + BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))); + + // create sponsorship with zero value + env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), sponsor::sponseeAcc(alice), fee(XRP(1))); + env.close(); + + sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(!sle->isFieldPresent(sfReserveCount)); + BEAST_EXPECT(!sle->isFieldPresent(sfFeeAmount)); + BEAST_EXPECT(!sle->isFieldPresent(sfMaxFee)); + + // update sponsorship with non-zero value + env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), sponsor::sponseeAcc(alice), fee(XRP(1))); + env.close(); + + sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfReserveCount) == 100); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100)); + BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1)); + + // update sponsorship with zero value + env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), sponsor::sponseeAcc(alice), fee(XRP(1))); + env.close(); + + sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(!sle->isFieldPresent(sfReserveCount)); + BEAST_EXPECT(!sle->isFieldPresent(sfFeeAmount)); + BEAST_EXPECT(!sle->isFieldPresent(sfMaxFee)); } { diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index d9b73dabedb..b152d296767 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -56,6 +56,17 @@ set_reserve(jtx::Account const& account, uint32_t flags, uint32_t reserveCount) return jv; } +Json::Value +set_max_fee(jtx::Account const& account, uint32_t flags, STAmount maxFee) +{ + Json::Value jv; + jv[jss::TransactionType] = jss::SponsorshipSet; + jv[jss::Account] = account.human(); + jv[sfFlags.jsonName] = flags; + jv[sfMaxFee.jsonName] = maxFee.getJson(JsonOptions::none); + return jv; +} + Json::Value del(jtx::Account const& account) { diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index b118bd4356c..a03a64865bd 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -28,6 +28,9 @@ set_fee( Json::Value set_reserve(jtx::Account const& account, std::uint32_t flags, std::uint32_t reserveCount); +Json::Value +set_max_fee(jtx::Account const& account, std::uint32_t flags, STAmount maxFee); + Json::Value del(jtx::Account const& account); From f86b255e7304c99c4b0cdc7c53fabe69c19f441f Mon Sep 17 00:00:00 2001 From: tequ Date: Sun, 22 Feb 2026 02:41:01 +0900 Subject: [PATCH 139/249] Add `SponsorshipEnd/Create/Reassign` flags for SponsorshipTransfer --- include/xrpl/protocol/TxFlags.h | 6 + .../transactors/Sponsor/SponsorshipTransfer.h | 3 + .../Sponsor/SponsorshipTransfer.cpp | 202 ++++++++++++---- src/test/app/NFToken_test.cpp | 4 +- src/test/app/Sponsor_test.cpp | 224 ++++++++++++------ src/test/jtx/impl/sponsor.cpp | 3 +- src/test/jtx/sponsor.h | 2 +- src/test/rpc/AccountSet_test.cpp | 4 +- src/test/rpc/AccountTx_test.cpp | 6 +- 9 files changed, 321 insertions(+), 133 deletions(-) diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index e87da141524..202e70085a6 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -307,6 +307,12 @@ constexpr std::uint32_t tfDeleteObject = 0 constexpr std::uint32_t tfSponsorshipSetMask = ~(tfUniversal | tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee | tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve | tfDeleteObject); constexpr std::uint32_t tfSponsorshipSetPermissionMask = ~(tfUniversal | tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve); +// SponsorshipTransfer flags: +constexpr std::uint32_t tfSponsorshipEnd = 0x00000001; +constexpr std::uint32_t tfSponsorshipCreate = 0x00000002; +constexpr std::uint32_t tfSponsorshipReassign = 0x00000004; +constexpr std::uint32_t tfSponsorshipTransferMask = ~(tfUniversal | tfSponsorshipEnd | tfSponsorshipCreate | tfSponsorshipReassign); + // clang-format on } // namespace xrpl diff --git a/include/xrpl/tx/transactors/Sponsor/SponsorshipTransfer.h b/include/xrpl/tx/transactors/Sponsor/SponsorshipTransfer.h index 2b356556ddd..077a003ae53 100644 --- a/include/xrpl/tx/transactors/Sponsor/SponsorshipTransfer.h +++ b/include/xrpl/tx/transactors/Sponsor/SponsorshipTransfer.h @@ -13,6 +13,9 @@ class SponsorshipTransfer : public Transactor { } + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index a11891d8c80..449f40b9c92 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -12,9 +12,48 @@ namespace xrpl { +std::uint32_t +SponsorshipTransfer::getFlagsMask(PreflightContext const& ctx) +{ + return tfSponsorshipTransferMask; +} + NotTEC SponsorshipTransfer::preflight(PreflightContext const& ctx) { + auto const flags = ctx.tx.getFlags(); + auto const flagsSet = flags & ~(tfSponsorshipTransferMask | tfUniversal); + if (std::popcount(flagsSet) != 1) + { + JLOG(ctx.j.debug()) << "preflight: Only one SponsorshipTransfer flag can be set per tx."; + return temINVALID_FLAG; + } + + if (flags & tfSponsorshipCreate) + { + if (!isReserveSponsored(ctx.tx)) + { + JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should not be set when creating sponsorship"; + return temINVALID_FLAG; + } + } + if (flags & tfSponsorshipReassign) + { + if (!isReserveSponsored(ctx.tx)) + { + JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should be set when reassigning sponsorship"; + return temINVALID_FLAG; + } + } + if (flags & tfSponsorshipEnd) + { + if (isReserveSponsored(ctx.tx)) + { + JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should not be set when ending sponsorship"; + return temINVALID_FLAG; + } + } + // When an account sponsoring, sfSponsorSignature must be provided auto const newSponsor = getTxReserveSponsorAccountID(ctx.tx); bool const isObjectSponsor = ctx.tx.isFieldPresent(sfObjectID); @@ -156,6 +195,7 @@ TER SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { auto const index = ctx.tx[~sfObjectID]; + auto const flags = ctx.tx.getFlags(); auto const newSponsor = getTxReserveSponsor(ctx.view, ctx.tx); bool const isObjectSponsor = index != std::nullopt; @@ -180,20 +220,29 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) auto const& sponsorField = getLedgerEntrySponsorField(sle, *owner); - if (newSponsor) + if (flags & tfSponsorshipCreate) { + if (!newSponsor) + return tecNO_PERMISSION; + + // check object is not sponsored yet if (sle->isFieldPresent(sponsorField)) - { - // transfer sponsor - // check if the object owner isn't the same as the new sponsor - if (newSponsor->getAccountID(sfAccount) == owner) - // checked in above - return tecINTERNAL; // LCOV_EXCL_LINE - } + return tecNO_PERMISSION; } - else + else if (flags & tfSponsorshipReassign) { - // dissolve sponsor + if (!newSponsor) + return tecNO_PERMISSION; + + // check object is already sponsored + if (!sle->isFieldPresent(sponsorField)) + return tecNO_PERMISSION; + } + else if (flags & tfSponsorshipEnd) + { + if (newSponsor) + return tecNO_PERMISSION; + // check object is sponsored if (!sle->isFieldPresent(sponsorField)) return tecNO_PERMISSION; @@ -207,19 +256,29 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } else { - if (newSponsor) + if (flags & tfSponsorshipCreate) { + if (!newSponsor) + return tecNO_PERMISSION; + + // check account is not sponsored yet if (accSle->isFieldPresent(sfSponsor)) - { - // check not same account - if (newSponsor->getAccountID(sfAccount) == accSle->getAccountID(sfAccount)) - // already checked in Transactor::preflight1() - return tecINTERNAL; // LCOV_EXCL_LINE - } + return tecNO_PERMISSION; } - else + else if (flags & tfSponsorshipReassign) + { + if (!newSponsor) + return tecNO_PERMISSION; + + // check account is already sponsored + if (!accSle->isFieldPresent(sfSponsor)) + return tecNO_PERMISSION; + } + else if (flags & tfSponsorshipEnd) { - // dissolve sponsor + if (newSponsor) + return tecNO_PERMISSION; + // check account is sponsored if (!accSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; @@ -268,6 +327,7 @@ SponsorshipTransfer::doApply() auto const& tx = ctx_.tx; auto const index = tx[~sfObjectID]; + auto const flags = tx.getFlags(); bool const isObjectSponsor = index != std::nullopt; auto const accSle = view().peek(keylet::account(account_)); @@ -303,35 +363,63 @@ SponsorshipTransfer::doApply() auto const& sponsorField = getLedgerEntrySponsorField(objSle, *owner); - if (tx.isFieldPresent(sfSponsor)) + if (flags & tfSponsorshipCreate) { - auto const oldSponsor = objSle->getAccountID(sponsorField); auto const newSponsor = tx.getAccountID(sfSponsor); - // decrement old sponsoring count if exists - if (auto const oldSponsorSle = view().peek(keylet::account(oldSponsor))) - { - setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); - view().update(oldSponsorSle); - } - else + XRPL_ASSERT(!!newSponsor, "New sponsor is required when creating sponsorship"); + + // update owner's sponsored count + setSponsorFieldU32(ownerSle, sfSponsoredOwnerCount, ownerCountDelta); + view().update(ownerSle); + + // increment new sponsor's sponsoring count + auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + if (!newSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + setSponsorFieldU32(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); + view().update(newSponsorSle); + + // set new sponsor to object + objSle->setAccountID(sponsorField, newSponsor); + view().update(objSle); + + if (!hasSignature) { - // update owner's sponsored count - setSponsorFieldU32(ownerSle, sfSponsoredOwnerCount, ownerCountDelta); - view().update(ownerSle); + // use ReserveCount for pre-funded sponsoring + if (auto const ter = adjustReserveCount(view(), account_, newSponsor, -ownerCountDelta); + !isTesSuccess(ter)) + return ter; } + } + else if (flags & tfSponsorshipReassign) + { + auto const newSponsor = tx.getAccountID(sfSponsor); + XRPL_ASSERT(!!newSponsor, "New sponsor is required when reassigning sponsorship"); - // increment new sponsoring count - auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + auto const oldSponsor = objSle->getAccountID(sponsorField); + XRPL_ASSERT(!!oldSponsor, "Old sponsor is required when reassigning sponsorship"); + // decrement old sponsor's sponsoring count + auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); + if (!oldSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); + view().update(oldSponsorSle); + + // increment new sponsor's sponsoring count + auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + if (!newSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE setSponsorFieldU32(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); view().update(newSponsorSle); + // set new sponsor to object objSle->setAccountID(sponsorField, newSponsor); view().update(objSle); if (!hasSignature) { - // pre-funded sponsor + // use ReserveCount for pre-funded sponsoring if (auto const ter = adjustReserveCount(view(), account_, newSponsor, -ownerCountDelta); !isTesSuccess(ter)) return ter; @@ -343,17 +431,17 @@ SponsorshipTransfer::doApply() !isTesSuccess(ter)) return ter; } - else + else if (flags & tfSponsorshipEnd) { - // dissolve object sponsor auto const oldSponsor = objSle->getAccountID(sponsorField); + XRPL_ASSERT(!!oldSponsor, "Old sponsor is required when ending sponsorship"); + auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE // decrement sponsored count setSponsorFieldU32(accSle, sfSponsoredOwnerCount, -ownerCountDelta); - view().update(accSle); // decrement old sponsoring count @@ -373,34 +461,52 @@ SponsorshipTransfer::doApply() } else { - // Transfer Account sponsor - if (tx.isFieldPresent(sfSponsor)) + if (flags & tfSponsorshipCreate) { - // transfer account sponsor + // create account sponsor // increment new sponsoring count auto const newSponsor = tx.getAccountID(sfSponsor); auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + if (!newSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); + view().update(newSponsorSle); + // set new sponsor to account + accSle->setAccountID(sfSponsor, newSponsor); + view().update(accSle); + } + else if (flags & tfSponsorshipReassign) + { + // reassign account sponsor + // increment new sponsoring count + auto const newSponsor = tx.getAccountID(sfSponsor); + auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); view().update(newSponsorSle); + // decrement old sponsoring count - if (accSle->isFieldPresent(sfSponsor)) - { - auto const oldSponsor = accSle->getAccountID(sfSponsor); - auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); - setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); - view().update(oldSponsorSle); - } + auto const oldSponsor = accSle->getAccountID(sfSponsor); + auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); + if (!oldSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); + view().update(oldSponsorSle); + + // set new sponsor to account accSle->setAccountID(sfSponsor, newSponsor); view().update(accSle); } - else + else if (flags & tfSponsorshipEnd) { // dissolve account sponsor auto const oldSponsor = accSle->getAccountID(sfSponsor); accSle->makeFieldAbsent(sfSponsor); + // decrement account sponsoring count auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); + if (!oldSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); view().update(oldSponsorSle); } diff --git a/src/test/app/NFToken_test.cpp b/src/test/app/NFToken_test.cpp index 8bef41b2320..74c23a5d60c 100644 --- a/src/test/app/NFToken_test.cpp +++ b/src/test/app/NFToken_test.cpp @@ -387,7 +387,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite env(did::set(bob), did::uri("uri"), sponsor::as(alice, tfSponsorReserve), sig(sfSponsorSignature, alice)); env.close(); - env(sponsor::transfer(bob), sponsor::as(alice, tfSponsorReserve), sig(sfSponsorSignature, alice)); + env(sponsor::transfer(bob, tfSponsorshipCreate), + sponsor::as(alice, tfSponsorReserve), + sig(sfSponsorSignature, alice)); env.close(); } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index f90eda8cee8..d4a4102050c 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -68,7 +68,7 @@ class Sponsor_test : public beast::unit_test::suite env(jt2, ter(temDISABLED)); // check Sponsor transactions - env(sponsor::transfer(alice), ter(temDISABLED)); + env(sponsor::transfer(alice, 0), ter(temDISABLED)); env(sponsor::set(sponsor, 0), ter(temDISABLED)); } @@ -548,6 +548,43 @@ class Sponsor_test : public beast::unit_test::suite testcase("Transfer Sponsor"); using namespace test::jtx; + { + // invalid flags + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + env.close(); + + env(sponsor::transfer(alice, (tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd) + 1), + ter(temINVALID_FLAG)); + + // invalid combination of flags + for (auto flag : { + tfSponsorshipCreate | tfSponsorshipReassign, + tfSponsorshipCreate | tfSponsorshipEnd, + tfSponsorshipReassign | tfSponsorshipEnd, + tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd, + }) + env(sponsor::transfer(alice, flag), ter(temINVALID_FLAG)); + + // invalid tfSponsorshipCreate + // no sponsor field present + env(sponsor::transfer(alice, tfSponsorshipCreate), ter(temINVALID_FLAG)); + + // invalid tfSponsorshipReassign + // no sponsor field present + env(sponsor::transfer(alice, tfSponsorshipReassign), ter(temINVALID_FLAG)); + + // invalid tfSponsorshipEnd + // sponsor field present + env(sponsor::transfer(alice, tfSponsorshipEnd), + sponsor::as(sponsor1, tfSponsorReserve), + ter(temINVALID_FLAG)); + } + { // sponsor account Env env{*this, testable_amendments()}; @@ -558,12 +595,14 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); // sfSponsor provided but sfSponsorSignature not provided - env(sponsor::transfer(alice), sponsor::as(sponsor1, tfSponsorReserve), ter(temMALFORMED)); + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::as(sponsor1, tfSponsorReserve), + ter(temMALFORMED)); env.close(); adjustAccountXRPBalance(env, sponsor1, accountReserve(env, 2) - drops(1)); - env(sponsor::transfer(alice), + env(sponsor::transfer(alice, tfSponsorshipCreate), sponsor::as(sponsor1, tfSponsorReserve), sig(sfSponsorSignature, sponsor1), ter(tecINSUFFICIENT_RESERVE)); @@ -571,7 +610,9 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor1, accountReserve(env, 2)); - env(sponsor::transfer(alice), sponsor::as(sponsor1, tfSponsorReserve), sig(sfSponsorSignature, sponsor1)); + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::as(sponsor1, tfSponsorReserve), + sig(sfSponsorSignature, sponsor1)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -587,7 +628,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 2) - drops(1)); - env(sponsor::transfer(alice), + env(sponsor::transfer(alice, tfSponsorshipReassign), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2), ter(tecINSUFFICIENT_RESERVE)); @@ -595,7 +636,9 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 2)); - env(sponsor::transfer(alice), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); + env(sponsor::transfer(alice, tfSponsorshipReassign), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -614,18 +657,20 @@ class Sponsor_test : public beast::unit_test::suite // sponsor 2 accounts adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 3)); - env(sponsor::transfer(bob), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); + env(sponsor::transfer(bob, tfSponsorshipCreate), + sponsor::as(sponsor2, tfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); env.close(); // dissolve sponsors adjustAccountXRPBalance(env, alice, accountReserve(env, 1) - drops(1)); - env(sponsor::transfer(alice), ter(tecINSUFFICIENT_RESERVE)); + env(sponsor::transfer(alice, tfSponsorshipEnd), ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, alice, accountReserve(env, 1)); - env(sponsor::transfer(alice)); + env(sponsor::transfer(alice, tfSponsorshipEnd)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -640,7 +685,7 @@ class Sponsor_test : public beast::unit_test::suite auto const sle3 = env.le(keylet::account(alice)); BEAST_EXPECT(!sle3->isFieldPresent(sfSponsor)); - env(sponsor::transfer(bob)); + env(sponsor::transfer(bob, tfSponsorshipEnd)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); @@ -657,7 +702,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(!sle4->isFieldPresent(sfSponsor)); // not sponsored - env(sponsor::transfer(bob), ter(tecNO_PERMISSION)); + env(sponsor::transfer(bob, tfSponsorshipEnd), ter(tecNO_PERMISSION)); env.close(); } { @@ -680,7 +725,7 @@ class Sponsor_test : public beast::unit_test::suite auto const checkId = keylet::check(alice, seq).key; BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); - env(sponsor::transfer(alice, checkId), + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), sponsor::as(sponsor1, tfSponsorReserve), sig(sfSponsorSignature, sponsor1), ter(tecINSUFFICIENT_RESERVE)); @@ -690,21 +735,21 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Invalid ObjectID (not found) - env(sponsor::transfer(alice, keylet::check(alice, 0).key), + env(sponsor::transfer(alice, tfSponsorshipCreate, keylet::check(alice, 0).key), sponsor::as(sponsor1, tfSponsorReserve), sig(sfSponsorSignature, sponsor1), ter(tecNO_ENTRY)); env.close(); // Invalid Owner - env(sponsor::transfer(bob, checkId), + env(sponsor::transfer(bob, tfSponsorshipCreate, checkId), sponsor::as(sponsor1, tfSponsorReserve), sig(sfSponsorSignature, sponsor1), ter(tecNO_PERMISSION)); env.close(); // Valid Owner - env(sponsor::transfer(alice, checkId), + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), sponsor::as(sponsor1, tfSponsorReserve), sig(sfSponsorSignature, sponsor1)); env.close(); @@ -721,7 +766,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle1->getAccountID(sfSponsor) == sponsor1.id()); // transfer sponsor - env(sponsor::transfer(alice, checkId), + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2), ter(tecINSUFFICIENT_RESERVE)); @@ -729,7 +774,7 @@ class Sponsor_test : public beast::unit_test::suite env(pay(alice, sponsor2, drops(1))); env.close(); - env(sponsor::transfer(alice, checkId), + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -750,7 +795,7 @@ class Sponsor_test : public beast::unit_test::suite // dissolve sponsor adjustAccountXRPBalance(env, alice, reserve(env, 1) - drops(1)); - env(sponsor::transfer(alice, checkId), ter(tecINSUFFICIENT_RESERVE)); + env(sponsor::transfer(alice, tfSponsorshipEnd, checkId), ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, alice, reserve(env, 1)); @@ -761,14 +806,14 @@ class Sponsor_test : public beast::unit_test::suite env.close(); auto ticketId = keylet::ticket(alice, ticketSeq + 1).key; BEAST_EXPECT(env.le(keylet::unchecked(ticketId))); - env(sponsor::transfer(alice, ticketId), ter(tecNO_PERMISSION)); + env(sponsor::transfer(alice, tfSponsorshipEnd, ticketId), ter(tecNO_PERMISSION)); env.close(); env(noop(alice), ticket::use(ticketSeq + 1)); env.close(); adjustAccountXRPBalance(env, alice, reserve(env, 1)); - env(sponsor::transfer(alice, checkId)); + env(sponsor::transfer(alice, tfSponsorshipEnd, checkId)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -801,16 +846,20 @@ class Sponsor_test : public beast::unit_test::suite auto const checkId = keylet::check(alice, seq).key; BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); - env(sponsor::transfer(alice, checkId), sponsor::as(sponsor1, tfSponsorReserve), ter(terNO_SPONSORSHIP)); + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::as(sponsor1, tfSponsorReserve), + ter(terNO_SPONSORSHIP)); env.close(); env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, checkId), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), sponsor::as(sponsor2, tfSponsorReserve)); env.close(); - env(sponsor::transfer(alice, checkId), sponsor::as(sponsor1, tfSponsorReserve), ter(terNO_SPONSORSHIP)); + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::as(sponsor1, tfSponsorReserve), + ter(terNO_SPONSORSHIP)); env.close(); } { @@ -833,7 +882,7 @@ class Sponsor_test : public beast::unit_test::suite // insufficient reserve count env(sponsor::set_fee(sponsor1, 0, XRP(100)), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, checkId), + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), sponsor::as(sponsor1, tfSponsorReserve), ter(tecINSUFFICIENT_RESERVE)); env.close(); @@ -841,7 +890,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor1, 0, 100), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, checkId), sponsor::as(sponsor1, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), sponsor::as(sponsor1, tfSponsorReserve)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -861,7 +910,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 100), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, checkId), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), sponsor::as(sponsor2, tfSponsorReserve)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -883,7 +932,7 @@ class Sponsor_test : public beast::unit_test::suite // dissolve sponsor adjustAccountXRPBalance(env, alice, reserve(env, 1)); - env(sponsor::transfer(alice, checkId)); + env(sponsor::transfer(alice, tfSponsorshipEnd, checkId)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -930,7 +979,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(trustId)); // transfer sponsor - env(sponsor::transfer(user, trustId.key), + env(sponsor::transfer(user, tfSponsorshipCreate, trustId.key), sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor)); env.close(); @@ -942,7 +991,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(trustId)->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); // dissolve sponsor - env(sponsor::transfer(user, trustId.key)); + env(sponsor::transfer(user, tfSponsorshipEnd, trustId.key)); env.close(); BEAST_EXPECT(env.le(trustId)); @@ -980,7 +1029,7 @@ class Sponsor_test : public beast::unit_test::suite }; for (auto const& keylet : keylets) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipCreate, keylet.key), sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor), ter(tecNO_PERMISSION)); @@ -1573,7 +1622,7 @@ class Sponsor_test : public beast::unit_test::suite auto const ammKeylet = keylet::amm(USD.issue(), EUR.issue()); if (cosigning) { - env(sponsor::transfer(alice, ammKeylet.key), + env(sponsor::transfer(alice, tfSponsorshipCreate, ammKeylet.key), sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor), ter(tecNO_PERMISSION)); @@ -1583,7 +1632,7 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice)); env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice)); - env(sponsor::transfer(alice, ammKeylet.key), + env(sponsor::transfer(alice, tfSponsorshipCreate, ammKeylet.key), sponsor::as(sponsor, tfSponsorReserve), ter(tecNO_PERMISSION)); env.close(); @@ -1815,7 +1864,7 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { // transfer sponsor - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -1826,7 +1875,8 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // transfer sponsor - env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -1953,7 +2003,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::offer(alice, seq); if (cosigning) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -1963,7 +2013,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -2140,7 +2191,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor if (cosigning) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2150,7 +2201,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -2203,7 +2255,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::credential(subject, issuer, credTypeSlice); if (cosigning) { - env(sponsor::transfer(issuer, keylet.key), + env(sponsor::transfer(issuer, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2213,7 +2265,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(issuer)); env.close(); - env(sponsor::transfer(issuer, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(issuer, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -2240,7 +2293,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer accepted credential if (cosigning) { - env(sponsor::transfer(subject, keylet.key), + env(sponsor::transfer(subject, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2250,7 +2303,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(subject)); env.close(); - env(sponsor::transfer(subject, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(subject, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -2318,7 +2372,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::delegate(alice, bob); if (cosigning) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2328,7 +2382,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -2371,7 +2426,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::depositPreauth(alice, sponsor); if (cosigning) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2380,7 +2435,7 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2426,7 +2481,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::did(alice); if (cosigning) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2435,7 +2490,8 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -2489,7 +2545,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor if (cosigning) { - env(sponsor::transfer(alice, keylet::escrow(alice, seq).key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::escrow(alice, seq).key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2499,7 +2555,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet::escrow(alice, seq).key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::escrow(alice, seq).key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -2644,7 +2701,7 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { - env(sponsor::transfer(alice, mptIssuanceKeylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, mptIssuanceKeylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2654,7 +2711,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, mptIssuanceKeylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, mptIssuanceKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -2683,7 +2741,7 @@ class Sponsor_test : public beast::unit_test::suite auto const mptTokenKeylet = keylet::mptoken(mptid, bob); if (cosigning) { - env(sponsor::transfer(bob, mptTokenKeylet.key), + env(sponsor::transfer(bob, tfSponsorshipReassign, mptTokenKeylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2693,7 +2751,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(bob)); env.close(); - env(sponsor::transfer(bob, mptTokenKeylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(bob, tfSponsorshipReassign, mptTokenKeylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -2824,7 +2883,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::nftpage_max(alice); if (cosigning) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2834,7 +2893,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); } // NFTokenBurn env(token::burn(alice, nftId)); @@ -2953,7 +3013,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor if (cosigning) { - env(sponsor::transfer(alice, offerIndex1), + env(sponsor::transfer(alice, tfSponsorshipReassign, offerIndex1), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -2963,7 +3023,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, offerIndex1), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, offerIndex1), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -3113,7 +3174,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor if (cosigning) { - env(sponsor::transfer(alice, chan), + env(sponsor::transfer(alice, tfSponsorshipReassign, chan), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -3123,7 +3184,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, chan), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, chan), sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -3171,7 +3232,7 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -3180,7 +3241,8 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -3283,7 +3345,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::oracle(alice, 1); if (cosigning) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -3292,7 +3354,8 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -3321,7 +3384,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::oracle(alice, 1); if (cosigning) { - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -3330,7 +3393,8 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor2, 0, 2), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -3445,7 +3509,7 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor if (cosigning) { - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::oracle(alice, 1).key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -3454,7 +3518,7 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor2, 0, ocount), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::oracle(alice, 1).key), sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -3465,7 +3529,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == ocount); // dissolve sponsor - env(sponsor::transfer(alice, keylet::oracle(alice, 1).key)); + env(sponsor::transfer(alice, tfSponsorshipEnd, keylet::oracle(alice, 1).key)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == ocount); @@ -3504,7 +3568,7 @@ class Sponsor_test : public beast::unit_test::suite { // invalid signer list owner 1 // account doesn't have signer list but specified signer list exists - env(sponsor::transfer(bob, keylet::signers(alice).key), + env(sponsor::transfer(bob, tfSponsorshipReassign, keylet::signers(alice).key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2), ter(tecNO_PERMISSION)); @@ -3512,11 +3576,11 @@ class Sponsor_test : public beast::unit_test::suite // account has signer list and specified signer list exists env(signers(bob, 1, {{alice, 1}})); env.close(); - env(sponsor::transfer(alice, keylet::signers(bob).key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::signers(bob).key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2), ter(tecNO_PERMISSION)); - env(sponsor::transfer(alice, keylet::signers(alice).key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::signers(alice).key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -3525,7 +3589,8 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, keylet::signers(alice).key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::signers(alice).key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -3595,16 +3660,16 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { // invalid owner - env(sponsor::transfer(charlie, keylet.key), + env(sponsor::transfer(charlie, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2), ter(tecNO_PERMISSION)); // invalid reserve owner - env(sponsor::transfer(issuer, keylet.key), + env(sponsor::transfer(issuer, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2), ter(tecNO_PERMISSION)); - env(sponsor::transfer(user, keylet.key), + env(sponsor::transfer(user, tfSponsorshipReassign, keylet.key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -3613,7 +3678,8 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(user)); env.close(); - env(sponsor::transfer(user, keylet.key), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(user, tfSponsorshipReassign, keylet.key), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -4388,7 +4454,7 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::check(alice, seq); - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipCreate, keylet.key), sponsor::as(bob, tfSponsorReserve), sig(sfSponsorSignature, bob), delegate::as(carol), @@ -4397,7 +4463,7 @@ class Sponsor_test : public beast::unit_test::suite env(delegate::set(alice, carol, {"SponsorshipTransfer"})); env.close(); - env(sponsor::transfer(alice, keylet.key), + env(sponsor::transfer(alice, tfSponsorshipCreate, keylet.key), sponsor::as(bob, tfSponsorReserve), sig(sfSponsorSignature, bob), delegate::as(carol), diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index b152d296767..4b93c4bc11f 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -78,11 +78,12 @@ del(jtx::Account const& account) } Json::Value -transfer(jtx::Account const& account, std::optional const& index) +transfer(jtx::Account const& account, uint32_t flags, std::optional const& index) { Json::Value jv; jv[jss::TransactionType] = jss::SponsorshipTransfer; jv[jss::Account] = account.human(); + jv[sfFlags.jsonName] = flags; if (index) jv[sfObjectID.jsonName] = to_string(*index); return jv; diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index a03a64865bd..dc7c4462cce 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -35,7 +35,7 @@ Json::Value del(jtx::Account const& account); Json::Value -transfer(jtx::Account const& account, std::optional const& index = std::nullopt); +transfer(jtx::Account const& account, uint32_t flags, std::optional const& index = std::nullopt); struct counterpartySponsor { diff --git a/src/test/rpc/AccountSet_test.cpp b/src/test/rpc/AccountSet_test.cpp index 0a38601c5a7..60c254fe368 100644 --- a/src/test/rpc/AccountSet_test.cpp +++ b/src/test/rpc/AccountSet_test.cpp @@ -403,7 +403,9 @@ class AccountSet_test : public beast::unit_test::suite env(did::set(alice), did::uri("uri"), sponsor::as(gw, tfSponsorReserve), sig(sfSponsorSignature, gw)); env.close(); - env(sponsor::transfer(alice), sponsor::as(gw, tfSponsorReserve), sig(sfSponsorSignature, gw)); + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::as(gw, tfSponsorReserve), + sig(sfSponsorSignature, gw)); env.close(); } diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 426a30285fa..39610912fe3 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -844,7 +844,7 @@ class AccountTx_test : public beast::unit_test::suite checkTx(sponsor, jss::TicketCreate); // transfer object sponsorship - env(sponsor::transfer(alice, keylet::ticket(alice, seq + 1).key), + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::ticket(alice, seq + 1).key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -860,7 +860,9 @@ class AccountTx_test : public beast::unit_test::suite checkTx(sponsor2, jss::AccountSet); // account sponsorship - env(sponsor::transfer(alice), sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor)); + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); env.close(); checkTx(alice, jss::SponsorshipTransfer); checkTx(sponsor, jss::SponsorshipTransfer); From 4a91351bcdc13ce67f426300b7b0a79dcd4ffbf8 Mon Sep 17 00:00:00 2001 From: tequ Date: Sun, 22 Feb 2026 17:21:04 +0900 Subject: [PATCH 140/249] Add `sfSponsee` to `ttSponsorshipTransfer` --- .../xrpl/protocol/detail/transactions.macro | 1 + .../Sponsor/SponsorshipTransfer.cpp | 64 ++++++++++----- src/test/app/Sponsor_test.cpp | 77 ++++++++++++++++++- 3 files changed, 120 insertions(+), 22 deletions(-) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 8231a723197..355290774a4 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -1068,6 +1068,7 @@ TRANSACTION(ttSPONSORSHIP_TRANSFER, 85, SponsorshipTransfer, noPriv, ({ {sfObjectID, soeOPTIONAL}, + {sfSponsee, soeOPTIONAL}, })) /** This transaction create sponsorship object */ diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 449f40b9c92..8795586ae0f 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -36,6 +36,11 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should not be set when creating sponsorship"; return temINVALID_FLAG; } + if (ctx.tx.isFieldPresent(sfSponsee)) + { + JLOG(ctx.j.debug()) << "preflight: sfSponsee should be available only when ending sponsorship"; + return temMALFORMED; + } } if (flags & tfSponsorshipReassign) { @@ -44,6 +49,11 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should be set when reassigning sponsorship"; return temINVALID_FLAG; } + if (ctx.tx.isFieldPresent(sfSponsee)) + { + JLOG(ctx.j.debug()) << "preflight: sfSponsee should not be set when reassigning sponsorship"; + return temMALFORMED; + } } if (flags & tfSponsorshipEnd) { @@ -52,6 +62,15 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should not be set when ending sponsorship"; return temINVALID_FLAG; } + + if (ctx.tx.isFieldPresent(sfSponsee)) + { + if (ctx.tx.getAccountID(sfSponsee) == ctx.tx.getAccountID(sfAccount)) + { + JLOG(ctx.j.debug()) << "preflight: sfSponsee should not be the same as the account"; + return temMALFORMED; + } + } } // When an account sponsoring, sfSponsorSignature must be provided @@ -202,8 +221,9 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) auto const account = ctx.tx[sfAccount]; - auto const accSle = ctx.view.read(keylet::account(account)); - if (!accSle) + auto const sponsee = ctx.tx[~sfSponsee].value_or(account); + auto const sponseeSle = ctx.view.read(keylet::account(sponsee)); + if (!sponseeSle) return tecINTERNAL; // LCOV_EXCL_LINE if (isObjectSponsor) @@ -214,8 +234,8 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) auto const ownerCountDelta = getLedgerEntryOwnerCount(sle); - auto const owner = getLedgerEntryOwner(ctx.view, sle, account); - if (!owner || owner != account) + auto const owner = getLedgerEntryOwner(ctx.view, sle, sponsee); + if (!owner || owner != sponsee) return tecNO_PERMISSION; auto const& sponsorField = getLedgerEntrySponsorField(sle, *owner); @@ -250,7 +270,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) // check new sponsor have sufficient balance if (auto const ter = checkInsufficientReserve( - ctx.view, ctx.tx, accSle, accSle->getFieldAmount(sfBalance), newSponsor, ownerCountDelta); + ctx.view, ctx.tx, sponseeSle, sponseeSle->getFieldAmount(sfBalance), newSponsor, ownerCountDelta); !isTesSuccess(ter)) return ter; } @@ -262,7 +282,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) return tecNO_PERMISSION; // check account is not sponsored yet - if (accSle->isFieldPresent(sfSponsor)) + if (sponseeSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; } else if (flags & tfSponsorshipReassign) @@ -271,7 +291,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) return tecNO_PERMISSION; // check account is already sponsored - if (!accSle->isFieldPresent(sfSponsor)) + if (!sponseeSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; } else if (flags & tfSponsorshipEnd) @@ -280,15 +300,15 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) return tecNO_PERMISSION; // check account is sponsored - if (!accSle->isFieldPresent(sfSponsor)) + if (!sponseeSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; } // check account have sufficient balance // In the case of removing an account sponsor, accSle should have no sfSponsor set (AccountReserve = 0). // However, by setting accountCountDelta = 1 here, we are able to calculate the actual required Account Reserve. - if (auto const ter = - checkInsufficientReserve(ctx.view, ctx.tx, accSle, accSle->getFieldAmount(sfBalance), newSponsor, 0, 1); + if (auto const ter = checkInsufficientReserve( + ctx.view, ctx.tx, sponseeSle, sponseeSle->getFieldAmount(sfBalance), newSponsor, 0, 1); !isTesSuccess(ter)) return ter; } @@ -330,8 +350,9 @@ SponsorshipTransfer::doApply() auto const flags = tx.getFlags(); bool const isObjectSponsor = index != std::nullopt; - auto const accSle = view().peek(keylet::account(account_)); - if (!accSle) + auto const sponsee = tx[~sfSponsee].value_or(account_); + auto const sponseeSle = view().peek(keylet::account(sponsee)); + if (!sponseeSle) return tefINTERNAL; // LCOV_EXCL_LINE auto const setSponsorFieldU32 = [](auto const& sle, auto const& field, auto const& delta) { @@ -441,8 +462,8 @@ SponsorshipTransfer::doApply() return tefINTERNAL; // LCOV_EXCL_LINE // decrement sponsored count - setSponsorFieldU32(accSle, sfSponsoredOwnerCount, -ownerCountDelta); - view().update(accSle); + setSponsorFieldU32(sponseeSle, sfSponsoredOwnerCount, -ownerCountDelta); + view().update(sponseeSle); // decrement old sponsoring count setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); @@ -473,8 +494,8 @@ SponsorshipTransfer::doApply() view().update(newSponsorSle); // set new sponsor to account - accSle->setAccountID(sfSponsor, newSponsor); - view().update(accSle); + sponseeSle->setAccountID(sfSponsor, newSponsor); + view().update(sponseeSle); } else if (flags & tfSponsorshipReassign) { @@ -486,7 +507,7 @@ SponsorshipTransfer::doApply() view().update(newSponsorSle); // decrement old sponsoring count - auto const oldSponsor = accSle->getAccountID(sfSponsor); + auto const oldSponsor = sponseeSle->getAccountID(sfSponsor); auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE @@ -494,14 +515,15 @@ SponsorshipTransfer::doApply() view().update(oldSponsorSle); // set new sponsor to account - accSle->setAccountID(sfSponsor, newSponsor); - view().update(accSle); + sponseeSle->setAccountID(sfSponsor, newSponsor); + view().update(sponseeSle); } else if (flags & tfSponsorshipEnd) { // dissolve account sponsor - auto const oldSponsor = accSle->getAccountID(sfSponsor); - accSle->makeFieldAbsent(sfSponsor); + auto const oldSponsor = sponseeSle->getAccountID(sfSponsor); + sponseeSle->makeFieldAbsent(sfSponsor); + view().update(sponseeSle); // decrement account sponsoring count auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index d4a4102050c..d2720a796e3 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -549,7 +549,7 @@ class Sponsor_test : public beast::unit_test::suite using namespace test::jtx; { - // invalid flags + // invalid fields Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); @@ -573,16 +573,28 @@ class Sponsor_test : public beast::unit_test::suite // invalid tfSponsorshipCreate // no sponsor field present env(sponsor::transfer(alice, tfSponsorshipCreate), ter(temINVALID_FLAG)); + // sponsee field present + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::sponseeAcc(bob), + sponsor::as(sponsor1, tfSponsorReserve), + ter(temMALFORMED)); // invalid tfSponsorshipReassign // no sponsor field present env(sponsor::transfer(alice, tfSponsorshipReassign), ter(temINVALID_FLAG)); + // sponsee field present + env(sponsor::transfer(alice, tfSponsorshipReassign), + sponsor::sponseeAcc(bob), + sponsor::as(sponsor1, tfSponsorReserve), + ter(temMALFORMED)); // invalid tfSponsorshipEnd // sponsor field present env(sponsor::transfer(alice, tfSponsorshipEnd), sponsor::as(sponsor1, tfSponsorReserve), ter(temINVALID_FLAG)); + // account = sponsee + env(sponsor::transfer(alice, tfSponsorshipEnd), sponsor::sponseeAcc(alice), ter(temMALFORMED)); } { @@ -705,6 +717,30 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer(bob, tfSponsorshipEnd), ter(tecNO_PERMISSION)); env.close(); } + { + // dissolve account sponsorship from sponsor + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + BEAST_EXPECT(env.le(alice)->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1); + + env(sponsor::transfer(sponsor, tfSponsorshipEnd), sponsor::sponseeAcc(alice)); + env.close(); + + BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 0); + } + { // sponsor object (co-signing) Env env{*this, testable_amendments()}; @@ -951,6 +987,45 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 100); // paybacked } + { + // Dissolve object sponsorship from sponsor + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + BEAST_EXPECT(env.le(keylet::unchecked(checkId))->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // not the owner of the object + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), ter(tecNO_PERMISSION)); + env.close(); + + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), sponsor::sponseeAcc(alice)); + env.close(); + + BEAST_EXPECT(!env.le(keylet::unchecked(checkId))->isFieldPresent(sfSponsor)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + { // sponsor trustline Account const alice("alice"); From 1ad6dd1846366b675e696838c47044a0c44ab160 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Feb 2026 11:10:46 +0900 Subject: [PATCH 141/249] Add tests for deleting Sponsor Account with ltSponsorship --- src/libxrpl/tx/transactors/DeleteAccount.cpp | 12 +++++--- src/test/app/Sponsor_test.cpp | 30 +++++++++++++++++++- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/libxrpl/tx/transactors/DeleteAccount.cpp b/src/libxrpl/tx/transactors/DeleteAccount.cpp index 08c76f60110..bfa2eb96144 100644 --- a/src/libxrpl/tx/transactors/DeleteAccount.cpp +++ b/src/libxrpl/tx/transactors/DeleteAccount.cpp @@ -395,9 +395,13 @@ DeleteAccount::doApply() return tefINTERNAL; // LCOV_EXCL_LINE // Transfer any XRP remaining after the fee is paid to the destination: - (*dst)[sfBalance] = (*dst)[sfBalance] + mSourceBalance; - (*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance; - ctx_.deliver(mSourceBalance); + // Use the current balance from the SLE, not mSourceBalance, because + // the cleanup loop may have returned pre-funded sfFeeAmount from + // ltSponsorship objects back to the account's sfBalance. + auto const srcCurrentBalance = STAmount{(*src)[sfBalance]}.xrp(); + (*dst)[sfBalance] = (*dst)[sfBalance] + srcCurrentBalance; + (*src)[sfBalance] = (*src)[sfBalance] - srcCurrentBalance; + ctx_.deliver(srcCurrentBalance); if (src->isFieldPresent(sfSponsor)) { @@ -437,7 +441,7 @@ DeleteAccount::doApply() } // Re-arm the password change fee if we can and need to. - if (mSourceBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent)) + if (srcCurrentBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent)) dst->clearFlag(lsfPasswordSpent); view().update(dst); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index d2720a796e3..025fc72e0ca 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -4449,7 +4449,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); { - // Delete Account with ltSponsorship + // Delete Sponsee Account with ltSponsorship Env env{*this, testable_amendments()}; env.fund(XRP(1000000), alice, bob, sponsor); env.close(); @@ -4476,6 +4476,34 @@ class Sponsor_test : public beast::unit_test::suite jv[jss::result][jss::error] == "entryNotFound"); } + { + // Delete Sponsor Account with ltSponsorship + Env env{*this, testable_amendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // set sponsor + env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env.close(); + + incLgrSeqForAccDel(env, sponsor); + + auto const keylet = keylet::sponsor(sponsor, alice); + auto const sponsorObj = env.le(keylet); + BEAST_EXPECT(sponsorObj); + + // AccountDelete + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(sponsor, alice), fee(requiredFee), ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(!env.le(keylet)); + auto const jv = sponsor::ledgerEntry(env, sponsor, alice); + BEAST_EXPECT( + jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) && + jv[jss::result][jss::error] == "entryNotFound"); + } + { // Delete SponsoredAccount Env env{*this, testable_amendments()}; From 99618877e463695e5ac8092994d424124e7bf459 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Feb 2026 23:55:25 +0900 Subject: [PATCH 142/249] clang-format --- include/xrpl/protocol/Fees.h | 3 +- .../Lending/LoanBrokerCoverDeposit.cpp | 3 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 41 +- .../Sponsor/SponsorshipTransfer.cpp | 59 +- src/test/app/Sponsor_test.cpp | 847 ++++++++++++++---- src/test/jtx/impl/sponsor.cpp | 6 +- src/test/jtx/sponsor.h | 5 +- 7 files changed, 734 insertions(+), 230 deletions(-) diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index d2fdcb1b5a6..73928eccb40 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -37,7 +37,8 @@ struct Fees XRPL_ASSERT( ownerCount >= sponsoredOwnerCount, - "xrpl::Fees::accountReserve : OwnerCount must be greater than or equal to SponsoredOwnerCount"); + "xrpl::Fees::accountReserve : OwnerCount must be greater than or equal to " + "SponsoredOwnerCount"); auto const ownerReserveUnits = (ownerCount - sponsoredOwnerCount) + sponsoringOwnerCount; diff --git a/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.cpp b/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.cpp index baa0066a56c..ef74e3481e4 100644 --- a/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.cpp +++ b/src/libxrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.cpp @@ -108,7 +108,8 @@ LoanBrokerCoverDeposit::doApply() auto const brokerPseudoID = broker->at(sfAccount); // Transfer assets from depositor to pseudo-account. - if (auto ter = accountSend(view(), account_, brokerPseudoID, amount, j_, {}, WaiveTransferFee::Yes)) + if (auto ter = + accountSend(view(), account_, brokerPseudoID, amount, j_, {}, WaiveTransferFee::Yes)) return ter; // Increase the LoanBroker's CoverAvailable by Amount diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index cefdbf8090d..cbb79606bfe 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -16,9 +16,11 @@ SponsorshipSet::preflight(PreflightContext const& ctx) { auto const flags = ctx.tx.getFlags(); - if ((flags & tfSponsorshipSetRequireSignForFee) && (flags & tfSponsorshipClearRequireSignForFee)) + if ((flags & tfSponsorshipSetRequireSignForFee) && + (flags & tfSponsorshipClearRequireSignForFee)) return temINVALID_FLAG; - if ((flags & tfSponsorshipSetRequireSignForReserve) && (flags & tfSponsorshipClearRequireSignForReserve)) + if ((flags & tfSponsorshipSetRequireSignForReserve) && + (flags & tfSponsorshipClearRequireSignForReserve)) return temINVALID_FLAG; auto const account = ctx.tx.getAccountID(sfAccount); @@ -117,9 +119,10 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) std::unordered_set granularPermissions; loadGranularPermission(sle, ttSPONSORSHIP_SET, granularPermissions); - auto const sponsoringFee = - tx.isFieldPresent(sfFeeAmount) || tx.isFieldPresent(sfMaxFee) || txFlags & tfSponsorshipSetRequireSignForFee; - auto const sponsoringReserve = tx.isFieldPresent(sfReserveCount) || txFlags & tfSponsorshipSetRequireSignForReserve; + auto const sponsoringFee = tx.isFieldPresent(sfFeeAmount) || tx.isFieldPresent(sfMaxFee) || + txFlags & tfSponsorshipSetRequireSignForFee; + auto const sponsoringReserve = + tx.isFieldPresent(sfReserveCount) || txFlags & tfSponsorshipSetRequireSignForReserve; if (sponsoringFee && !granularPermissions.contains(SponsorFee)) return terNO_DELEGATE_PERMISSION; @@ -190,9 +193,16 @@ SponsorshipSet::doApply() auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), sponsorObjSle); adjustOwnerCount(ctx_.view(), sponsorAccSle, sponsor, -1, ctx_.journal); - ctx_.view().dirRemove(keylet::ownerDir(sponsorAcc), (*sponsorObjSle)[sfOwnerNode], sponsorObjSle->key(), false); ctx_.view().dirRemove( - keylet::ownerDir(sponseeAcc), (*sponsorObjSle)[sfSponseeNode], sponsorObjSle->key(), false); + keylet::ownerDir(sponsorAcc), + (*sponsorObjSle)[sfOwnerNode], + sponsorObjSle->key(), + false); + ctx_.view().dirRemove( + keylet::ownerDir(sponseeAcc), + (*sponsorObjSle)[sfSponseeNode], + sponsorObjSle->key(), + false); // transfer feeAmount from ledger entry if (sponsorObjSle->isFieldPresent(sfFeeAmount)) @@ -220,8 +230,8 @@ SponsorshipSet::doApply() // Create auto newSle = std::make_shared(sponsorKeylet); - if (auto const ret = - checkInsufficientReserve(ctx_.view(), ctx_.tx, sponsorAccSle, mPriorBalance, reserveSponsorAccSle, 1); + if (auto const ret = checkInsufficientReserve( + ctx_.view(), ctx_.tx, sponsorAccSle, mPriorBalance, reserveSponsorAccSle, 1); !isTesSuccess(ret)) return tecUNFUNDED; @@ -246,12 +256,12 @@ SponsorshipSet::doApply() (*newSle)[sfFlags] = flags; - auto const sponsorPage = - view().dirInsert(keylet::ownerDir(sponsorAcc), sponsorKeylet, describeOwnerDir(sponsorAcc)); + auto const sponsorPage = view().dirInsert( + keylet::ownerDir(sponsorAcc), sponsorKeylet, describeOwnerDir(sponsorAcc)); (*newSle)[sfOwnerNode] = *sponsorPage; - auto const sponseePage = - view().dirInsert(keylet::ownerDir(sponseeAcc), sponsorKeylet, describeOwnerDir(sponseeAcc)); + auto const sponseePage = view().dirInsert( + keylet::ownerDir(sponseeAcc), sponsorKeylet, describeOwnerDir(sponseeAcc)); (*newSle)[sfSponseeNode] = *sponseePage; auto viewJ = ctx_.registry.journal("View"); @@ -320,7 +330,10 @@ SponsorshipSet::doApply() } TER -SponsorshipSet::deleteSponsorship(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) +SponsorshipSet::deleteSponsorship( + ApplyView& view, + std::shared_ptr const& sle, + beast::Journal j) { auto const sponsor = sle->getAccountID(sfOwner); auto const sponsee = sle->getAccountID(sfSponsee); diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 8795586ae0f..4cb4bec159a 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -33,12 +33,14 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) { if (!isReserveSponsored(ctx.tx)) { - JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should not be set when creating sponsorship"; + JLOG(ctx.j.debug()) + << "preflight: tfSponsorReserve should not be set when creating sponsorship"; return temINVALID_FLAG; } if (ctx.tx.isFieldPresent(sfSponsee)) { - JLOG(ctx.j.debug()) << "preflight: sfSponsee should be available only when ending sponsorship"; + JLOG(ctx.j.debug()) + << "preflight: sfSponsee should be available only when ending sponsorship"; return temMALFORMED; } } @@ -46,12 +48,14 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) { if (!isReserveSponsored(ctx.tx)) { - JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should be set when reassigning sponsorship"; + JLOG(ctx.j.debug()) + << "preflight: tfSponsorReserve should be set when reassigning sponsorship"; return temINVALID_FLAG; } if (ctx.tx.isFieldPresent(sfSponsee)) { - JLOG(ctx.j.debug()) << "preflight: sfSponsee should not be set when reassigning sponsorship"; + JLOG(ctx.j.debug()) + << "preflight: sfSponsee should not be set when reassigning sponsorship"; return temMALFORMED; } } @@ -59,7 +63,8 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) { if (isReserveSponsored(ctx.tx)) { - JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should not be set when ending sponsorship"; + JLOG(ctx.j.debug()) + << "preflight: tfSponsorReserve should not be set when ending sponsorship"; return temINVALID_FLAG; } @@ -270,7 +275,12 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) // check new sponsor have sufficient balance if (auto const ter = checkInsufficientReserve( - ctx.view, ctx.tx, sponseeSle, sponseeSle->getFieldAmount(sfBalance), newSponsor, ownerCountDelta); + ctx.view, + ctx.tx, + sponseeSle, + sponseeSle->getFieldAmount(sfBalance), + newSponsor, + ownerCountDelta); !isTesSuccess(ter)) return ter; } @@ -305,10 +315,17 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } // check account have sufficient balance - // In the case of removing an account sponsor, accSle should have no sfSponsor set (AccountReserve = 0). - // However, by setting accountCountDelta = 1 here, we are able to calculate the actual required Account Reserve. + // In the case of removing an account sponsor, accSle should have no sfSponsor set + // (AccountReserve = 0). However, by setting accountCountDelta = 1 here, we are able to + // calculate the actual required Account Reserve. if (auto const ter = checkInsufficientReserve( - ctx.view, ctx.tx, sponseeSle, sponseeSle->getFieldAmount(sfBalance), newSponsor, 0, 1); + ctx.view, + ctx.tx, + sponseeSle, + sponseeSle->getFieldAmount(sfBalance), + newSponsor, + 0, + 1); !isTesSuccess(ter)) return ter; } @@ -317,7 +334,11 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } TER -adjustReserveCount(ApplyView& view, AccountID const& account, AccountID const& sponsor, int32_t delta) +adjustReserveCount( + ApplyView& view, + AccountID const& account, + AccountID const& sponsor, + int32_t delta) { if (delta == 0) return tesSUCCESS; @@ -407,7 +428,8 @@ SponsorshipTransfer::doApply() if (!hasSignature) { // use ReserveCount for pre-funded sponsoring - if (auto const ter = adjustReserveCount(view(), account_, newSponsor, -ownerCountDelta); + if (auto const ter = + adjustReserveCount(view(), account_, newSponsor, -ownerCountDelta); !isTesSuccess(ter)) return ter; } @@ -441,14 +463,17 @@ SponsorshipTransfer::doApply() if (!hasSignature) { // use ReserveCount for pre-funded sponsoring - if (auto const ter = adjustReserveCount(view(), account_, newSponsor, -ownerCountDelta); + if (auto const ter = + adjustReserveCount(view(), account_, newSponsor, -ownerCountDelta); !isTesSuccess(ter)) return ter; } // payback the reserve count if ltSponsorship exists - if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); sponsorSle) - if (auto const ter = adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); + if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); + sponsorSle) + if (auto const ter = + adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); !isTesSuccess(ter)) return ter; } @@ -470,8 +495,10 @@ SponsorshipTransfer::doApply() view().update(oldSponsorSle); // payback the reserve count if ltSponsorship exists - if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); sponsorSle) - if (auto const ter = adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); + if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); + sponsorSle) + if (auto const ter = + adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); !isTesSuccess(ter)) return ter; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 025fc72e0ca..3efcaa44518 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -98,11 +98,16 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sponseeAcc(alice), ter(temINVALID_FLAG)); - env(sponsor::set(sponsor, tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee), + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee), sponsor::sponseeAcc(alice), ter(temINVALID_FLAG)); - env(sponsor::set(sponsor, tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve), + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForReserve | + tfSponsorshipClearRequireSignForReserve), sponsor::sponseeAcc(alice), ter(temINVALID_FLAG)); @@ -112,13 +117,17 @@ class Sponsor_test : public beast::unit_test::suite tfSponsorshipSetRequireSignForReserve, tfSponsorshipClearRequireSignForReserve}) { - env(sponsor::set(sponsor, tfDeleteObject | flag), sponsor::sponseeAcc(alice), ter(temINVALID_FLAG)); + env(sponsor::set(sponsor, tfDeleteObject | flag), + sponsor::sponseeAcc(alice), + ter(temINVALID_FLAG)); } } // invalid SponsorAccount / Sponsee // Account = Sponsor - env(sponsor::set(alice, tfDeleteObject), sponsor::counterpartySponsor(alice), ter(temMALFORMED)); + env(sponsor::set(alice, tfDeleteObject), + sponsor::counterpartySponsor(alice), + ter(temMALFORMED)); // Account = Sponsee env(sponsor::set(alice, tfDeleteObject), sponsor::sponseeAcc(alice), ter(temMALFORMED)); // Both Sponsor and Sponsee are specified @@ -135,17 +144,29 @@ class Sponsor_test : public beast::unit_test::suite // Invalid MaxFee for (auto amt : {XRP(-1), USD(1)}) { - env(sponsor::set_fee(sponsor, 0, XRP(1), amt), sponsor::sponseeAcc(alice), ter(temBAD_AMOUNT)); + env(sponsor::set_fee(sponsor, 0, XRP(1), amt), + sponsor::sponseeAcc(alice), + ter(temBAD_AMOUNT)); } // Invalid Delete operation - env(sponsor::set_reserve(sponsor, tfDeleteObject, 1), sponsor::sponseeAcc(alice), ter(temMALFORMED)); - env(sponsor::set_fee(sponsor, tfDeleteObject, XRP(1)), sponsor::sponseeAcc(alice), ter(temMALFORMED)); - env(sponsor::set_max_fee(sponsor, tfDeleteObject, XRP(1)), sponsor::sponseeAcc(alice), ter(temMALFORMED)); + env(sponsor::set_reserve(sponsor, tfDeleteObject, 1), + sponsor::sponseeAcc(alice), + ter(temMALFORMED)); + env(sponsor::set_fee(sponsor, tfDeleteObject, XRP(1)), + sponsor::sponseeAcc(alice), + ter(temMALFORMED)); + env(sponsor::set_max_fee(sponsor, tfDeleteObject, XRP(1)), + sponsor::sponseeAcc(alice), + ter(temMALFORMED)); // Invalid SponsorAccount with non-Delete operation - env(sponsor::set_reserve(sponsor, 0, 100), sponsor::counterpartySponsor(alice), ter(temMALFORMED)); - env(sponsor::set_fee(sponsor, 0, XRP(1), XRP(1)), sponsor::counterpartySponsor(alice), ter(temMALFORMED)); + env(sponsor::set_reserve(sponsor, 0, 100), + sponsor::counterpartySponsor(alice), + ter(temMALFORMED)); + env(sponsor::set_fee(sponsor, 0, XRP(1), XRP(1)), + sponsor::counterpartySponsor(alice), + ter(temMALFORMED)); // // preclaim @@ -156,7 +177,9 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Invalid Sponsor - env(sponsor::set(sponsor, tfDeleteObject), sponsor::counterpartySponsor(noFunded), ter(tecNO_DST)); + env(sponsor::set(sponsor, tfDeleteObject), + sponsor::counterpartySponsor(noFunded), + ter(tecNO_DST)); env.close(); // Invalid Delete operation (sponsorship not found) @@ -173,16 +196,25 @@ class Sponsor_test : public beast::unit_test::suite // FeeAmount + Fee > Balance /// Balance = 1000XRP, FeeAmount = 1001XRP adjustAccountXRPBalance(env, sponsor, XRP(1000)); - env(sponsor::set_fee(sponsor, 0, XRP(1001)), sponsor::sponseeAcc(alice), fee(XRP(1)), ter(tecUNFUNDED)); + env(sponsor::set_fee(sponsor, 0, XRP(1001)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tecUNFUNDED)); env.close(); /// Balance = 1000XRP, FeeAmount = 999XRP, Fee=2XRP adjustAccountXRPBalance(env, sponsor, XRP(1000)); - env(sponsor::set_fee(sponsor, 0, XRP(999)), sponsor::sponseeAcc(alice), fee(XRP(2)), ter(tecUNFUNDED)); + env(sponsor::set_fee(sponsor, 0, XRP(999)), + sponsor::sponseeAcc(alice), + fee(XRP(2)), + ter(tecUNFUNDED)); env.close(); // create sponsor to use above tests adjustAccountXRPBalance(env, sponsor, XRP(1001)); - env(sponsor::set(sponsor, 0, 100, XRP(1000)), sponsor::sponseeAcc(alice), fee(XRP(1)), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(1000)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tesSUCCESS)); env.close(); } @@ -311,7 +343,9 @@ class Sponsor_test : public beast::unit_test::suite ter(terNO_ACCOUNT)); // Invalid Flags - env(noop(alice), sponsor::as(sponsor, (tfSponsorFee | tfSponsorReserve) + 1), ter(temINVALID_FLAG)); + env(noop(alice), + sponsor::as(sponsor, (tfSponsorFee | tfSponsorReserve) + 1), + ter(temINVALID_FLAG)); // Invalid Flags without sponsor auto tx = noop(alice); @@ -402,7 +436,9 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))); // create sponsorship with zero value - env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), sponsor::sponseeAcc(alice), fee(XRP(1))); + env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), + sponsor::sponseeAcc(alice), + fee(XRP(1))); env.close(); sle = env.le(keylet::sponsor(sponsor, alice)); @@ -412,7 +448,9 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(!sle->isFieldPresent(sfMaxFee)); // update sponsorship with non-zero value - env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), sponsor::sponseeAcc(alice), fee(XRP(1))); + env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), + sponsor::sponseeAcc(alice), + fee(XRP(1))); env.close(); sle = env.le(keylet::sponsor(sponsor, alice)); @@ -422,7 +460,9 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1)); // update sponsorship with zero value - env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), sponsor::sponseeAcc(alice), fee(XRP(1))); + env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), + sponsor::sponseeAcc(alice), + fee(XRP(1))); env.close(); sle = env.le(keylet::sponsor(sponsor, alice)); @@ -439,7 +479,9 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // add FeeAmount - env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); @@ -448,7 +490,9 @@ class Sponsor_test : public beast::unit_test::suite { // Update Sponsorship (ReserveCount) // set empty ReserveCount - env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); // add ReserveCount @@ -460,7 +504,9 @@ class Sponsor_test : public beast::unit_test::suite } { // delete Sponsorship (only with FeeAmount) - env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); @@ -490,7 +536,9 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, sponsor); env.close(); - env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); env(did::set(alice), @@ -521,7 +569,9 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, sponsor); env.close(); - env(sponsor::set(sponsor, 0, 10, XRP(10), XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 10, XRP(10), XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); // fee insufficient @@ -558,7 +608,8 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); env.close(); - env(sponsor::transfer(alice, (tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd) + 1), + env(sponsor::transfer( + alice, (tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd) + 1), ter(temINVALID_FLAG)); // invalid combination of flags @@ -594,7 +645,9 @@ class Sponsor_test : public beast::unit_test::suite sponsor::as(sponsor1, tfSponsorReserve), ter(temINVALID_FLAG)); // account = sponsee - env(sponsor::transfer(alice, tfSponsorshipEnd), sponsor::sponseeAcc(alice), ter(temMALFORMED)); + env(sponsor::transfer(alice, tfSponsorshipEnd), + sponsor::sponseeAcc(alice), + ter(temMALFORMED)); } { @@ -662,7 +715,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); - BEAST_EXPECT(!env.le(keylet::account(sponsor1))->isFieldPresent(sfSponsoringAccountCount)); + BEAST_EXPECT( + !env.le(keylet::account(sponsor1))->isFieldPresent(sfSponsoringAccountCount)); auto const sle2 = env.le(keylet::account(alice)); BEAST_EXPECT(sle2->isFieldPresent(sfSponsor)); BEAST_EXPECT(sle2->getAccountID(sfSponsor) == sponsor2.id()); @@ -709,7 +763,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, bob) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); - BEAST_EXPECT(!env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringAccountCount)); + BEAST_EXPECT( + !env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringAccountCount)); auto const sle4 = env.le(keylet::account(bob)); BEAST_EXPECT(!sle4->isFieldPresent(sfSponsor)); @@ -861,7 +916,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); - BEAST_EXPECT(!env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); + BEAST_EXPECT( + !env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); auto const sle3 = env.le(keylet::unchecked(checkId)); BEAST_EXPECT(!sle3->isFieldPresent(sfSponsor)); } @@ -890,7 +946,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), @@ -926,7 +983,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor1, 0, 100), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), sponsor::as(sponsor1, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::as(sponsor1, tfSponsorReserve)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -946,7 +1004,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 100), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -980,7 +1039,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); - BEAST_EXPECT(!env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); + BEAST_EXPECT( + !env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); checkSle = env.le(keylet::unchecked(checkId)); BEAST_EXPECT(!checkSle->isFieldPresent(sfSponsor)); sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice)); @@ -1008,7 +1068,8 @@ class Sponsor_test : public beast::unit_test::suite sig(sfSponsorSignature, sponsor)); env.close(); - BEAST_EXPECT(env.le(keylet::unchecked(checkId))->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT( + env.le(keylet::unchecked(checkId))->getAccountID(sfSponsor) == sponsor.id()); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); @@ -1062,16 +1123,20 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(trustId)); BEAST_EXPECT( - env.le(trustId)->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == sponsor.id()); - BEAST_EXPECT(!env.le(trustId)->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); + env.le(trustId)->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == + sponsor.id()); + BEAST_EXPECT( + !env.le(trustId)->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); // dissolve sponsor env(sponsor::transfer(user, tfSponsorshipEnd, trustId.key)); env.close(); BEAST_EXPECT(env.le(trustId)); - BEAST_EXPECT(!env.le(trustId)->isFieldPresent(isIssuerHigh ? sfLowSponsor : sfHighSponsor)); - BEAST_EXPECT(!env.le(trustId)->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); + BEAST_EXPECT( + !env.le(trustId)->isFieldPresent(isIssuerHigh ? sfLowSponsor : sfHighSponsor)); + BEAST_EXPECT( + !env.le(trustId)->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); } } @@ -1287,13 +1352,17 @@ class Sponsor_test : public beast::unit_test::suite auto bobBalance = env.balance(bob); auto sponsorBalance = env.balance(sponsor); - env(pay(alice, bob, XRP(100)), fee(XRP(90)), sponsor::as(sponsor, tfSponsorFee), ter(tesSUCCESS)); + env(pay(alice, bob, XRP(100)), + fee(XRP(90)), + sponsor::as(sponsor, tfSponsorFee), + ter(tesSUCCESS)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100)); BEAST_EXPECT(env.balance(bob) == bobBalance + XRP(100)); BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); - BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount)); + BEAST_EXPECT( + !env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount)); } // reset FeeAmount and MaxFee @@ -1354,16 +1423,22 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // set flag - env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), sponsor::sponseeAcc(alice)); + env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), + sponsor::sponseeAcc(alice)); env.close(); - env(pay(alice, bob, XRP(100)), fee(XRP(10)), sponsor::as(sponsor, tfSponsorFee), ter(terNO_SPONSORSHIP)); + env(pay(alice, bob, XRP(100)), + fee(XRP(10)), + sponsor::as(sponsor, tfSponsorFee), + ter(terNO_SPONSORSHIP)); env.close(); - BEAST_EXPECT(env.le(keylet::sponsor(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(10)); + BEAST_EXPECT( + env.le(keylet::sponsor(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(10)); // clear flag - env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), sponsor::sponseeAcc(alice)); + env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), + sponsor::sponseeAcc(alice)); env.close(); // Payment is re-applied @@ -1405,7 +1480,9 @@ class Sponsor_test : public beast::unit_test::suite tfLimitQuality, }) { - env(pay(alice, bob, XRP(100)), txflags(tfSponsorCreatedAccount | flag), ter(temINVALID_FLAG)); + env(pay(alice, bob, XRP(100)), + txflags(tfSponsorCreatedAccount | flag), + ter(temINVALID_FLAG)); env.close(); } @@ -1470,7 +1547,8 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // set flag - env(sponsor::set_reserve(sponsor, tfSponsorshipSetRequireSignForReserve, 10), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, tfSponsorshipSetRequireSignForReserve, 10), + sponsor::sponseeAcc(alice)); env.close(); env(check::create(alice, bob, XRP(100)), @@ -1483,7 +1561,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); // clear flag - env(sponsor::set_reserve(sponsor, tfSponsorshipClearRequireSignForReserve, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, tfSponsorshipClearRequireSignForReserve, 1), + sponsor::sponseeAcc(alice)); env.close(); // CheckCreate is re-applied @@ -1503,7 +1582,8 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // set flag - env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), sponsor::sponseeAcc(alice)); + env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), + sponsor::sponseeAcc(alice)); env.close(); env(check::create(alice, bob, XRP(100)), @@ -1512,10 +1592,12 @@ class Sponsor_test : public beast::unit_test::suite ter(terNO_SPONSORSHIP)); BEAST_EXPECT(ownerCount(env, alice) == 0); - BEAST_EXPECT(env.le(keylet::sponsor(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(10)); + BEAST_EXPECT( + env.le(keylet::sponsor(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(10)); // clear flag - env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), sponsor::sponseeAcc(alice)); + env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), + sponsor::sponseeAcc(alice)); env.close(); // CheckCreate is re-applied @@ -1545,10 +1627,11 @@ class Sponsor_test : public beast::unit_test::suite auto const sponseeSponsoringOwnerCountBefore = sponsoringOwnerCount(env, sponsee); auto const sponsorSponsoringOwnerCountBefore = sponsoringOwnerCount(env, sponsor); - std::optional sponsorSig = cosigning ? std::optional(sig(sfSponsorSignature, sponsor)) : std::nullopt; + std::optional sponsorSig = + cosigning ? std::optional(sig(sfSponsorSignature, sponsor)) : std::nullopt; - auto const sponsorCurrentOwnerCount = - ownerCount(env, sponsor) - sponsoredOwnerCount(env, sponsor) + sponsoringOwnerCount(env, sponsor); + auto const sponsorCurrentOwnerCount = ownerCount(env, sponsor) - + sponsoredOwnerCount(env, sponsor) + sponsoringOwnerCount(env, sponsor); auto submit = [&](TER _ter) { return [&, _ter](Json::Value const& jv, auto const&... fN) { @@ -1564,7 +1647,9 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { adjustAccountXRPBalance( - env, sponsor, reserve(env, sponsorCurrentOwnerCount + sponsorReserveCount) - drops(1)); + env, + sponsor, + reserve(env, sponsorCurrentOwnerCount + sponsorReserveCount) - drops(1)); } else { @@ -1576,10 +1661,12 @@ class Sponsor_test : public beast::unit_test::suite } if (sponsorReserveCount - 1 > 0) - env(sponsor::set(sponsor, 0, sponsorReserveCount - 1, XRP(1)), sponsor::sponseeAcc(sponsee)); + env(sponsor::set(sponsor, 0, sponsorReserveCount - 1, XRP(1)), + sponsor::sponseeAcc(sponsee)); else // just create sponsor object - env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), sponsor::sponseeAcc(sponsee)); + env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), + sponsor::sponseeAcc(sponsee)); env.close(); } callback(env, submit(insufficientReserveResult)); @@ -1590,13 +1677,15 @@ class Sponsor_test : public beast::unit_test::suite { if (cosigning) { - adjustAccountXRPBalance(env, sponsor, reserve(env, sponsorCurrentOwnerCount + sponsorReserveCount)); + adjustAccountXRPBalance( + env, sponsor, reserve(env, sponsorCurrentOwnerCount + sponsorReserveCount)); } else { // reset sponsorship env(sponsor::del(sponsor), sponsor::sponseeAcc(sponsee)); - env(sponsor::set(sponsor, 0, sponsorReserveCount, XRP(1)), sponsor::sponseeAcc(sponsee)); + env(sponsor::set(sponsor, 0, sponsorReserveCount, XRP(1)), + sponsor::sponseeAcc(sponsee)); env.close(); } callback(env, submit(tesSUCCESS)); @@ -1615,9 +1704,14 @@ class Sponsor_test : public beast::unit_test::suite else { BEAST_EXPECT(ownerCount(env, sponsee) - sponseeOwnerCountBefore == reserveCount); - BEAST_EXPECT(sponsoredOwnerCount(env, sponsee) - sponseeSponsoredOwnerCountBefore == sponsorReserveCount); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsee) - sponseeSponsoringOwnerCountBefore == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) - sponsorSponsoringOwnerCountBefore == sponsorReserveCount); + BEAST_EXPECT( + sponsoredOwnerCount(env, sponsee) - sponseeSponsoredOwnerCountBefore == + sponsorReserveCount); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsee) - sponseeSponsoringOwnerCountBefore == 0); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) - sponsorSponsoringOwnerCountBefore == + sponsorReserveCount); } }; @@ -1634,7 +1728,10 @@ class Sponsor_test : public beast::unit_test::suite auto const USD = gw["USD"]; auto const EUR = gw["EUR"]; - auto const ammCreate = [&](Env& env, Account const& account, STAmount const& amount1, STAmount const& amount2) { + auto const ammCreate = [&](Env& env, + Account const& account, + STAmount const& amount1, + STAmount const& amount2) { Json::Value jv; jv[jss::TransactionType] = jss::AMMCreate; jv[jss::Account] = account.human(); @@ -1645,18 +1742,20 @@ class Sponsor_test : public beast::unit_test::suite return jv; }; - auto const ammDeposit = - [&](Env& env, Account const& account, STAmount const& amount1, STAmount const& amount2) { - Json::Value jv; - jv[jss::TransactionType] = jss::AMMDeposit; - jv[jss::Account] = account.human(); - jv[jss::Asset] = STIssue(sfAsset, amount1.issue()).getJson(JsonOptions::none); - jv[jss::Asset2] = STIssue(sfAsset, amount2.issue()).getJson(JsonOptions::none); - jv[jss::Amount] = amount1.value().getJson(JsonOptions::none); - jv[jss::Amount2] = amount2.value().getJson(JsonOptions::none); - jv[jss::Flags] = tfTwoAsset; - return jv; - }; + auto const ammDeposit = [&](Env& env, + Account const& account, + STAmount const& amount1, + STAmount const& amount2) { + Json::Value jv; + jv[jss::TransactionType] = jss::AMMDeposit; + jv[jss::Account] = account.human(); + jv[jss::Asset] = STIssue(sfAsset, amount1.issue()).getJson(JsonOptions::none); + jv[jss::Asset2] = STIssue(sfAsset, amount2.issue()).getJson(JsonOptions::none); + jv[jss::Amount] = amount1.value().getJson(JsonOptions::none); + jv[jss::Amount2] = amount2.value().getJson(JsonOptions::none); + jv[jss::Flags] = tfTwoAsset; + return jv; + }; { // AMMCreate @@ -1682,16 +1781,19 @@ class Sponsor_test : public beast::unit_test::suite 1, 1, tecINSUF_RESERVE_LINE, - [&](Env& env, auto const& submit) { submit(ammCreate(env, alice, USD(100), EUR(100))); }, + [&](Env& env, auto const& submit) { + submit(ammCreate(env, alice, USD(100), EUR(100))); + }, [&]() { auto const amm = env.current()->read(keylet::amm(USD.issue(), EUR.issue())); auto const ammAccount = Account("amm", amm->getAccountID(sfAccount)); - BEAST_EXPECT(ownerCount(env, alice) == 3); // RippleState (USD,EUR/LP Token) + BEAST_EXPECT(ownerCount(env, alice) == 3); // RippleState (USD,EUR/LP Token) BEAST_EXPECT(ownerCount(env, ammAccount) == 2); // USD, EUR BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); // LPToken BEAST_EXPECT(sponsoredOwnerCount(env, ammAccount) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken - BEAST_EXPECT(!env.le(keylet::amm(USD.issue(), EUR.issue()))->isFieldPresent(sfSponsor)); + BEAST_EXPECT( + !env.le(keylet::amm(USD.issue(), EUR.issue()))->isFieldPresent(sfSponsor)); }); auto const ammKeylet = keylet::amm(USD.issue(), EUR.issue()); @@ -1738,7 +1840,14 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState (USD,EUR) testEachSponsorship( - env, cosigning, sponsor, bob, 1, 1, tecINSUF_RESERVE_LINE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUF_RESERVE_LINE, + [&](Env& env, auto const& submit) { submit(ammDeposit(env, bob, USD(100), EUR(100))); }); } @@ -1784,9 +1893,14 @@ class Sponsor_test : public beast::unit_test::suite env.close(); testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(jv); - }); + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(jv); }); } { // Double Asset Withdraw @@ -1923,7 +2037,14 @@ class Sponsor_test : public beast::unit_test::suite uint32_t seq; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { seq = env.seq(alice); submit(check::create(alice, bob, XRP(1))); }); @@ -1981,7 +2102,14 @@ class Sponsor_test : public beast::unit_test::suite // CheckCreate -> CheckCash uint32_t seq2; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { seq2 = env.seq(alice); submit(check::create(alice, bob, XRP(1))); }); @@ -2015,7 +2143,14 @@ class Sponsor_test : public beast::unit_test::suite // CheckCreate -> CheckCash uint32_t seq2; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { seq2 = env.seq(alice); submit(check::create(alice, bob, USD(1))); }); @@ -2069,7 +2204,14 @@ class Sponsor_test : public beast::unit_test::suite // OfferCreate uint32_t seq; testEachSponsorship( - env, cosigning, sponsor1, alice, 1, 1, tecINSUF_RESERVE_OFFER, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor1, + alice, + 1, + 1, + tecINSUF_RESERVE_OFFER, + [&](Env& env, auto const& submit) { seq = env.seq(alice); submit(offer(alice, USD(1), XRP(1))); }); @@ -2120,7 +2262,14 @@ class Sponsor_test : public beast::unit_test::suite // OfferCreate uint32_t seq; testEachSponsorship( - env, cosigning, sponsor1, alice, 1, 1, tecINSUF_RESERVE_OFFER, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor1, + alice, + 1, + 1, + tecINSUF_RESERVE_OFFER, + [&](Env& env, auto const& submit) { seq = env.seq(alice); submit(offer(alice, USD(1), XRP(1))); }); @@ -2255,7 +2404,14 @@ class Sponsor_test : public beast::unit_test::suite uint32_t ticketSeq; testEachSponsorship( - env, cosigning, sponsor, alice, 250, 250, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 250, + 250, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { ticketSeq = env.seq(alice) + 1; submit(ticket::create(alice, 250)); }); @@ -2319,7 +2475,14 @@ class Sponsor_test : public beast::unit_test::suite env.close(); testEachSponsorship( - env, cosigning, sponsor, issuer, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + issuer, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(credentials::create(subject, issuer, credType), credentials::uri("uri")); }); @@ -2354,7 +2517,14 @@ class Sponsor_test : public beast::unit_test::suite // CredentialsAccept testEachSponsorship( - env, cosigning, sponsor, subject, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + subject, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(credentials::accept(subject, issuer, credType)); }); @@ -2402,7 +2572,14 @@ class Sponsor_test : public beast::unit_test::suite // Accept Sponsored Credentials without sponsoring testEachSponsorship( - env, cosigning, sponsor, issuer, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + issuer, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(credentials::create(subject, issuer, credType)); }); @@ -2415,7 +2592,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, issuer) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, subject) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(!env.le(keylet::credential(subject, issuer, credTypeSlice))->isFieldPresent(sfSponsor)); + BEAST_EXPECT(!env.le(keylet::credential(subject, issuer, credTypeSlice)) + ->isFieldPresent(sfSponsor)); env(credentials::deleteCred(subject, subject, issuer, credType)); env.close(); @@ -2439,7 +2617,14 @@ class Sponsor_test : public beast::unit_test::suite // DelegateSet testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(delegate::set(alice, bob, {"Payment"})); }); @@ -2493,9 +2678,14 @@ class Sponsor_test : public beast::unit_test::suite // DepositPreauthSet testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(deposit::auth(alice, sponsor)); - }); + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(deposit::auth(alice, sponsor)); }); // transfer sponsor auto const keylet = keylet::depositPreauth(alice, sponsor); @@ -2548,9 +2738,14 @@ class Sponsor_test : public beast::unit_test::suite // DIDSet testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(did::set(alice), did::uri("uri")); - }); + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(did::set(alice), did::uri("uri")); }); // transfer sponsor auto const keylet = keylet::did(alice); @@ -2608,14 +2803,22 @@ class Sponsor_test : public beast::unit_test::suite // EscrowCreate uint32_t seq; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { seq = env.seq(alice); submit( escrow::create(alice, bob, XRP(100)), escrow::condition(escrow::cb1), escrow::cancel_time(env.now() + 100s)); }); - BEAST_EXPECT(env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); // transfer sponsor if (cosigning) @@ -2640,7 +2843,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); - BEAST_EXPECT(env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor2.id()); + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor2.id()); // EscrowFinish env(escrow::finish(bob, alice, seq), @@ -2678,7 +2882,14 @@ class Sponsor_test : public beast::unit_test::suite // EscrowCreate uint32_t seq; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { seq = env.seq(alice); submit( escrow::create(alice, bob, USD(100)), @@ -2686,11 +2897,19 @@ class Sponsor_test : public beast::unit_test::suite escrow::cancel_time(env.now() + 100s)); }); - BEAST_EXPECT(env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); // EscrowFinish testEachSponsorship( - env, cosigning, sponsor2, bob, 1, 1, tecNO_LINE_INSUF_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor2, + bob, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { submit( escrow::finish(bob, alice, seq), escrow::condition(escrow::cb1), @@ -2702,7 +2921,9 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - BEAST_EXPECT(env.le(keylet::line(bob, gw, USD.currency))->getAccountID(sfHighSponsor) == sponsor2.id()); + BEAST_EXPECT( + env.le(keylet::line(bob, gw, USD.currency))->getAccountID(sfHighSponsor) == + sponsor2.id()); } { // MPT Escrow @@ -2711,7 +2932,8 @@ class Sponsor_test : public beast::unit_test::suite env.close(); MPTTester mptGw(env, gw, {.holders = {alice}}); - mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); auto const MPT = mptGw["MPT"]; env(pay(gw, alice, MPT(10'000))); @@ -2766,7 +2988,14 @@ class Sponsor_test : public beast::unit_test::suite jv[sfTransactionType] = jss::MPTokenIssuanceCreate; MPTID mptid; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { mptid = makeMptID(env.seq(alice), alice.id()); submit(jv); }); @@ -2805,9 +3034,14 @@ class Sponsor_test : public beast::unit_test::suite } testEachSponsorship( - env, cosigning, sponsor, bob, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(jv); - }); + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(jv); }); BEAST_EXPECT(ownerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -2917,7 +3151,10 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); - env(jv, sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor), ter(tesSUCCESS)); + env(jv, + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor), + ter(tesSUCCESS)); env.close(); } else @@ -2949,7 +3186,14 @@ class Sponsor_test : public beast::unit_test::suite // NFTokenMint uint256 nftId; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { nftId = token::getNextID(env, alice, 0); submit(token::mint(alice)); }); @@ -3015,7 +3259,9 @@ class Sponsor_test : public beast::unit_test::suite { for (auto i = 0; i < nftCount; i++) { - env(token::mint(alice), sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor)); + env(token::mint(alice), + sponsor::as(sponsor, tfSponsorReserve), + sig(sfSponsorSignature, sponsor)); } } else @@ -3073,16 +3319,36 @@ class Sponsor_test : public beast::unit_test::suite // NFTokenOfferCreate uint256 offerIndex1; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { offerIndex1 = keylet::nftoffer(alice, env.seq(alice)).key; - submit(token::createOffer(alice, nftId, XRP(1)), token::destination(bob), txflags(tfSellNFToken)); + submit( + token::createOffer(alice, nftId, XRP(1)), + token::destination(bob), + txflags(tfSellNFToken)); }); uint256 offerIndex2; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { offerIndex2 = keylet::nftoffer(alice, env.seq(alice)).key; - submit(token::createOffer(alice, nftId, XRP(1)), token::destination(bob), txflags(tfSellNFToken)); + submit( + token::createOffer(alice, nftId, XRP(1)), + token::destination(bob), + txflags(tfSellNFToken)); }); // transfer sponsor @@ -3132,9 +3398,19 @@ class Sponsor_test : public beast::unit_test::suite // NFTokenOfferCreate uint256 offerIndex; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { offerIndex = keylet::nftoffer(alice, env.seq(alice)).key; - submit(token::createOffer(alice, nftId, XRP(1)), token::destination(bob), txflags(tfSellNFToken)); + submit( + token::createOffer(alice, nftId, XRP(1)), + token::destination(bob), + txflags(tfSellNFToken)); }); // NFTokenOfferAccept @@ -3162,9 +3438,19 @@ class Sponsor_test : public beast::unit_test::suite // NFTokenOfferCreate uint256 offerIndex; testEachSponsorship( - env, cosigning, sponsor, bob, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { offerIndex = keylet::nftoffer(bob, env.seq(bob)).key; - submit(token::createOffer(bob, nftId, XRP(1)), token::owner(alice), token::destination(alice)); + submit( + token::createOffer(bob, nftId, XRP(1)), + token::owner(alice), + token::destination(alice)); }); // NFTokenOfferAccept @@ -3193,18 +3479,37 @@ class Sponsor_test : public beast::unit_test::suite // NFTokenOfferCreate (BuyOffer) uint256 buyOfferIndex; testEachSponsorship( - env, cosigning, sponsor, bob, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { buyOfferIndex = keylet::nftoffer(bob, env.seq(bob)).key; - submit(token::createOffer(bob, nftId, XRP(1)), token::owner(alice), token::destination(broker)); + submit( + token::createOffer(bob, nftId, XRP(1)), + token::owner(alice), + token::destination(broker)); }); // NFTokenOfferCreate (SellOffer) uint256 sellOfferIndex; testEachSponsorship( - env, cosigning, sponsor2, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor2, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { sellOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key; submit( - token::createOffer(alice, nftId, XRP(1)), txflags(tfSellNFToken), token::destination(broker)); + token::createOffer(alice, nftId, XRP(1)), + txflags(tfSellNFToken), + token::destination(broker)); }); // NFTokenOfferAccept @@ -3241,7 +3546,14 @@ class Sponsor_test : public beast::unit_test::suite auto const settleDelay = 10s; uint256 chan; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { chan = paychan::channel(alice, bob, env.seq(alice)); submit(paychan::create(alice, bob, XRP(100), settleDelay, pk)); }); @@ -3259,7 +3571,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, tfSponsorshipReassign, chan), sponsor::as(sponsor2, tfSponsorReserve)); + env(sponsor::transfer(alice, tfSponsorshipReassign, chan), + sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -3297,7 +3610,14 @@ class Sponsor_test : public beast::unit_test::suite pdomain::Credentials credentials{{alice, "first credential"}}; uint32_t seq; testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { seq = env.seq(alice); submit(pdomain::setTx(alice, credentials)); }); @@ -3345,7 +3665,8 @@ class Sponsor_test : public beast::unit_test::suite testcase("Oracle"); using namespace test::jtx; using namespace std::chrono; - using DataSeries = std::vector>; + using DataSeries = + std::vector>; Account const alice("alice"); Account const sponsor("sponsor"); @@ -3359,7 +3680,8 @@ class Sponsor_test : public beast::unit_test::suite jv[jss::Account] = to_string(account); jv[jss::OracleDocumentID] = 1; jv[jss::LastUpdateTime] = to_string( - duration_cast(env.current()->header().closeTime.time_since_epoch()).count() + + duration_cast(env.current()->header().closeTime.time_since_epoch()) + .count() + epoch_offset.count() + 100); jv[jss::PriceDataSeries] = Json::arrayValue; jv[jss::Provider] = strHex(std::string{"provider"}); @@ -3412,9 +3734,14 @@ class Sponsor_test : public beast::unit_test::suite { // OracleSet (reserve 1) testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(oracleSet(env, alice, 5)); - }); + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(oracleSet(env, alice, 5)); }); // transfer sponsor auto const keylet = keylet::oracle(alice, 1); @@ -3451,9 +3778,14 @@ class Sponsor_test : public beast::unit_test::suite { // OracleSet (reserve 2) testEachSponsorship( - env, cosigning, sponsor, alice, 2, 2, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(oracleSet(env, alice, 6)); - }); + env, + cosigning, + sponsor, + alice, + 2, + 2, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(oracleSet(env, alice, 6)); }); // transfer sponsor auto const keylet = keylet::oracle(alice, 1); @@ -3490,9 +3822,14 @@ class Sponsor_test : public beast::unit_test::suite { // OracleSet (reserve 1->2, sponsor1 -> no-sponsor) testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(oracleSet(env, alice, 5)); - }); + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(oracleSet(env, alice, 5)); }); // reserve 1->2 env(oracleSet(env, alice, 6)); @@ -3513,9 +3850,14 @@ class Sponsor_test : public beast::unit_test::suite { // OracleSet (reserve 1->2, sponsor1 -> sponsor2) testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(oracleSet(env, alice, 5)); - }); + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(oracleSet(env, alice, 5)); }); // return; // reserve 1->2 @@ -3553,9 +3895,14 @@ class Sponsor_test : public beast::unit_test::suite // reserve 1->2 testEachSponsorship( - env, cosigning, sponsor, alice, 1, 2, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(oracleSet(env, alice, 6)); - }); + env, + cosigning, + sponsor, + alice, + 1, + 2, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(oracleSet(env, alice, 6)); }); // OracleDelete env(oracleDelete(alice)); @@ -3579,12 +3926,15 @@ class Sponsor_test : public beast::unit_test::suite ocount, ocount, tecINSUFFICIENT_RESERVE, - [&](Env& env, auto const& submit) { submit(oracleSet(env, alice, dataSeriesSize)); }); + [&](Env& env, auto const& submit) { + submit(oracleSet(env, alice, dataSeriesSize)); + }); // transfer sponsor if (cosigning) { - env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::oracle(alice, 1).key), + env(sponsor::transfer( + alice, tfSponsorshipReassign, keylet::oracle(alice, 1).key), sponsor::as(sponsor2, tfSponsorReserve), sig(sfSponsorSignature, sponsor2)); env.close(); @@ -3593,7 +3943,8 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::set_reserve(sponsor2, 0, ocount), sponsor::sponseeAcc(alice)); env.close(); - env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::oracle(alice, 1).key), + env(sponsor::transfer( + alice, tfSponsorshipReassign, keylet::oracle(alice, 1).key), sponsor::as(sponsor2, tfSponsorReserve)); env.close(); } @@ -3634,9 +3985,14 @@ class Sponsor_test : public beast::unit_test::suite // SignerListSet testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(signers(alice, 1, {{bob, 1}})); - }); + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(signers(alice, 1, {{bob, 1}})); }); // transfer sponsor if (cosigning) @@ -3697,7 +4053,8 @@ class Sponsor_test : public beast::unit_test::suite auto const validateSponsoredTrustline = [&](std::shared_ptr const& sle, bool isIssuerHigh, Account const& sponsor) { - BEAST_EXPECT(sle->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == sponsor.id()); + BEAST_EXPECT( + sle->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == sponsor.id()); BEAST_EXPECT(!sle->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); }; @@ -3726,9 +4083,14 @@ class Sponsor_test : public beast::unit_test::suite } testEachSponsorship( - env, cosigning, sponsor, user, 1, 1, tecNO_LINE_INSUF_RESERVE, [&](Env& env, auto const& submit) { - submit(trust(user, USD(100))); - }); + env, + cosigning, + sponsor, + user, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { submit(trust(user, USD(100))); }); auto const keylet = keylet::line(user, issuer, currency); @@ -3796,9 +4158,14 @@ class Sponsor_test : public beast::unit_test::suite } testEachSponsorship( - env, cosigning, sponsor, user, 1, 1, tecINSUF_RESERVE_LINE, [&](Env& env, auto const& submit) { - submit(trust(user, USD(100))); - }); + env, + cosigning, + sponsor, + user, + 1, + 1, + tecINSUF_RESERVE_LINE, + [&](Env& env, auto const& submit) { submit(trust(user, USD(100))); }); auto const line = env.le(keylet::line(user, issuer, currency)); validateSponsoredTrustline(line, isIssuerHigh, sponsor); @@ -3921,15 +4288,21 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState - auto const depositTx = vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}); + auto const depositTx = + vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}); env(ticket::create(sponsor, 2)); // for free MPToken env.close(); testEachSponsorship( - env, cosigning, sponsor, bob, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(depositTx); - }); + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(depositTx); }); } // VaultWithdraw { @@ -3949,15 +4322,21 @@ class Sponsor_test : public beast::unit_test::suite env(pay(gw, bob, asset(100))); env.close(); - auto const depositTx = vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}); + auto const depositTx = + vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}); env(ticket::create(sponsor, 2)); // for free MPToken env.close(); testEachSponsorship( - env, cosigning, sponsor, bob, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(depositTx); - }); + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(depositTx); }); env(trust(bob, asset(0))); // remove trustline env.close(); @@ -3968,13 +4347,22 @@ class Sponsor_test : public beast::unit_test::suite // create Trustline with vault withdraw testEachSponsorship( - env, cosigning, sponsor, bob, 1, 1, tecNO_LINE_INSUF_RESERVE, [&](Env& env, auto const& submit) { - submit(vault.withdraw({.depositor = bob, .id = keylet.key, .amount = asset(50)})); + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { + submit(vault.withdraw( + {.depositor = bob, .id = keylet.key, .amount = asset(50)})); }); - BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState, MPToken(share) - BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 2); // RippleState, MPToken(share) - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); // RippleState, MPToken(share) + BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState, MPToken(share) + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 2); // RippleState, MPToken(share) + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == 2); // RippleState, MPToken(share) // remove sponsored MPToken(share) env(vault.withdraw({.depositor = bob, .id = keylet.key, .amount = asset(50)})); @@ -4009,15 +4397,21 @@ class Sponsor_test : public beast::unit_test::suite env(pay(gw, bob, asset(100))); env.close(); - auto const depositTx = vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}); + auto const depositTx = + vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}); env(ticket::create(sponsor, 2)); // for free MPToken env.close(); testEachSponsorship( - env, cosigning, sponsor, bob, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(depositTx); - }); + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(depositTx); }); BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState, MPToken(share) BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); // MPToken(share) @@ -4082,14 +4476,28 @@ class Sponsor_test : public beast::unit_test::suite // XChainCreateBridge { testEachSponsorship( - env, cosigning, sponsor, doorA, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + doorA, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(bridge_create(doorA, jvb, XRP(1), XRP(1))); }); } // XChainCreateClaimID { testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(xchain_create_claim_id(alice, jvb, XRP(1), bob)); }); } @@ -4111,7 +4519,8 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice)); env.close(); - env(xchain_commit(alice, jvb, 1, XRP(100), bob), sponsor::as(sponsor, tfSponsorReserve)); + env(xchain_commit(alice, jvb, 1, XRP(100), bob), + sponsor::as(sponsor, tfSponsorReserve)); env.close(); env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); @@ -4162,7 +4571,8 @@ class Sponsor_test : public beast::unit_test::suite sponsor::as(sponsor, tfSponsorReserve), sig(sfSponsorSignature, sponsor)); env(xchain_commit(alice, jvb, 2, XRP(100))); // omit destination - env(claim_attestation(alice, jvb, bob, XRP(100), bob, false, 2, std::nullopt, signer)); + env(claim_attestation( + alice, jvb, bob, XRP(100), bob, false, 2, std::nullopt, signer)); env.close(); } @@ -4203,11 +4613,13 @@ class Sponsor_test : public beast::unit_test::suite env(tx, THISLINE); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 3); // Vault, PseudoAccount(Vault), MPToken(Vault) + BEAST_EXPECT( + ownerCount(env, alice) == 3); // Vault, PseudoAccount(Vault), MPToken(Vault) // LoanBrokerSet testEachSponsorship( - // Both the Pseudo-account and LoanBroker objects are created, but only the LoanBroker is sponsored. + // Both the Pseudo-account and LoanBroker objects are created, but only the + // LoanBroker is sponsored. env, cosigning, sponsor, @@ -4215,7 +4627,9 @@ class Sponsor_test : public beast::unit_test::suite 2, 1, tecINSUFFICIENT_RESERVE, - [&](Env& env, auto const& submit) { submit(loanBroker::set(alice, keylet.key, 0)); }); + [&](Env& env, auto const& submit) { + submit(loanBroker::set(alice, keylet.key, 0)); + }); BEAST_EXPECT( ownerCount(env, alice) == @@ -4257,8 +4671,10 @@ class Sponsor_test : public beast::unit_test::suite env(loanBroker::set(alice, keylet.key, 0)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 6); // LoanBroker, PseudoAccount(LB), (Vault, PseudoAccount(Vault), - // MPToken(Vault), MPToken(issuer)) + BEAST_EXPECT( + ownerCount(env, alice) == + 6); // LoanBroker, PseudoAccount(LB), (Vault, PseudoAccount(Vault), + // MPToken(Vault), MPToken(issuer)) auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice) - 1); // LoanBrokerCoverDeposit @@ -4281,7 +4697,14 @@ class Sponsor_test : public beast::unit_test::suite // LoanBrokerCoverWithdraw testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(loanBroker::coverWithdraw(alice, brokerKeylet.key, asset(10))); }); @@ -4324,7 +4747,8 @@ class Sponsor_test : public beast::unit_test::suite auto const [tx, keylet] = vault.create({.owner = bob, .asset = asset}); env(tx, THISLINE); env.close(); - env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}), THISLINE); + env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}), + THISLINE); env.close(); auto const brokerKeylet = keylet::loanbroker(bob.id(), env.seq(bob)); @@ -4340,8 +4764,18 @@ class Sponsor_test : public beast::unit_test::suite auto const loanSeq = broker->getFieldU32(sfLoanSequence); testEachSponsorship( - env, cosigning, sponsor, alice, 1, 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(loan::set(alice, brokerKeylet.key, 10), sig(sfCounterpartySignature, bob), fee(XRP(1))); + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit( + loan::set(alice, brokerKeylet.key, 10), + sig(sfCounterpartySignature, bob), + fee(XRP(1))); }); broker = env.le(brokerKeylet); // broker'object doesn't sponsored @@ -4416,7 +4850,9 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Create sponsor should fail - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tecNO_PERMISSION)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tecNO_PERMISSION)); env.close(); // clear flag @@ -4455,7 +4891,9 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // set sponsor - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); incLgrSeqForAccDel(env, alice); @@ -4483,7 +4921,9 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // set sponsor - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); env.close(); incLgrSeqForAccDel(env, sponsor); @@ -4589,7 +5029,10 @@ class Sponsor_test : public beast::unit_test::suite env(delegate::set(alice, carol, {"SponsorshipSet"})); env.close(); - env(sponsor::set(alice, 0, 100, XRP(100)), sponsor::sponseeAcc(bob), delegate::as(carol), ter(tesSUCCESS)); + env(sponsor::set(alice, 0, 100, XRP(100)), + sponsor::sponseeAcc(bob), + delegate::as(carol), + ter(tesSUCCESS)); env.close(); } @@ -4651,7 +5094,10 @@ class Sponsor_test : public beast::unit_test::suite auto const testReservePermission = [&](TER result) { // ReserveCount - env(sponsor::set(alice, 0, 100), sponsor::sponseeAcc(bob), delegate::as(carol), ter(result)); + env(sponsor::set(alice, 0, 100), + sponsor::sponseeAcc(bob), + delegate::as(carol), + ter(result)); // SetRequireSignForReserve flag env(sponsor::set(alice, tfSponsorshipSetRequireSignForReserve), sponsor::sponseeAcc(bob), @@ -4726,7 +5172,10 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(1001), sponsor); env.close(); - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), fee(XRP(1)), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tesSUCCESS)); env.close(); auto const seq = env.seq(alice); @@ -4761,7 +5210,9 @@ class Sponsor_test : public beast::unit_test::suite env.close(); auto jt = env.jtnofill( - noop(alice), sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), sig(sfSponsorSignature, sponsor)); + noop(alice), + sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), + sig(sfSponsorSignature, sponsor)); auto const seq = env.seq(alice); // should fail because inner transaction cannot include SponsorSignature @@ -4777,11 +5228,15 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(1001), sponsor); env.close(); - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), fee(XRP(1)), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tesSUCCESS)); env.close(); BEAST_EXPECT(env.balance(sponsor) == XRP(900)); - auto jt = env.jtnofill(ticket::create(alice, 1), sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee)); + auto jt = env.jtnofill( + ticket::create(alice, 1), sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee)); // remove txn signature since it is filled by env.jtnofill() jt.jv.removeMember(jss::TxnSignature); diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 4b93c4bc11f..db3caf56cfc 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -33,7 +33,11 @@ set(jtx::Account const& account, } Json::Value -set_fee(jtx::Account const& account, uint32_t flags, STAmount feeAmount, std::optional maxFee) +set_fee( + jtx::Account const& account, + uint32_t flags, + STAmount feeAmount, + std::optional maxFee) { Json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index dc7c4462cce..01e3b081795 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -35,7 +35,10 @@ Json::Value del(jtx::Account const& account); Json::Value -transfer(jtx::Account const& account, uint32_t flags, std::optional const& index = std::nullopt); +transfer( + jtx::Account const& account, + uint32_t flags, + std::optional const& index = std::nullopt); struct counterpartySponsor { From fb867358006c0522bec5776f68b88dbc330a8ca6 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 17:26:13 +0900 Subject: [PATCH 143/249] use pragma once --- include/xrpl/protocol/Sponsor.h | 3 +-- src/test/jtx/sponsor.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/xrpl/protocol/Sponsor.h b/include/xrpl/protocol/Sponsor.h index ba6e4e1b93c..22b97515bc3 100644 --- a/include/xrpl/protocol/Sponsor.h +++ b/include/xrpl/protocol/Sponsor.h @@ -1,5 +1,4 @@ -#ifndef XRPL_PROTOCOL_SPONSOR_H_INCLUDED -#define XRPL_PROTOCOL_SPONSOR_H_INCLUDED +#pragma once #include #include diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index 01e3b081795..0e553299eb8 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -1,5 +1,4 @@ -#ifndef XRPL_TEST_JTX_SPONSOR_H_INCLUDED -#define XRPL_TEST_JTX_SPONSOR_H_INCLUDED +#pragma once #include #include From d1eb22b4035b96a1b4e15f6105b0fe8e21e2490b Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 17:38:04 +0900 Subject: [PATCH 144/249] fix transactions.macro format --- .../xrpl/protocol/detail/transactions.macro | 1291 +++++++---------- 1 file changed, 530 insertions(+), 761 deletions(-) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index d688b8747dc..175adcbaa05 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -22,173 +22,147 @@ /** This transaction type executes a payment. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPAYMENT, - 0, - Payment, +TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegable, uint256{}, createAcct, ({ - {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - {sfSendMax, soeOPTIONAL, soeMPTSupported}, - {sfPaths, soeDEFAULT}, - {sfInvoiceID, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - {sfDeliverMin, soeOPTIONAL, soeMPTSupported}, - {sfCredentialIDs, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, + {sfSendMax, soeOPTIONAL, soeMPTSupported}, + {sfPaths, soeDEFAULT}, + {sfInvoiceID, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, + {sfDeliverMin, soeOPTIONAL, soeMPTSupported}, + {sfCredentialIDs, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, +})) /** This transaction type creates an escrow object. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttESCROW_CREATE, - 1, - EscrowCreate, +TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegable, uint256{}, noPriv, ({ - {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - {sfCondition, soeOPTIONAL}, - {sfCancelAfter, soeOPTIONAL}, - {sfFinishAfter, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, + {sfCondition, soeOPTIONAL}, + {sfCancelAfter, soeOPTIONAL}, + {sfFinishAfter, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, +})) /** This transaction type completes an existing escrow. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttESCROW_FINISH, - 2, - EscrowFinish, +TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegable, uint256{}, noPriv, ({ - {sfOwner, soeREQUIRED}, - {sfOfferSequence, soeREQUIRED}, - {sfFulfillment, soeOPTIONAL}, - {sfCondition, soeOPTIONAL}, - {sfCredentialIDs, soeOPTIONAL}, - })) + {sfOwner, soeREQUIRED}, + {sfOfferSequence, soeREQUIRED}, + {sfFulfillment, soeOPTIONAL}, + {sfCondition, soeOPTIONAL}, + {sfCredentialIDs, soeOPTIONAL}, +})) + /** This transaction type adjusts various account settings. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttACCOUNT_SET, - 3, - AccountSet, +TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegable, uint256{}, noPriv, ({ - {sfEmailHash, soeOPTIONAL}, - {sfWalletLocator, soeOPTIONAL}, - {sfWalletSize, soeOPTIONAL}, - {sfMessageKey, soeOPTIONAL}, - {sfDomain, soeOPTIONAL}, - {sfTransferRate, soeOPTIONAL}, - {sfSetFlag, soeOPTIONAL}, - {sfClearFlag, soeOPTIONAL}, - {sfTickSize, soeOPTIONAL}, - {sfNFTokenMinter, soeOPTIONAL}, - })) + {sfEmailHash, soeOPTIONAL}, + {sfWalletLocator, soeOPTIONAL}, + {sfWalletSize, soeOPTIONAL}, + {sfMessageKey, soeOPTIONAL}, + {sfDomain, soeOPTIONAL}, + {sfTransferRate, soeOPTIONAL}, + {sfSetFlag, soeOPTIONAL}, + {sfClearFlag, soeOPTIONAL}, + {sfTickSize, soeOPTIONAL}, + {sfNFTokenMinter, soeOPTIONAL}, +})) /** This transaction type cancels an existing escrow. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttESCROW_CANCEL, - 4, - EscrowCancel, +TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, Delegation::delegable, uint256{}, noPriv, ({ - {sfOwner, soeREQUIRED}, - {sfOfferSequence, soeREQUIRED}, - })) + {sfOwner, soeREQUIRED}, + {sfOfferSequence, soeREQUIRED}, +})) /** This transaction type sets or clears an account's "regular key". */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttREGULAR_KEY_SET, - 5, - SetRegularKey, +TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, Delegation::notDelegable, uint256{}, noPriv, ({ - {sfRegularKey, soeOPTIONAL}, - })) + {sfRegularKey, soeOPTIONAL}, +})) // 6 deprecated /** This transaction type creates an offer to trade one asset for another. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttOFFER_CREATE, - 7, - OfferCreate, +TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegable, uint256{}, noPriv, ({ - {sfTakerPays, soeREQUIRED}, - {sfTakerGets, soeREQUIRED}, - {sfExpiration, soeOPTIONAL}, - {sfOfferSequence, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - })) + {sfTakerPays, soeREQUIRED}, + {sfTakerGets, soeREQUIRED}, + {sfExpiration, soeOPTIONAL}, + {sfOfferSequence, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, +})) /** This transaction type cancels existing offers to trade one asset for another. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttOFFER_CANCEL, - 8, - OfferCancel, +TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, Delegation::delegable, uint256{}, noPriv, ({ - {sfOfferSequence, soeREQUIRED}, - })) + {sfOfferSequence, soeREQUIRED}, +})) // 9 deprecated /** This transaction type creates a new set of tickets. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttTICKET_CREATE, - 10, - TicketCreate, +TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegable, uint256{}, noPriv, ({ - {sfTicketCount, soeREQUIRED}, - })) + {sfTicketCount, soeREQUIRED}, +})) // 11 deprecated @@ -196,1192 +170,987 @@ TRANSACTION( // The SignerEntries are optional because a SignerList is deleted by // setting the SignerQuorum to zero and omitting SignerEntries. #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttSIGNER_LIST_SET, - 12, - SignerListSet, +TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, Delegation::notDelegable, uint256{}, noPriv, ({ - {sfSignerQuorum, soeREQUIRED}, - {sfSignerEntries, soeOPTIONAL}, - })) + {sfSignerQuorum, soeREQUIRED}, + {sfSignerEntries, soeOPTIONAL}, +})) /** This transaction type creates a new unidirectional XRP payment channel. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPAYCHAN_CREATE, - 13, - PaymentChannelCreate, +TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegable, uint256{}, noPriv, ({ - {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfSettleDelay, soeREQUIRED}, - {sfPublicKey, soeREQUIRED}, - {sfCancelAfter, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfSettleDelay, soeREQUIRED}, + {sfPublicKey, soeREQUIRED}, + {sfCancelAfter, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, +})) /** This transaction type funds an existing unidirectional XRP payment channel. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPAYCHAN_FUND, - 14, - PaymentChannelFund, +TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, Delegation::delegable, uint256{}, noPriv, ({ - {sfChannel, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfExpiration, soeOPTIONAL}, - })) + {sfChannel, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfExpiration, soeOPTIONAL}, +})) /** This transaction type submits a claim against an existing unidirectional payment channel. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPAYCHAN_CLAIM, - 15, - PaymentChannelClaim, +TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegable, uint256{}, noPriv, ({ - {sfChannel, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - {sfBalance, soeOPTIONAL}, - {sfSignature, soeOPTIONAL}, - {sfPublicKey, soeOPTIONAL}, - {sfCredentialIDs, soeOPTIONAL}, - })) + {sfChannel, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, + {sfBalance, soeOPTIONAL}, + {sfSignature, soeOPTIONAL}, + {sfPublicKey, soeOPTIONAL}, + {sfCredentialIDs, soeOPTIONAL}, +})) /** This transaction type creates a new check. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCHECK_CREATE, - 16, - CheckCreate, +TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegable, uint256{}, noPriv, ({ - {sfDestination, soeREQUIRED}, - {sfSendMax, soeREQUIRED}, - {sfExpiration, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - {sfInvoiceID, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfSendMax, soeREQUIRED}, + {sfExpiration, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, + {sfInvoiceID, soeOPTIONAL}, +})) /** This transaction type cashes an existing check. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCHECK_CASH, - 17, - CheckCash, +TRANSACTION(ttCHECK_CASH, 17, CheckCash, Delegation::delegable, uint256{}, noPriv, ({ - {sfCheckID, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - {sfDeliverMin, soeOPTIONAL}, - })) + {sfCheckID, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, + {sfDeliverMin, soeOPTIONAL}, +})) /** This transaction type cancels an existing check. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCHECK_CANCEL, - 18, - CheckCancel, +TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, Delegation::delegable, uint256{}, noPriv, ({ - {sfCheckID, soeREQUIRED}, - })) + {sfCheckID, soeREQUIRED}, +})) /** This transaction type grants or revokes authorization to transfer funds. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttDEPOSIT_PREAUTH, - 19, - DepositPreauth, +TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, Delegation::delegable, uint256{}, noPriv, ({ - {sfAuthorize, soeOPTIONAL}, - {sfUnauthorize, soeOPTIONAL}, - {sfAuthorizeCredentials, soeOPTIONAL}, - {sfUnauthorizeCredentials, soeOPTIONAL}, - })) + {sfAuthorize, soeOPTIONAL}, + {sfUnauthorize, soeOPTIONAL}, + {sfAuthorizeCredentials, soeOPTIONAL}, + {sfUnauthorizeCredentials, soeOPTIONAL}, +})) /** This transaction type modifies a trustline between two accounts. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttTRUST_SET, - 20, - TrustSet, +TRANSACTION(ttTRUST_SET, 20, TrustSet, Delegation::delegable, uint256{}, noPriv, ({ - {sfLimitAmount, soeOPTIONAL}, - {sfQualityIn, soeOPTIONAL}, - {sfQualityOut, soeOPTIONAL}, - })) + {sfLimitAmount, soeOPTIONAL}, + {sfQualityIn, soeOPTIONAL}, + {sfQualityOut, soeOPTIONAL}, +})) /** This transaction type deletes an existing account. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttACCOUNT_DELETE, - 21, - AccountDelete, +TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegable, uint256{}, mustDeleteAcct, ({ - {sfDestination, soeREQUIRED}, - {sfDestinationTag, soeOPTIONAL}, - {sfCredentialIDs, soeOPTIONAL}, - })) + {sfDestination, soeREQUIRED}, + {sfDestinationTag, soeOPTIONAL}, + {sfCredentialIDs, soeOPTIONAL}, +})) // 22 reserved /** This transaction mints a new NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_MINT, - 25, - NFTokenMint, +TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, Delegation::delegable, uint256{}, changeNFTCounts, ({ - {sfNFTokenTaxon, soeREQUIRED}, - {sfTransferFee, soeOPTIONAL}, - {sfIssuer, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - {sfAmount, soeOPTIONAL}, - {sfDestination, soeOPTIONAL}, - {sfExpiration, soeOPTIONAL}, - })) + {sfNFTokenTaxon, soeREQUIRED}, + {sfTransferFee, soeOPTIONAL}, + {sfIssuer, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, + {sfAmount, soeOPTIONAL}, + {sfDestination, soeOPTIONAL}, + {sfExpiration, soeOPTIONAL}, +})) /** This transaction burns (i.e. destroys) an existing NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_BURN, - 26, - NFTokenBurn, +TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, Delegation::delegable, uint256{}, changeNFTCounts, ({ - {sfNFTokenID, soeREQUIRED}, - {sfOwner, soeOPTIONAL}, - })) + {sfNFTokenID, soeREQUIRED}, + {sfOwner, soeOPTIONAL}, +})) /** This transaction creates a new offer to buy or sell an NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_CREATE_OFFER, - 27, - NFTokenCreateOffer, +TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, Delegation::delegable, uint256{}, noPriv, ({ - {sfNFTokenID, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfDestination, soeOPTIONAL}, - {sfOwner, soeOPTIONAL}, - {sfExpiration, soeOPTIONAL}, - })) + {sfNFTokenID, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfDestination, soeOPTIONAL}, + {sfOwner, soeOPTIONAL}, + {sfExpiration, soeOPTIONAL}, +})) /** This transaction cancels an existing offer to buy or sell an existing NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_CANCEL_OFFER, - 28, - NFTokenCancelOffer, +TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, Delegation::delegable, uint256{}, noPriv, ({ - {sfNFTokenOffers, soeREQUIRED}, - })) + {sfNFTokenOffers, soeREQUIRED}, +})) /** This transaction accepts an existing offer to buy or sell an existing NFT. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_ACCEPT_OFFER, - 29, - NFTokenAcceptOffer, +TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, Delegation::delegable, uint256{}, noPriv, ({ - {sfNFTokenBuyOffer, soeOPTIONAL}, - {sfNFTokenSellOffer, soeOPTIONAL}, - {sfNFTokenBrokerFee, soeOPTIONAL}, - })) + {sfNFTokenBuyOffer, soeOPTIONAL}, + {sfNFTokenSellOffer, soeOPTIONAL}, + {sfNFTokenBrokerFee, soeOPTIONAL}, +})) /** This transaction claws back issued tokens. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCLAWBACK, - 30, - Clawback, +TRANSACTION(ttCLAWBACK, 30, Clawback, Delegation::delegable, featureClawback, noPriv, ({ - {sfAmount, soeREQUIRED, soeMPTSupported}, - {sfHolder, soeOPTIONAL}, - })) + {sfAmount, soeREQUIRED, soeMPTSupported}, + {sfHolder, soeOPTIONAL}, +})) /** This transaction claws back tokens from an AMM pool. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_CLAWBACK, - 31, - AMMClawback, +TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, Delegation::delegable, featureAMMClawback, mayDeleteAcct | overrideFreeze, ({ - {sfHolder, soeREQUIRED}, - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - })) + {sfHolder, soeREQUIRED}, + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, +})) /** This transaction type creates an AMM instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_CREATE, - 35, - AMMCreate, +TRANSACTION(ttAMM_CREATE, 35, AMMCreate, Delegation::delegable, featureAMM, createPseudoAcct, ({ - {sfAmount, soeREQUIRED}, - {sfAmount2, soeREQUIRED}, - {sfTradingFee, soeREQUIRED}, - })) + {sfAmount, soeREQUIRED}, + {sfAmount2, soeREQUIRED}, + {sfTradingFee, soeREQUIRED}, +})) /** This transaction type deposits into an AMM instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_DEPOSIT, - 36, - AMMDeposit, +TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, Delegation::delegable, featureAMM, noPriv, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - {sfAmount2, soeOPTIONAL}, - {sfEPrice, soeOPTIONAL}, - {sfLPTokenOut, soeOPTIONAL}, - {sfTradingFee, soeOPTIONAL}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, + {sfAmount2, soeOPTIONAL}, + {sfEPrice, soeOPTIONAL}, + {sfLPTokenOut, soeOPTIONAL}, + {sfTradingFee, soeOPTIONAL}, +})) /** This transaction type withdraws from an AMM instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_WITHDRAW, - 37, - AMMWithdraw, +TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, Delegation::delegable, featureAMM, mayDeleteAcct, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfAmount, soeOPTIONAL}, - {sfAmount2, soeOPTIONAL}, - {sfEPrice, soeOPTIONAL}, - {sfLPTokenIn, soeOPTIONAL}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfAmount, soeOPTIONAL}, + {sfAmount2, soeOPTIONAL}, + {sfEPrice, soeOPTIONAL}, + {sfLPTokenIn, soeOPTIONAL}, +})) /** This transaction type votes for the trading fee */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_VOTE, - 38, - AMMVote, +TRANSACTION(ttAMM_VOTE, 38, AMMVote, Delegation::delegable, featureAMM, noPriv, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfTradingFee, soeREQUIRED}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfTradingFee, soeREQUIRED}, +})) /** This transaction type bids for the auction slot */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_BID, - 39, - AMMBid, +TRANSACTION(ttAMM_BID, 39, AMMBid, Delegation::delegable, featureAMM, noPriv, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - {sfBidMin, soeOPTIONAL}, - {sfBidMax, soeOPTIONAL}, - {sfAuthAccounts, soeOPTIONAL}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, + {sfBidMin, soeOPTIONAL}, + {sfBidMax, soeOPTIONAL}, + {sfAuthAccounts, soeOPTIONAL}, +})) /** This transaction type deletes AMM in the empty state */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMM_DELETE, - 40, - AMMDelete, +TRANSACTION(ttAMM_DELETE, 40, AMMDelete, Delegation::delegable, featureAMM, mustDeleteAcct, ({ - {sfAsset, soeREQUIRED}, - {sfAsset2, soeREQUIRED}, - })) + {sfAsset, soeREQUIRED}, + {sfAsset2, soeREQUIRED}, +})) /** This transactions creates a crosschain sequence number */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttXCHAIN_CREATE_CLAIM_ID, - 41, - XChainCreateClaimID, +TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID, Delegation::delegable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfSignatureReward, soeREQUIRED}, - {sfOtherChainSource, soeREQUIRED}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfSignatureReward, soeREQUIRED}, + {sfOtherChainSource, soeREQUIRED}, +})) /** This transactions initiates a crosschain transaction */ -TRANSACTION( - ttXCHAIN_COMMIT, - 42, - XChainCommit, +TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, Delegation::delegable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfXChainClaimID, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfOtherChainDestination, soeOPTIONAL}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfXChainClaimID, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfOtherChainDestination, soeOPTIONAL}, +})) /** This transaction completes a crosschain transaction */ -TRANSACTION( - ttXCHAIN_CLAIM, - 43, - XChainClaim, +TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, Delegation::delegable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfXChainClaimID, soeREQUIRED}, - {sfDestination, soeREQUIRED}, - {sfDestinationTag, soeOPTIONAL}, - {sfAmount, soeREQUIRED}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfXChainClaimID, soeREQUIRED}, + {sfDestination, soeREQUIRED}, + {sfDestinationTag, soeOPTIONAL}, + {sfAmount, soeREQUIRED}, +})) /** This transaction initiates a crosschain account create transaction */ -TRANSACTION( - ttXCHAIN_ACCOUNT_CREATE_COMMIT, - 44, - XChainAccountCreateCommit, +TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, Delegation::delegable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfDestination, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfSignatureReward, soeREQUIRED}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfSignatureReward, soeREQUIRED}, +})) /** This transaction adds an attestation to a claim */ -TRANSACTION( - ttXCHAIN_ADD_CLAIM_ATTESTATION, - 45, - XChainAddClaimAttestation, +TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, Delegation::delegable, featureXChainBridge, createAcct, ({ - {sfXChainBridge, soeREQUIRED}, + {sfXChainBridge, soeREQUIRED}, - {sfAttestationSignerAccount, soeREQUIRED}, - {sfPublicKey, soeREQUIRED}, - {sfSignature, soeREQUIRED}, - {sfOtherChainSource, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfAttestationRewardAccount, soeREQUIRED}, - {sfWasLockingChainSend, soeREQUIRED}, + {sfAttestationSignerAccount, soeREQUIRED}, + {sfPublicKey, soeREQUIRED}, + {sfSignature, soeREQUIRED}, + {sfOtherChainSource, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfAttestationRewardAccount, soeREQUIRED}, + {sfWasLockingChainSend, soeREQUIRED}, - {sfXChainClaimID, soeREQUIRED}, - {sfDestination, soeOPTIONAL}, - })) + {sfXChainClaimID, soeREQUIRED}, + {sfDestination, soeOPTIONAL}, +})) /** This transaction adds an attestation to an account */ -TRANSACTION( - ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, - 46, +TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateAttestation, Delegation::delegable, featureXChainBridge, createAcct, ({ - {sfXChainBridge, soeREQUIRED}, + {sfXChainBridge, soeREQUIRED}, - {sfAttestationSignerAccount, soeREQUIRED}, - {sfPublicKey, soeREQUIRED}, - {sfSignature, soeREQUIRED}, - {sfOtherChainSource, soeREQUIRED}, - {sfAmount, soeREQUIRED}, - {sfAttestationRewardAccount, soeREQUIRED}, - {sfWasLockingChainSend, soeREQUIRED}, + {sfAttestationSignerAccount, soeREQUIRED}, + {sfPublicKey, soeREQUIRED}, + {sfSignature, soeREQUIRED}, + {sfOtherChainSource, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + {sfAttestationRewardAccount, soeREQUIRED}, + {sfWasLockingChainSend, soeREQUIRED}, - {sfXChainAccountCreateCount, soeREQUIRED}, - {sfDestination, soeREQUIRED}, - {sfSignatureReward, soeREQUIRED}, - })) + {sfXChainAccountCreateCount, soeREQUIRED}, + {sfDestination, soeREQUIRED}, + {sfSignatureReward, soeREQUIRED}, +})) /** This transaction modifies a sidechain */ -TRANSACTION( - ttXCHAIN_MODIFY_BRIDGE, - 47, - XChainModifyBridge, +TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge, Delegation::delegable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfSignatureReward, soeOPTIONAL}, - {sfMinAccountCreateAmount, soeOPTIONAL}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfSignatureReward, soeOPTIONAL}, + {sfMinAccountCreateAmount, soeOPTIONAL}, +})) /** This transactions creates a sidechain */ -TRANSACTION( - ttXCHAIN_CREATE_BRIDGE, - 48, - XChainCreateBridge, +TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, Delegation::delegable, featureXChainBridge, noPriv, ({ - {sfXChainBridge, soeREQUIRED}, - {sfSignatureReward, soeREQUIRED}, - {sfMinAccountCreateAmount, soeOPTIONAL}, - })) + {sfXChainBridge, soeREQUIRED}, + {sfSignatureReward, soeREQUIRED}, + {sfMinAccountCreateAmount, soeOPTIONAL}, +})) /** This transaction type creates or updates a DID */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttDID_SET, - 49, - DIDSet, +TRANSACTION(ttDID_SET, 49, DIDSet, Delegation::delegable, featureDID, noPriv, ({ - {sfDIDDocument, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - {sfData, soeOPTIONAL}, - })) + {sfDIDDocument, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, + {sfData, soeOPTIONAL}, +})) /** This transaction type deletes a DID */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION(ttDID_DELETE, 50, DIDDelete, Delegation::delegable, featureDID, noPriv, ({})) +TRANSACTION(ttDID_DELETE, 50, DIDDelete, + Delegation::delegable, + featureDID, + noPriv, + ({})) /** This transaction type creates an Oracle instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttORACLE_SET, - 51, - OracleSet, +TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegable, featurePriceOracle, noPriv, ({ - {sfOracleDocumentID, soeREQUIRED}, - {sfProvider, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - {sfAssetClass, soeOPTIONAL}, - {sfLastUpdateTime, soeREQUIRED}, - {sfPriceDataSeries, soeREQUIRED}, - })) + {sfOracleDocumentID, soeREQUIRED}, + {sfProvider, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, + {sfAssetClass, soeOPTIONAL}, + {sfLastUpdateTime, soeREQUIRED}, + {sfPriceDataSeries, soeREQUIRED}, +})) /** This transaction type deletes an Oracle instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttORACLE_DELETE, - 52, - OracleDelete, +TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, Delegation::delegable, featurePriceOracle, noPriv, ({ - {sfOracleDocumentID, soeREQUIRED}, - })) + {sfOracleDocumentID, soeREQUIRED}, +})) /** This transaction type fixes a problem in the ledger state */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLEDGER_STATE_FIX, - 53, - LedgerStateFix, +TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, Delegation::delegable, fixNFTokenPageLinks, noPriv, ({ - {sfLedgerFixType, soeREQUIRED}, - {sfOwner, soeOPTIONAL}, - })) + {sfLedgerFixType, soeREQUIRED}, + {sfOwner, soeOPTIONAL}, +})) /** This transaction type creates a MPTokensIssuance instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttMPTOKEN_ISSUANCE_CREATE, - 54, - MPTokenIssuanceCreate, +TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, Delegation::delegable, featureMPTokensV1, createMPTIssuance, ({ - {sfAssetScale, soeOPTIONAL}, - {sfTransferFee, soeOPTIONAL}, - {sfMaximumAmount, soeOPTIONAL}, - {sfMPTokenMetadata, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - {sfMutableFlags, soeOPTIONAL}, - })) + {sfAssetScale, soeOPTIONAL}, + {sfTransferFee, soeOPTIONAL}, + {sfMaximumAmount, soeOPTIONAL}, + {sfMPTokenMetadata, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, + {sfMutableFlags, soeOPTIONAL}, +})) /** This transaction type destroys a MPTokensIssuance instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttMPTOKEN_ISSUANCE_DESTROY, - 55, - MPTokenIssuanceDestroy, +TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, Delegation::delegable, featureMPTokensV1, destroyMPTIssuance, ({ - {sfMPTokenIssuanceID, soeREQUIRED}, - })) + {sfMPTokenIssuanceID, soeREQUIRED}, +})) /** This transaction type sets flags on a MPTokensIssuance or MPToken instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttMPTOKEN_ISSUANCE_SET, - 56, - MPTokenIssuanceSet, +TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, Delegation::delegable, featureMPTokensV1, noPriv, ({ - {sfMPTokenIssuanceID, soeREQUIRED}, - {sfHolder, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - {sfMPTokenMetadata, soeOPTIONAL}, - {sfTransferFee, soeOPTIONAL}, - {sfMutableFlags, soeOPTIONAL}, - })) + {sfMPTokenIssuanceID, soeREQUIRED}, + {sfHolder, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, + {sfMPTokenMetadata, soeOPTIONAL}, + {sfTransferFee, soeOPTIONAL}, + {sfMutableFlags, soeOPTIONAL}, +})) /** This transaction type authorizes a MPToken instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttMPTOKEN_AUTHORIZE, - 57, - MPTokenAuthorize, +TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, Delegation::delegable, featureMPTokensV1, mustAuthorizeMPT, ({ - {sfMPTokenIssuanceID, soeREQUIRED}, - {sfHolder, soeOPTIONAL}, - })) + {sfMPTokenIssuanceID, soeREQUIRED}, + {sfHolder, soeOPTIONAL}, +})) /** This transaction type create an Credential instance */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCREDENTIAL_CREATE, - 58, - CredentialCreate, +TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegable, featureCredentials, noPriv, ({ - {sfSubject, soeREQUIRED}, - {sfCredentialType, soeREQUIRED}, - {sfExpiration, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - })) + {sfSubject, soeREQUIRED}, + {sfCredentialType, soeREQUIRED}, + {sfExpiration, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, +})) /** This transaction type accept an Credential object */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCREDENTIAL_ACCEPT, - 59, - CredentialAccept, +TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, Delegation::delegable, featureCredentials, noPriv, ({ - {sfIssuer, soeREQUIRED}, - {sfCredentialType, soeREQUIRED}, - })) + {sfIssuer, soeREQUIRED}, + {sfCredentialType, soeREQUIRED}, +})) /** This transaction type delete an Credential object */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttCREDENTIAL_DELETE, - 60, - CredentialDelete, +TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, Delegation::delegable, featureCredentials, noPriv, ({ - {sfSubject, soeOPTIONAL}, - {sfIssuer, soeOPTIONAL}, - {sfCredentialType, soeREQUIRED}, - })) + {sfSubject, soeOPTIONAL}, + {sfIssuer, soeOPTIONAL}, + {sfCredentialType, soeREQUIRED}, +})) /** This transaction type modify a NFToken */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttNFTOKEN_MODIFY, - 61, - NFTokenModify, +TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, Delegation::delegable, featureDynamicNFT, noPriv, ({ - {sfNFTokenID, soeREQUIRED}, - {sfOwner, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - })) + {sfNFTokenID, soeREQUIRED}, + {sfOwner, soeOPTIONAL}, + {sfURI, soeOPTIONAL}, +})) /** This transaction type creates or modifies a Permissioned Domain */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPERMISSIONED_DOMAIN_SET, - 62, - PermissionedDomainSet, +TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, Delegation::delegable, featurePermissionedDomains, noPriv, ({ - {sfDomainID, soeOPTIONAL}, - {sfAcceptedCredentials, soeREQUIRED}, - })) + {sfDomainID, soeOPTIONAL}, + {sfAcceptedCredentials, soeREQUIRED}, +})) /** This transaction type deletes a Permissioned Domain */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttPERMISSIONED_DOMAIN_DELETE, - 63, - PermissionedDomainDelete, +TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, Delegation::delegable, featurePermissionedDomains, noPriv, ({ - {sfDomainID, soeREQUIRED}, - })) + {sfDomainID, soeREQUIRED}, +})) /** This transaction type delegates authorized account specified permissions */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttDELEGATE_SET, - 64, - DelegateSet, +TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, Delegation::notDelegable, featurePermissionDelegationV1_1, noPriv, ({ - {sfAuthorize, soeREQUIRED}, - {sfPermissions, soeREQUIRED}, - })) + {sfAuthorize, soeREQUIRED}, + {sfPermissions, soeREQUIRED}, +})) /** This transaction creates a single asset vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_CREATE, - 65, - VaultCreate, +TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, Delegation::notDelegable, featureSingleAssetVault, createPseudoAcct | createMPTIssuance | mustModifyVault, ({ - {sfAsset, soeREQUIRED, soeMPTSupported}, - {sfAssetsMaximum, soeOPTIONAL}, - {sfMPTokenMetadata, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - {sfWithdrawalPolicy, soeOPTIONAL}, - {sfData, soeOPTIONAL}, - {sfScale, soeOPTIONAL}, - })) + {sfAsset, soeREQUIRED, soeMPTSupported}, + {sfAssetsMaximum, soeOPTIONAL}, + {sfMPTokenMetadata, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, + {sfWithdrawalPolicy, soeOPTIONAL}, + {sfData, soeOPTIONAL}, + {sfScale, soeOPTIONAL}, +})) /** This transaction updates a single asset vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_SET, - 66, - VaultSet, +TRANSACTION(ttVAULT_SET, 66, VaultSet, Delegation::notDelegable, featureSingleAssetVault, mustModifyVault, ({ - {sfVaultID, soeREQUIRED}, - {sfAssetsMaximum, soeOPTIONAL}, - {sfDomainID, soeOPTIONAL}, - {sfData, soeOPTIONAL}, - })) + {sfVaultID, soeREQUIRED}, + {sfAssetsMaximum, soeOPTIONAL}, + {sfDomainID, soeOPTIONAL}, + {sfData, soeOPTIONAL}, +})) /** This transaction deletes a single asset vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_DELETE, - 67, - VaultDelete, +TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, Delegation::notDelegable, featureSingleAssetVault, mustDeleteAcct | destroyMPTIssuance | mustModifyVault, ({ - {sfVaultID, soeREQUIRED}, - })) + {sfVaultID, soeREQUIRED}, +})) /** This transaction trades assets for shares with a vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_DEPOSIT, - 68, - VaultDeposit, +TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, Delegation::notDelegable, featureSingleAssetVault, mayAuthorizeMPT | mustModifyVault, ({ - {sfVaultID, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - })) + {sfVaultID, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, +})) /** This transaction trades shares for assets with a vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_WITHDRAW, - 69, - VaultWithdraw, +TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, Delegation::notDelegable, featureSingleAssetVault, mayDeleteMPT | mayAuthorizeMPT | mustModifyVault, ({ - {sfVaultID, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - {sfDestination, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - })) + {sfVaultID, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, + {sfDestination, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, +})) /** This transaction claws back tokens from a vault. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttVAULT_CLAWBACK, - 70, - VaultClawback, +TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, Delegation::notDelegable, featureSingleAssetVault, mayDeleteMPT | mustModifyVault, ({ - {sfVaultID, soeREQUIRED}, - {sfHolder, soeREQUIRED}, - {sfAmount, soeOPTIONAL, soeMPTSupported}, - })) + {sfVaultID, soeREQUIRED}, + {sfHolder, soeREQUIRED}, + {sfAmount, soeOPTIONAL, soeMPTSupported}, +})) /** This transaction type batches together transactions. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttBATCH, - 71, - Batch, +TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegable, featureBatch, noPriv, ({ - {sfRawTransactions, soeREQUIRED}, - {sfBatchSigners, soeOPTIONAL}, - })) + {sfRawTransactions, soeREQUIRED}, + {sfBatchSigners, soeOPTIONAL}, +})) /** Reserve 72-73 for future Vault-related transactions */ /** This transaction creates and updates a Loan Broker */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLOAN_BROKER_SET, - 74, - LoanBrokerSet, +TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet, Delegation::notDelegable, featureLendingProtocol, - createPseudoAcct | mayAuthorizeMPT, - ({ - {sfVaultID, soeREQUIRED}, - {sfLoanBrokerID, soeOPTIONAL}, - {sfData, soeOPTIONAL}, - {sfManagementFeeRate, soeOPTIONAL}, - {sfDebtMaximum, soeOPTIONAL}, - {sfCoverRateMinimum, soeOPTIONAL}, - {sfCoverRateLiquidation, soeOPTIONAL}, - })) + createPseudoAcct | mayAuthorizeMPT, ({ + {sfVaultID, soeREQUIRED}, + {sfLoanBrokerID, soeOPTIONAL}, + {sfData, soeOPTIONAL}, + {sfManagementFeeRate, soeOPTIONAL}, + {sfDebtMaximum, soeOPTIONAL}, + {sfCoverRateMinimum, soeOPTIONAL}, + {sfCoverRateLiquidation, soeOPTIONAL}, +})) /** This transaction deletes a Loan Broker */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLOAN_BROKER_DELETE, - 75, - LoanBrokerDelete, +TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete, Delegation::notDelegable, featureLendingProtocol, - mustDeleteAcct | mayAuthorizeMPT, - ({ - {sfLoanBrokerID, soeREQUIRED}, - })) + mustDeleteAcct | mayAuthorizeMPT, ({ + {sfLoanBrokerID, soeREQUIRED}, +})) /** This transaction deposits First Loss Capital into a Loan Broker */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLOAN_BROKER_COVER_DEPOSIT, - 76, - LoanBrokerCoverDeposit, +TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, Delegation::notDelegable, featureLendingProtocol, - noPriv, - ({ - {sfLoanBrokerID, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - })) + noPriv, ({ + {sfLoanBrokerID, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, +})) /** This transaction withdraws First Loss Capital from a Loan Broker */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLOAN_BROKER_COVER_WITHDRAW, - 77, - LoanBrokerCoverWithdraw, +TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, Delegation::notDelegable, featureLendingProtocol, - mayAuthorizeMPT, - ({ - {sfLoanBrokerID, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - {sfDestination, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, - })) + mayAuthorizeMPT, ({ + {sfLoanBrokerID, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, + {sfDestination, soeOPTIONAL}, + {sfDestinationTag, soeOPTIONAL}, +})) /** This transaction claws back First Loss Capital from a Loan Broker to the issuer of the capital */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLOAN_BROKER_COVER_CLAWBACK, - 78, - LoanBrokerCoverClawback, +TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback, Delegation::notDelegable, featureLendingProtocol, - noPriv, - ({ - {sfLoanBrokerID, soeOPTIONAL}, - {sfAmount, soeOPTIONAL, soeMPTSupported}, - })) + noPriv, ({ + {sfLoanBrokerID, soeOPTIONAL}, + {sfAmount, soeOPTIONAL, soeMPTSupported}, +})) /** This transaction creates a Loan */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLOAN_SET, - 80, - LoanSet, +TRANSACTION(ttLOAN_SET, 80, LoanSet, Delegation::notDelegable, featureLendingProtocol, - mayAuthorizeMPT | mustModifyVault, - ({ - {sfLoanBrokerID, soeREQUIRED}, - {sfData, soeOPTIONAL}, - {sfCounterparty, soeOPTIONAL}, - {sfCounterpartySignature, soeOPTIONAL}, - {sfLoanOriginationFee, soeOPTIONAL}, - {sfLoanServiceFee, soeOPTIONAL}, - {sfLatePaymentFee, soeOPTIONAL}, - {sfClosePaymentFee, soeOPTIONAL}, - {sfOverpaymentFee, soeOPTIONAL}, - {sfInterestRate, soeOPTIONAL}, - {sfLateInterestRate, soeOPTIONAL}, - {sfCloseInterestRate, soeOPTIONAL}, - {sfOverpaymentInterestRate, soeOPTIONAL}, - {sfPrincipalRequested, soeREQUIRED}, - {sfPaymentTotal, soeOPTIONAL}, - {sfPaymentInterval, soeOPTIONAL}, - {sfGracePeriod, soeOPTIONAL}, - })) + mayAuthorizeMPT | mustModifyVault, ({ + {sfLoanBrokerID, soeREQUIRED}, + {sfData, soeOPTIONAL}, + {sfCounterparty, soeOPTIONAL}, + {sfCounterpartySignature, soeOPTIONAL}, + {sfLoanOriginationFee, soeOPTIONAL}, + {sfLoanServiceFee, soeOPTIONAL}, + {sfLatePaymentFee, soeOPTIONAL}, + {sfClosePaymentFee, soeOPTIONAL}, + {sfOverpaymentFee, soeOPTIONAL}, + {sfInterestRate, soeOPTIONAL}, + {sfLateInterestRate, soeOPTIONAL}, + {sfCloseInterestRate, soeOPTIONAL}, + {sfOverpaymentInterestRate, soeOPTIONAL}, + {sfPrincipalRequested, soeREQUIRED}, + {sfPaymentTotal, soeOPTIONAL}, + {sfPaymentInterval, soeOPTIONAL}, + {sfGracePeriod, soeOPTIONAL}, +})) /** This transaction deletes an existing Loan */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLOAN_DELETE, - 81, - LoanDelete, +TRANSACTION(ttLOAN_DELETE, 81, LoanDelete, Delegation::notDelegable, featureLendingProtocol, - noPriv, - ({ - {sfLoanID, soeREQUIRED}, - })) + noPriv, ({ + {sfLoanID, soeREQUIRED}, +})) /** This transaction is used to change the delinquency status of an existing Loan */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLOAN_MANAGE, - 82, - LoanManage, +TRANSACTION(ttLOAN_MANAGE, 82, LoanManage, Delegation::notDelegable, featureLendingProtocol, // All of the LoanManage options will modify the vault, but the // transaction can succeed without options, essentially making it // a noop. - mayModifyVault, - ({ - {sfLoanID, soeREQUIRED}, - })) + mayModifyVault, ({ + {sfLoanID, soeREQUIRED}, +})) /** The Borrower uses this transaction to make a Payment on the Loan. */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttLOAN_PAY, - 84, - LoanPay, +TRANSACTION(ttLOAN_PAY, 84, LoanPay, Delegation::notDelegable, featureLendingProtocol, - mayAuthorizeMPT | mustModifyVault, - ({ - {sfLoanID, soeREQUIRED}, - {sfAmount, soeREQUIRED, soeMPTSupported}, - })) + mayAuthorizeMPT | mustModifyVault, ({ + {sfLoanID, soeREQUIRED}, + {sfAmount, soeREQUIRED, soeMPTSupported}, +})) /** This transaction transfer sponsorship */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttSPONSORSHIP_TRANSFER, - 85, - SponsorshipTransfer, +TRANSACTION(ttSPONSORSHIP_TRANSFER, 85, SponsorshipTransfer, Delegation::delegable, featureSponsor, noPriv, ({ - {sfObjectID, soeOPTIONAL}, - {sfSponsee, soeOPTIONAL}, - })) + {sfObjectID, soeOPTIONAL}, + {sfSponsee, soeOPTIONAL}, +})) /** This transaction create sponsorship object */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttSPONSORSHIP_SET, - 86, - SponsorshipSet, +TRANSACTION(ttSPONSORSHIP_SET, 86, SponsorshipSet, Delegation::delegable, featureSponsor, noPriv, ({ - {sfCounterpartySponsor, soeOPTIONAL}, - {sfSponsee, soeOPTIONAL}, - {sfFeeAmount, soeOPTIONAL}, - {sfMaxFee, soeOPTIONAL}, - {sfReserveCount, soeOPTIONAL}, - })) + {sfCounterpartySponsor, soeOPTIONAL}, + {sfSponsee, soeOPTIONAL}, + {sfFeeAmount, soeOPTIONAL}, + {sfMaxFee, soeOPTIONAL}, + {sfReserveCount, soeOPTIONAL}, +})) /** This system-generated transaction type is used to update the status of the various amendments. For details, see: https://xrpl.org/amendments.html */ #if TRANSACTION_INCLUDE -#include +# include #endif -TRANSACTION( - ttAMENDMENT, - 100, - EnableAmendment, +TRANSACTION(ttAMENDMENT, 100, EnableAmendment, Delegation::notDelegable, uint256{}, noPriv, ({ - {sfLedgerSequence, soeREQUIRED}, - {sfAmendment, soeREQUIRED}, - })) + {sfLedgerSequence, soeREQUIRED}, + {sfAmendment, soeREQUIRED}, +})) /** This system-generated transaction type is used to update the network's fee settings. For details, see: https://xrpl.org/fee-voting.html */ -TRANSACTION( - ttFEE, - 101, - SetFee, +TRANSACTION(ttFEE, 101, SetFee, Delegation::notDelegable, uint256{}, noPriv, ({ - {sfLedgerSequence, soeOPTIONAL}, - // Old version uses raw numbers - {sfBaseFee, soeOPTIONAL}, - {sfReferenceFeeUnits, soeOPTIONAL}, - {sfReserveBase, soeOPTIONAL}, - {sfReserveIncrement, soeOPTIONAL}, - // New version uses Amounts - {sfBaseFeeDrops, soeOPTIONAL}, - {sfReserveBaseDrops, soeOPTIONAL}, - {sfReserveIncrementDrops, soeOPTIONAL}, - })) + {sfLedgerSequence, soeOPTIONAL}, + // Old version uses raw numbers + {sfBaseFee, soeOPTIONAL}, + {sfReferenceFeeUnits, soeOPTIONAL}, + {sfReserveBase, soeOPTIONAL}, + {sfReserveIncrement, soeOPTIONAL}, + // New version uses Amounts + {sfBaseFeeDrops, soeOPTIONAL}, + {sfReserveBaseDrops, soeOPTIONAL}, + {sfReserveIncrementDrops, soeOPTIONAL}, +})) /** This system-generated transaction type is used to update the network's negative UNL For details, see: https://xrpl.org/negative-unl.html */ -TRANSACTION( - ttUNL_MODIFY, - 102, - UNLModify, +TRANSACTION(ttUNL_MODIFY, 102, UNLModify, Delegation::notDelegable, uint256{}, noPriv, ({ - {sfUNLModifyDisabling, soeREQUIRED}, - {sfLedgerSequence, soeREQUIRED}, - {sfUNLModifyValidator, soeREQUIRED}, - })) + {sfUNLModifyDisabling, soeREQUIRED}, + {sfLedgerSequence, soeREQUIRED}, + {sfUNLModifyValidator, soeREQUIRED}, +})) From b9f1c97518bbf1075d65e39d5fd84fae5a119d56 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 17:43:35 +0900 Subject: [PATCH 145/249] format SetTrust.cpp --- src/libxrpl/tx/transactors/token/SetTrust.cpp | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libxrpl/tx/transactors/token/SetTrust.cpp b/src/libxrpl/tx/transactors/token/SetTrust.cpp index eb6d846cb9c..e04dbdd875f 100644 --- a/src/libxrpl/tx/transactors/token/SetTrust.cpp +++ b/src/libxrpl/tx/transactors/token/SetTrust.cpp @@ -602,9 +602,10 @@ SetTrust::doApply() terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if (auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sle, mPriorBalance, txSponsorSle, 0); - !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) + else if ( + auto const ret = + checkInsufficientReserve(view(), ctx_.tx, sle, mPriorBalance, txSponsorSle, 0); + !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -632,14 +633,15 @@ SetTrust::doApply() JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if (auto const ret = checkInsufficientReserve( - ctx_.view(), - ctx_.tx, - sle, - mPriorBalance, - txSponsorSle, - 1); - !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. + else if ( + auto const ret = checkInsufficientReserve( + ctx_.view(), + ctx_.tx, + sle, + mPriorBalance, + txSponsorSle, + 1); + !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; From 8c8ecffe1fc00ea5307ea93024b07078709e470a Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 18:14:24 +0900 Subject: [PATCH 146/249] remove tx parameter from adjustOwnerCount() --- include/xrpl/ledger/View.h | 14 ------- src/libxrpl/ledger/View.cpp | 2 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 2 +- .../tx/transactors/account/SetSignerList.cpp | 2 +- .../tx/transactors/bridge/XChainBridge.cpp | 6 +-- .../tx/transactors/check/CreateCheck.cpp | 2 +- .../credentials/CredentialAccept.cpp | 2 +- .../credentials/CredentialCreate.cpp | 2 +- .../tx/transactors/delegate/DelegateSet.cpp | 2 +- .../tx/transactors/dex/CreateOffer.cpp | 2 +- src/libxrpl/tx/transactors/did/DIDSet.cpp | 2 +- .../tx/transactors/escrow/EscrowCreate.cpp | 2 +- .../tx/transactors/escrow/EscrowHelpers.h | 2 +- .../tx/transactors/nft/NFTokenUtils.cpp | 3 +- .../tx/transactors/oracle/SetOracle.cpp | 40 +++++++------------ .../tx/transactors/payment/DepositPreauth.cpp | 4 +- .../payment_channel/PayChanCreate.cpp | 2 +- .../PermissionedDomainSet.cpp | 2 +- .../tx/transactors/system/CreateTicket.cpp | 2 +- .../token/MPTokenIssuanceCreate.cpp | 2 +- src/libxrpl/tx/transactors/token/SetTrust.cpp | 28 ++++++------- .../tx/transactors/vault/VaultCreate.cpp | 4 +- 22 files changed, 50 insertions(+), 79 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index b91ecd72ae1..b6b33077183 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -543,19 +543,6 @@ adjustOwnerCount( inline void adjustOwnerCount( ApplyView& view, - STTx const& tx, - std::shared_ptr const& accountSle, - std::shared_ptr const& sponsorSle, - std::int32_t amount, - beast::Journal j) -{ - adjustOwnerCount(view, accountSle, sponsorSle, amount, j); -} - -inline void -adjustOwnerCount( - ApplyView& view, - STTx const& tx, AccountID const& account, std::optional const& sponsor, std::int32_t amount, @@ -563,7 +550,6 @@ adjustOwnerCount( { return adjustOwnerCount( view, - tx, view.peek(keylet::account(account)), sponsor ? view.peek(keylet::account(*sponsor)) : std::shared_ptr(), amount, diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index eaf33d2e424..b1022df1e63 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1769,7 +1769,7 @@ authorizeMPToken( view.insert(mptoken); // Update owner count. - adjustOwnerCount(view, tx, sleAcct, sponsor, 1, journal); + adjustOwnerCount(view, sleAcct, sponsor, 1, journal); addSponsorToLedgerEntry(mptoken, sponsor); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index d5e4c21ea80..dca59ec669f 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -266,7 +266,7 @@ SponsorshipSet::doApply() auto viewJ = ctx_.registry.journal("View"); - adjustOwnerCount(view(), ctx_.tx, sponsorAccSle, reserveSponsorAccSle, 1, viewJ); + adjustOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, viewJ); addSponsorToLedgerEntry(newSle, reserveSponsorAccSle); ctx_.view().insert(newSle); diff --git a/src/libxrpl/tx/transactors/account/SetSignerList.cpp b/src/libxrpl/tx/transactors/account/SetSignerList.cpp index aa4d04dcb15..d30dfc952d7 100644 --- a/src/libxrpl/tx/transactors/account/SetSignerList.cpp +++ b/src/libxrpl/tx/transactors/account/SetSignerList.cpp @@ -328,7 +328,7 @@ SetSignerList::replaceSignerList() // If we succeeded, the new entry counts against the // creator's reserve. - adjustOwnerCount(view(), ctx_.tx, sle, sponsor, addedOwnerCount, viewJ); + adjustOwnerCount(view(), sle, sponsor, addedOwnerCount, viewJ); addSponsorToLedgerEntry(signerList, sponsor); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 8941d56fced..6b4f8866cb7 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -1113,7 +1113,7 @@ applyCreateAccountAttestations( // Reserve was already checked auto const sponsor = getTxReserveSponsor(psb, tx); - adjustOwnerCount(psb, tx, sleDoor, sponsor, 1, j); + adjustOwnerCount(psb, sleDoor, sponsor, 1, j); addSponsorToLedgerEntry(createdSleClaimID, sponsor); psb.insert(createdSleClaimID); psb.update(sleDoor); @@ -1452,7 +1452,7 @@ XChainCreateBridge::doApply() } auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - adjustOwnerCount(ctx_.view(), ctx_.tx, sleAcct, sponsor, 1, ctx_.journal); + adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(sleBridge, sponsor); ctx_.view().insert(sleBridge); @@ -1992,7 +1992,7 @@ XChainCreateClaimID::doApply() } auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - adjustOwnerCount(ctx_.view(), ctx_.tx, sleAcct, sponsor, 1, ctx_.journal); + adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(sleClaimID, sponsor); ctx_.view().insert(sleClaimID); diff --git a/src/libxrpl/tx/transactors/check/CreateCheck.cpp b/src/libxrpl/tx/transactors/check/CreateCheck.cpp index 143fa5e15da..71743625fc4 100644 --- a/src/libxrpl/tx/transactors/check/CreateCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CreateCheck.cpp @@ -194,7 +194,7 @@ CreateCheck::doApply() } // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), ctx_.tx, sle, sponsor, 1, viewJ); + adjustOwnerCount(view(), sle, sponsor, 1, viewJ); addSponsorToLedgerEntry(sleCheck, sponsor); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index e58ed4aaaaa..219990edb64 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -106,7 +106,7 @@ CredentialAccept::doApply() adjustOwnerCount(view(), sleIssuer, currentSponsor, -1, j_); removeSponsorFromLedgerEntry(sleCred); - adjustOwnerCount(view(), ctx_.tx, sleSubject, newSponsor, 1, j_); + adjustOwnerCount(view(), sleSubject, newSponsor, 1, j_); addSponsorToLedgerEntry(sleCred, newSponsor); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index 5bb06e4667f..6cc60f7d983 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -136,7 +136,7 @@ CredentialCreate::doApply() return tecDIR_FULL; sleCred->setFieldU64(sfIssuerNode, *page); - adjustOwnerCount(view(), ctx_.tx, sleIssuer, sponsor, 1, j_); + adjustOwnerCount(view(), sleIssuer, sponsor, 1, j_); addSponsorToLedgerEntry(sleCred, sponsor); } diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 98a0e30edc5..8128e279747 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -89,7 +89,7 @@ DelegateSet::doApply() (*sle)[sfOwnerNode] = *page; ctx_.view().insert(sle); - adjustOwnerCount(ctx_.view(), ctx_.tx, sleOwner, sponsor, 1, ctx_.journal); + adjustOwnerCount(ctx_.view(), sleOwner, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(sle, sponsor); } diff --git a/src/libxrpl/tx/transactors/dex/CreateOffer.cpp b/src/libxrpl/tx/transactors/dex/CreateOffer.cpp index c7f8dee226a..e28adafc4b2 100644 --- a/src/libxrpl/tx/transactors/dex/CreateOffer.cpp +++ b/src/libxrpl/tx/transactors/dex/CreateOffer.cpp @@ -762,7 +762,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel) // Update owner count. auto const sponsor = getTxReserveSponsor(sb, ctx_.tx); - adjustOwnerCount(sb, ctx_.tx, sleCreator, sponsor, 1, viewJ); + adjustOwnerCount(sb, sleCreator, sponsor, 1, viewJ); JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue()) << " : " << to_string(saTakerGets.issue()) diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index 61f1eee92a7..534995b3c24 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -73,7 +73,7 @@ addSLE(ApplyContext& ctx, std::shared_ptr const& sle, AccountID const& owne return tecDIR_FULL; // LCOV_EXCL_LINE (*sle)[sfOwnerNode] = *page; } - adjustOwnerCount(ctx.view(), ctx.tx, sleAccount, sponsor, 1, ctx.journal); + adjustOwnerCount(ctx.view(), sleAccount, sponsor, 1, ctx.journal); addSponsorToLedgerEntry(sle, sponsor); ctx.view().update(sleAccount); diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 6fa4522f473..0edb548a0ec 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -493,7 +493,7 @@ EscrowCreate::doApply() } // increment owner count - adjustOwnerCount(ctx_.view(), ctx_.tx, sle, sponsor, 1, ctx_.journal); + adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(slep, sponsor); ctx_.view().update(sle); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h b/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h index d5a50daf4ec..c71b8882fcf 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h +++ b/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h @@ -197,7 +197,7 @@ escrowUnlockApplyHelper( } // update owner count. - adjustOwnerCount(view, tx, sleDest, sponsor, 1, journal); + adjustOwnerCount(view, sleDest, sponsor, 1, journal); auto mptSle = view.peek(mptKeylet); addSponsorToLedgerEntry(mptSle, sponsor); } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp b/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp index a4090da6b84..45594a0fccf 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenUtils.cpp @@ -275,7 +275,6 @@ insertToken( } adjustOwnerCount( view, - tx, view.peek(keylet::account(owner)), sponsorSle, 1, @@ -1033,7 +1032,7 @@ tokenOfferCreateApply( } // Update owner count. - adjustOwnerCount(view, tx, view.peek(acctKeylet), sponsor, 1, j); + adjustOwnerCount(view, view.peek(acctKeylet), sponsor, 1, j); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/oracle/SetOracle.cpp b/src/libxrpl/tx/transactors/oracle/SetOracle.cpp index d5f305b9687..39c7e42be08 100644 --- a/src/libxrpl/tx/transactors/oracle/SetOracle.cpp +++ b/src/libxrpl/tx/transactors/oracle/SetOracle.cpp @@ -161,21 +161,6 @@ SetOracle::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } -static bool -adjustOwnerCount(ApplyContext& ctx, std::shared_ptr const& sponsor, int count) -{ - if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) - { - if (count > 0) - adjustOwnerCount(ctx.view(), ctx.tx, sleAccount, sponsor, count, ctx.journal); - else - adjustOwnerCount(ctx.view(), sleAccount, sponsor, count, ctx.journal); - return true; - } - - return false; // LCOV_EXCL_LINE -} - static void setPriceDataInnerObjTemplate(STObject& obj) { @@ -255,6 +240,10 @@ SetOracle::doApply() auto const newCount = calculateOracleReserve(pairs.size()); int32_t const adjust = newCount - oldCount; + auto const accountSle = ctx_.view().peek(keylet::account(ctx_.tx[sfAccount])); + if (!accountSle) + return tefINTERNAL; // LCOV_EXCL_LINE + if (adjust > 0) { // To continue receiving sponsorship from the same account after the @@ -262,24 +251,21 @@ SetOracle::doApply() // the sponsor decrease current sponsored owner count. // Otherwise, the sponsorship will be deleted. - auto const currentsponsor = getLedgerEntryReserveSponsor(ctx_.view(), sle); - auto const newSponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + auto const currentSponsorSle = getLedgerEntryReserveSponsor(ctx_.view(), sle); + auto const newSponsorSle = getTxReserveSponsor(ctx_.view(), ctx_.tx); // decrease current sponsored owner count - if (!adjustOwnerCount(ctx_, currentsponsor, -oldCount)) - return tefINTERNAL; // LCOV_EXCL_LINE + adjustOwnerCount(ctx_.view(), accountSle, currentSponsorSle, -oldCount, ctx_.journal); removeSponsorFromLedgerEntry(sle); // increase new owner count - if (!adjustOwnerCount(ctx_, newSponsor, newCount)) - return tefINTERNAL; // LCOV_EXCL_LINE - addSponsorToLedgerEntry(sle, newSponsor); + adjustOwnerCount(ctx_.view(), accountSle, newSponsorSle, newCount, ctx_.journal); + addSponsorToLedgerEntry(sle, newSponsorSle); } else if (adjust < 0) { // decrease owner count - auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), sle); - if (!adjustOwnerCount(ctx_, sponsor, adjust)) - return tefINTERNAL; // LCOV_EXCL_LINE + auto const sponsorSle = getLedgerEntryReserveSponsor(ctx_.view(), sle); + adjustOwnerCount(ctx_.view(), accountSle, sponsorSle, adjust, ctx_.journal); } ctx_.view().update(sle); @@ -330,8 +316,10 @@ SetOracle::doApply() auto const count = calculateOracleReserve(series.size()); auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - if (!adjustOwnerCount(ctx_, sponsor, count)) + auto const accountSle = ctx_.view().peek(keylet::account(ctx_.tx[sfAccount])); + if (!accountSle) return tefINTERNAL; // LCOV_EXCL_LINE + adjustOwnerCount(ctx_.view(), accountSle, sponsor, count, ctx_.journal); addSponsorToLedgerEntry(sle, sponsor); diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 2ed31221e1a..295e5b278f9 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -171,7 +171,7 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), ctx_.tx, sleOwner, sponsor, 1, j_); + adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); addSponsorToLedgerEntry(slePreauth, sponsor); } else if (ctx_.tx.isFieldPresent(sfUnauthorize)) @@ -231,7 +231,7 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), ctx_.tx, sleOwner, sponsor, 1, j_); + adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); addSponsorToLedgerEntry(slePreauth, sponsor); } else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials)) diff --git a/src/libxrpl/tx/transactors/payment_channel/PayChanCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PayChanCreate.cpp index d124fa73603..909027e1ee5 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PayChanCreate.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PayChanCreate.cpp @@ -167,7 +167,7 @@ PayChanCreate::doApply() // Deduct owner's balance, increment owner count (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - adjustOwnerCount(ctx_.view(), ctx_.tx, sle, sponsor, 1, ctx_.journal); + adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(slep, sponsor); ctx_.view().update(sle); diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index 22b7cb63827..850bb60254c 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -113,7 +113,7 @@ PermissionedDomainSet::doApply() slePd->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), ctx_.tx, ownerSle, sponsor, 1, ctx_.journal); + adjustOwnerCount(view(), ownerSle, sponsor, 1, ctx_.journal); addSponsorToLedgerEntry(slePd, sponsor); view().insert(slePd); } diff --git a/src/libxrpl/tx/transactors/system/CreateTicket.cpp b/src/libxrpl/tx/transactors/system/CreateTicket.cpp index 23d0db27602..4d6e74197b5 100644 --- a/src/libxrpl/tx/transactors/system/CreateTicket.cpp +++ b/src/libxrpl/tx/transactors/system/CreateTicket.cpp @@ -111,7 +111,7 @@ CreateTicket::doApply() sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount); // Every added Ticket counts against the creator's reserve. - adjustOwnerCount(view(), ctx_.tx, sleAccountRoot, sponsor, ticketCount, viewJ); + adjustOwnerCount(view(), sleAccountRoot, sponsor, ticketCount, viewJ); // TicketCreate is the only transaction that can cause an account root's // Sequence field to increase by more than one. October 2018. diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index c093b14ce50..39f04edf550 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -137,7 +137,7 @@ MPTokenIssuanceCreate::create( } // Update owner count. - adjustOwnerCount(view, tx, acct, sponsor, 1, journal); + adjustOwnerCount(view, acct, sponsor, 1, journal); return mptId; } diff --git a/src/libxrpl/tx/transactors/token/SetTrust.cpp b/src/libxrpl/tx/transactors/token/SetTrust.cpp index e04dbdd875f..bedfb8398bf 100644 --- a/src/libxrpl/tx/transactors/token/SetTrust.cpp +++ b/src/libxrpl/tx/transactors/token/SetTrust.cpp @@ -547,7 +547,7 @@ SetTrust::doApply() return tecINSUF_RESERVE_LINE; // Set reserve for low account. - adjustOwnerCount(view(), ctx_.tx, sleLowAccount, txSponsorSle, 1, viewJ); + adjustOwnerCount(view(), sleLowAccount, txSponsorSle, 1, viewJ); uFlagsOut |= lsfLowReserve; addSponsorToLedgerEntry(sleRippleState, txSponsorSle, sfLowSponsor); @@ -574,7 +574,7 @@ SetTrust::doApply() return tecINSUF_RESERVE_LINE; // Set reserve for high account. - adjustOwnerCount(view(), ctx_.tx, sleHighAccount, txSponsorSle, 1, viewJ); + adjustOwnerCount(view(), sleHighAccount, txSponsorSle, 1, viewJ); uFlagsOut |= lsfHighReserve; addSponsorToLedgerEntry(sleRippleState, txSponsorSle, sfHighSponsor); @@ -602,10 +602,9 @@ SetTrust::doApply() terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if ( - auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sle, mPriorBalance, txSponsorSle, 0); - !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) + else if (auto const ret = + checkInsufficientReserve(view(), ctx_.tx, sle, mPriorBalance, txSponsorSle, 0); + !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -633,15 +632,14 @@ SetTrust::doApply() JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if ( - auto const ret = checkInsufficientReserve( - ctx_.view(), - ctx_.tx, - sle, - mPriorBalance, - txSponsorSle, - 1); - !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. + else if (auto const ret = checkInsufficientReserve( + ctx_.view(), + ctx_.tx, + sle, + mPriorBalance, + txSponsorSle, + 1); + !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index e2e795b22af..7c33946e498 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -138,7 +138,7 @@ VaultCreate::doApply() auto const sponsor = getTxReserveSponsor(view(), tx); if (!ctx_.view().rules().enabled(featureSponsor)) { - adjustOwnerCount(view(), tx, owner, sponsor, 2, j_); + adjustOwnerCount(view(), owner, sponsor, 2, j_); addSponsorToLedgerEntry(vault, sponsor); if (auto const ret = checkInsufficientReserve(view(), tx, owner, mPriorBalance, sponsor, 0); !isTesSuccess(ret)) @@ -150,7 +150,7 @@ VaultCreate::doApply() if (auto const ret = checkInsufficientReserve(view(), tx, owner, mPriorBalance, sponsor, 2); !isTesSuccess(ret)) return ret; - adjustOwnerCount(view(), tx, owner, sponsor, 2, j_); + adjustOwnerCount(view(), owner, sponsor, 2, j_); addSponsorToLedgerEntry(vault, sponsor); } From 8a05e3d93bae93850135c959dd9371fef43efb1c Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 18:14:48 +0900 Subject: [PATCH 147/249] remove unnecessary #endif in Sponsor.h and sponsor.h --- include/xrpl/protocol/Sponsor.h | 2 -- src/test/jtx/sponsor.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/include/xrpl/protocol/Sponsor.h b/include/xrpl/protocol/Sponsor.h index 22b97515bc3..2f95fb8398f 100644 --- a/include/xrpl/protocol/Sponsor.h +++ b/include/xrpl/protocol/Sponsor.h @@ -15,5 +15,3 @@ addSerializeSponsorData(Serializer& msg, AccountID const& sponsorID, std::uint32 } } // namespace xrpl - -#endif diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index 0e553299eb8..8c7b01569ab 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -89,5 +89,3 @@ ledgerEntry(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& spon } // namespace jtx } // namespace test } // namespace xrpl - -#endif From 85e1f2b16c8b003816e0f058aecb8b5c408aa28e Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 18:32:30 +0900 Subject: [PATCH 148/249] Rename [sponsor|sponsee]Acc,[sponsor|sponsee]Account to [sponsor|sponsee]AccountID for clarity --- include/xrpl/ledger/View.h | 4 +- src/libxrpl/ledger/View.cpp | 51 ++++++------ src/libxrpl/tx/Transactor.cpp | 5 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 54 ++++++------- .../Sponsor/SponsorshipTransfer.cpp | 80 ++++++++++--------- .../tx/transactors/account/DeleteAccount.cpp | 4 +- .../tx/transactors/check/CashCheck.cpp | 8 +- .../tx/transactors/escrow/EscrowHelpers.h | 8 +- src/xrpld/rpc/handlers/LedgerEntry.cpp | 14 ++-- 9 files changed, 117 insertions(+), 111 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index b6b33077183..1aa5639d729 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -934,7 +934,7 @@ accountSend( AccountID const& to, STAmount const& saAmount, beast::Journal j, - std::optional const& sponsorAcc = std::nullopt, + std::optional const& sponsorAcccountID = std::nullopt, WaiveTransferFee waiveFee = WaiveTransferFee::No); using MultiplePaymentDestinations = std::vector>; @@ -951,7 +951,7 @@ accountSendMulti( Asset const& asset, MultiplePaymentDestinations const& receivers, beast::Journal j, - std::optional const& sponsorAccount, + std::optional const& sponsorAccountID, WaiveTransferFee waiveFee = WaiveTransferFee::No); [[nodiscard]] TER diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index b1022df1e63..085ffe4322e 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1226,12 +1226,12 @@ adjustOwnerCount( if (sponsorSle) { auto const account = accountSle->getAccountID(sfAccount); - auto const sponsorAcc = (sponsorSle)->getAccountID(sfAccount); + auto const sponsorAccountID = (sponsorSle)->getAccountID(sfAccount); { // modify sponsor's SponsoringOwnerCount std::uint32_t const current{(sponsorSle)->getFieldU32(sfSponsoringOwnerCount)}; - std::uint32_t const adjusted = confineOwnerCount(current, amount, sponsorAcc, j); - view.adjustOwnerCountHook(sponsorAcc, current, adjusted); + std::uint32_t const adjusted = confineOwnerCount(current, amount, sponsorAccountID, j); + view.adjustOwnerCountHook(sponsorAccountID, current, adjusted); if (adjusted == 0) (sponsorSle)->makeFieldAbsent(sfSponsoringOwnerCount); else @@ -1250,7 +1250,7 @@ adjustOwnerCount( view.update(accountSle); } - auto sponsorObjSle = view.peek(keylet::sponsor(sponsorAcc, account)); + auto sponsorObjSle = view.peek(keylet::sponsor(sponsorAccountID, account)); if (sponsorObjSle) { @@ -1261,7 +1261,7 @@ adjustOwnerCount( // Reserve count moves opposite to amount: +amount => consume reserve (-), -amount => // payback (+) std::uint32_t const adjusted = - confineOwnerCount(currentReserveCount, -amount, sponsorAcc, j); + confineOwnerCount(currentReserveCount, -amount, sponsorAccountID, j); if (adjusted == 0) sponsorObjSle->makeFieldAbsent(sfReserveCount); else @@ -1588,11 +1588,12 @@ doWithdraw( // LCOV_EXCL_STOP } - auto const sponsorAcc = getTxReserveSponsorAccountID(tx); + auto const sponsorAccountID = getTxReserveSponsorAccountID(tx); // Move the funds directly from the broker's pseudo-account to the // dstAcct - return accountSend(view, sourceAcct, dstAcct, amount, j, sponsorAcc, WaiveTransferFee::Yes); + return accountSend( + view, sourceAcct, dstAcct, amount, j, sponsorAccountID, WaiveTransferFee::Yes); } [[nodiscard]] TER @@ -2128,7 +2129,7 @@ rippleCreditIOU( AccountID const& uReceiverID, STAmount const& saAmount, bool bCheckIssuer, - std::optional const& sponsorAccount, + std::optional const& sponsorAccountID, beast::Journal j) { AccountID const& issuer = saAmount.getIssuer(); @@ -2263,7 +2264,7 @@ rippleCreditIOU( saReceiverLimit, 0, 0, - sponsorAccount, + sponsorAccountID, j); } @@ -2278,7 +2279,7 @@ rippleSendIOU( STAmount const& saAmount, STAmount& saActual, beast::Journal j, - std::optional const& sponsorAccount, + std::optional const& sponsorAccountID, WaiveTransferFee waiveFee) { auto const& issuer = saAmount.getIssuer(); @@ -2292,7 +2293,7 @@ rippleSendIOU( { // Direct send: redeeming IOUs and/or sending own IOUs. auto const ter = - rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, sponsorAccount, j); + rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, sponsorAccountID, j); if (ter != tesSUCCESS) return ter; saActual = saAmount; @@ -2310,10 +2311,10 @@ rippleSendIOU( << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() << " cost=" << saActual.getFullText(); - TER terResult = rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, sponsorAccount, j); + TER terResult = rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, sponsorAccountID, j); if (tesSUCCESS == terResult) - terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, sponsorAccount, j); + terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, sponsorAccountID, j); return terResult; } @@ -2329,7 +2330,7 @@ rippleSendMultiIOU( MultiplePaymentDestinations const& receivers, STAmount& actual, beast::Journal j, - std::optional const& sponsorAccount, + std::optional const& sponsorAccountID, WaiveTransferFee waiveFee) { auto const& issuer = issue.getIssuer(); @@ -2358,7 +2359,7 @@ rippleSendMultiIOU( { // Direct send: redeeming IOUs and/or sending own IOUs. if (auto const ter = - rippleCreditIOU(view, senderID, receiverID, amount, false, sponsorAccount, j)) + rippleCreditIOU(view, senderID, receiverID, amount, false, sponsorAccountID, j)) return ter; actual += amount; // Do not add amount to takeFromSender, because rippleCreditIOU took @@ -2382,14 +2383,14 @@ rippleSendMultiIOU( << " cost=" << actual.getFullText(); if (TER const terResult = - rippleCreditIOU(view, issuer, receiverID, amount, true, sponsorAccount, j)) + rippleCreditIOU(view, issuer, receiverID, amount, true, sponsorAccountID, j)) return terResult; } if (senderID != issuer && takeFromSender) { if (TER const terResult = - rippleCreditIOU(view, senderID, issuer, takeFromSender, true, sponsorAccount, j)) + rippleCreditIOU(view, senderID, issuer, takeFromSender, true, sponsorAccountID, j)) return terResult; } @@ -2403,7 +2404,7 @@ accountSendIOU( AccountID const& uReceiverID, STAmount const& saAmount, beast::Journal j, - std::optional const& sponsorAccount, + std::optional const& sponsorAccountID, WaiveTransferFee waiveFee) { if (view.rules().enabled(fixAMMv1_1)) @@ -2436,7 +2437,7 @@ accountSendIOU( << to_string(uReceiverID) << " : " << saAmount.getFullText(); return rippleSendIOU( - view, uSenderID, uReceiverID, saAmount, saActual, j, sponsorAccount, waiveFee); + view, uSenderID, uReceiverID, saAmount, saActual, j, sponsorAccountID, waiveFee); } /* XRP send which does not check reserve and can do pure adjustment. @@ -2524,7 +2525,7 @@ accountSendMultiIOU( Issue const& issue, MultiplePaymentDestinations const& receivers, beast::Journal j, - std::optional const& sponsorAccount, + std::optional const& sponsorAccountID, WaiveTransferFee waiveFee) { XRPL_ASSERT_PARTS( @@ -2537,7 +2538,7 @@ accountSendMultiIOU( << receivers.size() << " IOUs"; return rippleSendMultiIOU( - view, senderID, issue, receivers, actual, j, sponsorAccount, waiveFee); + view, senderID, issue, receivers, actual, j, sponsorAccountID, waiveFee); } /* XRP send which does not check reserve and can do pure adjustment. @@ -2900,14 +2901,14 @@ accountSend( AccountID const& uReceiverID, STAmount const& saAmount, beast::Journal j, - std::optional const& sponsorAcc, + std::optional const& sponsorAccountID, WaiveTransferFee waiveFee) { return std::visit( [&](TIss const& issue) { if constexpr (std::is_same_v) return accountSendIOU( - view, uSenderID, uReceiverID, saAmount, j, sponsorAcc, waiveFee); + view, uSenderID, uReceiverID, saAmount, j, sponsorAccountID, waiveFee); else return accountSendMPT(view, uSenderID, uReceiverID, saAmount, j, waiveFee); }, @@ -2921,7 +2922,7 @@ accountSendMulti( Asset const& asset, MultiplePaymentDestinations const& receivers, beast::Journal j, - std::optional const& sponsorAccount, + std::optional const& sponsorAccountID, WaiveTransferFee waiveFee) { XRPL_ASSERT_PARTS( @@ -2930,7 +2931,7 @@ accountSendMulti( [&](TIss const& issue) { if constexpr (std::is_same_v) return accountSendMultiIOU( - view, senderID, issue, receivers, j, sponsorAccount, waiveFee); + view, senderID, issue, receivers, j, sponsorAccountID, waiveFee); else return accountSendMultiMPT(view, senderID, issue, receivers, j, waiveFee); }, diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 7577948609d..23161e66e21 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -775,9 +775,10 @@ Transactor::checkSign( if (!sigObject.isFieldPresent(sfSponsor)) return tefINTERNAL; // LCOV_EXCL_LINE - auto const sponsorAcc = sigObject.getAccountID(sfSponsor); + auto const sponsorAccountID = sigObject.getAccountID(sfSponsor); auto const sponsorSignature = sigObject.getFieldObject(sfSponsorSignature); - if (auto const ret = checkSign(view, flags, std::nullopt, sponsorAcc, sponsorSignature, j); + if (auto const ret = + checkSign(view, flags, std::nullopt, sponsorAccountID, sponsorSignature, j); !isTesSuccess(ret)) return ret; } diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index dca59ec669f..a622a393d92 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -31,10 +31,10 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (hasSponsor == hasSponsee) return temMALFORMED; - auto const sponsor = ctx.tx[~sfCounterpartySponsor].value_or(account); - auto const sponsee = ctx.tx[~sfSponsee].value_or(account); + auto const sponsorAccountID = ctx.tx[~sfCounterpartySponsor].value_or(account); + auto const sponseeAccountID = ctx.tx[~sfSponsee].value_or(account); - if (sponsor == sponsee) + if (sponsorAccountID == sponseeAccountID) return temMALFORMED; if (flags & tfDeleteObject) @@ -56,7 +56,7 @@ SponsorshipSet::preflight(PreflightContext const& ctx) { // although both Sponsor and Sponsee can delete, // only the Sponsor can create or update sponsorship. - if (account != sponsor) + if (account != sponsorAccountID) return temMALFORMED; // Check FeeAmount and MaxFee @@ -136,24 +136,24 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) TER SponsorshipSet::preclaim(PreclaimContext const& ctx) { - auto const sponsor = ctx.tx[~sfCounterpartySponsor].value_or(ctx.tx[sfAccount]); - auto const sponsee = ctx.tx[~sfSponsee].value_or(ctx.tx[sfAccount]); + auto const sponsorAccountID = ctx.tx[~sfCounterpartySponsor].value_or(ctx.tx[sfAccount]); + auto const sponseeAccountID = ctx.tx[~sfSponsee].value_or(ctx.tx[sfAccount]); - if (sponsee == sponsor) + if (sponseeAccountID == sponsorAccountID) return tecINTERNAL; // LCOV_EXCL_LINE // check Sponsor - auto const sponsorAccSle = ctx.view.read(keylet::account(sponsor)); + auto const sponsorAccSle = ctx.view.read(keylet::account(sponsorAccountID)); if (!sponsorAccSle) return tecNO_DST; // check Sponsee - auto const sponseeSle = ctx.view.read(keylet::account(sponsee)); + auto const sponseeSle = ctx.view.read(keylet::account(sponseeAccountID)); if (!sponseeSle) return tecNO_DST; // check if object exists - auto const sponsorObjSle = ctx.view.read(keylet::sponsor(sponsor, sponsee)); + auto const sponsorObjSle = ctx.view.read(keylet::sponsor(sponsorAccountID, sponseeAccountID)); if (ctx.tx.isFlag(tfDeleteObject) && !sponsorObjSle) return tecNO_ENTRY; @@ -168,20 +168,20 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) TER SponsorshipSet::doApply() { - auto const sponsorAcc = ctx_.tx[~sfCounterpartySponsor].value_or(account_); - auto const sponseeAcc = ctx_.tx[~sfSponsee].value_or(account_); + auto const sponsorAccountID = ctx_.tx[~sfCounterpartySponsor].value_or(account_); + auto const sponseeAccountID = ctx_.tx[~sfSponsee].value_or(account_); - if (sponseeAcc == sponsorAcc) + if (sponseeAccountID == sponsorAccountID) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorAcc)); + auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorAccountID)); if (!sponsorAccSle) return tecINTERNAL; // LCOV_EXCL_LINE - if (!ctx_.view().exists(keylet::account(sponseeAcc))) + if (!ctx_.view().exists(keylet::account(sponseeAccountID))) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsorKeylet = keylet::sponsor(sponsorAcc, sponseeAcc); + auto const sponsorKeylet = keylet::sponsor(sponsorAccountID, sponseeAccountID); auto const sponsorObjSle = ctx_.view().peek(sponsorKeylet); if (ctx_.tx.isFlag(tfDeleteObject)) @@ -194,12 +194,12 @@ SponsorshipSet::doApply() adjustOwnerCount(ctx_.view(), sponsorAccSle, sponsor, -1, ctx_.journal); ctx_.view().dirRemove( - keylet::ownerDir(sponsorAcc), + keylet::ownerDir(sponsorAccountID), (*sponsorObjSle)[sfOwnerNode], sponsorObjSle->key(), false); ctx_.view().dirRemove( - keylet::ownerDir(sponseeAcc), + keylet::ownerDir(sponseeAccountID), (*sponsorObjSle)[sfSponseeNode], sponsorObjSle->key(), false); @@ -235,8 +235,8 @@ SponsorshipSet::doApply() !isTesSuccess(ret)) return tecUNFUNDED; - (*newSle)[sfOwner] = sponsorAcc; - (*newSle)[sfSponsee] = sponseeAcc; + (*newSle)[sfOwner] = sponsorAccountID; + (*newSle)[sfSponsee] = sponseeAccountID; if (feeAmount && *feeAmount > XRPAmount(0)) { (*sponsorAccSle)[sfBalance] -= *feeAmount; @@ -257,11 +257,11 @@ SponsorshipSet::doApply() (*newSle)[sfFlags] = flags; auto const sponsorPage = view().dirInsert( - keylet::ownerDir(sponsorAcc), sponsorKeylet, describeOwnerDir(sponsorAcc)); + keylet::ownerDir(sponsorAccountID), sponsorKeylet, describeOwnerDir(sponsorAccountID)); (*newSle)[sfOwnerNode] = *sponsorPage; auto const sponseePage = view().dirInsert( - keylet::ownerDir(sponseeAcc), sponsorKeylet, describeOwnerDir(sponseeAcc)); + keylet::ownerDir(sponseeAccountID), sponsorKeylet, describeOwnerDir(sponseeAccountID)); (*newSle)[sfSponseeNode] = *sponseePage; auto viewJ = ctx_.registry.journal("View"); @@ -335,11 +335,11 @@ SponsorshipSet::deleteSponsorship( std::shared_ptr const& sle, beast::Journal j) { - auto const sponsor = sle->getAccountID(sfOwner); - auto const sponsee = sle->getAccountID(sfSponsee); + auto const sponsorAccountID = sle->getAccountID(sfOwner); + auto const sponseeAccountID = sle->getAccountID(sfSponsee); // adjust balance - auto const sponsorAccSle = view.peek(keylet::account(sponsor)); + auto const sponsorAccSle = view.peek(keylet::account(sponsorAccountID)); if (!sponsorAccSle) return tecINTERNAL; // LCOV_EXCL_LINE @@ -355,9 +355,9 @@ SponsorshipSet::deleteSponsorship( view.update(sponsorAccSle); // delete sponsor node - view.dirRemove(keylet::ownerDir(sponsor), (*sle)[sfOwnerNode], sle->key(), false); + view.dirRemove(keylet::ownerDir(sponsorAccountID), (*sle)[sfOwnerNode], sle->key(), false); // delete sponsee node - view.dirRemove(keylet::ownerDir(sponsee), (*sle)[sfSponseeNode], sle->key(), false); + view.dirRemove(keylet::ownerDir(sponseeAccountID), (*sle)[sfSponseeNode], sle->key(), false); view.erase(sle); diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index e59db6d5edd..f5c756a7697 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -226,8 +226,8 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) auto const account = ctx.tx[sfAccount]; - auto const sponsee = ctx.tx[~sfSponsee].value_or(account); - auto const sponseeSle = ctx.view.read(keylet::account(sponsee)); + auto const sponseeAccountID = ctx.tx[~sfSponsee].value_or(account); + auto const sponseeSle = ctx.view.read(keylet::account(sponseeAccountID)); if (!sponseeSle) return tecINTERNAL; // LCOV_EXCL_LINE @@ -239,8 +239,8 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) auto const ownerCountDelta = getLedgerEntryOwnerCount(sle); - auto const owner = getLedgerEntryOwner(ctx.view, sle, sponsee); - if (!owner || owner != sponsee) + auto const owner = getLedgerEntryOwner(ctx.view, sle, sponseeAccountID); + if (!owner || owner != sponseeAccountID) return tecNO_PERMISSION; auto const& sponsorField = getLedgerEntrySponsorField(sle, *owner); @@ -371,8 +371,8 @@ SponsorshipTransfer::doApply() auto const flags = tx.getFlags(); bool const isObjectSponsor = index != std::nullopt; - auto const sponsee = tx[~sfSponsee].value_or(account_); - auto const sponseeSle = view().peek(keylet::account(sponsee)); + auto const sponseeAccountID = tx[~sfSponsee].value_or(account_); + auto const sponseeSle = view().peek(keylet::account(sponseeAccountID)); if (!sponseeSle) return tefINTERNAL; // LCOV_EXCL_LINE @@ -393,96 +393,99 @@ SponsorshipTransfer::doApply() if (!objSle) return tefINTERNAL; // LCOV_EXCL_LINE - auto const owner = getLedgerEntryOwner(view(), objSle, account_); - if (!owner) + auto const ownerAccountID = getLedgerEntryOwner(view(), objSle, account_); + if (!ownerAccountID) return tefINTERNAL; // LCOV_EXCL_LINE - auto const ownerSle = view().peek(keylet::account(*owner)); + auto const ownerSle = view().peek(keylet::account(*ownerAccountID)); if (!ownerSle) return tefINTERNAL; // LCOV_EXCL_LINE auto const ownerCountDelta = getLedgerEntryOwnerCount(objSle); - auto const& sponsorField = getLedgerEntrySponsorField(objSle, *owner); + auto const& sponsorField = getLedgerEntrySponsorField(objSle, *ownerAccountID); if (flags & tfSponsorshipCreate) { - auto const newSponsor = tx.getAccountID(sfSponsor); - XRPL_ASSERT(!!newSponsor, "New sponsor is required when creating sponsorship"); + auto const newSponsorAccountID = tx.getAccountID(sfSponsor); + XRPL_ASSERT(!!newSponsorAccountID, "New sponsor is required when creating sponsorship"); // update owner's sponsored count setSponsorFieldU32(ownerSle, sfSponsoredOwnerCount, ownerCountDelta); view().update(ownerSle); // increment new sponsor's sponsoring count - auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + auto const newSponsorSle = view().peek(keylet::account(newSponsorAccountID)); if (!newSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE setSponsorFieldU32(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); view().update(newSponsorSle); // set new sponsor to object - objSle->setAccountID(sponsorField, newSponsor); + objSle->setAccountID(sponsorField, newSponsorAccountID); view().update(objSle); if (!hasSignature) { // use ReserveCount for pre-funded sponsoring if (auto const ter = - adjustReserveCount(view(), account_, newSponsor, -ownerCountDelta); + adjustReserveCount(view(), account_, newSponsorAccountID, -ownerCountDelta); !isTesSuccess(ter)) return ter; } } else if (flags & tfSponsorshipReassign) { - auto const newSponsor = tx.getAccountID(sfSponsor); - XRPL_ASSERT(!!newSponsor, "New sponsor is required when reassigning sponsorship"); + auto const newSponsorAccountID = tx.getAccountID(sfSponsor); + XRPL_ASSERT( + !!newSponsorAccountID, "New sponsor is required when reassigning sponsorship"); - auto const oldSponsor = objSle->getAccountID(sponsorField); - XRPL_ASSERT(!!oldSponsor, "Old sponsor is required when reassigning sponsorship"); + auto const oldSponsorAccountID = objSle->getAccountID(sponsorField); + XRPL_ASSERT( + !!oldSponsorAccountID, "Old sponsor is required when reassigning sponsorship"); // decrement old sponsor's sponsoring count - auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); + auto const oldSponsorSle = view().peek(keylet::account(oldSponsorAccountID)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); view().update(oldSponsorSle); // increment new sponsor's sponsoring count - auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + auto const newSponsorSle = view().peek(keylet::account(newSponsorAccountID)); if (!newSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE setSponsorFieldU32(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); view().update(newSponsorSle); // set new sponsor to object - objSle->setAccountID(sponsorField, newSponsor); + objSle->setAccountID(sponsorField, newSponsorAccountID); view().update(objSle); if (!hasSignature) { // use ReserveCount for pre-funded sponsoring if (auto const ter = - adjustReserveCount(view(), account_, newSponsor, -ownerCountDelta); + adjustReserveCount(view(), account_, newSponsorAccountID, -ownerCountDelta); !isTesSuccess(ter)) return ter; } // payback the reserve count if ltSponsorship exists - if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); + if (auto const sponsorSle = + view().exists(keylet::sponsor(oldSponsorAccountID, account_)); sponsorSle) if (auto const ter = - adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); + adjustReserveCount(view(), account_, oldSponsorAccountID, ownerCountDelta); !isTesSuccess(ter)) return ter; } else if (flags & tfSponsorshipEnd) { - auto const oldSponsor = objSle->getAccountID(sponsorField); - XRPL_ASSERT(!!oldSponsor, "Old sponsor is required when ending sponsorship"); + auto const oldSponsorAccountID = objSle->getAccountID(sponsorField); + XRPL_ASSERT(!!oldSponsorAccountID, "Old sponsor is required when ending sponsorship"); - auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); + auto const oldSponsorSle = view().peek(keylet::account(oldSponsorAccountID)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE @@ -495,10 +498,11 @@ SponsorshipTransfer::doApply() view().update(oldSponsorSle); // payback the reserve count if ltSponsorship exists - if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); + if (auto const sponsorSle = + view().exists(keylet::sponsor(oldSponsorAccountID, account_)); sponsorSle) if (auto const ter = - adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); + adjustReserveCount(view(), account_, oldSponsorAccountID, ownerCountDelta); !isTesSuccess(ter)) return ter; @@ -513,23 +517,23 @@ SponsorshipTransfer::doApply() { // create account sponsor // increment new sponsoring count - auto const newSponsor = tx.getAccountID(sfSponsor); - auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + auto const newSponsorAccountID = tx.getAccountID(sfSponsor); + auto const newSponsorSle = view().peek(keylet::account(newSponsorAccountID)); if (!newSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); view().update(newSponsorSle); // set new sponsor to account - sponseeSle->setAccountID(sfSponsor, newSponsor); + sponseeSle->setAccountID(sfSponsor, newSponsorAccountID); view().update(sponseeSle); } else if (flags & tfSponsorshipReassign) { // reassign account sponsor // increment new sponsoring count - auto const newSponsor = tx.getAccountID(sfSponsor); - auto const newSponsorSle = view().peek(keylet::account(newSponsor)); + auto const newSponsorAccountID = tx.getAccountID(sfSponsor); + auto const newSponsorSle = view().peek(keylet::account(newSponsorAccountID)); setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); view().update(newSponsorSle); @@ -542,18 +546,18 @@ SponsorshipTransfer::doApply() view().update(oldSponsorSle); // set new sponsor to account - sponseeSle->setAccountID(sfSponsor, newSponsor); + sponseeSle->setAccountID(sfSponsor, newSponsorAccountID); view().update(sponseeSle); } else if (flags & tfSponsorshipEnd) { // dissolve account sponsor - auto const oldSponsor = sponseeSle->getAccountID(sfSponsor); + auto const oldSponsorAccountID = sponseeSle->getAccountID(sfSponsor); sponseeSle->makeFieldAbsent(sfSponsor); view().update(sponseeSle); // decrement account sponsoring count - auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); + auto const oldSponsorSle = view().peek(keylet::account(oldSponsorAccountID)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); diff --git a/src/libxrpl/tx/transactors/account/DeleteAccount.cpp b/src/libxrpl/tx/transactors/account/DeleteAccount.cpp index fec5b2757c1..afefb8eb45f 100644 --- a/src/libxrpl/tx/transactors/account/DeleteAccount.cpp +++ b/src/libxrpl/tx/transactors/account/DeleteAccount.cpp @@ -407,8 +407,8 @@ DeleteAccount::doApply() if (src->isFieldPresent(sfSponsor)) { - auto const sponsorAcc = src->getAccountID(sfSponsor); - auto sponsorSle = view().peek(keylet::account(sponsorAcc)); + auto const sponsorAccountID = src->getAccountID(sfSponsor); + auto sponsorSle = view().peek(keylet::account(sponsorAccountID)); if (!sponsorSle || !sponsorSle->isFieldPresent(sfSponsoringAccountCount)) return tefINTERNAL; // LCOV_EXCL_LINE diff --git a/src/libxrpl/tx/transactors/check/CashCheck.cpp b/src/libxrpl/tx/transactors/check/CashCheck.cpp index 3411964fddd..f32d2813e36 100644 --- a/src/libxrpl/tx/transactors/check/CashCheck.cpp +++ b/src/libxrpl/tx/transactors/check/CashCheck.cpp @@ -309,10 +309,10 @@ CashCheck::doApply() auto const sleDst = psb.peek(keylet::account(account_)); - auto const sponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); + auto const sponsorAccountID = getTxReserveSponsorAccountID(ctx_.tx); std::shared_ptr sponsorSle = {}; - if (sponsorAcc) - sponsorSle = psb.peek(keylet::account(*sponsorAcc)); + if (sponsorAccountID) + sponsorSle = psb.peek(keylet::account(*sponsorAccountID)); // Can the account cover the trust line's reserve? if (auto const ret = checkInsufficientReserve( @@ -345,7 +345,7 @@ CashCheck::doApply() Issue(currency, account_), // limit of zero 0, // quality in 0, // quality out - sponsorAcc, // sponsor + sponsorAccountID, // sponsor viewJ); // journal !isTesSuccess(ter)) { diff --git a/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h b/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h index c71b8882fcf..8d76dcef775 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h +++ b/src/libxrpl/tx/transactors/escrow/EscrowHelpers.h @@ -55,10 +55,10 @@ escrowUnlockApplyHelper( if (!view.exists(trustLineKey) && createAsset && !receiverIssuer) { // Can the account cover the trust line's reserve? - auto const sponsorAcc = getTxReserveSponsorAccountID(tx); + auto const sponsorAccountID = getTxReserveSponsorAccountID(tx); std::shared_ptr sponsorSle = {}; - if (sponsorAcc) - sponsorSle = view.peek(keylet::account(*sponsorAcc)); + if (sponsorAccountID) + sponsorSle = view.peek(keylet::account(*sponsorAccountID)); if (auto const ret = checkInsufficientReserve(view, tx, sleDest, xrpBalance, sponsorSle, 1); !isTesSuccess(ret)) { @@ -88,7 +88,7 @@ escrowUnlockApplyHelper( Issue(currency, receiver), // limit of zero 0, // quality in 0, // quality out -sponsorAcc, // sponsor + sponsorAccountID, // sponsor journal); // journal !isTesSuccess(ter)) { diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 53ea9af63b1..08d9885bd7d 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -755,17 +755,17 @@ parseSponsorship( return parseObjectID(params, fieldName); } - auto const sponsor = + auto const sponsorAccountID = LedgerEntryHelpers::requiredAccountID(params, jss::sponsor, "malformedSponsor"); - if (!sponsor) - return Unexpected(sponsor.error()); + if (!sponsorAccountID) + return Unexpected(sponsorAccountID.error()); - auto const sponsee = + auto const sponseeAccountID = LedgerEntryHelpers::requiredAccountID(params, jss::sponsee, "malformedSponsee"); - if (!sponsee) - return Unexpected(sponsee.error()); + if (!sponseeAccountID) + return Unexpected(sponseeAccountID.error()); - return keylet::sponsor(*sponsor, *sponsee).key; + return keylet::sponsor(*sponsorAccountID, *sponseeAccountID).key; } static Expected From 839ba17dae0f1c711d586e398056ea53ac3105cf Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 20:11:49 +0900 Subject: [PATCH 149/249] Refactor: sponsorship-related helpers --- include/xrpl/ledger/SponsorHelpers.h | 77 +++++++++++++ include/xrpl/ledger/View.h | 43 +------- include/xrpl/tx/Transactor.h | 1 + src/libxrpl/ledger/CredentialHelpers.cpp | 1 + src/libxrpl/ledger/SponsorHelpers.cpp | 65 +++++++++++ src/libxrpl/ledger/View.cpp | 102 +----------------- .../tx/transactors/Sponsor/SponsorshipSet.h | 36 ------- .../transactors/Sponsor/SponsorshipTransfer.h | 26 ----- .../payment_channel/PayChanHelpers.cpp | 1 + 9 files changed, 150 insertions(+), 202 deletions(-) create mode 100644 include/xrpl/ledger/SponsorHelpers.h create mode 100644 src/libxrpl/ledger/SponsorHelpers.cpp delete mode 100644 src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.h delete mode 100644 src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.h diff --git a/include/xrpl/ledger/SponsorHelpers.h b/include/xrpl/ledger/SponsorHelpers.h new file mode 100644 index 00000000000..500f25c654e --- /dev/null +++ b/include/xrpl/ledger/SponsorHelpers.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +bool +isReserveSponsored(STTx const& tx); + +bool +isSponsorReserveCoSigning(STTx const& tx); + +std::optional +getTxReserveSponsorAccountID(STTx const& tx); + +inline std::shared_ptr +getTxReserveSponsor(ApplyView& view, STTx const& tx) +{ + auto const sponsorID = getTxReserveSponsorAccountID(tx); + if (sponsorID) + return view.peek(keylet::account(*sponsorID)); + return {}; +} + +inline std::shared_ptr +getTxReserveSponsor(ReadView const& view, STTx const& tx) +{ + auto const sponsorID = getTxReserveSponsorAccountID(tx); + if (sponsorID) + return view.read(keylet::account(*sponsorID)); + return {}; +} + +std::optional +getLedgerEntryReserveSponsorAccountID( + std::shared_ptr const& sle, + SF_ACCOUNT const& field = sfSponsor); + +inline std::shared_ptr +getLedgerEntryReserveSponsor( + ApplyView& view, + std::shared_ptr const& sle, + SF_ACCOUNT const& field = sfSponsor) +{ + auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); + if (sponsorID) + return view.peek(keylet::account(*sponsorID)); + return {}; +} + +inline std::shared_ptr +getLedgerEntryReserveSponsor( + ReadView const& view, + std::shared_ptr const& sle, + SF_ACCOUNT const& field = sfSponsor) +{ + auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); + if (sponsorID) + return view.read(keylet::account(*sponsorID)); + return {}; +} + +void +addSponsorToLedgerEntry( + std::shared_ptr const& sle, + std::shared_ptr const& sponsorSle, + SF_ACCOUNT const& field = sfSponsor); + +void +removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsor); + +} // namespace xrpl diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 1aa5639d729..f925b97b75d 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -474,11 +474,11 @@ ownerCount(std::shared_ptr const& sponsorSle); XRPAmount calculateReserve(std::shared_ptr const& sle, Fees const& fees); -bool -isReserveSponsored(STTx const& tx); +// bool +// isReserveSponsored(STTx const& tx); -bool -isSponsorReserveCoSigning(STTx const& tx); +// bool +// isSponsorReserveCoSigning(STTx const& tx); TER checkInsufficientReserve( @@ -490,41 +490,6 @@ checkInsufficientReserve( std::int32_t ownerCountDelta, std::int32_t accountCountDelta = 0); -std::optional -getTxReserveSponsorAccountID(STTx const& tx); - -std::shared_ptr -getTxReserveSponsor(ApplyView& view, STTx const& tx); - -std::shared_ptr -getTxReserveSponsor(ReadView const& view, STTx const& tx); - -std::optional -getLedgerEntryReserveSponsorAccountID( - std::shared_ptr const& sle, - SF_ACCOUNT const& field = sfSponsor); - -std::shared_ptr -getLedgerEntryReserveSponsor( - ApplyView& view, - std::shared_ptr const& sle, - SF_ACCOUNT const& field = sfSponsor); - -std::shared_ptr -getLedgerEntryReserveSponsor( - ReadView const& view, - std::shared_ptr const& sle, - SF_ACCOUNT const& field = sfSponsor); - -void -addSponsorToLedgerEntry( - std::shared_ptr const& sle, - std::shared_ptr const& sponsorSle, - SF_ACCOUNT const& field = sfSponsor); - -void -removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsor); - //------------------------------------------------------------------------------ // // Modifiers diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index 79b24f4ff6d..cd859a16f1e 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include diff --git a/src/libxrpl/ledger/CredentialHelpers.cpp b/src/libxrpl/ledger/CredentialHelpers.cpp index 86ac051b1bd..04a8a191230 100644 --- a/src/libxrpl/ledger/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/CredentialHelpers.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/src/libxrpl/ledger/SponsorHelpers.cpp b/src/libxrpl/ledger/SponsorHelpers.cpp new file mode 100644 index 00000000000..d86b7bfe3c7 --- /dev/null +++ b/src/libxrpl/ledger/SponsorHelpers.cpp @@ -0,0 +1,65 @@ +#include +#include + +namespace xrpl { + +bool +isReserveSponsored(STTx const& tx) +{ + return tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve; +} + +bool +isSponsorReserveCoSigning(STTx const& tx) +{ + if (!tx.isFieldPresent(sfSponsorSignature)) + return false; + return isReserveSponsored(tx); +} + +std::optional +getTxReserveSponsorAccountID(STTx const& tx) +{ + if (tx.isFieldPresent(sfSponsor) && isReserveSponsored(tx)) + { + return tx.getAccountID(sfSponsor); + } + return {}; +} + +std::optional +getLedgerEntryReserveSponsorAccountID( + std::shared_ptr const& sle, + SF_ACCOUNT const& field) +{ + if (sle->isFieldPresent(field)) + return sle->getAccountID(field); + return {}; +} + +void +addSponsorToLedgerEntry( + std::shared_ptr const& sle, + std::shared_ptr const& sponsorSle, + SF_ACCOUNT const& field) +{ + XRPL_ASSERT( + (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || + (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), + "addSponsorToLedgerEntry : Invalid field to the LedgerEntry"); + if (sponsorSle) + sle->setAccountID(field, sponsorSle->getAccountID(sfAccount)); +} + +void +removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field) +{ + XRPL_ASSERT( + (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || + (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), + "removeSponsorFromLedgerEntry : Invalid field to the LedgerEntry"); + if (sle->isFieldPresent(field)) + sle->makeFieldAbsent(field); +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 085ffe4322e..cdb048d0bf9 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -1040,20 +1041,6 @@ calculateReserve(std::shared_ptr const& sle, Fees const& fees) sle->getFieldU32(sfSponsoringAccountCount)); } -bool -isReserveSponsored(STTx const& tx) -{ - return tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve; -} - -bool -isSponsorReserveCoSigning(STTx const& tx) -{ - if (!tx.isFieldPresent(sfSponsorSignature)) - return false; - return isReserveSponsored(tx); -} - TER checkInsufficientReserve( ReadView const& view, @@ -1118,93 +1105,6 @@ checkInsufficientReserve( return tesSUCCESS; } -std::optional -getTxReserveSponsorAccountID(STTx const& tx) -{ - if (tx.isFieldPresent(sfSponsor) && isReserveSponsored(tx)) - { - return tx.getAccountID(sfSponsor); - } - return {}; -} - -std::shared_ptr -getTxReserveSponsor(ApplyView& view, STTx const& tx) -{ - auto const sponsorID = getTxReserveSponsorAccountID(tx); - if (sponsorID) - return view.peek(keylet::account(*sponsorID)); - return {}; -} - -std::shared_ptr -getTxReserveSponsor(ReadView const& view, STTx const& tx) -{ - auto const sponsorID = getTxReserveSponsorAccountID(tx); - if (sponsorID) - return view.read(keylet::account(*sponsorID)); - return {}; -} - -std::optional -getLedgerEntryReserveSponsorAccountID( - std::shared_ptr const& sle, - SF_ACCOUNT const& field) -{ - if (sle->isFieldPresent(field)) - return sle->getAccountID(field); - return {}; -} - -std::shared_ptr -getLedgerEntryReserveSponsor( - ApplyView& view, - std::shared_ptr const& sle, - SF_ACCOUNT const& field) -{ - auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); - if (sponsorID) - return view.peek(keylet::account(*sponsorID)); - return {}; -} - -std::shared_ptr -getLedgerEntryReserveSponsor( - ReadView const& view, - std::shared_ptr const& sle, - SF_ACCOUNT const& field) -{ - auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); - if (sponsorID) - return view.read(keylet::account(*sponsorID)); - return {}; -} - -void -addSponsorToLedgerEntry( - std::shared_ptr const& sle, - std::shared_ptr const& sponsorSle, - SF_ACCOUNT const& field) -{ - XRPL_ASSERT( - (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || - (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), - "addSponsorToLedgerEntry : Invalid field to the LedgerEntry"); - if (sponsorSle) - sle->setAccountID(field, sponsorSle->getAccountID(sfAccount)); -} - -void -removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field) -{ - XRPL_ASSERT( - (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || - (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), - "removeSponsorFromLedgerEntry : Invalid field to the LedgerEntry"); - if (sle->isFieldPresent(field)) - sle->makeFieldAbsent(field); -} - //------------------------------------------------------------------------------ // // Modifiers diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.h b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.h deleted file mode 100644 index d1cf244f8b7..00000000000 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -namespace xrpl { - -class SponsorshipSet : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit SponsorshipSet(ApplyContext& ctx) : Transactor(ctx) - { - } - - static std::uint32_t - getFlagsMask(PreflightContext const& ctx); - - static NotTEC - preflight(PreflightContext const& ctx); - - static NotTEC - checkPermission(ReadView const& view, STTx const& tx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; - - // Interface used by DeleteAccount - static TER - deleteSponsorship(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); -}; - -} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.h b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.h deleted file mode 100644 index 2b356556ddd..00000000000 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -namespace xrpl { - -class SponsorshipTransfer : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit SponsorshipTransfer(ApplyContext& ctx) : Transactor(ctx) - { - } - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/payment_channel/PayChanHelpers.cpp b/src/libxrpl/tx/transactors/payment_channel/PayChanHelpers.cpp index 70a53b47329..4301702b01d 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PayChanHelpers.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PayChanHelpers.cpp @@ -1,4 +1,5 @@ #include +#include #include #include From c9666d7b45e13bd2ba321eee785a5fbc4281caf5 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 20:28:01 +0900 Subject: [PATCH 150/249] refactor `adjustOwnerCount()` to use `adjustSponsorOwnerCountHlp()` --- src/libxrpl/ledger/View.cpp | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index cdb048d0bf9..c62b451650b 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1111,6 +1111,25 @@ checkInsufficientReserve( // //------------------------------------------------------------------------------ +void +adjustSponsorOwnerCountHlp( + ApplyView& view, + std::shared_ptr const& sle, + SField const& sfield, + std::int32_t amount, + beast::Journal j) +{ + auto const accID = sle->getAccountID(sfAccount); + std::uint32_t const current{(sle)->getFieldU32(sfield)}; + std::uint32_t const adjusted = confineOwnerCount(current, amount, accID, j); + view.adjustOwnerCountHook(accID, current, adjusted); + if (adjusted == 0) + sle->makeFieldAbsent(sfield); + else + sle->setFieldU32(sfield, adjusted); + view.update(sle); +} + void adjustOwnerCount( ApplyView& view, @@ -1125,30 +1144,11 @@ adjustOwnerCount( if (sponsorSle) { + adjustSponsorOwnerCountHlp(view, sponsorSle, sfSponsoringOwnerCount, amount, j); + adjustSponsorOwnerCountHlp(view, accountSle, sfSponsoredOwnerCount, amount, j); + auto const account = accountSle->getAccountID(sfAccount); auto const sponsorAccountID = (sponsorSle)->getAccountID(sfAccount); - { - // modify sponsor's SponsoringOwnerCount - std::uint32_t const current{(sponsorSle)->getFieldU32(sfSponsoringOwnerCount)}; - std::uint32_t const adjusted = confineOwnerCount(current, amount, sponsorAccountID, j); - view.adjustOwnerCountHook(sponsorAccountID, current, adjusted); - if (adjusted == 0) - (sponsorSle)->makeFieldAbsent(sfSponsoringOwnerCount); - else - (sponsorSle)->setFieldU32(sfSponsoringOwnerCount, adjusted); - view.update(sponsorSle); - } - { - // modify account's SponsoredOwnerCount - std::uint32_t const current{accountSle->getFieldU32(sfSponsoredOwnerCount)}; - std::uint32_t const adjusted = confineOwnerCount(current, amount, account, j); - view.adjustOwnerCountHook(account, current, adjusted); - if (adjusted == 0) - accountSle->makeFieldAbsent(sfSponsoredOwnerCount); - else - accountSle->setFieldU32(sfSponsoredOwnerCount, adjusted); - view.update(accountSle); - } auto sponsorObjSle = view.peek(keylet::sponsor(sponsorAccountID, account)); From 15be71469d535dc975c4e80098cece9ef1fa5ca1 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 20:35:50 +0900 Subject: [PATCH 151/249] remove unnecessary reserveCount check --- src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index a622a393d92..d5bd7e36f7f 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -80,14 +80,6 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (auto const ret = checkOptionalAmountField(sfMaxFee); !isTesSuccess(ret)) return ret; - - // Check ReserveCount - if (ctx.tx.isFieldPresent(sfReserveCount)) - { - auto const reserveCount = ctx.tx.getFieldU32(sfReserveCount); - if (reserveCount < 0) - return temMALFORMED; // LCOV_EXCL_LINE - } } return tesSUCCESS; From c79814a03753a8db9fc38541a84069f0b139eb56 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 20:39:57 +0900 Subject: [PATCH 152/249] remove unused Sponsor.h --- include/xrpl/protocol/Sponsor.h | 17 ----------------- src/test/jtx/impl/sponsor.cpp | 1 - 2 files changed, 18 deletions(-) delete mode 100644 include/xrpl/protocol/Sponsor.h diff --git a/include/xrpl/protocol/Sponsor.h b/include/xrpl/protocol/Sponsor.h deleted file mode 100644 index 2f95fb8398f..00000000000 --- a/include/xrpl/protocol/Sponsor.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace xrpl { - -inline void -addSerializeSponsorData(Serializer& msg, AccountID const& sponsorID, std::uint32_t const& flags) -{ - msg.addBitString(sponsorID); - msg.add32(flags); -} - -} // namespace xrpl diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index db3caf56cfc..59adb58f030 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include From eb7e01de7ef6756773033380724f7d74f8e09317 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 21:04:40 +0900 Subject: [PATCH 153/249] Remove unused tx parameter from removeEmptyHolding() --- include/xrpl/ledger/View.h | 6 ++++-- src/libxrpl/ledger/View.cpp | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index f925b97b75d..4a32044e5c2 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -803,7 +803,6 @@ trustCreate( [[nodiscard]] TER removeEmptyHolding( ApplyView& view, - STTx const& tx, AccountID const& accountID, Issue const& issue, beast::Journal journal); @@ -826,7 +825,10 @@ removeEmptyHolding( { return std::visit( [&](TIss const& issue) -> TER { - return removeEmptyHolding(view, tx, accountID, issue, journal); + if constexpr (std::is_same_v) + return removeEmptyHolding(view, accountID, issue, journal); + else + return removeEmptyHolding(view, tx, accountID, issue, journal); }, asset.value()); } diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index c62b451650b..fd4da70a83b 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1833,7 +1833,6 @@ trustCreate( [[nodiscard]] TER removeEmptyHolding( ApplyView& view, - STTx const& tx, AccountID const& accountID, Issue const& issue, beast::Journal journal) From 8d75f3bdc67ba68f3f5c1012af02e78adb15fd11 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 21:13:35 +0900 Subject: [PATCH 154/249] remove `asfDisallowIncomingSponsor` --- include/xrpl/protocol/LedgerFormats.h | 3 +- include/xrpl/protocol/TxFlags.h | 3 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 4 -- .../tx/transactors/account/SetAccount.cpp | 8 ---- src/test/app/AccountSet_test.cpp | 3 +- src/test/app/Sponsor_test.cpp | 46 ------------------- 6 files changed, 3 insertions(+), 64 deletions(-) diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index ffd45ebd265..54ade0dcd82 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -135,8 +135,7 @@ enum LedgerEntryType : std::uint16_t { LSF_FLAG(lsfDisallowIncomingPayChan, 0x10000000) /* True, reject new paychans */ \ LSF_FLAG(lsfDisallowIncomingTrustline, 0x20000000) /* True, reject new trustlines (only if no issued assets) */ \ LSF_FLAG(lsfAllowTrustLineLocking, 0x40000000) /* True, enable trustline locking */ \ - LSF_FLAG(lsfAllowTrustLineClawback, 0x80000000) /* True, enable clawback */ \ - LSF_FLAG(lsfDisallowIncomingSponsor, 0x00004000)) /* True, reject new sponsor */ \ + LSF_FLAG(lsfAllowTrustLineClawback, 0x80000000)) /* True, enable clawback */ \ \ LEDGER_OBJECT(Offer, \ LSF_FLAG(lsfPassive, 0x00010000) \ diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 03a118f6f84..ad5ad8e46ab 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -437,8 +437,7 @@ inline constexpr FlagValue tfDepositSubTx = ASF_FLAG(asfDisallowIncomingPayChan, 14) \ ASF_FLAG(asfDisallowIncomingTrustline, 15) \ ASF_FLAG(asfAllowTrustLineClawback, 16) \ - ASF_FLAG(asfAllowTrustLineLocking, 17) \ - ASF_FLAG(asfDisallowIncomingSponsor, 19) + ASF_FLAG(asfAllowTrustLineLocking, 17) #define ACCOUNTSET_FLAG_TO_VALUE(name, value) inline constexpr FlagValue name = value; #define ACCOUNTSET_FLAG_TO_MAP(name, value) {#name, value}, diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index d5bd7e36f7f..e0d599be6ba 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -150,10 +150,6 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) if (ctx.tx.isFlag(tfDeleteObject) && !sponsorObjSle) return tecNO_ENTRY; - if (sponseeSle->isFlag(lsfDisallowIncomingSponsor) && !sponsorObjSle) - // new sponsor creation is not allowed by disallowIncomingSponsor flag - return tecNO_PERMISSION; - return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/account/SetAccount.cpp b/src/libxrpl/tx/transactors/account/SetAccount.cpp index f08abb13f03..032b4727adf 100644 --- a/src/libxrpl/tx/transactors/account/SetAccount.cpp +++ b/src/libxrpl/tx/transactors/account/SetAccount.cpp @@ -584,14 +584,6 @@ SetAccount::doApply() else if (uClearFlag == asfDisallowIncomingTrustline) uFlagsOut &= ~lsfDisallowIncomingTrustline; - if (ctx_.view().rules().enabled(featureSponsor)) - { - if (uSetFlag == asfDisallowIncomingSponsor) - uFlagsOut |= lsfDisallowIncomingSponsor; - else if (uClearFlag == asfDisallowIncomingSponsor) - uFlagsOut &= ~lsfDisallowIncomingSponsor; - } - // Set or clear flags for disallowing escrow if (ctx_.view().rules().enabled(featureTokenEscrow)) { diff --git a/src/test/app/AccountSet_test.cpp b/src/test/app/AccountSet_test.cpp index 3ac71672d79..c2abb9d759b 100644 --- a/src/test/app/AccountSet_test.cpp +++ b/src/test/app/AccountSet_test.cpp @@ -64,8 +64,7 @@ class AccountSet_test : public beast::unit_test::suite } if (flag == asfDisallowIncomingCheck || flag == asfDisallowIncomingPayChan || - flag == asfDisallowIncomingNFTokenOffer || - flag == asfDisallowIncomingTrustline || flag == asfDisallowIncomingSponsor) + flag == asfDisallowIncomingNFTokenOffer || flag == asfDisallowIncomingTrustline) { // These flags are part of the DisallowIncoming amendment // and are tested elsewhere diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 4c237726f6c..164c519698a 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -186,8 +186,6 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set(sponsor, tfDeleteObject), sponsor::sponseeAcc(alice), ter(tecNO_ENTRY)); env.close(); - // DisallowIncomingSponsor: tested in other testcase - // insufficent reserve to create sponsorship adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tecUNFUNDED)); @@ -4832,48 +4830,6 @@ class Sponsor_test : public beast::unit_test::suite } } - void - testDisallowIncoming() - { - testcase("DisallowIncoming"); - using namespace test::jtx; - Env env{*this, testable_amendments()}; - Account const alice("alice"); - Account const sponsor("sponsor"); - - env.fund(XRP(1000000), alice, sponsor); - env.close(); - - // set DisallowIncomingSponsor - env(fset(alice, asfDisallowIncomingSponsor)); - env.close(); - - // Create sponsor should fail - env(sponsor::set(sponsor, 0, 100, XRP(100)), - sponsor::sponseeAcc(alice), - ter(tecNO_PERMISSION)); - env.close(); - - // clear flag - env(fclear(alice, asfDisallowIncomingSponsor)); - env.close(); - - // Create sponsor - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); - env.close(); - - // set flag - env(fset(alice, asfDisallowIncomingSponsor)); - env.close(); - - // Update sponsor should success - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); - env.close(); - - // Delete sponsor should success - env(sponsor::set(sponsor, tfDeleteObject), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); - env.close(); - } void testAccountDelete() { @@ -5309,8 +5265,6 @@ class Sponsor_test : public beast::unit_test::suite testSponsorFee(); testSponsorAccount(); - testDisallowIncoming(); - testAccountDelete(); testDelegatePermission(); From d95b6e41e56abe9afeabf57973ab682a3384947d Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 21:15:41 +0900 Subject: [PATCH 155/249] Refactor variable declarations in xrpLiquid --- src/libxrpl/ledger/View.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index fd4da70a83b..0650d359114 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -639,10 +639,10 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, std::uint32_t const ownerCount = confineOwnerCount(view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); - std::size_t sponsoredOwnerCount = sle->getFieldU32(sfSponsoredOwnerCount); - std::size_t sponsoringOwnerCount = sle->getFieldU32(sfSponsoringOwnerCount); - bool isAccountSponsored = sle->isFieldPresent(sfSponsor); - std::size_t sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount); + std::uint32_t const sponsoredOwnerCount = sle->getFieldU32(sfSponsoredOwnerCount); + std::uint32_t const sponsoringOwnerCount = sle->getFieldU32(sfSponsoringOwnerCount); + bool const isAccountSponsored = sle->isFieldPresent(sfSponsor); + std::uint32_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount); // Pseudo-accounts have no reserve requirement auto const reserve = isPseudoAccount(sle) ? XRPAmount{0} From 024ab07eb47dc2d4832a9c5a8479a05cf4ac02a2 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 21:35:08 +0900 Subject: [PATCH 156/249] minor fixes --- src/libxrpl/ledger/CredentialHelpers.cpp | 4 ++-- src/libxrpl/ledger/View.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libxrpl/ledger/CredentialHelpers.cpp b/src/libxrpl/ledger/CredentialHelpers.cpp index 04a8a191230..69e8ccc3233 100644 --- a/src/libxrpl/ledger/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/CredentialHelpers.cpp @@ -71,8 +71,8 @@ deleteSLE(ApplyView& view, std::shared_ptr const& sleCredential, beast::Jou if (isOwner) { - auto const sponsor = getLedgerEntryReserveSponsor(view, sleCredential); - adjustOwnerCount(view, sleAccount, sponsor, -1, j); + auto const sponsorSle = getLedgerEntryReserveSponsor(view, sleCredential); + adjustOwnerCount(view, sleAccount, sponsorSle, -1, j); } return tesSUCCESS; diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 0650d359114..5730b1a7ce7 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1518,7 +1518,7 @@ addEmptyHolding( auto const& dstId = accountID; auto const high = srcId > dstId; auto const index = keylet::line(srcId, dstId, currency); - auto const sleSrc = view.peek(keylet::account(srcId)); + auto const sleSrc = view.read(keylet::account(srcId)); auto const sleDst = view.peek(keylet::account(dstId)); if (!sleDst || !sleSrc) return tefINTERNAL; // LCOV_EXCL_LINE @@ -1536,7 +1536,7 @@ addEmptyHolding( tx, sleDst, priorBalance, - sponsorAccountID ? view.peek(keylet::account(*sponsorAccountID)) + sponsorAccountID ? view.read(keylet::account(*sponsorAccountID)) : std::shared_ptr(), 1); !isTesSuccess(ret)) @@ -1571,12 +1571,12 @@ addEmptyHolding( beast::Journal journal) { auto const& mptID = mptIssue.getMptID(); - auto const mpt = view.peek(keylet::mptIssuance(mptID)); + auto const mpt = view.read(keylet::mptIssuance(mptID)); if (!mpt) return tefINTERNAL; // LCOV_EXCL_LINE if (mpt->isFlag(lsfMPTLocked)) return tefINTERNAL; // LCOV_EXCL_LINE - if (view.peek(keylet::mptoken(mptID, accountID))) + if (view.exists(keylet::mptoken(mptID, accountID))) return tecDUPLICATE; if (accountID == mptIssue.getIssuer()) return tesSUCCESS; From 234d2e54141860f212a9e78b011effcae9759534 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Mar 2026 21:38:25 +0900 Subject: [PATCH 157/249] Refactor ownerCount and calculateReserve to use SLE::const_ref --- include/xrpl/ledger/View.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 4a32044e5c2..5048bf09e66 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -469,16 +469,10 @@ areCompatible( char const* reason); uint32_t -ownerCount(std::shared_ptr const& sponsorSle); +ownerCount(SLE::const_ref sponsorSle); XRPAmount -calculateReserve(std::shared_ptr const& sle, Fees const& fees); - -// bool -// isReserveSponsored(STTx const& tx); - -// bool -// isSponsorReserveCoSigning(STTx const& tx); +calculateReserve(SLE::const_ref sle, Fees const& fees); TER checkInsufficientReserve( From 8baec5634c03d5c23e0e274ffa120537d00c3f9e Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Mar 2026 00:03:31 +0900 Subject: [PATCH 158/249] pre-commit run --all-files --- .../tx/transactors/lending/LoanSet.cpp | 11 +++++---- src/libxrpl/tx/transactors/token/SetTrust.cpp | 24 ++++++++++--------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index 137ebe999d8..36c00bcb9af 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -84,11 +84,12 @@ LoanSet::preflight(PreflightContext const& ctx) !validNumericMinimum(paymentInterval, LoanSet::minPaymentInterval)) return temINVALID; // Grace period is between min default value and payment interval - else if (auto const gracePeriod = tx[~sfGracePeriod]; // - !validNumericRange( - gracePeriod, - paymentInterval.value_or(LoanSet::defaultPaymentInterval), - defaultGracePeriod)) + else if ( + auto const gracePeriod = tx[~sfGracePeriod]; // + !validNumericRange( + gracePeriod, + paymentInterval.value_or(LoanSet::defaultPaymentInterval), + defaultGracePeriod)) return temINVALID; // Copied from preflight2 diff --git a/src/libxrpl/tx/transactors/token/SetTrust.cpp b/src/libxrpl/tx/transactors/token/SetTrust.cpp index 8e5330861f3..c760228b3ee 100644 --- a/src/libxrpl/tx/transactors/token/SetTrust.cpp +++ b/src/libxrpl/tx/transactors/token/SetTrust.cpp @@ -602,9 +602,10 @@ SetTrust::doApply() terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); - !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) + else if ( + auto const ret = + checkInsufficientReserve(view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); + !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -632,14 +633,15 @@ SetTrust::doApply() JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if (auto const ret = checkInsufficientReserve( - ctx_.view(), - ctx_.tx, - sle, - preFeeBalance_, - txSponsorSle, - 1); - !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. + else if ( + auto const ret = checkInsufficientReserve( + ctx_.view(), + ctx_.tx, + sle, + preFeeBalance_, + txSponsorSle, + 1); + !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; From b32ff18c21ae09c07fc7531c8d72622461946456 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 18 Mar 2026 00:19:21 +0900 Subject: [PATCH 159/249] refactor getFeePayer() --- src/libxrpl/tx/Transactor.cpp | 36 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index e3c0f167880..8e57f045e08 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -1176,36 +1176,28 @@ Transactor::reset(XRPAmount fee) FeePayer Transactor::getFeePayer(ReadView const& view, STTx const& tx) { - auto const id = tx.getAccountID(sfAccount); - if (tx.isFieldPresent(sfSponsor) && tx.getFieldU32(sfSponsorFlags) & spfSponsorFee) + if (tx.isFieldPresent(sfSponsor) && (tx.getFieldU32(sfSponsorFlags) & spfSponsorFee)) { - auto const sponsor = tx.getAccountID(sfSponsor); - auto const hasSignature = tx.isFieldPresent(sfSponsorSignature); - auto const sponsorKeylet = keylet::sponsor(sponsor, id); - - if (hasSignature) - { - // if pre-funded sponsorship exists, prefer it - if (view.exists(sponsorKeylet)) - return FeePayer{sponsorKeylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; + auto const sponsorAccountID = tx.getAccountID(sfSponsor); + auto const sponseeAccountID = tx.getAccountID(sfAccount); + auto const hasSponsorSignature = tx.isFieldPresent(sfSponsorSignature); + auto const sponsorshipKeylet = keylet::sponsor(sponsorAccountID, sponseeAccountID); + // if pre-funded sponsorship exists, prefer it + if (hasSponsorSignature && !view.exists(sponsorshipKeylet)) // co-signed - auto const sponsorAccountKeylet = keylet::account(sponsor); - return FeePayer{sponsorAccountKeylet, sfBalance, FeePayerType::SponsorCoSigned}; - } + return FeePayer{ + keylet::account(sponsorAccountID), sfBalance, FeePayerType::SponsorCoSigned}; // pre funded - return FeePayer{sponsorKeylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; + return FeePayer{sponsorshipKeylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; } - if (tx.isFieldPresent(sfDelegate)) - { - auto const delegatorKeylet = keylet::account(tx.getAccountID(sfDelegate)); - return FeePayer{delegatorKeylet, sfBalance, FeePayerType::Delegate}; - } + auto const payerAccountKeylet = keylet::account(tx.getFeePayer()); + auto const payerType = + tx.isFieldPresent(sfDelegate) ? FeePayerType::Delegate : FeePayerType::Account; - auto const accountKeylet = keylet::account(id); - return FeePayer{accountKeylet, sfBalance, FeePayerType::Account}; + return FeePayer{payerAccountKeylet, sfBalance, payerType}; } // The sole purpose of this function is to provide a convenient, named From 6555001cd9f7c8e542f48cdc415b64406382f445 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 19 Mar 2026 11:50:55 +0900 Subject: [PATCH 160/249] make sponsor helpers inline --- include/xrpl/ledger/SponsorHelpers.h | 61 ++++++++++++++++++++----- src/libxrpl/ledger/SponsorHelpers.cpp | 65 --------------------------- 2 files changed, 49 insertions(+), 77 deletions(-) delete mode 100644 src/libxrpl/ledger/SponsorHelpers.cpp diff --git a/include/xrpl/ledger/SponsorHelpers.h b/include/xrpl/ledger/SponsorHelpers.h index 500f25c654e..3b9ae39a579 100644 --- a/include/xrpl/ledger/SponsorHelpers.h +++ b/include/xrpl/ledger/SponsorHelpers.h @@ -6,17 +6,33 @@ #include #include #include +#include namespace xrpl { -bool -isReserveSponsored(STTx const& tx); +inline bool +isReserveSponsored(STTx const& tx) +{ + return tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve; +} -bool -isSponsorReserveCoSigning(STTx const& tx); +inline bool +isSponsorReserveCoSigning(STTx const& tx) +{ + if (!tx.isFieldPresent(sfSponsorSignature)) + return false; + return isReserveSponsored(tx); +} -std::optional -getTxReserveSponsorAccountID(STTx const& tx); +inline std::optional +getTxReserveSponsorAccountID(STTx const& tx) +{ + if (tx.isFieldPresent(sfSponsor) && isReserveSponsored(tx)) + { + return tx.getAccountID(sfSponsor); + } + return {}; +} inline std::shared_ptr getTxReserveSponsor(ApplyView& view, STTx const& tx) @@ -36,10 +52,15 @@ getTxReserveSponsor(ReadView const& view, STTx const& tx) return {}; } -std::optional +inline std::optional getLedgerEntryReserveSponsorAccountID( std::shared_ptr const& sle, - SF_ACCOUNT const& field = sfSponsor); + SF_ACCOUNT const& field = sfSponsor) +{ + if (sle->isFieldPresent(field)) + return sle->getAccountID(field); + return {}; +} inline std::shared_ptr getLedgerEntryReserveSponsor( @@ -65,13 +86,29 @@ getLedgerEntryReserveSponsor( return {}; } -void +inline void addSponsorToLedgerEntry( std::shared_ptr const& sle, std::shared_ptr const& sponsorSle, - SF_ACCOUNT const& field = sfSponsor); + SF_ACCOUNT const& field = sfSponsor) +{ + XRPL_ASSERT( + (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || + (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), + "addSponsorToLedgerEntry : Invalid field to the LedgerEntry"); + if (sponsorSle) + sle->setAccountID(field, sponsorSle->getAccountID(sfAccount)); +} -void -removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsor); +inline void +removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsor) +{ + XRPL_ASSERT( + (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || + (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), + "removeSponsorFromLedgerEntry : Invalid field to the LedgerEntry"); + if (sle->isFieldPresent(field)) + sle->makeFieldAbsent(field); +} } // namespace xrpl diff --git a/src/libxrpl/ledger/SponsorHelpers.cpp b/src/libxrpl/ledger/SponsorHelpers.cpp deleted file mode 100644 index d86b7bfe3c7..00000000000 --- a/src/libxrpl/ledger/SponsorHelpers.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -namespace xrpl { - -bool -isReserveSponsored(STTx const& tx) -{ - return tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve; -} - -bool -isSponsorReserveCoSigning(STTx const& tx) -{ - if (!tx.isFieldPresent(sfSponsorSignature)) - return false; - return isReserveSponsored(tx); -} - -std::optional -getTxReserveSponsorAccountID(STTx const& tx) -{ - if (tx.isFieldPresent(sfSponsor) && isReserveSponsored(tx)) - { - return tx.getAccountID(sfSponsor); - } - return {}; -} - -std::optional -getLedgerEntryReserveSponsorAccountID( - std::shared_ptr const& sle, - SF_ACCOUNT const& field) -{ - if (sle->isFieldPresent(field)) - return sle->getAccountID(field); - return {}; -} - -void -addSponsorToLedgerEntry( - std::shared_ptr const& sle, - std::shared_ptr const& sponsorSle, - SF_ACCOUNT const& field) -{ - XRPL_ASSERT( - (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || - (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), - "addSponsorToLedgerEntry : Invalid field to the LedgerEntry"); - if (sponsorSle) - sle->setAccountID(field, sponsorSle->getAccountID(sfAccount)); -} - -void -removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field) -{ - XRPL_ASSERT( - (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || - (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), - "removeSponsorFromLedgerEntry : Invalid field to the LedgerEntry"); - if (sle->isFieldPresent(field)) - sle->makeFieldAbsent(field); -} - -} // namespace xrpl From e40e15a86630f76ac1f7780e762a7156515b4d6d Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 19 Mar 2026 14:01:11 +0900 Subject: [PATCH 161/249] fix C2, C4 --- src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp | 2 ++ src/test/app/Sponsor_test.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 40e155cd532..2b2ec6647a2 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -534,6 +534,8 @@ SponsorshipTransfer::doApply() // increment new sponsoring count auto const newSponsorAccountID = tx.getAccountID(sfSponsor); auto const newSponsorSle = view().peek(keylet::account(newSponsorAccountID)); + if (!newSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); view().update(newSponsorSle); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 164c519698a..890e232487c 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2936,7 +2936,6 @@ class Sponsor_test : public beast::unit_test::suite auto const MPT = mptGw["MPT"]; env(pay(gw, alice, MPT(10'000))); env.close(); - return; // create Escrow from alice to bob auto const seq = env.seq(alice); From f8a8c2301cb4386ae1d9302fe7530f8ba92a0280 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 19 Mar 2026 14:33:55 +0900 Subject: [PATCH 162/249] fix M2 --- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 10 +++++-- src/test/app/Sponsor_test.cpp | 29 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 15e8d3b1b3c..6a1d1822c66 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -210,9 +210,6 @@ SponsorshipSet::doApply() auto reserveSponsorAccSle = getTxReserveSponsor(view(), ctx_.tx); - if (feeAmount && (*feeAmount).xrp() > (*sponsorAccSle)[sfBalance]) - return tecUNFUNDED; - if (!sponsorObjSle) { // Create @@ -225,6 +222,9 @@ SponsorshipSet::doApply() (*newSle)[sfOwner] = sponsorAccountID; (*newSle)[sfSponsee] = sponseeAccountID; + if (feeAmount && (*feeAmount).xrp() > (*sponsorAccSle)[sfBalance]) + return tecUNFUNDED; + if (feeAmount && *feeAmount > XRPAmount(0)) { (*sponsorAccSle)[sfBalance] -= *feeAmount; @@ -267,6 +267,10 @@ SponsorshipSet::doApply() auto const currentFeeAmount = (*sponsorObjSle)[~sfFeeAmount].value_or(XRPAmount(0)); auto feeAmountDelta = XRPAmount(*feeAmount - currentFeeAmount); + if (feeAmountDelta > beast::zero && + feeAmountDelta > (*sponsorAccSle)[sfBalance]) + return tecUNFUNDED; + // transfer feeAmount to ledger entry if (feeAmountDelta != beast::zero) { diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 890e232487c..a83362258b6 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -214,6 +214,35 @@ class Sponsor_test : public beast::unit_test::suite fee(XRP(1)), ter(tesSUCCESS)); env.close(); + + // delta-based balance check + // After create: sponsor balance ~ 0, feeAmount = XRP(1000) + + // Decreasing feeAmount should succeed (refund, negative delta) + adjustAccountXRPBalance(env, sponsor, XRP(500)); + env(sponsor::set_fee(sponsor, 0, XRP(800)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tesSUCCESS)); + env.close(); + // balance was 500, delta = 800-1000 = -200 (refund), balance = 500+200-1 = 699 + + // Increasing feeAmount within delta budget should succeed + adjustAccountXRPBalance(env, sponsor, XRP(500)); + env(sponsor::set_fee(sponsor, 0, XRP(850)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tesSUCCESS)); + env.close(); + // balance was 500, delta = 850-800 = 50, balance = 500-50-1 = 449 + + // Increasing feeAmount where delta exceeds balance should fail + adjustAccountXRPBalance(env, sponsor, XRP(310)); + env(sponsor::set_fee(sponsor, 0, XRP(1200)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tecUNFUNDED)); + env.close(); } void From 9a6432dd66808ac159b80711925495f0d26876d7 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 19 Mar 2026 14:50:09 +0900 Subject: [PATCH 163/249] fix M3 --- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 19 ++++++++++++------- src/test/app/Sponsor_test.cpp | 10 +++++++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 6a1d1822c66..073226b96b7 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -215,11 +215,6 @@ SponsorshipSet::doApply() // Create auto newSle = std::make_shared(sponsorKeylet); - if (auto const ret = checkInsufficientReserve( - ctx_.view(), ctx_.tx, sponsorAccSle, preFeeBalance_, reserveSponsorAccSle, 1); - !isTesSuccess(ret)) - return tecUNFUNDED; - (*newSle)[sfOwner] = sponsorAccountID; (*newSle)[sfSponsee] = sponseeAccountID; if (feeAmount && (*feeAmount).xrp() > (*sponsorAccSle)[sfBalance]) @@ -230,6 +225,17 @@ SponsorshipSet::doApply() (*sponsorAccSle)[sfBalance] -= *feeAmount; (*newSle)[sfFeeAmount] = *feeAmount; } + + if (auto const ret = checkInsufficientReserve( + ctx_.view(), + ctx_.tx, + sponsorAccSle, + STAmount{(*sponsorAccSle)[sfBalance]}.xrp(), + reserveSponsorAccSle, + 1); + !isTesSuccess(ret)) + return tecUNFUNDED; + if (maxFee && *maxFee > XRPAmount(0)) (*newSle)[sfMaxFee] = *maxFee; if (reserveCount && *reserveCount > 0) @@ -267,8 +273,7 @@ SponsorshipSet::doApply() auto const currentFeeAmount = (*sponsorObjSle)[~sfFeeAmount].value_or(XRPAmount(0)); auto feeAmountDelta = XRPAmount(*feeAmount - currentFeeAmount); - if (feeAmountDelta > beast::zero && - feeAmountDelta > (*sponsorAccSle)[sfBalance]) + if (feeAmountDelta > beast::zero && feeAmountDelta > (*sponsorAccSle)[sfBalance]) return tecUNFUNDED; // transfer feeAmount to ledger entry diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index a83362258b6..2b83fc634fc 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -187,8 +187,11 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // insufficent reserve to create sponsorship - adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tecUNFUNDED)); + adjustAccountXRPBalance(env, sponsor, XRP(100) + XRP(1) + reserve(env, 1) - drops(1)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tecUNFUNDED)); env.close(); // FeeAmount + Fee > Balance @@ -208,7 +211,8 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // create sponsor to use above tests - adjustAccountXRPBalance(env, sponsor, XRP(1001)); + // need feeAmount(1000) + fee(1) + reserve(~250) = ~1251 + adjustAccountXRPBalance(env, sponsor, XRP(1000) + XRP(1) + reserve(env, 1)); env(sponsor::set(sponsor, 0, 100, XRP(1000)), sponsor::sponseeAcc(alice), fee(XRP(1)), From d6803868a1cfc0d854440185cd822419d49b34be Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 19 Mar 2026 15:00:09 +0900 Subject: [PATCH 164/249] fix M5 --- .../Sponsor/SponsorshipTransfer.cpp | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 2b2ec6647a2..41665c75b17 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -351,13 +351,19 @@ adjustReserveCount( int32_t const afterReserveCount = reserveCount + delta; if (afterReserveCount < 0) + { // already checked in preclaim() return tefINTERNAL; // LCOV_EXCL_LINE + } if (afterReserveCount == 0) + { sponsorSle->makeFieldAbsent(sfReserveCount); + } else + { sponsorSle->setFieldU32(sfReserveCount, afterReserveCount); + } view.update(sponsorSle); return tesSUCCESS; } @@ -377,11 +383,15 @@ SponsorshipTransfer::doApply() return tefINTERNAL; // LCOV_EXCL_LINE auto const setSponsorFieldU32 = [](auto const& sle, auto const& field, auto const& delta) { - auto const newValue = sle->getFieldU32(field) + delta; - if (newValue == 0) + int32_t const newValue = static_cast(sle->getFieldU32(field)) + delta; + if (newValue <= 0) + { sle->makeFieldAbsent(field); + } else - sle->setFieldU32(field, newValue); + { + sle->setFieldU32(field, static_cast(newValue)); + } }; if (isObjectSponsor) @@ -475,10 +485,12 @@ SponsorshipTransfer::doApply() if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsorAccountID, account_)); sponsorSle) + { if (auto const ter = adjustReserveCount(view(), account_, oldSponsorAccountID, ownerCountDelta); !isTesSuccess(ter)) return ter; + } } else if (flags & tfSponsorshipEnd) { @@ -501,10 +513,12 @@ SponsorshipTransfer::doApply() if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsorAccountID, account_)); sponsorSle) + { if (auto const ter = adjustReserveCount(view(), account_, oldSponsorAccountID, ownerCountDelta); !isTesSuccess(ter)) return ter; + } // remove sponsor from object objSle->makeFieldAbsent(sponsorField); From d1346fa3f60a8b69ef01b4aa0a79b7d26c4c8a46 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 19 Mar 2026 15:14:14 +0900 Subject: [PATCH 165/249] fix M6 --- src/test/rpc/AccountObjects_test.cpp | 95 +++++++++++++++++++++++ src/xrpld/rpc/handlers/AccountObjects.cpp | 16 +++- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 81a77744cde..b3a00638ccc 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -1357,6 +1358,99 @@ class AccountObjects_test : public beast::unit_test::suite } } + void + testSponsoredFilter() + { + testcase("SponsoredFilter"); + using namespace jtx; + + Env env(*this, testable_amendments()); + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const gw("gw"); + auto const USD = gw["USD"]; + + env.fund(XRP(10000), alice, bob, sponsor1, gw); + env.close(); + + // Helper to call account_objects with sponsored filter + auto acctObjsSponsored = [&env]( + AccountID const& acct, + bool sponsored, + std::optional const& type = std::nullopt) { + Json::Value params; + params[jss::account] = to_string(acct); + params[jss::sponsored] = sponsored; + if (type) + params[jss::type] = *type; + params[jss::ledger_index] = "validated"; + return env.rpc("json", "account_objects", to_string(params)); + }; + + // Create a sponsorship (alice sponsors bob) + env(sponsor::set(alice, 0, 100, XRP(100)), sponsor::sponseeAcc(bob), fee(XRP(1))); + env.close(); + + // Create a trust line for bob (not sponsored) + env(trust(bob, USD(1000))); + env.close(); + + // sponsored=true should find the sponsorship object for bob + { + auto const resp = acctObjsSponsored(bob.id(), true); + auto const& objs = resp[jss::result][jss::account_objects]; + // bob has sponsorship object (which has sfSponsor? no - it's in owner dir) + // Actually the Sponsorship SLE itself doesn't have sfSponsor on it + // Let's just check the count + BEAST_EXPECT(objs.size() >= 0); + } + + // Now sponsor bob's trust line + auto const trustId = keylet::line(bob, gw, USD.currency); + BEAST_EXPECT(env.le(trustId)); + + env(sponsor::transfer(bob, tfSponsorshipCreate, trustId.key), + sponsor::as(sponsor1, spfSponsorReserve), + sig(sfSponsorSignature, sponsor1)); + env.close(); + + // Verify trust line has sponsor field + { + auto const sle = env.le(trustId); + BEAST_EXPECT(sle->isFieldPresent(sfHighSponsor) || sle->isFieldPresent(sfLowSponsor)); + } + + // sponsored=true on bob should include the sponsored trust line + { + auto const resp = acctObjsSponsored(bob.id(), true); + auto const& objs = resp[jss::result][jss::account_objects]; + bool foundTrustLine = false; + for (auto const& obj : objs) + { + if (obj[sfLedgerEntryType.jsonName] == jss::RippleState) + foundTrustLine = true; + } + BEAST_EXPECT(foundTrustLine); + } + + // sponsored=false on bob should NOT include the sponsored trust line + { + auto const resp = acctObjsSponsored(bob.id(), false); + auto const& objs = resp[jss::result][jss::account_objects]; + bool foundSponsoredTrustLine = false; + for (auto const& obj : objs) + { + if (obj[sfLedgerEntryType.jsonName] == jss::RippleState) + { + if (obj.isMember(sfHighSponsor.jsonName) || obj.isMember(sfLowSponsor.jsonName)) + foundSponsoredTrustLine = true; + } + } + BEAST_EXPECT(!foundSponsoredTrustLine); + } + } + void run() override { @@ -1367,6 +1461,7 @@ class AccountObjects_test : public beast::unit_test::suite testNFTsMarker(); testAccountNFTs(); testAccountObjectMarker(); + testSponsoredFilter(); } }; diff --git a/src/xrpld/rpc/handlers/AccountObjects.cpp b/src/xrpld/rpc/handlers/AccountObjects.cpp index 5d896302f05..e59c056009d 100644 --- a/src/xrpld/rpc/handlers/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/AccountObjects.cpp @@ -334,9 +334,19 @@ getAccountObjects( !typeMatchesFilter(typeFilter.value(), sleNode->getType())) canAppend = false; - std::optional const sponsor = sleNode->isFieldPresent(sfSponsor) - ? sleNode->getAccountID(sfSponsor) - : std::optional(std::nullopt); + auto const getSponsor = [&sleNode]() -> std::optional { + if (sleNode->isFieldPresent(sfSponsor)) + return sleNode->getAccountID(sfSponsor); + if (sleNode->getType() == ltRIPPLE_STATE) + { + if (sleNode->isFieldPresent(sfHighSponsor)) + return sleNode->getAccountID(sfHighSponsor); + if (sleNode->isFieldPresent(sfLowSponsor)) + return sleNode->getAccountID(sfLowSponsor); + } + return std::nullopt; + }; + std::optional const sponsor = getSponsor(); if (sponsored.has_value() && !sponsoredMatchesFilter(sponsored.value(), sponsor)) canAppend = false; From 6d7ffcbb914971faf3f363701c65928d049af392 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 19 Mar 2026 15:39:55 +0900 Subject: [PATCH 166/249] fix M7 --- src/libxrpl/tx/invariants/InvariantCheck.cpp | 23 +++++-- src/test/app/Invariants_test.cpp | 72 ++++++++++++++++++++ 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 90679c2dcf4..dd31f02ca2c 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -497,6 +497,20 @@ AccountRootsDeletedClean::finalize( if (enforce) return false; } + // An account should not be deleted with sponsorship fields + if (after->isFieldPresent(sfSponsoredOwnerCount) || + after->isFieldPresent(sfSponsoringOwnerCount) || + after->isFieldPresent(sfSponsoringAccountCount) || after->isFieldPresent(sfSponsor)) + { + JLOG(j.fatal()) << "Invariant failed: account deletion left " + "behind a sponsorship field"; + XRPL_ASSERT( + enforce, + "xrpl::AccountRootsDeletedClean::finalize : " + "deleted account has no sponsorship fields"); + if (enforce) + return false; + } // Simple types for (auto const& [keyletfunc, _1, _2] : directAccountKeylets) { @@ -849,10 +863,10 @@ ValidPseudoAccounts::visitEntry( // 1. Exactly one of the pseudo-account fields is set. // 2. The sequence number is not changed. // 3. The lsfDisableMaster, lsfDefaultRipple, and lsfDepositAuth - // flags are set. + // flags are set. // 4. The RegularKey is not set. - // 5. The SponsoredOwnerCount, SponsoringOwnerCount, and - // SponsorAccount fields are not set. + // 5. The SponsoredOwnerCount, SponsoringOwnerCount, SponsoringAccountCount, Sponsor + // fields are not set. { std::vector const& fields = getPseudoAccountFields(); @@ -880,7 +894,8 @@ ValidPseudoAccounts::visitEntry( errors_.emplace_back("pseudo-account has a regular key"); } if (after->isFieldPresent(sfSponsoredOwnerCount) || - after->isFieldPresent(sfSponsoringOwnerCount) || after->isFieldPresent(sfSponsor)) + after->isFieldPresent(sfSponsoringOwnerCount) || after->isFieldPresent(sfSponsor) || + after->isFieldPresent(sfSponsoringAccountCount)) { errors_.emplace_back("pseudo-account has a sponsorship field"); } diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index ee852445062..3f5a9aa08a3 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -277,6 +277,74 @@ class Invariants_test : public beast::unit_test::suite XRPAmount{}, STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + doInvariantCheck( + {{"account deletion left behind a sponsorship field"}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + auto const a1 = A1.id(); + auto const sleA1 = ac.view().peek(keylet::account(a1)); + if (!sleA1) + return false; + sleA1->at(sfBalance) = beast::zero; + sleA1->setFieldU32(sfSponsoredOwnerCount, 1); + + ac.view().erase(sleA1); + + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + + doInvariantCheck( + {{"account deletion left behind a sponsorship field"}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + auto const a1 = A1.id(); + auto const sleA1 = ac.view().peek(keylet::account(a1)); + if (!sleA1) + return false; + sleA1->at(sfBalance) = beast::zero; + sleA1->setFieldU32(sfSponsoringOwnerCount, 1); + + ac.view().erase(sleA1); + + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + + doInvariantCheck( + {{"account deletion left behind a sponsorship field"}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + auto const a1 = A1.id(); + auto const sleA1 = ac.view().peek(keylet::account(a1)); + if (!sleA1) + return false; + sleA1->at(sfBalance) = beast::zero; + sleA1->setFieldU32(sfSponsoringAccountCount, 1); + + ac.view().erase(sleA1); + + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + + doInvariantCheck( + {{"account deletion left behind a sponsorship field"}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + auto const a1 = A1.id(); + auto const sleA1 = ac.view().peek(keylet::account(a1)); + if (!sleA1) + return false; + sleA1->at(sfBalance) = beast::zero; + sleA1->setAccountID(sfSponsor, A2.id()); + + ac.view().erase(sleA1); + + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + for (auto const& keyletInfo : directAccountKeylets) { // TODO: Use structured binding once LLVM 16 is the minimum @@ -1650,6 +1718,10 @@ class Invariants_test : public beast::unit_test::suite "pseudo-account has a sponsorship field", [](SLE::pointer& sle) { sle->at(sfSponsoringOwnerCount) = 1; }, }, + { + "pseudo-account has a sponsorship field", + [](SLE::pointer& sle) { sle->at(sfSponsoringAccountCount) = 1; }, + }, { "pseudo-account has a sponsorship field", [](SLE::pointer& sle) { sle->at(sfSponsor) = Account("sponsor").id(); }, From b695b4bf9b07c3b1bcea7121977ae0eb9d2ea802 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 10:27:18 +0900 Subject: [PATCH 167/249] Add comments for PreFunded sponsor reserve checks on TrustSet --- src/libxrpl/tx/transactors/token/TrustSet.cpp | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index a918a386d2c..03e4d3c7a46 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -554,6 +554,8 @@ TrustSet::doApply() if (bLowReserveSet && !bLowReserved) { // should be checked PreFunded Sponsor before adjustOwnerCount() + // For PreFunded sponsors, we need to check if there are sufficient reserves before + // calling adjustOwnerCount(). if (auto const ret = checkInsufficientReserve( view(), ctx_.tx, sleLowAccount, preFeeBalance_, txSponsorSle, 1); isSponsoredAndPreFunded && !isTesSuccess(ret)) @@ -581,6 +583,8 @@ TrustSet::doApply() if (bHighReserveSet && !bHighReserved) { // should be checked PreFunded Sponsor before adjustOwnerCount() + // For PreFunded sponsors, we need to check if there are sufficient reserves before + // calling adjustOwnerCount(). if (auto const ret = checkInsufficientReserve( view(), ctx_.tx, sleHighAccount, preFeeBalance_, txSponsorSle, 1); isSponsoredAndPreFunded && !isTesSuccess(ret)) @@ -615,10 +619,9 @@ TrustSet::doApply() terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if ( - auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); - !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) + else if (auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); + !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -646,15 +649,14 @@ TrustSet::doApply() JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if ( - auto const ret = checkInsufficientReserve( - ctx_.view(), - ctx_.tx, - sle, - preFeeBalance_, - txSponsorSle, - 1); - !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. + else if (auto const ret = checkInsufficientReserve( + ctx_.view(), + ctx_.tx, + sle, + preFeeBalance_, + txSponsorSle, + 1); + !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; From 53bdfb2bc7de179b7cb5ff04907fa355768f8b62 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 10:28:27 +0900 Subject: [PATCH 168/249] Add comment to clarify that the door account should not have a sponsor in the reserve check --- src/libxrpl/tx/transactors/bridge/XChainBridge.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 8d18a19882c..ec4712ac6c5 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -1007,6 +1007,7 @@ applyCreateAccountAttestations( // Check reserve auto const balance = (*sleDoor)[sfBalance]; + // Door account should not have a sponsor if (auto const ret = checkInsufficientReserve(psb, tx, sleDoor, balance, {}, 1); !isTesSuccess(ret)) return Unexpected(ret); // tecINSUFFICIENT_RESERVE From 2f0adfae3b5f3bda938f8c27fb38c7d53f9439f4 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 10:55:42 +0900 Subject: [PATCH 169/249] fix M11 --- src/test/app/Sponsor_test.cpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 2b83fc634fc..c2725aceb7e 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -3255,24 +3255,17 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); // NFTokenMintOffer - adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); - env(token::mint(alice), - token::amount(XRP(10000)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env.close(); - - // testEachSponsorship( - // env, - // cosigning, - // sponsor, - // alice, - // 2, - // 2, - // tecINSUFFICIENT_RESERVE, - // [&](Env& env, auto const& submit) { - // token::mint(alice), token::amount(XRP(100)); - // }); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 2, + 2, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(token::mint(alice), token::amount(XRP(100))); + }); } { From 22bfc5e743f39b072561b5307c705b4eec6aa4d3 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 11:02:59 +0900 Subject: [PATCH 170/249] fix M15 --- src/test/app/Sponsor_test.cpp | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index c2725aceb7e..261c9cdb086 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -4952,6 +4952,69 @@ class Sponsor_test : public beast::unit_test::suite auto const sponsorSle2 = env.le(keylet::account(sponsor)); BEAST_EXPECT(!sponsorSle2->isFieldPresent(sfSponsoringAccountCount)); } + + { + // Sponsor with sfSponsoringOwnerCount cannot delete (tecHAS_OBLIGATIONS) + Env env{*this, testable_amendments()}; + Account const gw("gw"); + env.fund(XRP(1000000), alice, bob, sponsor, gw); + env.close(); + + auto const USD = gw["USD"]; + + // Create sponsorship allowing reserve sponsoring + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::sponseeAcc(alice), + ter(tesSUCCESS)); + env.close(); + + // Create a trust line for alice + env(trust(alice, USD(1000))); + env.close(); + + // Transfer reserve sponsorship of trust line to sponsor + auto const trustId = keylet::line(alice, gw, USD.currency); + BEAST_EXPECT(env.le(trustId)); + + env(sponsor::transfer(alice, tfSponsorshipCreate, trustId.key), + sponsor::as(sponsor, spfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + // Verify sfSponsoringOwnerCount is set on sponsor + auto const sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->isFieldPresent(sfSponsoringOwnerCount)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringOwnerCount) >= 1); + + incLgrSeqForAccDel(env, sponsor); + + // AccountDelete should fail + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(sponsor, bob), fee(requiredFee), ter(tecHAS_OBLIGATIONS)); + } + + { + // Sponsor with sfSponsoringAccountCount cannot delete (tecHAS_OBLIGATIONS) + Env env{*this, testable_amendments()}; + env.memoize(alice); + env.fund(XRP(1000000), bob, sponsor); + env.close(); + + // Create SponsoredAccount (sets sfSponsoringAccountCount on sponsor) + env(pay(sponsor, alice, XRP(10000)), txflags(tfSponsorCreatedAccount)); + env.close(); + + // Verify sfSponsoringAccountCount is set on sponsor + auto const sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->isFieldPresent(sfSponsoringAccountCount)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringAccountCount) == 1); + + incLgrSeqForAccDel(env, sponsor); + + // AccountDelete should fail + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(sponsor, bob), fee(requiredFee), ter(tecHAS_OBLIGATIONS)); + } } void From 968493f43beb2a9c817f82aa68fa34dc6b116a06 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 11:10:45 +0900 Subject: [PATCH 171/249] fix M16 --- src/test/app/Sponsor_test.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 261c9cdb086..b5be947ab2a 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1475,6 +1475,39 @@ class Sponsor_test : public beast::unit_test::suite // Payment is re-applied BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount)); } + + // RequireSignForFee: co-signing should succeed + { + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // set flag + env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), + sponsor::sponseeAcc(alice)); + env.close(); + + // pre-funded (no sig) should fail + env(pay(alice, bob, XRP(100)), + fee(XRP(1)), + sponsor::as(sponsor, spfSponsorFee), + ter(terNO_SPONSORSHIP)); + env.close(); + + // co-signing (with sig) should succeed + env(pay(alice, bob, XRP(100)), + fee(XRP(1)), + sponsor::as(sponsor, spfSponsorFee), + sig(sfSponsorSignature, sponsor), + ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT( + env.le(keylet::sponsor(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(9)); + } } void From 64f699a9745c693c74011c1766b3cfb013367d8a Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 11:14:41 +0900 Subject: [PATCH 172/249] fix M17 --- src/test/app/Sponsor_test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index b5be947ab2a..0297a023d9b 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1585,6 +1585,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // to non-funded account + auto const sponsor2BalanceBefore = env.balance(sponsor2); env(pay(sponsor2, charlie, drops(1)), txflags(tfSponsorCreatedAccount), fee(XRP(1))); env.close(); @@ -1593,6 +1594,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(charlieSle->getAccountID(sfSponsor) == sponsor2.id()); BEAST_EXPECT(sponsoredOwnerCount(env, charlie) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); + // verify sponsor balance decreased by payment + fee + BEAST_EXPECT(env.balance(sponsor2) == sponsor2BalanceBefore - drops(1) - XRP(1)); } } From 5280a185d068acc232c648a006d467f2e47623f8 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 11:59:46 +0900 Subject: [PATCH 173/249] fix L4 --- include/xrpl/protocol/TxFlags.h | 3 ++- src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp | 5 +++-- src/test/app/Sponsor_test.cpp | 10 ++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index ad5ad8e46ab..90da8d79cd2 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -352,7 +352,8 @@ inline constexpr FlagValue tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment); inline constexpr FlagValue tfTrustSetPermissionMask = ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze); inline constexpr FlagValue tfSponsorshipSetPermissionMask = - ~(tfUniversal | tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve); + ~(tfUniversal | tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve | + tfSponsorshipClearRequireSignForFee | tfSponsorshipClearRequireSignForReserve); // MPTokenIssuanceCreate MutableFlags: // Indicating specific fields or flags may be changed after issuance. diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 073226b96b7..8f1dcb567f8 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -112,9 +112,10 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) loadGranularPermission(sle, ttSPONSORSHIP_SET, granularPermissions); auto const sponsoringFee = tx.isFieldPresent(sfFeeAmount) || tx.isFieldPresent(sfMaxFee) || - txFlags & tfSponsorshipSetRequireSignForFee; + (txFlags & (tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee)); auto const sponsoringReserve = - tx.isFieldPresent(sfReserveCount) || txFlags & tfSponsorshipSetRequireSignForReserve; + tx.isFieldPresent(sfReserveCount) || + (txFlags & (tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve)); if (sponsoringFee && !granularPermissions.contains(SponsorFee)) return terNO_DELEGATE_PERMISSION; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 0297a023d9b..e3e230479fa 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -5138,6 +5138,11 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sponseeAcc(bob), delegate::as(carol), ter(result)); + // ClearRequireSignForFee flag + env(sponsor::set(alice, tfSponsorshipClearRequireSignForFee), + sponsor::sponseeAcc(bob), + delegate::as(carol), + ter(result)); env.close(); }; @@ -5182,6 +5187,11 @@ class Sponsor_test : public beast::unit_test::suite sponsor::sponseeAcc(bob), delegate::as(carol), ter(result)); + // ClearRequireSignForReserve flag + env(sponsor::set(alice, tfSponsorshipClearRequireSignForReserve), + sponsor::sponseeAcc(bob), + delegate::as(carol), + ter(result)); env.close(); }; From 7ddda1b1e6d384f06ff80602e6d80585beaab354 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 12:01:30 +0900 Subject: [PATCH 174/249] fix L5 --- src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 41665c75b17..3c63e3c42ba 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -34,7 +34,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) if (!isReserveSponsored(ctx.tx)) { JLOG(ctx.j.debug()) - << "preflight: spfSponsorReserve should not be set when creating sponsorship"; + << "preflight: spfSponsorReserve should be set when creating sponsorship"; return temINVALID_FLAG; } if (ctx.tx.isFieldPresent(sfSponsee)) From 1adaeac8a7a2ed22d7156fab25f8dbe1d9cacd11 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 12:14:44 +0900 Subject: [PATCH 175/249] fix L14 --- src/test/rpc/AccountObjects_test.cpp | 35 +++++++++++++++++++++++ src/xrpld/rpc/handlers/AccountObjects.cpp | 13 ++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index b3a00638ccc..9e59197afae 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -1449,6 +1449,41 @@ class AccountObjects_test : public beast::unit_test::suite } BEAST_EXPECT(!foundSponsoredTrustLine); } + + // NFT page sponsored filter + { + // Mint an NFT for bob (creates NFT page) + env(token::mint(bob, 0)); + env.close(); + + auto const nftPageKeylet = keylet::nftpage_max(bob); + BEAST_EXPECT(env.le(nftPageKeylet)); + + // Sponsor the NFT page + env(sponsor::transfer(bob, tfSponsorshipCreate, nftPageKeylet.key), + sponsor::as(sponsor1, spfSponsorReserve), + sig(sfSponsorSignature, sponsor1)); + env.close(); + + // Verify NFT page has sponsor field + BEAST_EXPECT(env.le(nftPageKeylet)->isFieldPresent(sfSponsor)); + + // sponsored=true should include the sponsored NFT page + // sponsored=false should NOT include the sponsored NFT page + for (auto const sponsored : {true, false}) + { + auto const resp = acctObjsSponsored(bob.id(), sponsored); + auto const& objs = resp[jss::result][jss::account_objects]; + bool foundNFTPage = false; + for (auto const& obj : objs) + { + if (obj[sfLedgerEntryType.jsonName] == jss::NFTokenPage && + obj.isMember(sfSponsor.jsonName)) + foundNFTPage = true; + } + BEAST_EXPECT(foundNFTPage == sponsored); + } + } } void diff --git a/src/xrpld/rpc/handlers/AccountObjects.cpp b/src/xrpld/rpc/handlers/AccountObjects.cpp index e59c056009d..ede0754fc30 100644 --- a/src/xrpld/rpc/handlers/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/AccountObjects.cpp @@ -239,7 +239,18 @@ getAccountObjects( while (cp) { - jvObjects.append(cp->getJson(JsonOptions::none)); + bool canAppendNFT = true; + if (sponsored.has_value()) + { + std::optional const nftSponsor = + cp->isFieldPresent(sfSponsor) + ? cp->getAccountID(sfSponsor) + : std::optional(std::nullopt); + if (!sponsoredMatchesFilter(sponsored.value(), nftSponsor)) + canAppendNFT = false; + } + if (canAppendNFT) + jvObjects.append(cp->getJson(JsonOptions::none)); auto const npm = (*cp)[~sfNextPageMin]; if (npm) { From de64bc785563563191d9751b9aaa50cd07886df7 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 12:34:33 +0900 Subject: [PATCH 176/249] fix L18 --- src/libxrpl/tx/Transactor.cpp | 33 ++++++++++++++++++++++++++------- src/test/app/Sponsor_test.cpp | 5 ++++- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 2745079f6e1..c387c3a5862 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -160,16 +160,28 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return temINVALID_FLAG; } - if (!hasSponsor && hasSponsorFlags) + if (!hasSponsor) { - JLOG(ctx.j.debug()) << "preflight1: sponsor flags without sponsor definition"; - return temINVALID_FLAG; - } + if (hasSponsorFlags) + { + JLOG(ctx.j.debug()) << "preflight1: sponsor flags without sponsor definition"; + return temINVALID_FLAG; + } - if (!hasSponsor && hasSponsorSig) + if (hasSponsorSig) + { + JLOG(ctx.j.debug()) << "preflight1: sponsor signature without sponsor definition"; + return temMALFORMED; + } + } + else if (hasSponsorFlags) { - JLOG(ctx.j.debug()) << "preflight1: sponsor signature without sponsor definition"; - return temMALFORMED; + auto const sponsorFlags = ctx.tx.getFieldU32(sfSponsorFlags); + if ((sponsorFlags & ~(spfSponsorFee | spfSponsorReserve)) || sponsorFlags == 0) + { + JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; + return temINVALID_FLAG; + } } if (auto const ret = preflight0(ctx, flagMask)) @@ -218,6 +230,13 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return temMALFORMED; } + // if (hasSponsor && hasSponsorFlags && + // ctx.tx.getFieldU32(sfSponsorFlags) == 0) + // { + // JLOG(ctx.j.debug()) << "preflight1: sponsor with no sponsorship flags"; + // return temINVALID_FLAG; + // } + return tesSUCCESS; } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e3e230479fa..9f56a5c8a5e 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -360,7 +360,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Invalid Sponsor Account (Account = Sponsor.Account) - env(noop(alice), sponsor::as(alice), ter(temMALFORMED)); + env(noop(alice), sponsor::as(alice, spfSponsorFee), ter(temMALFORMED)); // Invalid Sponsor Account // (SponsorSignature is specified but Sponsor.Account is not specified) @@ -378,6 +378,9 @@ class Sponsor_test : public beast::unit_test::suite sponsor::as(sponsor, (spfSponsorFee | spfSponsorReserve) + 1), ter(temINVALID_FLAG)); + // SponsorFlags=0 with valid sponsor (no sponsorship purpose) + env(noop(alice), sponsor::as(sponsor, 0), ter(temINVALID_FLAG)); + // Invalid Flags without sponsor auto tx = noop(alice); tx[sfSponsorFlags.jsonName] = spfSponsorFee | spfSponsorReserve; From fd393685dd666c80e94b4dec540d7f72b1aa3c31 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 12:46:17 +0900 Subject: [PATCH 177/249] fix L20 --- src/test/app/Sponsor_test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 9f56a5c8a5e..0ffc70de94e 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -480,6 +480,9 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(!sle->isFieldPresent(sfReserveCount)); BEAST_EXPECT(!sle->isFieldPresent(sfFeeAmount)); BEAST_EXPECT(!sle->isFieldPresent(sfMaxFee)); + // verify flags from previous sponsorship are not carried over + BEAST_EXPECT(!sle->isFlag(lsfSponsorshipRequireSignForFee)); + BEAST_EXPECT(!sle->isFlag(lsfSponsorshipRequireSignForReserve)); // update sponsorship with non-zero value env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), From 98257d3b96b189fd32d02627e59d40d687fe8404 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 13:07:28 +0900 Subject: [PATCH 178/249] Fix SponsorshipSet targeting Pseudo Accounts to return an error --- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 10 +++-- src/test/app/Sponsor_test.cpp | 43 +++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 8f1dcb567f8..fb150a1a999 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -113,9 +113,9 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) auto const sponsoringFee = tx.isFieldPresent(sfFeeAmount) || tx.isFieldPresent(sfMaxFee) || (txFlags & (tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee)); - auto const sponsoringReserve = - tx.isFieldPresent(sfReserveCount) || - (txFlags & (tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve)); + auto const sponsoringReserve = tx.isFieldPresent(sfReserveCount) || + (txFlags & + (tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve)); if (sponsoringFee && !granularPermissions.contains(SponsorFee)) return terNO_DELEGATE_PERMISSION; @@ -145,6 +145,10 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) if (!sponseeSle) return tecNO_DST; + // Pseudo accounts cannot be sponsors or sponsees + if (isPseudoAccount(sponsorAccSle) || isPseudoAccount(sponseeSle)) + return tecNO_PERMISSION; + // check if object exists auto const sponsorObjSle = ctx.view.read(keylet::sponsor(sponsorAccountID, sponseeAccountID)); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 0ffc70de94e..b512ccda8f1 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -249,6 +249,48 @@ class Sponsor_test : public beast::unit_test::suite env.close(); } + void + testPseudoAccountSponsorship() + { + testcase("Pseudo account sponsorship"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const gw("gw"); + Account const sp("sponsor"); + + Asset asset = gw["IOU"].asset(); + + env.fund(XRP(1000000), alice, bob, gw, sp); + env.close(); + + // Create a vault to get a pseudo account + Vault vault{env}; + auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); + env(tx); + env.close(); + + auto const vaultSle = env.le(keylet); + BEAST_EXPECT(vaultSle); + Account const pseudoAcc( + "vault", vaultSle->getAccountID(sfAccount)); + env.memoize(pseudoAcc); + + // Sponsee is a pseudo account -> tecNO_PERMISSION + env(sponsor::set(sp, 0, 100, XRP(100)), + sponsor::sponseeAcc(pseudoAcc), + ter(tecNO_PERMISSION)); + env.close(); + + // Sponsor is a pseudo account -> tecNO_PERMISSION + // (submitted by bob with counterpartySponsor pointing to pseudo account) + env(sponsor::set(bob, tfDeleteObject), + sponsor::counterpartySponsor(pseudoAcc), + ter(tecNO_PERMISSION)); + env.close(); + } + void testSingleSigning() { @@ -5391,6 +5433,7 @@ class Sponsor_test : public beast::unit_test::suite { testDisabled(); testInvalidSponsorshipSet(); + testPseudoAccountSponsorship(); testSingleSigning(); testMultiSigning(); From aacb8e4a68ddccad008e9a64f267755a75a5a22a Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 23 Mar 2026 13:31:36 +0900 Subject: [PATCH 179/249] return tem error when SponsorReserve flag is set in Batch OuterTxn --- src/libxrpl/tx/transactors/system/Batch.cpp | 11 +++++++++ src/test/app/Sponsor_test.cpp | 25 +++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/libxrpl/tx/transactors/system/Batch.cpp b/src/libxrpl/tx/transactors/system/Batch.cpp index 80938f62ff6..3423aaab484 100644 --- a/src/libxrpl/tx/transactors/system/Batch.cpp +++ b/src/libxrpl/tx/transactors/system/Batch.cpp @@ -194,6 +194,17 @@ Batch::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } + if (ctx.tx.isFieldPresent(sfSponsorFlags)) + { + auto const sponsorFlags = ctx.tx.getFieldU32(sfSponsorFlags); + if (sponsorFlags & spfSponsorReserve) + { + JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]:" + << "spfSponsorReserve is not allowed on outer Batch."; + return temINVALID_FLAG; + } + } + auto const& rawTxns = ctx.tx.getFieldArray(sfRawTransactions); if (rawTxns.size() <= 1) { diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index b512ccda8f1..de78cfc3810 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -273,8 +273,7 @@ class Sponsor_test : public beast::unit_test::suite auto const vaultSle = env.le(keylet); BEAST_EXPECT(vaultSle); - Account const pseudoAcc( - "vault", vaultSle->getAccountID(sfAccount)); + Account const pseudoAcc("vault", vaultSle->getAccountID(sfAccount)); env.memoize(pseudoAcc); // Sponsee is a pseudo account -> tecNO_PERMISSION @@ -5288,7 +5287,7 @@ class Sponsor_test : public beast::unit_test::suite env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), batch::inner(noop(alice), seq + 1), batch::inner(ticket::create(alice, 1), seq + 2), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), + sponsor::as(sponsor, spfSponsorFee), sig(sfSponsorSignature, sponsor), ter(tesSUCCESS)); env.close(); @@ -5302,6 +5301,24 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(alice) == XRP(1000)); BEAST_EXPECT(env.balance(sponsor) == XRP(1000 - 1)); } + { + Env env{*this, testable_amendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + // spfSponsorReserve on outer Batch is rejected + for (auto const flags : {spfSponsorReserve | spfSponsorFee, spfSponsorReserve}) + { + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::inner(noop(alice), seq + 1), + batch::inner(noop(alice), seq + 2), + sponsor::as(sponsor, flags), + sig(sfSponsorSignature, sponsor), + ter(temINVALID_FLAG)); + env.close(); + } + } { // test outer transaction with prefunded sponsor Env env{*this, testable_amendments()}; @@ -5319,7 +5336,7 @@ class Sponsor_test : public beast::unit_test::suite env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), batch::inner(noop(alice), seq + 1), batch::inner(ticket::create(alice, 1), seq + 2), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), + sponsor::as(sponsor, spfSponsorFee), ter(tesSUCCESS)); env.close(); From 2505fa86f7fbf7cd8c7526da7dca39665580c5d5 Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 30 Mar 2026 17:52:36 +0900 Subject: [PATCH 180/249] fix --- src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 3196752baf7..0606c8d4e8c 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -266,9 +266,7 @@ SponsorshipSet::doApply() keylet::ownerDir(sponseeAccountID), sponsorKeylet, describeOwnerDir(sponseeAccountID)); (*newSle)[sfSponseeNode] = *sponseePage; - auto viewJ = ctx_.registry.journal("View"); - - adjustOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, viewJ); + adjustOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, ctx_.journal); addSponsorToLedgerEntry(newSle, reserveSponsorAccSle); ctx_.view().insert(newSle); From 5a195d25853b1e903883ce4da037a32976b25375 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 8 Apr 2026 11:55:35 +0900 Subject: [PATCH 181/249] fix path --- include/xrpl/protocol/detail/transactions.macro | 4 ++-- src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp | 2 +- src/libxrpl/tx/transactors/account/AccountDelete.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 41bbbbc82a2..92f9cfaa73b 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -1078,7 +1078,7 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay, /** This transaction transfer sponsorship */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttSPONSORSHIP_TRANSFER, 85, SponsorshipTransfer, Delegation::delegable, @@ -1091,7 +1091,7 @@ TRANSACTION(ttSPONSORSHIP_TRANSFER, 85, SponsorshipTransfer, /** This transaction create sponsorship object */ #if TRANSACTION_INCLUDE -# include +# include #endif TRANSACTION(ttSPONSORSHIP_SET, 86, SponsorshipSet, Delegation::delegable, diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 3f10fb54f46..f01342f09f5 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index 7f8fd71932e..a436b4ba8a1 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include namespace xrpl { bool From 3ea0067e6ae6d297b126814091e589ff24c40af6 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 8 Apr 2026 12:03:12 +0900 Subject: [PATCH 182/249] clang-format --- cspell.config.yaml | 1 + include/xrpl/ledger/helpers/EscrowHelpers.h | 2 +- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 2 +- .../ledger/helpers/PaymentChannelHelpers.cpp | 2 +- src/libxrpl/ledger/helpers/TokenHelpers.cpp | 28 ++++++++++++------- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 2 +- src/libxrpl/tx/transactors/token/TrustSet.cpp | 24 ++++++++-------- src/test/app/MPToken_test.cpp | 4 +-- .../rpc/handlers/account/AccountObjects.cpp | 7 ++--- 9 files changed, 41 insertions(+), 31 deletions(-) diff --git a/cspell.config.yaml b/cspell.config.yaml index b0c538a5b8f..01d84976fb5 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -242,6 +242,7 @@ words: - soci - socidb - sponsee + - sponsees - sslws - statsd - STATSDCOLLECTOR diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h index 7c67cd3af22..8bd512cfc99 100644 --- a/include/xrpl/ledger/helpers/EscrowHelpers.h +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -4,9 +4,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index 91db0f58fd4..71e400ad339 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include #include #include #include diff --git a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp index 4774e488fa9..1e68022c002 100644 --- a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp +++ b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp @@ -1,8 +1,8 @@ #include #include #include -#include #include +#include #include namespace xrpl { diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index 02774210bc4..c75d9a2fa38 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -690,7 +690,8 @@ directSendNoLimitIOU( if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) { // Direct send: redeeming IOUs and/or sending own IOUs. - auto const ter = directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, false,sponsorAccountID, j); + auto const ter = + directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, false, sponsorAccountID, j); if (!isTesSuccess(ter)) return ter; saActual = saAmount; @@ -708,10 +709,12 @@ directSendNoLimitIOU( << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() << " cost=" << saActual.getFullText(); - TER terResult = directSendNoFeeIOU(view, issuer, uReceiverID, saAmount, true,sponsorAccountID, j); + TER terResult = + directSendNoFeeIOU(view, issuer, uReceiverID, saAmount, true, sponsorAccountID, j); if (tesSUCCESS == terResult) - terResult = directSendNoFeeIOU(view, uSenderID, issuer, saActual, true,sponsorAccountID, j); + terResult = + directSendNoFeeIOU(view, uSenderID, issuer, saActual, true, sponsorAccountID, j); return terResult; } @@ -755,7 +758,8 @@ directSendNoLimitMultiIOU( if (senderID == issuer || receiverID == issuer || issuer == noAccount()) { // Direct send: redeeming IOUs and/or sending own IOUs. - if (auto const ter = directSendNoFeeIOU(view, senderID, receiverID, amount, false, sponsorAccountID, j)) + if (auto const ter = directSendNoFeeIOU( + view, senderID, receiverID, amount, false, sponsorAccountID, j)) return ter; actual += amount; // Do not add amount to takeFromSender, because directSendNoFeeIOU took @@ -778,14 +782,15 @@ directSendNoLimitMultiIOU( << to_string(receiverID) << " : deliver=" << amount.getFullText() << " cost=" << actual.getFullText(); - if (TER const terResult = directSendNoFeeIOU(view, issuer, receiverID, amount, true, sponsorAccountID, j)) + if (TER const terResult = + directSendNoFeeIOU(view, issuer, receiverID, amount, true, sponsorAccountID, j)) return terResult; } if (senderID != issuer && takeFromSender) { - if (TER const terResult = - directSendNoFeeIOU(view, senderID, issuer, takeFromSender, true,sponsorAccountID, j)) + if (TER const terResult = directSendNoFeeIOU( + view, senderID, issuer, takeFromSender, true, sponsorAccountID, j)) return terResult; } @@ -831,7 +836,8 @@ accountSendIOU( JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : " << saAmount.getFullText(); - return directSendNoLimitIOU(view, uSenderID, uReceiverID, saAmount, saActual, j, sponsorAccountID, waiveFee); + return directSendNoLimitIOU( + view, uSenderID, uReceiverID, saAmount, saActual, j, sponsorAccountID, waiveFee); } /* XRP send which does not check reserve and can do pure adjustment. @@ -931,7 +937,8 @@ accountSendMultiIOU( JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID) << " sending " << receivers.size() << " IOUs"; - return directSendNoLimitMultiIOU(view, senderID, issue, receivers, actual, j, sponsorAccountID, waiveFee); + return directSendNoLimitMultiIOU( + view, senderID, issue, receivers, actual, j, sponsorAccountID, waiveFee); } /* XRP send which does not check reserve and can do pure adjustment. @@ -1338,7 +1345,8 @@ directSendNoFee( [&](TIss const& issue) { if constexpr (std::is_same_v) { - return directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, std::nullopt, j); + return directSendNoFeeIOU( + view, uSenderID, uReceiverID, saAmount, bCheckIssuer, std::nullopt, j); } else { diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index f01342f09f5..805a51dbb7c 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -1,9 +1,9 @@ #include #include +#include #include #include #include -#include #include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index 4c713604c8e..a209638fce4 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -621,9 +621,10 @@ TrustSet::doApply() terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); - !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) + else if ( + auto const ret = + checkInsufficientReserve(view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); + !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -651,14 +652,15 @@ TrustSet::doApply() JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if (auto const ret = checkInsufficientReserve( - ctx_.view(), - ctx_.tx, - sle, - preFeeBalance_, - txSponsorSle, - 1); - !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. + else if ( + auto const ret = checkInsufficientReserve( + ctx_.view(), + ctx_.tx, + sle, + preFeeBalance_, + txSponsorSle, + 1); + !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index ac33bb67b29..950c66b6b84 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -3314,8 +3314,8 @@ class MPToken_test : public beast::unit_test::suite std::optional expectedOutstanding, std::string const& label) { ApplyViewImpl av(&*env.current(), tapNONE); - auto const ter = - accountSendMulti(av, issuer.id(), asset, receivers, env.app().getJournal("View"), {}); + auto const ter = accountSendMulti( + av, issuer.id(), asset, receivers, env.app().getJournal("View"), {}); BEAST_EXPECTS(ter == expectedTer, label); // Only verify OutstandingAmount on success — on error the diff --git a/src/xrpld/rpc/handlers/account/AccountObjects.cpp b/src/xrpld/rpc/handlers/account/AccountObjects.cpp index 6287473b903..5764fd7109f 100644 --- a/src/xrpld/rpc/handlers/account/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/account/AccountObjects.cpp @@ -98,10 +98,9 @@ getAccountObjects( bool canAppendNFT = true; if (sponsored.has_value()) { - std::optional const nftSponsor = - cp->isFieldPresent(sfSponsor) - ? cp->getAccountID(sfSponsor) - : std::optional(std::nullopt); + std::optional const nftSponsor = cp->isFieldPresent(sfSponsor) + ? cp->getAccountID(sfSponsor) + : std::optional(std::nullopt); if (!sponsoredMatchesFilter(sponsored.value(), nftSponsor)) canAppendNFT = false; } From 07d0887efb6a67691a96d001f39d6b6e60518529 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 9 Apr 2026 00:46:20 +0900 Subject: [PATCH 183/249] fix path --- include/xrpl/tx/transactors/{Sponsor => sponsor}/SponsorshipSet.h | 0 .../tx/transactors/{Sponsor => sponsor}/SponsorshipTransfer.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename include/xrpl/tx/transactors/{Sponsor => sponsor}/SponsorshipSet.h (100%) rename include/xrpl/tx/transactors/{Sponsor => sponsor}/SponsorshipTransfer.h (100%) diff --git a/include/xrpl/tx/transactors/Sponsor/SponsorshipSet.h b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h similarity index 100% rename from include/xrpl/tx/transactors/Sponsor/SponsorshipSet.h rename to include/xrpl/tx/transactors/sponsor/SponsorshipSet.h diff --git a/include/xrpl/tx/transactors/Sponsor/SponsorshipTransfer.h b/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h similarity index 100% rename from include/xrpl/tx/transactors/Sponsor/SponsorshipTransfer.h rename to include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h From 4d2945323d8d8638b295a3415dce4b33d18bc7cc Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 04:21:11 +0900 Subject: [PATCH 184/249] fix AccountObjects_test.cpp error --- src/test/rpc/AccountObjects_test.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 4a4e067305c..ed261bca849 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -1396,14 +1396,11 @@ class AccountObjects_test : public beast::unit_test::suite env(trust(bob, USD(1000))); env.close(); - // sponsored=true should find the sponsorship object for bob + // sponsored=true should not find any objects for bob (doesn't have any sponsored objects) { auto const resp = acctObjsSponsored(bob.id(), true); auto const& objs = resp[jss::result][jss::account_objects]; - // bob has sponsorship object (which has sfSponsor? no - it's in owner dir) - // Actually the Sponsorship SLE itself doesn't have sfSponsor on it - // Let's just check the count - BEAST_EXPECT(objs.size() >= 0); + BEAST_EXPECT(objs.size() == 0); } // Now sponsor bob's trust line @@ -1426,10 +1423,16 @@ class AccountObjects_test : public beast::unit_test::suite auto const resp = acctObjsSponsored(bob.id(), true); auto const& objs = resp[jss::result][jss::account_objects]; bool foundTrustLine = false; + BEAST_EXPECT(objs.size() == 1); for (auto const& obj : objs) { if (obj[sfLedgerEntryType.jsonName] == jss::RippleState) + { + BEAST_EXPECT( + obj.isMember(sfHighSponsor.jsonName) || + obj.isMember(sfLowSponsor.jsonName)); foundTrustLine = true; + } } BEAST_EXPECT(foundTrustLine); } From 831f99862d79bc81aca012736db19d996e4fe8a4 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 05:51:22 +0900 Subject: [PATCH 185/249] clang-tidy --- include/xrpl/tx/Transactor.h | 2 +- src/test/app/Oracle_test.cpp | 13 +++- src/test/app/Sponsor_test.cpp | 64 +++++++++---------- src/test/jtx/Oracle.h | 5 +- src/test/rpc/AccountObjects_test.cpp | 2 +- .../rpc/handlers/transaction/Simulate.cpp | 2 +- 6 files changed, 49 insertions(+), 39 deletions(-) diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index 93beaa69540..63456c92483 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -118,7 +118,7 @@ struct FeePayer { Keylet entry; SF_AMOUNT const& balanceField; - FeePayerType type; + FeePayerType type{FeePayerType::Account}; }; class Transactor diff --git a/src/test/app/Oracle_test.cpp b/src/test/app/Oracle_test.cpp index 83b658ac41e..6052b312281 100644 --- a/src/test/app/Oracle_test.cpp +++ b/src/test/app/Oracle_test.cpp @@ -1,5 +1,12 @@ #include - +#include +#include +#include +#include +#include +#include + +#include #include namespace xrpl { @@ -451,7 +458,7 @@ struct Oracle_test : public beast::unit_test::suite oracle.remove({.owner = invalid, .fee = baseFee, .err = ter(tecNO_ENTRY)}); // Invalid flags - oracle.remove({.flags = tfSellNFToken, .fee = baseFee, .err = ter(temINVALID_FLAG)}); + oracle.remove({.flags = lsfSellNFToken, .fee = baseFee, .err = ter(temINVALID_FLAG)}); // Bad fee oracle.remove({.fee = -1, .err = ter(temBAD_FEE)}); @@ -687,7 +694,7 @@ struct Oracle_test : public beast::unit_test::suite // alice uses a regular key with the master disabled. Account const alie{"alie", KeyType::secp256k1}; env(regkey(alice, alie)); - env(fset(alice, asfDisableMaster), sig(alice)); + env(fset(alice, lsfDisableMaster), sig(alice)); // Attach signers to alice. env(signers(alice, 2, {{becky, 1}, {bogie, 1}, {ed, 2}}), sig(alie)); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index c00a0b8a51b..891162174c1 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -137,12 +137,12 @@ class Sponsor_test : public beast::unit_test::suite ter(temMALFORMED)); // Invalid feeAmount - for (auto amt : {XRP(-1), USD(1)}) + for (auto const& amt : {XRP(-1), USD(1)}) { env(sponsor::set_fee(sponsor, 0, amt), sponsor::sponseeAcc(alice), ter(temBAD_AMOUNT)); } // Invalid MaxFee - for (auto amt : {XRP(-1), USD(1)}) + for (auto const& amt : {XRP(-1), USD(1)}) { env(sponsor::set_fee(sponsor, 0, XRP(1), amt), sponsor::sponseeAcc(alice), @@ -260,13 +260,13 @@ class Sponsor_test : public beast::unit_test::suite Account const gw("gw"); Account const sp("sponsor"); - Asset asset = gw["IOU"].asset(); + Asset const asset = gw["IOU"].asset(); env.fund(XRP(1000000), alice, bob, gw, sp); env.close(); - // Create a vault to get a pseudo account - Vault vault{env}; + // Create a vault to get a pseudo acconst count + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx); env.close(); @@ -1174,7 +1174,7 @@ class Sponsor_test : public beast::unit_test::suite auto const& highAcc = alice > bob ? alice : bob; auto const& lowAcc = alice > bob ? bob : alice; - for (bool isIssuerHigh : {false, true}) + for (bool const isIssuerHigh : {false, true}) { Env env{*this, testable_amendments()}; env.fund(XRP(10000), alice, bob, sponsor); @@ -2147,9 +2147,9 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, sponsor, sponsor2); env.close(); - // CheckCreate -> CheckCancel + // CheckCreate -> Check = 0Cancel - uint32_t seq; + uint32_t seq = 0; testEachSponsorship( env, cosigning, @@ -2213,8 +2213,8 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, sponsor); env.close(); - // CheckCreate -> CheckCash - uint32_t seq2; + // CheckCreate -> = 0 CheckCash + uint32_t seq2 = 0; testEachSponsorship( env, cosigning, @@ -2254,8 +2254,8 @@ class Sponsor_test : public beast::unit_test::suite env(pay(gw, alice, USD(100))); env.close(); - // CheckCreate -> CheckCash - uint32_t seq2; + // CheckCreat = 0e -> CheckCash + uint32_t seq2 = 0; testEachSponsorship( env, cosigning, @@ -2316,7 +2316,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // OfferCreate - uint32_t seq; + uint32_t seq = 0; testEachSponsorship( env, cosigning, @@ -2374,7 +2374,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // OfferCreate - uint32_t seq; + uint32_t seq = 0; testEachSponsorship( env, cosigning, @@ -2515,7 +2515,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // TicketCreate - uint32_t ticketSeq; + uint32_t ticketSeq = 0; testEachSponsorship( env, @@ -2915,7 +2915,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // EscrowCreate - uint32_t seq; + uint32_t seq = 0; testEachSponsorship( env, cosigning, @@ -2994,7 +2994,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 1); // EscrowCreate - uint32_t seq; + uint32_t seq = 0; testEachSponsorship( env, cosigning, @@ -3227,8 +3227,8 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // for free mptoken checks - // adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); - std::uint32_t ticketSeq{env.seq(sponsor) + 1}; + // adjustAccountXRPBalance(env, sponsconst or, reserve(env, 2)); + std::uint32_t const ticketSeq{env.seq(sponsor) + 1}; env(ticket::create(sponsor, 2)); env.close(); @@ -3714,7 +3714,7 @@ class Sponsor_test : public beast::unit_test::suite // PermissionedDomainSet pdomain::Credentials credentials{{alice, "first credential"}}; - uint32_t seq; + uint32_t seq = 0; testEachSponsorship( env, cosigning, @@ -3806,7 +3806,7 @@ class Sponsor_test : public beast::unit_test::suite {"XRP", "U10", 750, 1}, }; - DataSeries actualSeries(series.begin(), series.begin() + dataSeriesSize); + DataSeries const actualSeries(series.begin(), series.begin() + dataSeriesSize); Json::Value dataSeries(Json::arrayValue); for (auto const& data : actualSeries) @@ -4018,7 +4018,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } - for (bool isTwoOwnerCount : {false, true}) + for (bool const isTwoOwnerCount : {false, true}) { // test sponsor transfer auto const dataSeriesSize = isTwoOwnerCount ? 6 : 5; @@ -4168,7 +4168,7 @@ class Sponsor_test : public beast::unit_test::suite auto const& lowAcc = alice > bob ? bob : alice; // create and delete - for (bool isIssuerHigh : {false, true}) + for (bool const isIssuerHigh : {false, true}) { Env env{*this, testable_amendments()}; env.fund(XRP(1000000), alice, bob, charlie, sponsor, sponsor2); @@ -4238,7 +4238,7 @@ class Sponsor_test : public beast::unit_test::suite } // update - for (bool isIssuerHigh : {false, true}) + for (bool const isIssuerHigh : {false, true}) { Env env{*this, testable_amendments()}; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); @@ -4355,7 +4355,7 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(1000000), alice, bob, gw, sponsor); env.close(); - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(ticket::create(sponsor, 2)); @@ -4382,7 +4382,7 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(1000000), alice, bob, gw, sponsor); env.close(); - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx); env.close(); @@ -4418,7 +4418,7 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(1000000), alice, bob, gw, sponsor); env.close(); - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx); env.close(); @@ -4493,7 +4493,7 @@ class Sponsor_test : public beast::unit_test::suite env(fset(gw, asfAllowTrustLineClawback)); env.close(); - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx); env.close(); @@ -4541,7 +4541,7 @@ class Sponsor_test : public beast::unit_test::suite env(fset(gw, asfAllowTrustLineClawback)); env.close(); - Vault vault{env}; + Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx, sponsor::as(sponsor, spfSponsorReserve), sig(sfSponsorSignature, sponsor)); env.close(); @@ -4714,7 +4714,7 @@ class Sponsor_test : public beast::unit_test::suite PrettyAsset const asset{xrpIssue(), 1'000'000}; - Vault vault{env}; + Vault const vault{env}; auto const [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx); env.close(); @@ -4770,7 +4770,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); - Vault vault{env}; + Vault const vault{env}; auto const [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx); env.close(); @@ -4849,7 +4849,7 @@ class Sponsor_test : public beast::unit_test::suite env(pay(issuer, bob, asset(1000))); env.close(); - Vault vault{env}; + Vault const vault{env}; auto const [tx, keylet] = vault.create({.owner = bob, .asset = asset}); env(tx); env.close(); diff --git a/src/test/jtx/Oracle.h b/src/test/jtx/Oracle.h index 8f48ad4d373..5bb8b19fdc0 100644 --- a/src/test/jtx/Oracle.h +++ b/src/test/jtx/Oracle.h @@ -1,6 +1,9 @@ #pragma once -#include +#include +#include +#include +#include #include diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index ed261bca849..fb2f4043580 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -915,7 +915,7 @@ class AccountObjects_test : public beast::unit_test::suite env.close(); // Find the sponsorship. - for (auto acct : {alice, gw}) + for (const auto& acct : {alice, gw}) { Json::Value const resp = acctObjs(acct, jss::sponsorship); BEAST_EXPECT(acctObjsIsSize(resp, 1)); diff --git a/src/xrpld/rpc/handlers/transaction/Simulate.cpp b/src/xrpld/rpc/handlers/transaction/Simulate.cpp index 6a8f73e16cd..1dbd643f45b 100644 --- a/src/xrpld/rpc/handlers/transaction/Simulate.cpp +++ b/src/xrpld/rpc/handlers/transaction/Simulate.cpp @@ -131,7 +131,7 @@ autofillTx(Json::Value& tx_json, RPC::JsonContext& context) if (tx_json.isMember(sfSponsorSignature.jsonName)) { if (auto error = autofillSignature(tx_json[sfSponsorSignature.jsonName])) - return *error; + return error; } if (!tx_json.isMember(jss::Sequence)) From 71003699dceeeeaef84438d47edba1c503f0c141 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 13:45:14 +0900 Subject: [PATCH 186/249] Add test for no SponsorFlag with valid sponsor --- src/libxrpl/tx/Transactor.cpp | 12 +++++------- src/test/app/Sponsor_test.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 9db31b1f616..2aefaa4a08a 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -185,6 +185,11 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return temINVALID_FLAG; } } + else + { + JLOG(ctx.j.debug()) << "preflight1: no sponsor flags"; + return temINVALID_FLAG; + } if (auto const ret = preflight0(ctx, flagMask)) return ret; @@ -232,13 +237,6 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return temMALFORMED; } - // if (hasSponsor && hasSponsorFlags && - // ctx.tx.getFieldU32(sfSponsorFlags) == 0) - // { - // JLOG(ctx.j.debug()) << "preflight1: sponsor with no sponsorship flags"; - // return temINVALID_FLAG; - // } - return tesSUCCESS; } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 891162174c1..caf08ad8a24 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -422,8 +422,13 @@ class Sponsor_test : public beast::unit_test::suite // SponsorFlags=0 with valid sponsor (no sponsorship purpose) env(noop(alice), sponsor::as(sponsor, 0), ter(temINVALID_FLAG)); - // Invalid Flags without sponsor + // no SponsorFlag with valid sponsor auto tx = noop(alice); + tx[sfSponsor.jsonName] = sponsor.human(); + env(tx, ter(temINVALID_FLAG)); + + // Invalid Flags without sponsor + tx = noop(alice); tx[sfSponsorFlags.jsonName] = spfSponsorFee | spfSponsorReserve; env(tx, ter(temINVALID_FLAG)); } From a115d73c56385e8a0df28fb1e88b985408f35a4d Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 14:13:25 +0900 Subject: [PATCH 187/249] return `tecHAS_OBLIGATIONS` for any conditions when deleting account linked to l`tSponsorship` --- .../tx/transactors/sponsor/SponsorshipSet.h | 4 -- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 35 ---------------- .../tx/transactors/account/AccountDelete.cpp | 14 ------- src/test/app/Sponsor_test.cpp | 42 ++----------------- 4 files changed, 3 insertions(+), 92 deletions(-) diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h index d1cf244f8b7..7ebd34e1272 100644 --- a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h @@ -27,10 +27,6 @@ class SponsorshipSet : public Transactor TER doApply() override; - - // Interface used by DeleteAccount - static TER - deleteSponsorship(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); }; } // namespace xrpl diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 805a51dbb7c..f5e9f333f65 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -332,39 +332,4 @@ SponsorshipSet::doApply() return tesSUCCESS; } -TER -SponsorshipSet::deleteSponsorship( - ApplyView& view, - std::shared_ptr const& sle, - beast::Journal j) -{ - auto const sponsorAccountID = sle->getAccountID(sfOwner); - auto const sponseeAccountID = sle->getAccountID(sfSponsee); - - // adjust balance - auto const sponsorAccSle = view.peek(keylet::account(sponsorAccountID)); - if (!sponsorAccSle) - return tecINTERNAL; // LCOV_EXCL_LINE - - if (sle->isFieldPresent(sfFeeAmount)) - { - auto const feeAmount = sle->getFieldAmount(sfFeeAmount); - (*sponsorAccSle)[sfBalance] += feeAmount; - } - - auto const reserveSponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, sponsorAccSle, reserveSponsor, -1, j); - - view.update(sponsorAccSle); - - // delete sponsor node - view.dirRemove(keylet::ownerDir(sponsorAccountID), (*sle)[sfOwnerNode], sle->key(), false); - // delete sponsee node - view.dirRemove(keylet::ownerDir(sponseeAccountID), (*sle)[sfSponseeNode], sle->key(), false); - - view.erase(sle); - - return tesSUCCESS; -} - } // namespace xrpl diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index a436b4ba8a1..4bb66034d79 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -169,18 +169,6 @@ removeDelegateFromLedger( return DelegateSet::deleteDelegate(view, sleDel, account, j); } -TER -removeSponsorshipFromLedger( - ServiceRegistry& registry, - ApplyView& view, - AccountID const&, - uint256 const& delIndex, - std::shared_ptr const& sleDel, - beast::Journal j) -{ - return SponsorshipSet::deleteSponsorship(view, sleDel, j); -} - // Return nullptr if the LedgerEntryType represents an obligation that can't // be deleted. Otherwise return the pointer to the function that can delete // the non-obligation @@ -207,8 +195,6 @@ nonObligationDeleter(LedgerEntryType t) return removeCredentialFromLedger; case ltDELEGATE: return removeDelegateFromLedger; - case ltSPONSORSHIP: - return removeSponsorshipFromLedger; default: return nullptr; } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index caf08ad8a24..3b63c7aefe0 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -4953,37 +4953,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); { - // Delete Sponsee Account with ltSponsorship - Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor); - env.close(); - - // set sponsor - env(sponsor::set(sponsor, 0, 100, XRP(100)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); - env.close(); - - incLgrSeqForAccDel(env, alice); - - auto const keylet = keylet::sponsor(sponsor, alice); - auto const sponsorObj = env.le(keylet); - BEAST_EXPECT(sponsorObj); - - // AccountDelete - auto const requiredFee = drops(env.current()->fees().increment); - env(acctdelete(alice, bob), fee(requiredFee), ter(tesSUCCESS)); - env.close(); - - BEAST_EXPECT(!env.le(keylet)); - auto const jv = sponsor::ledgerEntry(env, sponsor, alice); - BEAST_EXPECT( - jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) && - jv[jss::result][jss::error] == "entryNotFound"); - } - - { - // Delete Sponsor Account with ltSponsorship + // Delete Sponsor/Sponsee Account with ltSponsorship (tecHAS_OBLIGATIONS) Env env{*this, testable_amendments()}; env.fund(XRP(1000000), alice, bob, sponsor); env.close(); @@ -5002,14 +4972,8 @@ class Sponsor_test : public beast::unit_test::suite // AccountDelete auto const requiredFee = drops(env.current()->fees().increment); - env(acctdelete(sponsor, alice), fee(requiredFee), ter(tesSUCCESS)); - env.close(); - - BEAST_EXPECT(!env.le(keylet)); - auto const jv = sponsor::ledgerEntry(env, sponsor, alice); - BEAST_EXPECT( - jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) && - jv[jss::result][jss::error] == "entryNotFound"); + env(acctdelete(alice, bob), fee(requiredFee), ter(tecHAS_OBLIGATIONS)); + env(acctdelete(sponsor, bob), fee(requiredFee), ter(tecHAS_OBLIGATIONS)); } { From 4026af175ba8af08021a63ce5d30f67b93f5658b Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 14:13:55 +0900 Subject: [PATCH 188/249] format --- src/test/rpc/AccountObjects_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index fb2f4043580..1aea1289546 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -915,7 +915,7 @@ class AccountObjects_test : public beast::unit_test::suite env.close(); // Find the sponsorship. - for (const auto& acct : {alice, gw}) + for (auto const& acct : {alice, gw}) { Json::Value const resp = acctObjs(acct, jss::sponsorship); BEAST_EXPECT(acctObjsIsSize(resp, 1)); From 25de0ce1b1a94a9acee7a8593503230cec4169e8 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 14:21:05 +0900 Subject: [PATCH 189/249] fix comment --- src/libxrpl/tx/transactors/account/AccountDelete.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index 4bb66034d79..86bb4d27f37 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -384,9 +384,6 @@ AccountDelete::doApply() return tefINTERNAL; // LCOV_EXCL_LINE // Transfer any XRP remaining after the fee is paid to the destination: - // Use the current balance from the SLE, not mSourceBalance, because - // the cleanup loop may have returned pre-funded sfFeeAmount from - // ltSponsorship objects back to the account's sfBalance. auto const remainingBalance = src->getFieldAmount(sfBalance).xrp(); (*dst)[sfBalance] = (*dst)[sfBalance] + remainingBalance; (*src)[sfBalance] = (*src)[sfBalance] - remainingBalance; From d524670bcd5e7fc61cd8a54c6270c1c00daec52a Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 15:54:16 +0900 Subject: [PATCH 190/249] fix SponsorshipEnd Bypass --- .../Sponsor/SponsorshipTransfer.cpp | 10 +++++ src/test/app/Sponsor_test.cpp | 41 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 3c63e3c42ba..bf051a45ed0 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -271,6 +271,11 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) // check object is sponsored if (!sle->isFieldPresent(sponsorField)) return tecNO_PERMISSION; + + // only the sponsor or sponsee can end sponsorship + auto const sponsor = sle->getAccountID(sponsorField); + if (account != sponsor && account != sponseeAccountID) + return tecNO_PERMISSION; } // check new sponsor have sufficient balance @@ -312,6 +317,11 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) // check account is sponsored if (!sponseeSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; + + // only the sponsor or sponsee can end sponsorship + auto const sponsor = sponseeSle->getAccountID(sfSponsor); + if (account != sponsor && account != sponseeAccountID) + return tecNO_PERMISSION; } // check account have sufficient balance diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 3b63c7aefe0..c875aa27523 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -7,6 +7,8 @@ #include #include +#include "test/jtx/did.h" + namespace xrpl { namespace test { @@ -265,7 +267,7 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(1000000), alice, bob, gw, sp); env.close(); - // Create a vault to get a pseudo acconst count + // Create a vault to get a pseudo account Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); env(tx); @@ -733,6 +735,41 @@ class Sponsor_test : public beast::unit_test::suite ter(temMALFORMED)); } + { + // Invalid SponsorshipEnd permission (sponsor object/sponsor account) + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + { + // sponsor object + env(did::set(alice), + did::uri("uri"), + sponsor::as(sponsor, spfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + auto const keylet = keylet::did(alice); + env(sponsor::transfer(bob, tfSponsorshipEnd, keylet.key), + sponsor::sponseeAcc(alice), + ter(tecNO_PERMISSION)); + } + { + // sponsor object + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::as(sponsor, spfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + env(sponsor::transfer(bob, tfSponsorshipEnd), + sponsor::sponseeAcc(alice), + ter(tecNO_PERMISSION)); + } + } + { // sponsor account Env env{*this, testable_amendments()}; @@ -3232,7 +3269,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // for free mptoken checks - // adjustAccountXRPBalance(env, sponsconst or, reserve(env, 2)); + // adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); std::uint32_t const ticketSeq{env.seq(sponsor) + 1}; env(ticket::create(sponsor, 2)); env.close(); From 828b4753fe1d105faaa399226ca165a55ee347d1 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 15:57:15 +0900 Subject: [PATCH 191/249] fix Wrong Account Passed to getLedgerEntryOwner in doApply --- src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index bf051a45ed0..b84c840322e 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -413,7 +413,7 @@ SponsorshipTransfer::doApply() if (!objSle) return tefINTERNAL; // LCOV_EXCL_LINE - auto const ownerAccountID = getLedgerEntryOwner(view(), objSle, account_); + auto const ownerAccountID = getLedgerEntryOwner(view(), objSle, sponseeAccountID); if (!ownerAccountID) return tefINTERNAL; // LCOV_EXCL_LINE From 6bd2e911eb53944200b0c2cbbe47e63deeddcd2c Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 17:07:36 +0900 Subject: [PATCH 192/249] fix Wrong Account in Sponsorship Keylet Lookup Causes Reserve Count Leakage --- .../Sponsor/SponsorshipTransfer.cpp | 20 ++++---- src/test/app/Sponsor_test.cpp | 49 ++++++++++++++++++- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index b84c840322e..50bdb5aac01 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -448,8 +448,8 @@ SponsorshipTransfer::doApply() if (!hasSignature) { // use ReserveCount for pre-funded sponsoring - if (auto const ter = - adjustReserveCount(view(), account_, newSponsorAccountID, -ownerCountDelta); + if (auto const ter = adjustReserveCount( + view(), sponseeAccountID, newSponsorAccountID, -ownerCountDelta); !isTesSuccess(ter)) return ter; } @@ -485,19 +485,19 @@ SponsorshipTransfer::doApply() if (!hasSignature) { // use ReserveCount for pre-funded sponsoring - if (auto const ter = - adjustReserveCount(view(), account_, newSponsorAccountID, -ownerCountDelta); + if (auto const ter = adjustReserveCount( + view(), sponseeAccountID, newSponsorAccountID, -ownerCountDelta); !isTesSuccess(ter)) return ter; } // payback the reserve count if ltSponsorship exists if (auto const sponsorSle = - view().exists(keylet::sponsor(oldSponsorAccountID, account_)); + view().exists(keylet::sponsor(oldSponsorAccountID, sponseeAccountID)); sponsorSle) { - if (auto const ter = - adjustReserveCount(view(), account_, oldSponsorAccountID, ownerCountDelta); + if (auto const ter = adjustReserveCount( + view(), sponseeAccountID, oldSponsorAccountID, ownerCountDelta); !isTesSuccess(ter)) return ter; } @@ -521,11 +521,11 @@ SponsorshipTransfer::doApply() // payback the reserve count if ltSponsorship exists if (auto const sponsorSle = - view().exists(keylet::sponsor(oldSponsorAccountID, account_)); + view().exists(keylet::sponsor(oldSponsorAccountID, sponseeAccountID)); sponsorSle) { - if (auto const ter = - adjustReserveCount(view(), account_, oldSponsorAccountID, ownerCountDelta); + if (auto const ter = adjustReserveCount( + view(), sponseeAccountID, oldSponsorAccountID, ownerCountDelta); !isTesSuccess(ter)) return ter; } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index c875aa27523..3cb5d920408 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1168,7 +1168,7 @@ class Sponsor_test : public beast::unit_test::suite } { - // Dissolve object sponsorship from sponsor + // Dissolve object sponsorship from sponsor(no-ltSponsorship) Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); @@ -1207,6 +1207,53 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } + { + // Dissolve object sponsorship from sponsor (with ltSponsorship) + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::as(sponsor, spfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::sponseeAcc(alice)); + env.close(); + + BEAST_EXPECT( + env.le(keylet::unchecked(checkId))->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT( + env.le(keylet::sponsor(sponsor, alice))->getFieldU32(sfReserveCount) == 100); + + // not the owner of the object + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), ter(tecNO_PERMISSION)); + env.close(); + + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), sponsor::sponseeAcc(alice)); + env.close(); + + BEAST_EXPECT(!env.le(keylet::unchecked(checkId))->isFieldPresent(sfSponsor)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT( + env.le(keylet::sponsor(sponsor, alice))->getFieldU32(sfReserveCount) == 101); + } + { // sponsor trustline Account const alice("alice"); From 9264136b36ef9e248f28cf65016bd08975938c32 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 17:41:33 +0900 Subject: [PATCH 193/249] fix deleteAMMTrustLine Reads Sponsor Fields After They Are Erased --- .../ledger/helpers/RippleStateHelpers.cpp | 6 +- src/test/app/Sponsor_test.cpp | 57 +++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index cc01220738a..c5730e46d08 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -781,6 +781,9 @@ deleteAMMTrustLine( if (ammAccountID && (low != *ammAccountID && high != *ammAccountID)) return terNO_AMM; + auto const sponsorSle = + getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor); + if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter)) { JLOG(j.error()) << "deleteAMMTrustLine: failed to delete the trustline."; @@ -791,10 +794,7 @@ deleteAMMTrustLine( if ((sleState->getFlags() & uFlags) == 0u) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsorSle = - getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor); adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, -1, j); - removeSponsorFromLedgerEntry(sleState, !ammLow ? sfLowSponsor : sfHighSponsor); return tesSUCCESS; } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 3cb5d920408..b48c81660e4 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2216,6 +2216,63 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } } + { + // AMMDelete + // - remove sponsored LPToken trustlines + Env env( + *this, + envconfig([](std::unique_ptr cfg) { + cfg->FEES.reference_fee = XRPAmount(1); + return cfg; + }), + testable_amendments()); + env.fund(XRP(20'000), alice, gw, sponsor); + env.close(); + env(trust(alice, USD(10'000))); + env.close(); + env(pay(gw, alice, USD(10'000))); + env.close(); + + AMM amm(env, gw, XRP(10'000), USD(10'000)); + for (auto i = 0; i < (maxDeletableAMMTrustLines * 2) + 10; ++i) + { + Account const a{std::to_string(i)}; + env.fund(XRP(1'000), a); + if (cosigning) + { + env(trust(a, STAmount{amm.lptIssue(), 10'000}), + sponsor::as(sponsor, spfSponsorReserve), + sig(sfSponsorSignature, sponsor)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(a)); + env.close(); + env(trust(a, STAmount{amm.lptIssue(), 10'000}), + sponsor::as(sponsor, spfSponsorReserve)); + env.close(); + } + } + + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == maxDeletableAMMTrustLines * 2 + 10); + + // The trustlines are partially deleted. + amm.withdrawAll(gw); + BEAST_EXPECT(amm.ammExists()); + + // AMMDelete has to be called twice to delete AMM. + amm.ammDelete(alice, ter(tecINCOMPLETE)); + BEAST_EXPECT(amm.ammExists()); + + // Deletes remaining trustlines and deletes AMM. + amm.ammDelete(alice); + BEAST_EXPECT(!amm.ammExists()); + BEAST_EXPECT(!env.le(keylet::ownerDir(amm.ammAccount()))); + + BEAST_EXPECT( + !env.le(keylet::account(sponsor))->isFieldPresent(sfSponsoringAccountCount)); + } } void From 3df1e668da892181e3cee3078de64e5d12098f85 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 17:55:40 +0900 Subject: [PATCH 194/249] fix Unchecked std::optional Dereference in SponsorshipSet Create Path --- src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index f5e9f333f65..376cb6afc6c 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -260,10 +260,14 @@ SponsorshipSet::doApply() auto const sponsorPage = view().dirInsert( keylet::ownerDir(sponsorAccountID), sponsorKeylet, describeOwnerDir(sponsorAccountID)); + if (!sponsorPage) + return tecDIR_FULL; // LCOV_EXCL_LINE (*newSle)[sfOwnerNode] = *sponsorPage; auto const sponseePage = view().dirInsert( keylet::ownerDir(sponseeAccountID), sponsorKeylet, describeOwnerDir(sponseeAccountID)); + if (!sponseePage) + return tecDIR_FULL; // LCOV_EXCL_LINE (*newSle)[sfSponseeNode] = *sponseePage; adjustOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, ctx_.journal); From 16e4f24226742e74934b0786f4872f8811a02354 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 17:59:25 +0900 Subject: [PATCH 195/249] fix XRPL_ASSERT(false) to UNREACHABLE --- src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 50bdb5aac01..c914de50859 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -204,10 +204,7 @@ getLedgerEntrySponsorField(T const& sle, AccountID const& owner) return sfLowSponsor; } // LCOV_EXCL_START - XRPL_ASSERT( - false, - "Should not happen. Owner should be checked before calling " - "this function."); + UNREACHABLE("Should not happen. Owner should be checked before calling this function."); // LCOV_EXCL_STOP } default: From 525adf122fc883625a430e46d07d1160a600eb88 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 18:28:28 +0900 Subject: [PATCH 196/249] fix reset() Does Not makeFieldAbsent for Zero FeeAmount --- src/libxrpl/tx/Transactor.cpp | 7 ++++++- src/test/app/Sponsor_test.cpp | 23 +++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 2aefaa4a08a..b2e48f93a11 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -1211,7 +1211,12 @@ Transactor::reset(XRPAmount fee) // If for some reason we are unable to consume the ticket or sequence // then the ledger is corrupted. Rather than make things worse we // reject the transaction. - payerSle->setFieldAmount(payer.balanceField, balance - fee); + auto const feeAmountAfter = balance - fee; + if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) + // Because ltSponsorship.sfFeeAmount is soeOptional + payerSle->makeFieldAbsent(payer.balanceField); + else + payerSle->setFieldAmount(payer.balanceField, feeAmountAfter); TER const ter{consumeSeqProxy(txnAcct)}; XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS"); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index b48c81660e4..d14fe7a2982 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -7,6 +7,7 @@ #include #include +#include "test/jtx/check.h" #include "test/jtx/did.h" namespace xrpl { @@ -1448,8 +1449,8 @@ class Sponsor_test : public beast::unit_test::suite env.fund(XRP(10000), alice, bob, sponsor); env.close(); - auto const sponsorFeeBalance = [&](Account const& sponsor, Account const& alice) { - return env.le(keylet::sponsor(sponsor, alice))->getFieldAmount(sfFeeAmount).xrp(); + auto const sponsorFeeBalance = [&](Account const& sponsor, Account const& sponsee) { + return env.le(keylet::sponsor(sponsor, sponsee))->getFieldAmount(sfFeeAmount).xrp(); }; { @@ -1578,6 +1579,24 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); BEAST_EXPECT(sponsorFeeBalance(sponsor, alice) == sponsorFee - feeAmt); } + + // make sfFeeAmount absent if tec error and all fee is paid + { + // reset FeeAmount and MaxFee + env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); + env(sponsor::set_fee(sponsor, 0, XRP(10)), sponsor::sponseeAcc(alice)); + env.close(); + + BEAST_EXPECT(env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount)); + auto sponsorAvailableFee = sponsorFeeBalance(sponsor, alice); + printf("sponsorAvailableFee: %s\n", to_string(sponsorAvailableFee).c_str()); + env(check::cancel(alice, uint256(1)), + fee(sponsorAvailableFee), + sponsor::as(sponsor, spfSponsorFee), + ter(tecNO_ENTRY)); + env.close(); + BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount)); + } } // test lsfSponsorshipRequireSignForFee From b6b317597f2214abae06ad24ac197eecf929e1f2 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 10 Apr 2026 21:43:35 +0900 Subject: [PATCH 197/249] add test for insufficient balance to sponsor fee --- src/test/app/Sponsor_test.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index d14fe7a2982..e1638de7f1a 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -9,6 +9,7 @@ #include "test/jtx/check.h" #include "test/jtx/did.h" +#include "test/jtx/sponsor.h" namespace xrpl { namespace test { @@ -189,6 +190,11 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::set(sponsor, tfDeleteObject), sponsor::sponseeAcc(alice), ter(tecNO_ENTRY)); env.close(); + // insufficient balance to sponsor fee + adjustAccountXRPBalance(env, sponsor, env.current()->fees().reserve); + env(sponsor::set_fee(sponsor, 0, XRP(4)), sponsor::sponseeAcc(alice), ter(tecUNFUNDED)); + env.close(); + // insufficent reserve to create sponsorship adjustAccountXRPBalance(env, sponsor, XRP(100) + XRP(1) + reserve(env, 1) - drops(1)); env(sponsor::set(sponsor, 0, 100, XRP(100)), From 84e84fbadf3c08e61a9b63ca15da945c94d662ac Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 13 Apr 2026 14:03:37 +0900 Subject: [PATCH 198/249] fix Sponsor able to provide sponsorship with just base reserve --- src/libxrpl/tx/Transactor.cpp | 10 +++++++++- src/test/app/Sponsor_test.cpp | 22 +++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index b2e48f93a11..413a8213540 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -479,7 +479,15 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (payerSle->getType() != ltACCOUNT_ROOT) return tefINTERNAL; // LCOV_EXCL_LINE - maxSpendable = payerSle->getFieldAmount(payer.balanceField).xrp(); + if (payer.type == FeePayerType::SponsorCoSigned) + { + STAmount const sponsorReserve = calculateReserve(payerSle, ctx.view.fees()); + maxSpendable = payerSle->getFieldAmount(sfBalance).xrp() - sponsorReserve.xrp(); + } + else + { + maxSpendable = payerSle->getFieldAmount(payer.balanceField).xrp(); + } } // NOTE: Because preclaim evaluates against a static readview, it diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index e1638de7f1a..9c48f8ba25b 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1444,6 +1444,27 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(bob) == bobBalance); BEAST_EXPECT(env.balance(sponsor) == sponsorBalance - feeAmt); } + + { + // below reserve + adjustAccountXRPBalance(env, sponsor, env.current()->fees().reserve); + env.close(); + auto const feeAmt = XRP(4); + + env(noop(alice), + fee(env.current()->fees().base), + sponsor::as(sponsor, spfSponsorFee), + sig(sfSponsorSignature, sponsor), + ter(terINSUF_FEE_B)); + env.close(); + + env(noop(alice), + fee(XRP(10)), + sponsor::as(sponsor, spfSponsorFee), + sig(sfSponsorSignature, sponsor), + ter(terINSUF_FEE_B)); + env.close(); + } } { @@ -1595,7 +1616,6 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount)); auto sponsorAvailableFee = sponsorFeeBalance(sponsor, alice); - printf("sponsorAvailableFee: %s\n", to_string(sponsorAvailableFee).c_str()); env(check::cancel(alice, uint256(1)), fee(sponsorAvailableFee), sponsor::as(sponsor, spfSponsorFee), From 021eaa81bf5d2d3ae83a0d3f86996ac7d650fe2f Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 13 Apr 2026 15:26:24 +0900 Subject: [PATCH 199/249] fix Co-Signed Sponsoring Bypasses Sponsor Balance Check --- src/libxrpl/ledger/View.cpp | 40 ++++++++++---------------- src/test/app/Sponsor_test.cpp | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index f861aff41c8..3efd6ab2b77 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -326,37 +326,27 @@ checkInsufficientReserve( auto const sle = view.read( keylet::sponsor(sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount))); - if (isCoSigning) - { - if (sle) - { - auto const reserveCountAllowed = sle->getFieldU32(sfReserveCount); - if (reserveCountAllowed < ownerCountDelta) - return tecINSUFFICIENT_RESERVE; + if (!isCoSigning && !sle) + // prefunded sponsor should have a sponsorship entry + return tecINTERNAL; // LCOV_EXCL_LINE - return tesSUCCESS; - } - auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance); - STAmount const sponsorReserve{view.fees().accountReserve( - sponsorSle->getFieldU32(sfOwnerCount), - sponsorSle->getFieldU32(sfSponsoredOwnerCount), - sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ownerCountDelta, - sponsorSle->isFieldPresent(sfSponsor), - sponsorSle->getFieldU32(sfSponsoringAccountCount) + accountCountDelta)}; - - if (sponsorBalance < sponsorReserve) - return tecINSUFFICIENT_RESERVE; - } - else + if (sle) { - // pre funded - if (!sle) - return tecINTERNAL; // LCOV_EXCL_LINE - auto const reserveCountAllowed = sle->getFieldU32(sfReserveCount); if (reserveCountAllowed < ownerCountDelta) return tecINSUFFICIENT_RESERVE; } + + auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance); + STAmount const sponsorReserve{view.fees().accountReserve( + sponsorSle->getFieldU32(sfOwnerCount), + sponsorSle->getFieldU32(sfSponsoredOwnerCount), + sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ownerCountDelta, + sponsorSle->isFieldPresent(sfSponsor), + sponsorSle->getFieldU32(sfSponsoringAccountCount) + accountCountDelta)}; + + if (sponsorBalance < sponsorReserve) + return tecINSUFFICIENT_RESERVE; } else { diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 9c48f8ba25b..cad1892fca0 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1854,6 +1854,58 @@ class Sponsor_test : public beast::unit_test::suite } } + void + testSponsorReserveSimple(bool cosigning) + { + testcase("SponsorReserveSimple"); + using namespace test::jtx; + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + + env.fund(XRP(10000), alice, sponsor); + env.close(); + + // test Sufficient sponsor balance + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 99)); + + env(ticket::create(alice, 100), + sponsor::as(sponsor, spfSponsorReserve), + sig(sfSponsorSignature, sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 100)); + + env(ticket::create(alice, 100), + sponsor::as(sponsor, spfSponsorReserve), + sig(sfSponsorSignature, sponsor), + ter(tesSUCCESS)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 250), sponsor::sponseeAcc(alice)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 99 + 1 /* sponsor object*/)); + + env(ticket::create(alice, 100), + sponsor::as(sponsor, spfSponsorReserve), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 100 + 1 /* sponsor object*/)); + + env(ticket::create(alice, 100), + sponsor::as(sponsor, spfSponsorReserve), + ter(tesSUCCESS)); + env.close(); + } + } + // test helper for both cosigning and pre-funded sponsorship template void @@ -5577,6 +5629,7 @@ class Sponsor_test : public beast::unit_test::suite testSponsorReserve(bool cosigning) { testRequireFlag(); + testSponsorReserveSimple(cosigning); testAMM(cosigning); testCheck(cosigning); testOffer(cosigning); From 31bc25973a612d451f7477f679b9f79640f2f4fb Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 13 Apr 2026 15:55:16 +0900 Subject: [PATCH 200/249] fix SponsorshipSet Update FeeAmount Lacks Reserve Floor Check --- src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp | 10 ++++++++++ src/test/app/Sponsor_test.cpp | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 376cb6afc6c..28f90297213 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -295,6 +295,16 @@ SponsorshipSet::doApply() (*sponsorObjSle).makeFieldAbsent(sfFeeAmount); else (*sponsorObjSle).setFieldAmount(sfFeeAmount, *feeAmount); + + if (auto const ret = checkInsufficientReserve( + ctx_.view(), + ctx_.tx, + sponsorAccSle, + STAmount{(*sponsorAccSle)[sfBalance]}.xrp(), + reserveSponsorAccSle, + 0); + !isTesSuccess(ret)) + return tecUNFUNDED; } } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index cad1892fca0..41b5e88b654 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -256,6 +256,16 @@ class Sponsor_test : public beast::unit_test::suite fee(XRP(1)), ter(tecUNFUNDED)); env.close(); + + // Increasing feeAmount to reach insufficient reserve + auto const currentFeeAmount = + env.le(keylet::sponsor(sponsor.id(), alice.id()))->getFieldAmount(sfFeeAmount).xrp(); + adjustAccountXRPBalance(env, sponsor, XRP(310)); + env(sponsor::set_fee(sponsor, 0, currentFeeAmount + XRP(309)), + sponsor::sponseeAcc(alice), + fee(XRP(1)), + ter(tecUNFUNDED)); + env.close(); } void From 52e7cdc24dbc0448907e2167b16b0e3e7cf1734e Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 21 Apr 2026 13:48:07 +0900 Subject: [PATCH 201/249] fix: Sponsored account creation double-counts base reserve #6894 --- .../tx/transactors/payment/Payment.cpp | 3 +-- src/test/app/Sponsor_test.cpp | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 4475bbd7613..81f0b06dc24 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -611,8 +611,7 @@ Payment::doApply() // the number of reserves in this ledger for this account that require a // reserve. - auto const reserve = calculateReserve(sleSrc, view().fees()) + - (((txFlags & tfSponsorCreatedAccount) != 0u) ? view().fees().reserve : beast::zero); + auto const reserve = calculateReserve(sleSrc, view().fees()); // In a delegated payment, the fee payer is the delegated account, // not the source account (account_). diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 41b5e88b654..3cbb4d16fae 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1710,8 +1710,10 @@ class Sponsor_test : public beast::unit_test::suite Account const alice("alice"); Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); + Account const sponsor3("sponsor3"); Account const bob("bob"); Account const charlie("charlie"); + Account const dave("dave"); Account const gw("gw"); auto const USD = gw["USD"]; @@ -1725,7 +1727,7 @@ class Sponsor_test : public beast::unit_test::suite } Env env{*this, testable_amendments()}; - env.fund(XRP(10000), alice, sponsor, sponsor2); + env.fund(XRP(10000), alice, sponsor, sponsor2, sponsor3); env.close(); // Invalid flags @@ -1788,6 +1790,26 @@ class Sponsor_test : public beast::unit_test::suite // verify sponsor balance decreased by payment + fee BEAST_EXPECT(env.balance(sponsor2) == sponsor2BalanceBefore - drops(1) - XRP(1)); } + { + // insufficient reserve to sponsor acount + + auto const sendAmount = drops(1); + // 2 account reserve + send amount + auto const requireBalance = accountReserve(env, 2) + sendAmount; + adjustAccountXRPBalance(env, sponsor3, requireBalance - drops(1)); + env(pay(sponsor3, dave, sendAmount), + txflags(tfSponsorCreatedAccount), + fee(XRP(1)), + ter(tecUNFUNDED_PAYMENT)); + env.close(); + + adjustAccountXRPBalance(env, sponsor3, requireBalance); + env(pay(sponsor3, dave, sendAmount), + txflags(tfSponsorCreatedAccount), + fee(XRP(1)), + ter(tesSUCCESS)); + env.close(); + } } void From 207a33d3da6c766fdc79822e08e1b5cce8059bba Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 21 Apr 2026 15:56:28 +0900 Subject: [PATCH 202/249] fix: SponsorshipSet Update FeeAmount Lacks Reserve Floor Check --- src/libxrpl/tx/transactors/system/Batch.cpp | 4 + src/test/app/Sponsor_test.cpp | 103 +++++++++++++++++--- 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/src/libxrpl/tx/transactors/system/Batch.cpp b/src/libxrpl/tx/transactors/system/Batch.cpp index 8f16fcaa3b3..4d125cadc81 100644 --- a/src/libxrpl/tx/transactors/system/Batch.cpp +++ b/src/libxrpl/tx/transactors/system/Batch.cpp @@ -406,6 +406,10 @@ Batch::preflightSigValidated(PreflightContext const& ctx) if (auto const counterparty = rb.at(~sfCounterparty); counterparty && counterparty != outerAccount) requiredSigners.insert(*counterparty); + + if (auto const sponsor = rb.at(~sfSponsor); + sponsor && rb.isFieldPresent(sfSponsorSignature) && sponsor != outerAccount) + requiredSigners.insert(*sponsor); } // Validation Batch Signers diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 3cbb4d16fae..8a231c76475 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -5597,25 +5597,69 @@ class Sponsor_test : public beast::unit_test::suite // Inner transaction // { - // test inner transaction with co-signing sponsor + // test invalid inner transaction with co-signing sponsor + Account const signerAccount("signer"); Env env{*this, testable_amendments()}; - env.fund(XRP(1000), alice, bob, sponsor); + env.fund(XRP(1000), alice, bob, sponsor, signerAccount); env.close(); - auto jt = env.jtnofill( - noop(alice), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), - sig(sfSponsorSignature, sponsor)); + env(signers(sponsor, 1, {signer(signerAccount, 1)})); + env.close(); - auto const seq = env.seq(alice); - // should fail because inner transaction cannot include SponsorSignature - env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), - batch::inner(jt.jv, seq + 1), - batch::inner(ticket::create(alice, 1), seq + 2), - ter(temBAD_SIGNATURE)); + { + auto jt = env.jtnofill( + noop(alice), + sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), + sig(sfSponsorSignature, sponsor)); + jt.jv.removeMember(sfTxnSignature.jsonName); + + auto const seq = env.seq(alice); + // should fail because inner transaction cannot include SponsorSignature with + // TxnSignature + BEAST_EXPECT(jt.jv[sfSponsorSignature.jsonName].isMember(sfTxnSignature.jsonName)); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::inner(jt.jv, seq + 1), + batch::inner(ticket::create(alice, 1), seq + 2), + ter(temBAD_SIGNATURE)); + } + + { + auto jt = env.jtnofill( + noop(alice), + sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), + msig(sfSponsorSignature, sponsor, signerAccount)); + jt.jv.removeMember(sfTxnSignature.jsonName); + + auto const seq = env.seq(alice); + // should fail because inner transaction cannot include SponsorSignature with + // Signers + BEAST_EXPECT(jt.jv[sfSponsorSignature.jsonName].isMember(sfSigners.jsonName)); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::inner(jt.jv, seq + 1), + batch::inner(ticket::create(alice, 1), seq + 2), + ter(temBAD_SIGNER)); + } + + { + auto jt = env.jtnofill( + noop(alice), + sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), + sig(sfSponsorSignature, sponsor)); + jt.jv.removeMember(sfTxnSignature.jsonName); + jt.jv[sfSponsorSignature.jsonName].removeMember(sfTxnSignature.jsonName); + jt.jv[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = ""; + + auto const seq = env.seq(alice); + // should fail BatchSigners does have signer for SponsorSignature + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::inner(jt.jv, seq + 1), + batch::inner(ticket::create(alice, 1), seq + 2), + ter(temBAD_SIGNER)); + } } + { - // test outer transaction with prefunded sponsor + // test inner transaction with prefunded sponsor Env env{*this, testable_amendments()}; env.fund(XRP(1000), alice, bob); env.fund(XRP(1001), sponsor); @@ -5655,6 +5699,39 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsorshipSle->at(sfFeeAmount) == XRP(100)); BEAST_EXPECT(sponsorshipSle->at(sfReserveCount) == 99); } + + { + // test inner transaction with co-signing sponsor + Env env{*this, testable_amendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + auto jt = env.jtnofill( + ticket::create(alice, 1), + sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), + sig(sfSponsorSignature, sponsor)); + // remove txn signature since it is filled by env.jtnofill() + jt.jv.removeMember(sfTxnSignature.jsonName); + jt.jv[sfSponsorSignature.jsonName].removeMember(sfTxnSignature.jsonName); + jt.jv[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = ""; + + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::inner(noop(alice), seq + 1), + batch::inner(jt.jv, seq + 2), + batch::sig(sponsor), + ter(tesSUCCESS)); + env.close(); + + // affect sponsor reserve + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // fee is paid by outer transaction originator (alice) + BEAST_EXPECT(env.balance(alice) == XRP(999)); + BEAST_EXPECT(env.balance(sponsor) == XRP(1000)); + } } void From 7639bd90616379a0997a31847f17c7d879e5db8b Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 21 Apr 2026 16:27:33 +0900 Subject: [PATCH 203/249] fix: MPTokenIssuanceDestroy reads sponsor from erased SLE #6895 --- src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index 6e6861b3bf5..f4492303357 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -44,11 +44,10 @@ MPTokenIssuanceDestroy::doApply() if (!view().dirRemove(keylet::ownerDir(account_), (*mpt)[sfOwnerNode], mpt->key(), false)) return tefBAD_LEDGER; // LCOV_EXCL_LINE - view().erase(mpt); - auto const sponsor = getLedgerEntryReserveSponsor(view(), mpt); adjustOwnerCount(view(), view().peek(keylet::account(account_)), sponsor, -1, j_); + view().erase(mpt); return tesSUCCESS; } From 773acf0af762f2a868d435ff43da5965045c8054 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 21 Apr 2026 17:20:22 +0900 Subject: [PATCH 204/249] fix: SponsoringAccountCount has no overflow protection #6898 --- src/libxrpl/tx/transactors/payment/Payment.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 81f0b06dc24..163b7f6cac0 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -437,6 +437,12 @@ Payment::doApply() return tefINTERNAL; // LCOV_EXCL_LINE auto const currentSponsoringAccountCount = sponsor->getFieldU32(sfSponsoringAccountCount); + if (currentSponsoringAccountCount == std::numeric_limits::max()) + { + JLOG(j_.fatal()) << "Sponsoring account count overflow for account " + << to_string(account_); + return tecINTERNAL; // LCOV_EXCL_LINE + } sponsor->setFieldU32(sfSponsoringAccountCount, currentSponsoringAccountCount + 1); addSponsorToLedgerEntry(sleDst, sponsor); From 278e25d8ad4dcf0e70e7f1a7e7c7e22b4029991a Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 21 Apr 2026 17:25:33 +0900 Subject: [PATCH 205/249] fix: Preclaim XRP reserve overestimate for sponsored checks #6899 --- src/libxrpl/tx/transactors/check/CheckCash.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index 943f6b968ef..25974aa48d1 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -150,7 +150,7 @@ CheckCash::preclaim(PreclaimContext const& ctx) // once the check is cashed, since the check's reserve will no // longer be required. So, if we're dealing in XRP, we add one // reserve's worth to the available funds. - if (value.native()) + if (value.native() && !sleCheck->isFieldPresent(sfSponsor)) availableFunds += XRPAmount{ctx.view.fees().increment}; if (value > availableFunds) From bb27479686c3fba348f2d803f9d1ab359762429a Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 21 Apr 2026 17:51:50 +0900 Subject: [PATCH 206/249] fix: TrustSet free trust line check uses sponsor's ownerCount, skipping sponsor reserve validation #6901 --- src/libxrpl/tx/transactors/token/TrustSet.cpp | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index f5a22e0c99d..160c3beaaf4 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -356,7 +356,7 @@ TrustSet::doApply() bool const isSponsoredAndPreFunded = txSponsorSle && !isSponsorReserveCoSigning(ctx_.tx); // If PreFunded Sponsor, it must be checked whether sufficient // ReserveCount exists. - bool const freeTrustLine = uOwnerCount < 2 && !isSponsoredAndPreFunded; + bool const freeTrustLine = uOwnerCount < 2 && !txSponsorSle; std::uint32_t const uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0); std::uint32_t uQualityOut(bQualityOut ? ctx_.tx.getFieldU32(sfQualityOut) : 0); @@ -622,10 +622,9 @@ TrustSet::doApply() terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if ( - auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); - !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) + else if (auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); + !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -653,15 +652,14 @@ TrustSet::doApply() JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if ( - auto const ret = checkInsufficientReserve( - ctx_.view(), - ctx_.tx, - sle, - preFeeBalance_, - txSponsorSle, - 1); - !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. + else if (auto const ret = checkInsufficientReserve( + ctx_.view(), + ctx_.tx, + sle, + preFeeBalance_, + txSponsorSle, + 1); + !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; From b2b2babe1eb9e4636ac35e83e41e3a6c8f7cf2fc Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 21 Apr 2026 18:28:09 +0900 Subject: [PATCH 207/249] fix: PermissionedDomain invariant skips credential validation for SponsorshipTransfer #6902 --- src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp index 0f82766ea20..951601d9d69 100644 --- a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp @@ -148,7 +148,7 @@ ValidPermissionedDomain::finalize( "deleted by SponsorshipTransfer"; return false; } - return true; + return check(sleStatus_[0], j); } default: { if (!sleStatus_.empty()) From c072b125a0d6be59770faeb76761fd4475f6f456 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 21 Apr 2026 18:59:36 +0900 Subject: [PATCH 208/249] fix: No invariant verifying sfSponsor field on objects matches sponsoring/sponsored count deltas #6897 --- .../xrpl/tx/invariants/SponsorshipInvariant.h | 1 + .../tx/invariants/SponsorshipInvariant.cpp | 54 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/include/xrpl/tx/invariants/SponsorshipInvariant.h b/include/xrpl/tx/invariants/SponsorshipInvariant.h index fc6f87f3f03..15088eda00a 100644 --- a/include/xrpl/tx/invariants/SponsorshipInvariant.h +++ b/include/xrpl/tx/invariants/SponsorshipInvariant.h @@ -21,6 +21,7 @@ class SponsorshipOwnerCountsMatch { std::int64_t deltaSponsoredOwnerCount_ = 0; std::int64_t deltaSponsoringOwnerCount_ = 0; + std::int64_t deltaSponsoredObjectOwnerCount_ = 0; std::uint64_t invalidOwnerCountLessThanSponsoredOwnerCount_ = 0; public: diff --git a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp index 72fdb5c8427..c429a00455f 100644 --- a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp +++ b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp @@ -1,13 +1,15 @@ #include // #include +#include +#include namespace xrpl { // Add new sponsorship-related invariants implementations void SponsorshipOwnerCountsMatch::visitEntry( - bool, + bool isDelete, std::shared_ptr const& before, std::shared_ptr const& after) { @@ -33,14 +35,57 @@ SponsorshipOwnerCountsMatch::visitEntry( return 0; }; + auto getSponsoredObjectOwnerCount = + [&](std::shared_ptr const& sle) -> std::uint32_t { + if (!sle) + return 0; + switch (sle->getType()) + { + case ltACCOUNT_ROOT: { + return 0; + } + case ltRIPPLE_STATE: { + uint32_t ownerCount = 0; + if (sle->isFieldPresent(sfHighSponsor)) + ownerCount++; + if (sle->isFieldPresent(sfLowSponsor)) + ownerCount++; + return ownerCount; + } + case ltORACLE: { + if (!sle->isFieldPresent(sfSponsor)) + return 0; + auto const priceDataSeries = sle->getFieldArray(sfPriceDataSeries); + return OracleSet::calculateOracleReserve(priceDataSeries.size()); + } + case ltVAULT: { + if (!sle->isFieldPresent(sfSponsor)) + return 0; + return 2; + } + default: { + if (sle->isFieldPresent(sfSponsor)) + return 1; + return 0; + } + } + }; + std::int64_t const beforeSponsored = getSponsored(before); std::int64_t const afterSponsored = getSponsored(after); std::int64_t const beforeSponsoring = getSponsoring(before); std::int64_t const afterSponsoring = getSponsoring(after); + std::int64_t const beforeSponsoredObjectOwnerCount = getSponsoredObjectOwnerCount(before); + std::int64_t const afterSponsoredObjectOwnerCount = + isDelete ? 0 : getSponsoredObjectOwnerCount(after); + deltaSponsoredOwnerCount_ += (afterSponsored - beforeSponsored); deltaSponsoringOwnerCount_ += (afterSponsoring - beforeSponsoring); + deltaSponsoredObjectOwnerCount_ += + (afterSponsoredObjectOwnerCount - beforeSponsoredObjectOwnerCount); + if (getOwnerCount(after) < getSponsoredOwnerCount(after)) invalidOwnerCountLessThanSponsoredOwnerCount_ += 1; } @@ -60,6 +105,13 @@ SponsorshipOwnerCountsMatch::finalize( return false; } + if (deltaSponsoredObjectOwnerCount_ != deltaSponsoredOwnerCount_) + { + JLOG(j.fatal()) << "Invariant failed: SponsoredObjectOwnerCount does not " + "equal SponsoredOwnerCount delta."; + return false; + } + if (invalidOwnerCountLessThanSponsoredOwnerCount_ > 0) { JLOG(j.fatal()) From 6ee071c5b06825b781dee7b36624f2b52452528c Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 28 Apr 2026 15:57:49 +0900 Subject: [PATCH 209/249] fix: An incorrect available XRP balance check in AMM deposit allows reserve-locked funds to be used as adding liquidity --- src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 23 +++++++---- src/test/app/Sponsor_test.cpp | 41 +++++++++++++++++++ 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index bfbb82675bf..1f0fa57f0a3 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -239,6 +239,15 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); auto const accountSle = ctx.view.read(keylet::account(accountID)); + auto const reserveAdj = (sponsorSle || sle) ? 0 : 1; + + if (xrpLiquid(ctx.view, accountID, reserveAdj, ctx.j) < deposit) + { + if (sle) + return tecUNFUNDED_AMM; + return tecINSUF_RESERVE_LINE; + } + if (auto const ret = checkInsufficientReserve( ctx.view, ctx.tx, @@ -247,11 +256,10 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) sponsorSle, 1, !sle); - isTesSuccess(ret)) - return TER(tesSUCCESS); - if (sle) - return tecUNFUNDED_AMM; - return tecINSUF_RESERVE_LINE; + sponsorSle && !isTesSuccess(ret)) + return tecINSUF_RESERVE_LINE; + + return tesSUCCESS; } return accountFunds( ctx.view, @@ -556,9 +564,8 @@ AMMDeposit::deposit( // Adjust the reserve if LP doesn't have LPToken trustline auto const trustlineExists = view.exists(keylet::line(account_, lpIssue.account, lpIssue.currency)); - auto const ownerCountAdj = trustlineExists ? 0 : 1; - if (xrpLiquid(view, sponsor.value_or(account_), sponsor ? ownerCountAdj : 0, j_) >= - depositAmount) + auto const reserveAdj = (sponsor || trustlineExists) ? 0 : 1; + if (xrpLiquid(view, account_, reserveAdj, j_) >= depositAmount) return tesSUCCESS; } else if ( diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index d427b4f52bb..fea76a7bac3 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2182,6 +2182,47 @@ class Sponsor_test : public beast::unit_test::suite submit(ammDeposit(env, bob, USD(100), EUR(100))); }); } + { + // AMMDeposit single-asset XRP: reserve sponsor covers LP trustline reserve + // but depositor's own liquid XRP is insufficient for the deposit → tecUNFUNDED_AMM + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor); + env.close(); + + env(trust(bob, USD(10000))); + env(trust(alice, USD(10000))); + env.close(); + env(pay(gw, bob, USD(1000))); + env.close(); + + AMM amm(env, bob, XRP(1000), USD(100)); + + // alice has 1 owner object (USD trust line); give her reserve + 5 XRP liquid + adjustAccountXRPBalance(env, alice, reserve(env, ownerCount(env, alice)) + XRP(5)); + + auto const jv = AMM::depositJv( + {.account = alice, + .asset1In = XRP(10), + .assets = std::make_pair(Asset{xrpIssue()}, Asset{USD.issue()})}); + + if (cosigning) + { + env(jv, + sponsor::as(sponsor, spfSponsorReserve), + sig(sfSponsorSignature, sponsor), + ter(tecINSUF_RESERVE_LINE)); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice)); + env.close(); + env(jv, sponsor::as(sponsor, spfSponsorReserve), ter(tecINSUF_RESERVE_LINE)); + env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); + } + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); // no LP token was created + } { // AMMWithdraw { From e8deaa12d8852b0f28a2d64a5d783d69f6a9697b Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 28 Apr 2026 16:54:30 +0900 Subject: [PATCH 210/249] pre-commit run --- src/libxrpl/tx/transactors/token/TrustSet.cpp | 24 ++++++++++--------- src/test/jtx/impl/sponsor.cpp | 1 + 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index 9011b3ff270..87fe1eff2cb 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -642,9 +642,10 @@ TrustSet::doApply() terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); } // Reserve is not scaled by load. - else if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); - !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) + else if ( + auto const ret = + checkInsufficientReserve(view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); + !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " "add trust line."; @@ -672,14 +673,15 @@ TrustSet::doApply() JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if (auto const ret = checkInsufficientReserve( - ctx_.view(), - ctx_.tx, - sle, - preFeeBalance_, - txSponsorSle, - 1); - !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. + else if ( + auto const ret = checkInsufficientReserve( + ctx_.view(), + ctx_.tx, + sle, + preFeeBalance_, + txSponsorSle, + 1); + !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 59adb58f030..be550a36f12 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -1,4 +1,5 @@ #include + #include #include From fbff1c065ca4efd6802f4c5ac3f33e596a72abca Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 28 Apr 2026 17:04:14 +0900 Subject: [PATCH 211/249] levelization --- .github/scripts/levelization/results/ordering.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/scripts/levelization/results/ordering.txt b/.github/scripts/levelization/results/ordering.txt index d2a18945850..c3b35bd3790 100644 --- a/.github/scripts/levelization/results/ordering.txt +++ b/.github/scripts/levelization/results/ordering.txt @@ -50,6 +50,7 @@ libxrpl.tx > xrpl.protocol libxrpl.tx > xrpl.server libxrpl.tx > xrpl.tx test.app > test.jtx +test.app > test.toplevel test.app > test.unit_test test.app > xrpl.basics test.app > xrpl.core From c12eff6646f4f5d065212226f3c1d912a0bb5dda Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 28 Apr 2026 17:09:57 +0900 Subject: [PATCH 212/249] apply tidy diff --- .../xrpl/ledger/helpers/AccountRootHelpers.h | 2 +- include/xrpl/ledger/helpers/SponsorHelpers.h | 2 +- .../xrpl/tx/invariants/SponsorshipInvariant.h | 4 +- src/libxrpl/ledger/View.cpp | 4 +- .../ledger/helpers/AccountRootHelpers.cpp | 10 ++- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 2 +- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 3 + .../ledger/helpers/RippleStateHelpers.cpp | 2 + src/libxrpl/ledger/helpers/TokenHelpers.cpp | 17 ++-- src/libxrpl/tx/Transactor.cpp | 36 ++++---- .../tx/invariants/SponsorshipInvariant.cpp | 14 ++- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 47 ++++++---- .../Sponsor/SponsorshipTransfer.cpp | 47 ++++++---- .../tx/transactors/account/AccountDelete.cpp | 9 +- .../tx/transactors/account/SignerListSet.cpp | 2 + .../tx/transactors/bridge/XChainBridge.cpp | 2 + .../tx/transactors/check/CheckCancel.cpp | 1 + .../tx/transactors/check/CheckCash.cpp | 2 +- .../tx/transactors/check/CheckCreate.cpp | 1 + .../credentials/CredentialAccept.cpp | 2 + .../credentials/CredentialCreate.cpp | 2 + .../tx/transactors/delegate/DelegateSet.cpp | 2 + src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 1 + src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 1 + .../tx/transactors/dex/AMMWithdraw.cpp | 2 + .../tx/transactors/dex/OfferCreate.cpp | 1 + src/libxrpl/tx/transactors/did/DIDDelete.cpp | 1 + src/libxrpl/tx/transactors/did/DIDSet.cpp | 2 + .../tx/transactors/escrow/EscrowCancel.cpp | 1 + .../tx/transactors/escrow/EscrowCreate.cpp | 1 + .../tx/transactors/escrow/EscrowFinish.cpp | 1 + .../transactors/lending/LoanBrokerDelete.cpp | 1 + .../tx/transactors/lending/LoanBrokerSet.cpp | 1 + .../tx/transactors/lending/LoanDelete.cpp | 1 + .../tx/transactors/lending/LoanSet.cpp | 1 + .../tx/transactors/nft/NFTokenAcceptOffer.cpp | 1 + .../tx/transactors/nft/NFTokenMint.cpp | 1 + .../tx/transactors/oracle/OracleDelete.cpp | 1 + .../tx/transactors/oracle/OracleSet.cpp | 7 +- .../tx/transactors/payment/DepositPreauth.cpp | 2 + .../tx/transactors/payment/Payment.cpp | 3 + .../payment_channel/PaymentChannelCreate.cpp | 1 + .../payment_channel/PaymentChannelFund.cpp | 2 + .../PermissionedDomainDelete.cpp | 1 + .../PermissionedDomainSet.cpp | 2 + src/libxrpl/tx/transactors/system/Batch.cpp | 2 +- .../tx/transactors/system/TicketCreate.cpp | 2 + .../token/MPTokenIssuanceCreate.cpp | 2 + .../token/MPTokenIssuanceDestroy.cpp | 1 + src/libxrpl/tx/transactors/token/TrustSet.cpp | 2 + .../tx/transactors/vault/VaultCreate.cpp | 1 + .../tx/transactors/vault/VaultDelete.cpp | 1 + .../tx/transactors/vault/VaultDeposit.cpp | 2 + .../tx/transactors/vault/VaultWithdraw.cpp | 1 + src/test/app/Invariants_test.cpp | 16 ++-- src/test/app/Oracle_test.cpp | 1 + src/test/app/Sponsor_test.cpp | 86 +++++++++++++++---- src/test/jtx/impl/sponsor.cpp | 28 +++--- src/test/jtx/owners.h | 10 +-- src/test/jtx/sponsor.h | 24 +++--- src/test/rpc/AccountObjects_test.cpp | 1 + 61 files changed, 303 insertions(+), 126 deletions(-) diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index 26f5e0d918f..bfc43f2b316 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -51,7 +51,7 @@ adjustOwnerCount( std::int32_t amount, beast::Journal j) { - return adjustOwnerCount( + adjustOwnerCount( view, view.peek(keylet::account(account)), sponsor ? view.peek(keylet::account(*sponsor)) : std::shared_ptr(), diff --git a/include/xrpl/ledger/helpers/SponsorHelpers.h b/include/xrpl/ledger/helpers/SponsorHelpers.h index 3b9ae39a579..74cbbd069c2 100644 --- a/include/xrpl/ledger/helpers/SponsorHelpers.h +++ b/include/xrpl/ledger/helpers/SponsorHelpers.h @@ -13,7 +13,7 @@ namespace xrpl { inline bool isReserveSponsored(STTx const& tx) { - return tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve; + return (tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve) != 0u; } inline bool diff --git a/include/xrpl/tx/invariants/SponsorshipInvariant.h b/include/xrpl/tx/invariants/SponsorshipInvariant.h index 15088eda00a..fbe3cd2aaad 100644 --- a/include/xrpl/tx/invariants/SponsorshipInvariant.h +++ b/include/xrpl/tx/invariants/SponsorshipInvariant.h @@ -29,7 +29,7 @@ class SponsorshipOwnerCountsMatch visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; /** @@ -50,7 +50,7 @@ class SponsorshipAccountCountMatchesField visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool - finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; } // namespace xrpl diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 88bd68a4983..cdd781c0724 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -339,9 +340,10 @@ checkInsufficientReserve( auto const sle = view.read( keylet::sponsor(sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount))); - if (!isCoSigning && !sle) + if (!isCoSigning && !sle) { // prefunded sponsor should have a sponsorship entry return tecINTERNAL; // LCOV_EXCL_LINE +} if (sle) { diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 0d916255e76..3de6f98f7f6 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -146,10 +146,11 @@ adjustSponsorOwnerCountHlp( std::uint32_t const current{(sle)->getFieldU32(sfield)}; std::uint32_t const adjusted = confineOwnerCount(current, amount, accID, j); view.adjustOwnerCountHook(accID, current, adjusted); - if (adjusted == 0) + if (adjusted == 0) { sle->makeFieldAbsent(sfield); - else + } else { sle->setFieldU32(sfield, adjusted); +} view.update(sle); } @@ -185,10 +186,11 @@ adjustOwnerCount( // payback (+) std::uint32_t const adjusted = confineOwnerCount(currentReserveCount, -amount, sponsorAccountID, j); - if (adjusted == 0) + if (adjusted == 0) { sponsorObjSle->makeFieldAbsent(sfReserveCount); - else + } else { sponsorObjSle->setFieldU32(sfReserveCount, adjusted); +} view.update(sponsorObjSle); } } diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index a2a1b48eed7..3920db6dda5 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -29,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index 8635a94d9b1..cce41ccda22 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include #include diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index 826224af94a..3061a7f0ffe 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index 33f830a419d..5d5fdfd68ab 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -26,21 +26,18 @@ #include #include #include +#include #include #include +#include #include #include namespace xrpl { // Forward declaration for function that remains in View.h/cpp -bool -isLPTokenFrozen( - ReadView const& view, - AccountID const& account, - Asset const& asset, - Asset const& asset2); + //------------------------------------------------------------------------------ // @@ -475,10 +472,11 @@ removeEmptyHolding( { return std::visit( [&](TIss const& issue) -> TER { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { return removeEmptyHolding(view, accountID, issue, journal); - else + } else { return removeEmptyHolding(view, tx, accountID, issue, journal); +} }, asset.value()); } @@ -713,9 +711,10 @@ directSendNoLimitIOU( TER terResult = directSendNoFeeIOU(view, issuer, uReceiverID, saAmount, true, sponsorAccountID, j); - if (tesSUCCESS == terResult) + if (tesSUCCESS == terResult) { terResult = directSendNoFeeIOU(view, uSenderID, issuer, saActual, true, sponsorAccountID, j); +} return terResult; } diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 07ebaa661ae..c989b76ff1a 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include // IWYU pragma: keep @@ -41,7 +40,10 @@ #include #include #include +#include +#include +#include #include #include #include @@ -187,7 +189,7 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return temDISABLED; if (hasSponsorFlags && - ctx.tx.getFieldU32(sfSponsorFlags) & ~(spfSponsorFee | spfSponsorReserve)) + ((ctx.tx.getFieldU32(sfSponsorFlags) & ~(spfSponsorFee | spfSponsorReserve)) != 0u)) { JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; return temINVALID_FLAG; @@ -210,7 +212,7 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) else if (hasSponsorFlags) { auto const sponsorFlags = ctx.tx.getFieldU32(sfSponsorFlags); - if ((sponsorFlags & ~(spfSponsorFee | spfSponsorReserve)) || sponsorFlags == 0) + if (((sponsorFlags & ~(spfSponsorFee | spfSponsorReserve)) != 0u) || sponsorFlags == 0) { JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; return temINVALID_FLAG; @@ -374,10 +376,10 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) auto const sponsorFlags = tx.getFieldU32(sfSponsorFlags); - if (sponsorFlags & spfSponsorFee && sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) + if (((sponsorFlags & spfSponsorFee) != 0u) && sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) return terNO_SPONSORSHIP; - if (sponsorFlags & spfSponsorReserve && sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) + if (((sponsorFlags & spfSponsorReserve) != 0u) && sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) return terNO_SPONSORSHIP; return tesSUCCESS; @@ -482,9 +484,10 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (!payerSle) { - if (payer.type == FeePayerType::SponsorPreFunded) + if (payer.type == FeePayerType::SponsorPreFunded) { // Sanity check: already checked in checkSponsor return tefINTERNAL; // LCOV_EXCL_LINE +} return terNO_ACCOUNT; } @@ -560,11 +563,12 @@ Transactor::payFee() auto const feeAmountAfter = sle->getFieldAmount(payer.balanceField) - feePaid; - if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) + if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) { // Because ltSponsorship.sfFeeAmount is soeOptional sle->makeFieldAbsent(payer.balanceField); - else + } else { sle->setFieldAmount(payer.balanceField, feeAmountAfter); +} view().update(sle); @@ -1251,11 +1255,12 @@ Transactor::reset(XRPAmount fee) // then the ledger is corrupted. Rather than make things worse we // reject the transaction. auto const feeAmountAfter = balance - fee; - if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) + if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) { // Because ltSponsorship.sfFeeAmount is soeOptional payerSle->makeFieldAbsent(payer.balanceField); - else + } else { payerSle->setFieldAmount(payer.balanceField, feeAmountAfter); +} TER const ter{consumeSeqProxy(txnAcct)}; XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS"); @@ -1273,7 +1278,7 @@ Transactor::reset(XRPAmount fee) FeePayer Transactor::getFeePayer(ReadView const& view, STTx const& tx) { - if (tx.isFieldPresent(sfSponsor) && (tx.getFieldU32(sfSponsorFlags) & spfSponsorFee)) + if (tx.isFieldPresent(sfSponsor) && ((tx.getFieldU32(sfSponsorFlags) & spfSponsorFee) != 0u)) { auto const sponsorAccountID = tx.getAccountID(sfSponsor); auto const sponseeAccountID = tx.getAccountID(sfAccount); @@ -1281,20 +1286,21 @@ Transactor::getFeePayer(ReadView const& view, STTx const& tx) auto const sponsorshipKeylet = keylet::sponsor(sponsorAccountID, sponseeAccountID); // if pre-funded sponsorship exists, prefer it - if (hasSponsorSignature && !view.exists(sponsorshipKeylet)) + if (hasSponsorSignature && !view.exists(sponsorshipKeylet)) { // co-signed return FeePayer{ - keylet::account(sponsorAccountID), sfBalance, FeePayerType::SponsorCoSigned}; + .entry=keylet::account(sponsorAccountID), .balanceField=sfBalance, .type=FeePayerType::SponsorCoSigned}; +} // pre funded - return FeePayer{sponsorshipKeylet, sfFeeAmount, FeePayerType::SponsorPreFunded}; + return FeePayer{.entry=sponsorshipKeylet, .balanceField=sfFeeAmount, .type=FeePayerType::SponsorPreFunded}; } auto const payerAccountKeylet = keylet::account(tx.getFeePayer()); auto const payerType = tx.isFieldPresent(sfDelegate) ? FeePayerType::Delegate : FeePayerType::Account; - return FeePayer{payerAccountKeylet, sfBalance, payerType}; + return FeePayer{.entry=payerAccountKeylet, .balanceField=sfBalance, .type=payerType}; } // The sole purpose of this function is to provide a convenient, named diff --git a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp index c429a00455f..e49749abf8a 100644 --- a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp +++ b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp @@ -3,6 +3,16 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace xrpl { @@ -96,7 +106,7 @@ SponsorshipOwnerCountsMatch::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (deltaSponsoredOwnerCount_ != deltaSponsoringOwnerCount_) { @@ -153,7 +163,7 @@ SponsorshipAccountCountMatchesField::finalize( TER const, XRPAmount const, ReadView const&, - beast::Journal const& j) + beast::Journal const& j) const { if (deltaSponsoringAccountCount_ != deltaSponsorFieldPresence_) { diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index a8f056bbd6b..683a4e7623c 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -6,6 +6,20 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace xrpl { @@ -20,11 +34,11 @@ SponsorshipSet::preflight(PreflightContext const& ctx) { auto const flags = ctx.tx.getFlags(); - if ((flags & tfSponsorshipSetRequireSignForFee) && - (flags & tfSponsorshipClearRequireSignForFee)) + if (((flags & tfSponsorshipSetRequireSignForFee) != 0u) && + ((flags & tfSponsorshipClearRequireSignForFee) != 0u)) return temINVALID_FLAG; - if ((flags & tfSponsorshipSetRequireSignForReserve) && - (flags & tfSponsorshipClearRequireSignForReserve)) + if (((flags & tfSponsorshipSetRequireSignForReserve) != 0u) && + ((flags & tfSponsorshipClearRequireSignForReserve) != 0u)) return temINVALID_FLAG; auto const account = ctx.tx.getAccountID(sfAccount); @@ -48,7 +62,7 @@ SponsorshipSet::preflight(PreflightContext const& ctx) tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForFee | tfSponsorshipClearRequireSignForReserve; - if (flags & modifyFlags) + if ((flags & modifyFlags) != 0u) return temINVALID_FLAG; // can not include these fields when deleting @@ -109,17 +123,17 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) // this is added in case more flags will be added for SponsorshipSet // in the future. Currently unreachable. - if (txFlags & tfSponsorshipSetPermissionMask) + if ((txFlags & tfSponsorshipSetPermissionMask) != 0u) return terNO_DELEGATE_PERMISSION; std::unordered_set granularPermissions; loadGranularPermission(sle, ttSPONSORSHIP_SET, granularPermissions); auto const sponsoringFee = tx.isFieldPresent(sfFeeAmount) || tx.isFieldPresent(sfMaxFee) || - (txFlags & (tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee)); + ((txFlags & (tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee)) != 0u); auto const sponsoringReserve = tx.isFieldPresent(sfReserveCount) || - (txFlags & - (tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve)); + ((txFlags & + (tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve)) != 0u); if (sponsoringFee && !granularPermissions.contains(SponsorFee)) return terNO_DELEGATE_PERMISSION; @@ -292,10 +306,11 @@ SponsorshipSet::doApply() { (*sponsorAccSle)[sfBalance] -= feeAmountDelta; - if (*feeAmount == XRPAmount(0)) + if (*feeAmount == XRPAmount(0)) { (*sponsorObjSle).makeFieldAbsent(sfFeeAmount); - else + } else { (*sponsorObjSle).setFieldAmount(sfFeeAmount, *feeAmount); +} if (auto const ret = checkInsufficientReserve( ctx_.view(), @@ -311,18 +326,20 @@ SponsorshipSet::doApply() if (maxFee) { - if (*maxFee == XRPAmount(0)) + if (*maxFee == XRPAmount(0)) { (*sponsorObjSle).makeFieldAbsent(sfMaxFee); - else + } else { (*sponsorObjSle)[sfMaxFee] = *maxFee; +} } if (reserveCount) { - if (*reserveCount == 0) + if (*reserveCount == 0) { (*sponsorObjSle).makeFieldAbsent(sfReserveCount); - else + } else { (*sponsorObjSle)[sfReserveCount] = *reserveCount; +} } // update Flags diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 3fa29237e41..135a79f8f7c 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -2,14 +2,25 @@ #include #include -#include -#include #include #include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace xrpl { @@ -30,7 +41,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } - if (flags & tfSponsorshipCreate) + if ((flags & tfSponsorshipCreate) != 0u) { if (!isReserveSponsored(ctx.tx)) { @@ -45,7 +56,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) return temMALFORMED; } } - if (flags & tfSponsorshipReassign) + if ((flags & tfSponsorshipReassign) != 0u) { if (!isReserveSponsored(ctx.tx)) { @@ -60,7 +71,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) return temMALFORMED; } } - if (flags & tfSponsorshipEnd) + if ((flags & tfSponsorshipEnd) != 0u) { if (isReserveSponsored(ctx.tx)) { @@ -243,7 +254,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) auto const& sponsorField = getLedgerEntrySponsorField(sle, *owner); - if (flags & tfSponsorshipCreate) + if ((flags & tfSponsorshipCreate) != 0u) { if (!newSponsor) return tecNO_PERMISSION; @@ -252,7 +263,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (sle->isFieldPresent(sponsorField)) return tecNO_PERMISSION; } - else if (flags & tfSponsorshipReassign) + else if ((flags & tfSponsorshipReassign) != 0u) { if (!newSponsor) return tecNO_PERMISSION; @@ -261,7 +272,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (!sle->isFieldPresent(sponsorField)) return tecNO_PERMISSION; } - else if (flags & tfSponsorshipEnd) + else if ((flags & tfSponsorshipEnd) != 0u) { if (newSponsor) return tecNO_PERMISSION; @@ -289,7 +300,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } else { - if (flags & tfSponsorshipCreate) + if ((flags & tfSponsorshipCreate) != 0u) { if (!newSponsor) return tecNO_PERMISSION; @@ -298,7 +309,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (sponseeSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; } - else if (flags & tfSponsorshipReassign) + else if ((flags & tfSponsorshipReassign) != 0u) { if (!newSponsor) return tecNO_PERMISSION; @@ -307,7 +318,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (!sponseeSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; } - else if (flags & tfSponsorshipEnd) + else if ((flags & tfSponsorshipEnd) != 0u) { if (newSponsor) return tecNO_PERMISSION; @@ -423,7 +434,7 @@ SponsorshipTransfer::doApply() auto const& sponsorField = getLedgerEntrySponsorField(objSle, *ownerAccountID); - if (flags & tfSponsorshipCreate) + if ((flags & tfSponsorshipCreate) != 0u) { auto const newSponsorAccountID = tx.getAccountID(sfSponsor); XRPL_ASSERT(!!newSponsorAccountID, "New sponsor is required when creating sponsorship"); @@ -452,7 +463,7 @@ SponsorshipTransfer::doApply() return ter; } } - else if (flags & tfSponsorshipReassign) + else if ((flags & tfSponsorshipReassign) != 0u) { auto const newSponsorAccountID = tx.getAccountID(sfSponsor); XRPL_ASSERT( @@ -500,7 +511,7 @@ SponsorshipTransfer::doApply() return ter; } } - else if (flags & tfSponsorshipEnd) + else if ((flags & tfSponsorshipEnd) != 0u) { auto const oldSponsorAccountID = objSle->getAccountID(sponsorField); XRPL_ASSERT(!!oldSponsorAccountID, "Old sponsor is required when ending sponsorship"); @@ -535,7 +546,7 @@ SponsorshipTransfer::doApply() } else { - if (flags & tfSponsorshipCreate) + if ((flags & tfSponsorshipCreate) != 0u) { // create account sponsor // increment new sponsoring count @@ -550,7 +561,7 @@ SponsorshipTransfer::doApply() sponseeSle->setAccountID(sfSponsor, newSponsorAccountID); view().update(sponseeSle); } - else if (flags & tfSponsorshipReassign) + else if ((flags & tfSponsorshipReassign) != 0u) { // reassign account sponsor // increment new sponsoring count @@ -573,7 +584,7 @@ SponsorshipTransfer::doApply() sponseeSle->setAccountID(sfSponsor, newSponsorAccountID); view().update(sponseeSle); } - else if (flags & tfSponsorshipEnd) + else if ((flags & tfSponsorshipEnd) != 0u) { // dissolve account sponsor auto const oldSponsorAccountID = sponseeSle->getAccountID(sfSponsor); diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index a9c3fd2cc97..23833d10731 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -417,16 +416,18 @@ AccountDelete::doApply() auto const sponsoringAccountCount = sponsorSle->getFieldU32(sfSponsoringAccountCount); - if (sponsoringAccountCount == 0) + if (sponsoringAccountCount == 0) { // sanity check // Since sfSponsoringAccountCount is set to soeDEFAULT, the field will not be // populated with a value of 0. return tefINTERNAL; // LCOV_EXCL_LINE +} - if (sponsoringAccountCount == 1) + if (sponsoringAccountCount == 1) { sponsorSle->makeFieldAbsent(sfSponsoringAccountCount); - else + } else { sponsorSle->setFieldU32(sfSponsoringAccountCount, sponsoringAccountCount - 1); +} view().update(sponsorSle); // Following line might look redundant, but without it, sfSponsor diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index 3a096b79251..75a9e0cc7a3 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index a731e75405b..4b294af8b57 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/check/CheckCancel.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp index 99a8c2fa409..69a8b54b6ad 100644 --- a/src/libxrpl/tx/transactors/check/CheckCancel.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index 59640b62506..ae6a912e0c5 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -28,9 +28,9 @@ #include #include #include +#include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/check/CheckCreate.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp index 2ce045f40a6..93069232ea4 100644 --- a/src/libxrpl/tx/transactors/check/CheckCreate.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index 6eb6a39d4ee..b957b0ed31a 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index 6c8b6a15dad..b4a2b73c25d 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 14c110bce58..a85b9d7f1d6 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index f4a190a9e7c..bef09c155d3 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index 1f0fa57f0a3..ee4cb063954 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index a4344cba00a..4957f0017a7 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index c8f9c2c9230..f04a7b5534e 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp index eaeeb56c918..893f75f3015 100644 --- a/src/libxrpl/tx/transactors/did/DIDDelete.cpp +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index fbbd540ae6c..29026841d6a 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp index 732f2d991ca..2e543b2bd60 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 77c6b8c7697..276b8b01e7e 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index cbfd2b95352..bbd6fdf2a56 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 11104fc5f08..f455778865b 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index 4f562316e48..b246544effc 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index f41f47c9eed..713c370912e 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index d3031737a93..567c56b92ee 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index f0117558206..9d6f03984b1 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index 50de9742450..2f3654ac7ab 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index 173912dc333..db24999ecd3 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index 33121f7d0d0..80eb80c93e6 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -163,10 +165,11 @@ OracleSet::preclaim(PreclaimContext const& ctx) auto const currentSponsor = getLedgerEntryReserveSponsorAccountID(sle); auto const newSponsor = getTxReserveSponsorAccountID(ctx.tx); if ((!currentSponsor && !newSponsor) || - (currentSponsor && newSponsor && *currentSponsor == *newSponsor)) + (currentSponsor && newSponsor && *currentSponsor == *newSponsor)) { adjustReserve = newCount - oldCount; - else + } else { adjustReserve = newCount; +} } else { diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 67616282dad..a39182e0f94 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index c4288c9e561..82849bf8b14 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -36,9 +36,12 @@ #include #include #include +#include +#include #include #include +#include #include #include #include diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp index fa77711d7ab..f4786339c35 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp index 4cb8aad2308..2d79b50f25d 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index a82d6188111..47ce12d2260 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index 2bd79b43efe..549577869ee 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/system/Batch.cpp b/src/libxrpl/tx/transactors/system/Batch.cpp index 10af4b99f88..84190d98308 100644 --- a/src/libxrpl/tx/transactors/system/Batch.cpp +++ b/src/libxrpl/tx/transactors/system/Batch.cpp @@ -216,7 +216,7 @@ Batch::preflight(PreflightContext const& ctx) if (ctx.tx.isFieldPresent(sfSponsorFlags)) { auto const sponsorFlags = ctx.tx.getFieldU32(sfSponsorFlags); - if (sponsorFlags & spfSponsorReserve) + if ((sponsorFlags & spfSponsorReserve) != 0u) { JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]:" << "spfSponsorReserve is not allowed on outer Batch."; diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp index 67b2297cb12..17da80e9ac8 100644 --- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index d1614936157..6eaac19719e 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index 3311c6c96b7..a1ed15da743 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index 87fe1eff2cb..c32383d3164 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index 1cb44eebbb1..e12766d7396 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index e398e3babff..4431c685c9e 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index 59a7898111b..5d16ecae45c 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -19,8 +19,10 @@ #include #include #include +#include #include +#include #include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index 3b9818c9057..95e697ca927 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 068060d1395..51d6a26dbcf 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -1771,20 +1771,20 @@ class Invariants_test : public beast::unit_test::suite .func = [](SLE::pointer& sle) { sle->at(sfRegularKey) = Account("regular").id(); }, }, { - "pseudo-account has a sponsorship field", - [](SLE::pointer& sle) { sle->at(sfSponsoredOwnerCount) = 1; }, + .expectedFailure="pseudo-account has a sponsorship field", + .func=[](SLE::pointer& sle) { sle->at(sfSponsoredOwnerCount) = 1; }, }, { - "pseudo-account has a sponsorship field", - [](SLE::pointer& sle) { sle->at(sfSponsoringOwnerCount) = 1; }, + .expectedFailure="pseudo-account has a sponsorship field", + .func=[](SLE::pointer& sle) { sle->at(sfSponsoringOwnerCount) = 1; }, }, { - "pseudo-account has a sponsorship field", - [](SLE::pointer& sle) { sle->at(sfSponsoringAccountCount) = 1; }, + .expectedFailure="pseudo-account has a sponsorship field", + .func=[](SLE::pointer& sle) { sle->at(sfSponsoringAccountCount) = 1; }, }, { - "pseudo-account has a sponsorship field", - [](SLE::pointer& sle) { sle->at(sfSponsor) = Account("sponsor").id(); }, + .expectedFailure="pseudo-account has a sponsorship field", + .func=[](SLE::pointer& sle) { sle->at(sfSponsor) = Account("sponsor").id(); }, }, }); diff --git a/src/test/app/Oracle_test.cpp b/src/test/app/Oracle_test.cpp index 62a8f61e27d..5dab84a6b9f 100644 --- a/src/test/app/Oracle_test.cpp +++ b/src/test/app/Oracle_test.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index fea76a7bac3..6d7ac1a9cfb 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -9,9 +8,63 @@ #include #include - -namespace xrpl { -namespace test { +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace xrpl::test { static STAmount accountReserve(jtx::Env& env, std::uint32_t count = 1) @@ -35,13 +88,14 @@ adjustAccountXRPBalance(jtx::Env& env, jtx::Account const& account, STAmount con return; auto const baseFee = env.current()->fees().base; - if (currentBalance > balanceTo) + if (currentBalance > balanceTo) { env(pay(account, env.master, currentBalance - (balanceTo)), fee(XRP(1)), sponsor::as(env.master, spfSponsorFee), sig(sfSponsorSignature, env.master)); - else + } else { env(pay(env.master, account, balanceTo - currentBalance), fee(baseFee)); +} env.close(); } @@ -1966,10 +2020,11 @@ class Sponsor_test : public beast::unit_test::suite auto submit = [&](TER _ter) { return [&, _ter](Json::Value const& jv, auto const&... fN) { - if (sponsorSig) + if (sponsorSig) { env(jv, fN..., sponsor::as(sponsor, spfSponsorReserve), *sponsorSig, ter(_ter)); - else + } else { env(jv, fN..., sponsor::as(sponsor, spfSponsorReserve), ter(_ter)); +} }; }; @@ -1991,13 +2046,14 @@ class Sponsor_test : public beast::unit_test::suite env.close(); } - if (sponsorReserveCount - 1 > 0) + if (sponsorReserveCount - 1 > 0) { env(sponsor::set(sponsor, 0, sponsorReserveCount - 1, XRP(1)), sponsor::sponseeAcc(sponsee)); - else + } else { // just create sponsor object env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), sponsor::sponseeAcc(sponsee)); +} env.close(); } callback(env, submit(insufficientReserveResult)); @@ -2030,9 +2086,9 @@ class Sponsor_test : public beast::unit_test::suite } } - if (expected) + if (expected) { (*expected)(); - else + } else { BEAST_EXPECT(ownerCount(env, sponsee) - sponseeOwnerCountBefore == reserveCount); BEAST_EXPECT( @@ -2195,7 +2251,7 @@ class Sponsor_test : public beast::unit_test::suite env(pay(gw, bob, USD(1000))); env.close(); - AMM amm(env, bob, XRP(1000), USD(100)); + AMM const amm(env, bob, XRP(1000), USD(100)); // alice has 1 owner object (USD trust line); give her reserve + 5 XRP liquid adjustAccountXRPBalance(env, alice, reserve(env, ownerCount(env, alice)) + XRP(5)); @@ -5864,5 +5920,5 @@ BEAST_DEFINE_TESTSUITE(Sponsor, app, xrpl); BEAST_DEFINE_TESTSUITE(SponsorTxCosigning, app, xrpl); BEAST_DEFINE_TESTSUITE(SponsorTxPrefunded, app, xrpl); -} // namespace test -} // namespace xrpl +} // namespace xrpl::test + diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index be550a36f12..57a9cc32e68 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -1,16 +1,24 @@ #include -#include -#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -namespace xrpl { -namespace test { -namespace jtx { -namespace sponsor { + + + +namespace xrpl::test::jtx::sponsor { Json::Value set(jtx::Account const& account, @@ -122,7 +130,7 @@ ledgerEntry(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& spon return env.rpc("json", "ledger_entry", to_string(jvParams)); } -} // namespace sponsor -} // namespace jtx -} // namespace test -} // namespace xrpl +} // namespace xrpl::test::jtx::sponsor + + + diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index e912b953789..56512c30b16 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -71,7 +71,7 @@ class sponsored_owners std::uint32_t value_; public: - sponsored_owners(Account const& account, std::uint32_t value) : account_(account), value_(value) + sponsored_owners(Account account, std::uint32_t value) : account_(std::move(account)), value_(value) { } @@ -87,8 +87,8 @@ class sponsoring_owners std::uint32_t value_; public: - sponsoring_owners(Account const& account, std::uint32_t value) - : account_(account), value_(value) + sponsoring_owners(Account account, std::uint32_t value) + : account_(std::move(account)), value_(value) { } @@ -104,8 +104,8 @@ class sponsoring_account_count std::uint32_t value_; public: - sponsoring_account_count(Account const& account, std::uint32_t value) - : account_(account), value_(value) + sponsoring_account_count(Account account, std::uint32_t value) + : account_(std::move(account)), value_(value) { } diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index 8c7b01569ab..9dc99e4e191 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -4,11 +4,13 @@ #include #include -namespace xrpl { -namespace test { -namespace jtx { +#include -namespace sponsor { + + + + +namespace xrpl::test::jtx::sponsor { Json::Value set(jtx::Account const& account, @@ -45,7 +47,7 @@ struct counterpartySponsor jtx::Account sponsor_; public: - counterpartySponsor(jtx::Account const& account) : sponsor_(account) + counterpartySponsor(jtx::Account account) : sponsor_(std::move(account)) { } @@ -59,7 +61,7 @@ struct sponseeAcc jtx::Account sponsee_; public: - sponseeAcc(jtx::Account const& account) : sponsee_(account) + sponseeAcc(jtx::Account account) : sponsee_(std::move(account)) { } @@ -74,7 +76,7 @@ struct as std::uint32_t flags; public: - as(jtx::Account const& account, std::uint32_t flags = 0) : sponsor_(account), flags(flags) + as(jtx::Account account, std::uint32_t flags = 0) : sponsor_(std::move(account)), flags(flags) { } @@ -85,7 +87,7 @@ struct as Json::Value ledgerEntry(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& sponsee); -} // namespace sponsor -} // namespace jtx -} // namespace test -} // namespace xrpl +} // namespace xrpl::test::jtx::sponsor + + + diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 6c7a4217391..d68dcd3f4af 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include From b153213e83445fe6953c5ca00a85fd08e1b03d11 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 28 Apr 2026 17:20:00 +0900 Subject: [PATCH 213/249] run pre-commit --- src/libxrpl/ledger/View.cpp | 7 ++- .../ledger/helpers/AccountRootHelpers.cpp | 18 ++++-- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 2 +- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 6 +- .../ledger/helpers/RippleStateHelpers.cpp | 4 +- src/libxrpl/ledger/helpers/TokenHelpers.cpp | 17 +++--- src/libxrpl/tx/Transactor.cpp | 49 +++++++++------ .../tx/invariants/SponsorshipInvariant.cpp | 5 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 41 ++++++++----- .../Sponsor/SponsorshipTransfer.cpp | 15 ++--- .../tx/transactors/account/AccountDelete.cpp | 14 +++-- .../tx/transactors/account/SignerListSet.cpp | 4 +- .../tx/transactors/bridge/XChainBridge.cpp | 4 +- .../tx/transactors/check/CheckCancel.cpp | 2 +- .../tx/transactors/check/CheckCash.cpp | 2 +- .../tx/transactors/check/CheckCreate.cpp | 2 +- .../credentials/CredentialAccept.cpp | 4 +- .../credentials/CredentialCreate.cpp | 4 +- .../tx/transactors/delegate/DelegateSet.cpp | 4 +- src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 2 +- src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 2 +- .../tx/transactors/dex/AMMWithdraw.cpp | 4 +- .../tx/transactors/dex/OfferCreate.cpp | 2 +- src/libxrpl/tx/transactors/did/DIDDelete.cpp | 2 +- src/libxrpl/tx/transactors/did/DIDSet.cpp | 4 +- .../tx/transactors/escrow/EscrowCancel.cpp | 2 +- .../tx/transactors/escrow/EscrowCreate.cpp | 2 +- .../tx/transactors/escrow/EscrowFinish.cpp | 2 +- .../transactors/lending/LoanBrokerDelete.cpp | 2 +- .../tx/transactors/lending/LoanBrokerSet.cpp | 2 +- .../tx/transactors/lending/LoanDelete.cpp | 2 +- .../tx/transactors/lending/LoanSet.cpp | 2 +- .../tx/transactors/nft/NFTokenAcceptOffer.cpp | 2 +- .../tx/transactors/nft/NFTokenMint.cpp | 2 +- .../tx/transactors/oracle/OracleDelete.cpp | 2 +- .../tx/transactors/oracle/OracleSet.cpp | 13 ++-- .../tx/transactors/payment/DepositPreauth.cpp | 4 +- .../tx/transactors/payment/Payment.cpp | 4 +- .../payment_channel/PaymentChannelCreate.cpp | 2 +- .../payment_channel/PaymentChannelFund.cpp | 4 +- .../PermissionedDomainDelete.cpp | 2 +- .../PermissionedDomainSet.cpp | 4 +- .../tx/transactors/system/TicketCreate.cpp | 4 +- .../token/MPTokenIssuanceCreate.cpp | 4 +- .../token/MPTokenIssuanceDestroy.cpp | 2 +- src/libxrpl/tx/transactors/token/TrustSet.cpp | 4 +- .../tx/transactors/vault/VaultCreate.cpp | 2 +- .../tx/transactors/vault/VaultDelete.cpp | 2 +- .../tx/transactors/vault/VaultDeposit.cpp | 2 +- .../tx/transactors/vault/VaultWithdraw.cpp | 2 +- src/test/app/Invariants_test.cpp | 16 ++--- src/test/app/Oracle_test.cpp | 2 +- src/test/app/Sponsor_test.cpp | 61 +++++++++++-------- src/test/jtx/impl/sponsor.cpp | 16 ++--- src/test/jtx/owners.h | 7 ++- src/test/jtx/sponsor.h | 15 ++--- src/test/rpc/AccountObjects_test.cpp | 2 +- 57 files changed, 229 insertions(+), 185 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index cdd781c0724..0856f061ec9 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,6 @@ #include #include #include -#include #include #include @@ -340,10 +340,11 @@ checkInsufficientReserve( auto const sle = view.read( keylet::sponsor(sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount))); - if (!isCoSigning && !sle) { + if (!isCoSigning && !sle) + { // prefunded sponsor should have a sponsorship entry return tecINTERNAL; // LCOV_EXCL_LINE -} + } if (sle) { diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 3de6f98f7f6..b24b4123dec 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -146,11 +146,14 @@ adjustSponsorOwnerCountHlp( std::uint32_t const current{(sle)->getFieldU32(sfield)}; std::uint32_t const adjusted = confineOwnerCount(current, amount, accID, j); view.adjustOwnerCountHook(accID, current, adjusted); - if (adjusted == 0) { + if (adjusted == 0) + { sle->makeFieldAbsent(sfield); - } else { + } + else + { sle->setFieldU32(sfield, adjusted); -} + } view.update(sle); } @@ -186,11 +189,14 @@ adjustOwnerCount( // payback (+) std::uint32_t const adjusted = confineOwnerCount(currentReserveCount, -amount, sponsorAccountID, j); - if (adjusted == 0) { + if (adjusted == 0) + { sponsorObjSle->makeFieldAbsent(sfReserveCount); - } else { + } + else + { sponsorObjSle->setFieldU32(sfReserveCount, adjusted); -} + } view.update(sponsorObjSle); } } diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index 3920db6dda5..e28581a3051 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -23,12 +23,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index cce41ccda22..4ba0513cf06 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -7,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -22,15 +24,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include -#include -#include #include #include diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index 3061a7f0ffe..30a588daac8 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -22,11 +23,10 @@ #include #include #include +#include #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index 5d5fdfd68ab..436542f9b3d 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -23,10 +23,10 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -38,7 +38,6 @@ namespace xrpl { // Forward declaration for function that remains in View.h/cpp - //------------------------------------------------------------------------------ // // Freeze checking (Asset-based) @@ -472,11 +471,14 @@ removeEmptyHolding( { return std::visit( [&](TIss const& issue) -> TER { - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) + { return removeEmptyHolding(view, accountID, issue, journal); - } else { + } + else + { return removeEmptyHolding(view, tx, accountID, issue, journal); -} + } }, asset.value()); } @@ -711,10 +713,11 @@ directSendNoLimitIOU( TER terResult = directSendNoFeeIOU(view, issuer, uReceiverID, saAmount, true, sponsorAccountID, j); - if (tesSUCCESS == terResult) { + if (tesSUCCESS == terResult) + { terResult = directSendNoFeeIOU(view, uSenderID, issuer, saActual, true, sponsorAccountID, j); -} + } return terResult; } diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index c989b76ff1a..f6d2d7c619e 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -12,12 +12,14 @@ #include // IWYU pragma: keep #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -40,8 +42,6 @@ #include #include #include -#include -#include #include #include @@ -376,10 +376,12 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) auto const sponsorFlags = tx.getFieldU32(sfSponsorFlags); - if (((sponsorFlags & spfSponsorFee) != 0u) && sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) + if (((sponsorFlags & spfSponsorFee) != 0u) && + sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) return terNO_SPONSORSHIP; - if (((sponsorFlags & spfSponsorReserve) != 0u) && sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) + if (((sponsorFlags & spfSponsorReserve) != 0u) && + sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) return terNO_SPONSORSHIP; return tesSUCCESS; @@ -484,10 +486,11 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (!payerSle) { - if (payer.type == FeePayerType::SponsorPreFunded) { + if (payer.type == FeePayerType::SponsorPreFunded) + { // Sanity check: already checked in checkSponsor return tefINTERNAL; // LCOV_EXCL_LINE -} + } return terNO_ACCOUNT; } @@ -563,12 +566,15 @@ Transactor::payFee() auto const feeAmountAfter = sle->getFieldAmount(payer.balanceField) - feePaid; - if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) { + if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) + { // Because ltSponsorship.sfFeeAmount is soeOptional sle->makeFieldAbsent(payer.balanceField); - } else { + } + else + { sle->setFieldAmount(payer.balanceField, feeAmountAfter); -} + } view().update(sle); @@ -1255,12 +1261,15 @@ Transactor::reset(XRPAmount fee) // then the ledger is corrupted. Rather than make things worse we // reject the transaction. auto const feeAmountAfter = balance - fee; - if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) { + if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) + { // Because ltSponsorship.sfFeeAmount is soeOptional payerSle->makeFieldAbsent(payer.balanceField); - } else { + } + else + { payerSle->setFieldAmount(payer.balanceField, feeAmountAfter); -} + } TER const ter{consumeSeqProxy(txnAcct)}; XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS"); @@ -1286,21 +1295,27 @@ Transactor::getFeePayer(ReadView const& view, STTx const& tx) auto const sponsorshipKeylet = keylet::sponsor(sponsorAccountID, sponseeAccountID); // if pre-funded sponsorship exists, prefer it - if (hasSponsorSignature && !view.exists(sponsorshipKeylet)) { + if (hasSponsorSignature && !view.exists(sponsorshipKeylet)) + { // co-signed return FeePayer{ - .entry=keylet::account(sponsorAccountID), .balanceField=sfBalance, .type=FeePayerType::SponsorCoSigned}; -} + .entry = keylet::account(sponsorAccountID), + .balanceField = sfBalance, + .type = FeePayerType::SponsorCoSigned}; + } // pre funded - return FeePayer{.entry=sponsorshipKeylet, .balanceField=sfFeeAmount, .type=FeePayerType::SponsorPreFunded}; + return FeePayer{ + .entry = sponsorshipKeylet, + .balanceField = sfFeeAmount, + .type = FeePayerType::SponsorPreFunded}; } auto const payerAccountKeylet = keylet::account(tx.getFeePayer()); auto const payerType = tx.isFieldPresent(sfDelegate) ? FeePayerType::Delegate : FeePayerType::Account; - return FeePayer{.entry=payerAccountKeylet, .balanceField=sfBalance, .type=payerType}; + return FeePayer{.entry = payerAccountKeylet, .balanceField = sfBalance, .type = payerType}; } // The sole purpose of this function is to provide a convenient, named diff --git a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp index e49749abf8a..028b3b4fded 100644 --- a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp +++ b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp @@ -1,16 +1,17 @@ #include // #include -#include -#include #include #include #include #include #include +#include #include #include #include +#include + #include #include diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 683a4e7623c..426fdfc3a8b 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -1,22 +1,23 @@ #include +#include +#include +#include #include #include #include #include #include -#include -#include -#include -#include #include #include #include #include #include #include +#include #include #include + #include #include #include @@ -130,10 +131,11 @@ SponsorshipSet::checkPermission(ReadView const& view, STTx const& tx) loadGranularPermission(sle, ttSPONSORSHIP_SET, granularPermissions); auto const sponsoringFee = tx.isFieldPresent(sfFeeAmount) || tx.isFieldPresent(sfMaxFee) || - ((txFlags & (tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee)) != 0u); + ((txFlags & (tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee)) != + 0u); auto const sponsoringReserve = tx.isFieldPresent(sfReserveCount) || ((txFlags & - (tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve)) != 0u); + (tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve)) != 0u); if (sponsoringFee && !granularPermissions.contains(SponsorFee)) return terNO_DELEGATE_PERMISSION; @@ -306,11 +308,14 @@ SponsorshipSet::doApply() { (*sponsorAccSle)[sfBalance] -= feeAmountDelta; - if (*feeAmount == XRPAmount(0)) { + if (*feeAmount == XRPAmount(0)) + { (*sponsorObjSle).makeFieldAbsent(sfFeeAmount); - } else { + } + else + { (*sponsorObjSle).setFieldAmount(sfFeeAmount, *feeAmount); -} + } if (auto const ret = checkInsufficientReserve( ctx_.view(), @@ -326,20 +331,26 @@ SponsorshipSet::doApply() if (maxFee) { - if (*maxFee == XRPAmount(0)) { + if (*maxFee == XRPAmount(0)) + { (*sponsorObjSle).makeFieldAbsent(sfMaxFee); - } else { + } + else + { (*sponsorObjSle)[sfMaxFee] = *maxFee; -} + } } if (reserveCount) { - if (*reserveCount == 0) { + if (*reserveCount == 0) + { (*sponsorObjSle).makeFieldAbsent(sfReserveCount); - } else { + } + else + { (*sponsorObjSle)[sfReserveCount] = *reserveCount; -} + } } // update Flags diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 135a79f8f7c..0acfaa6f2d9 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -1,22 +1,23 @@ #include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include #include +#include +#include #include #include +#include #include +#include +#include +#include #include #include +#include + #include #include #include diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index 23833d10731..77164afa0cb 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -416,18 +416,22 @@ AccountDelete::doApply() auto const sponsoringAccountCount = sponsorSle->getFieldU32(sfSponsoringAccountCount); - if (sponsoringAccountCount == 0) { + if (sponsoringAccountCount == 0) + { // sanity check // Since sfSponsoringAccountCount is set to soeDEFAULT, the field will not be // populated with a value of 0. return tefINTERNAL; // LCOV_EXCL_LINE -} + } - if (sponsoringAccountCount == 1) { + if (sponsoringAccountCount == 1) + { sponsorSle->makeFieldAbsent(sfSponsoringAccountCount); - } else { + } + else + { sponsorSle->setFieldU32(sfSponsoringAccountCount, sponsoringAccountCount - 1); -} + } view().update(sponsorSle); // Following line might look redundant, but without it, sfSponsor diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index 75a9e0cc7a3..a61f2d0a719 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -6,8 +6,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -23,8 +25,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 4b294af8b57..d418366359b 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -38,8 +40,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/check/CheckCancel.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp index 69a8b54b6ad..6f738864e72 100644 --- a/src/libxrpl/tx/transactors/check/CheckCancel.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -12,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index ae6a912e0c5..77de70963a1 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/check/CheckCreate.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp index 93069232ea4..a2eb0624230 100644 --- a/src/libxrpl/tx/transactors/check/CheckCreate.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index b957b0ed31a..c6d930155b9 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -18,8 +20,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index b4a2b73c25d..3e69879ca1c 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -4,9 +4,11 @@ #include #include #include +#include #include #include // IWYU pragma: keep #include +#include #include #include #include @@ -20,8 +22,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index a85b9d7f1d6..157aabe5a27 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -15,8 +17,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index bef09c155d3..2d36ddc274a 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index ee4cb063954..05d36f21cf4 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index 4957f0017a7..77f5168ccf3 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -6,10 +6,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -29,8 +31,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index f04a7b5534e..e146248a4ca 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -41,7 +42,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp index 893f75f3015..b6d7ef94b49 100644 --- a/src/libxrpl/tx/transactors/did/DIDDelete.cpp +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -15,7 +16,6 @@ #include #include #include -#include #include diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index 29026841d6a..9c1f185015a 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -17,8 +19,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp index 2e543b2bd60..cd69438a462 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 276b8b01e7e..7d81d6923bf 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index bbd6fdf2a56..8e5c60ce8b3 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index f455778865b..68ef2f46524 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -16,7 +17,6 @@ #include #include #include -#include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index b246544effc..3a3e8111b98 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index 713c370912e..363f5cb12ae 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include // IWYU pragma: keep @@ -15,7 +16,6 @@ #include #include #include -#include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index 567c56b92ee..38d11ae830c 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index 9d6f03984b1..b73132a6c48 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index 2f3654ac7ab..f6e48b6663b 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,6 @@ #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index db24999ecd3..bc46107cf98 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -12,7 +13,6 @@ #include #include #include -#include #include diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index 80eb80c93e6..cd5bec2fba8 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -18,8 +20,6 @@ #include #include #include -#include -#include #include #include @@ -165,11 +165,14 @@ OracleSet::preclaim(PreclaimContext const& ctx) auto const currentSponsor = getLedgerEntryReserveSponsorAccountID(sle); auto const newSponsor = getTxReserveSponsorAccountID(ctx.tx); if ((!currentSponsor && !newSponsor) || - (currentSponsor && newSponsor && *currentSponsor == *newSponsor)) { + (currentSponsor && newSponsor && *currentSponsor == *newSponsor)) + { adjustReserve = newCount - oldCount; - } else { + } + else + { adjustReserve = newCount; -} + } } else { diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index a39182e0f94..581bc16f13e 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -4,9 +4,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -20,8 +22,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 82849bf8b14..da934890245 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -6,11 +6,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -36,8 +38,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp index f4786339c35..0cc5a448ee1 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,6 @@ #include #include #include -#include #include diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp index 2d79b50f25d..532c8104815 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -17,8 +19,6 @@ #include #include #include -#include -#include #include diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index 47ce12d2260..40e18596975 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -11,7 +12,6 @@ #include #include #include -#include #include diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index 549577869ee..116cf661b9c 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -2,9 +2,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -16,8 +18,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp index 17da80e9ac8..d521996096e 100644 --- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -4,8 +4,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -15,8 +17,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 6eaac19719e..c5f5f1407f0 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -6,8 +6,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -19,8 +21,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index a1ed15da743..35fc414f6ac 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -8,7 +9,6 @@ #include #include #include -#include #include diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index c32383d3164..b57b733d5ac 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -5,9 +5,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -25,8 +27,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index e12766d7396..1b420971063 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index 4431c685c9e..7920b9bdfa7 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -16,7 +17,6 @@ #include #include #include -#include #include diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index 5d16ecae45c..1c5b9293326 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index 95e697ca927..86ffd25b37e 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 51d6a26dbcf..cbad2ce8c0f 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -1771,20 +1771,20 @@ class Invariants_test : public beast::unit_test::suite .func = [](SLE::pointer& sle) { sle->at(sfRegularKey) = Account("regular").id(); }, }, { - .expectedFailure="pseudo-account has a sponsorship field", - .func=[](SLE::pointer& sle) { sle->at(sfSponsoredOwnerCount) = 1; }, + .expectedFailure = "pseudo-account has a sponsorship field", + .func = [](SLE::pointer& sle) { sle->at(sfSponsoredOwnerCount) = 1; }, }, { - .expectedFailure="pseudo-account has a sponsorship field", - .func=[](SLE::pointer& sle) { sle->at(sfSponsoringOwnerCount) = 1; }, + .expectedFailure = "pseudo-account has a sponsorship field", + .func = [](SLE::pointer& sle) { sle->at(sfSponsoringOwnerCount) = 1; }, }, { - .expectedFailure="pseudo-account has a sponsorship field", - .func=[](SLE::pointer& sle) { sle->at(sfSponsoringAccountCount) = 1; }, + .expectedFailure = "pseudo-account has a sponsorship field", + .func = [](SLE::pointer& sle) { sle->at(sfSponsoringAccountCount) = 1; }, }, { - .expectedFailure="pseudo-account has a sponsorship field", - .func=[](SLE::pointer& sle) { sle->at(sfSponsor) = Account("sponsor").id(); }, + .expectedFailure = "pseudo-account has a sponsorship field", + .func = [](SLE::pointer& sle) { sle->at(sfSponsor) = Account("sponsor").id(); }, }, }); diff --git a/src/test/app/Oracle_test.cpp b/src/test/app/Oracle_test.cpp index 5dab84a6b9f..71875b429e5 100644 --- a/src/test/app/Oracle_test.cpp +++ b/src/test/app/Oracle_test.cpp @@ -21,12 +21,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 6d7ac1a9cfb..4990cbf679b 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1,47 +1,49 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include #include #include +#include #include #include #include #include #include +#include #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include #include +#include + +#include + #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -52,7 +54,7 @@ #include #include #include -#include + #include #include #include @@ -63,7 +65,6 @@ #include #include - namespace xrpl::test { static STAmount @@ -88,14 +89,17 @@ adjustAccountXRPBalance(jtx::Env& env, jtx::Account const& account, STAmount con return; auto const baseFee = env.current()->fees().base; - if (currentBalance > balanceTo) { + if (currentBalance > balanceTo) + { env(pay(account, env.master, currentBalance - (balanceTo)), fee(XRP(1)), sponsor::as(env.master, spfSponsorFee), sig(sfSponsorSignature, env.master)); - } else { + } + else + { env(pay(env.master, account, balanceTo - currentBalance), fee(baseFee)); -} + } env.close(); } @@ -2020,11 +2024,14 @@ class Sponsor_test : public beast::unit_test::suite auto submit = [&](TER _ter) { return [&, _ter](Json::Value const& jv, auto const&... fN) { - if (sponsorSig) { + if (sponsorSig) + { env(jv, fN..., sponsor::as(sponsor, spfSponsorReserve), *sponsorSig, ter(_ter)); - } else { + } + else + { env(jv, fN..., sponsor::as(sponsor, spfSponsorReserve), ter(_ter)); -} + } }; }; @@ -2046,14 +2053,17 @@ class Sponsor_test : public beast::unit_test::suite env.close(); } - if (sponsorReserveCount - 1 > 0) { + if (sponsorReserveCount - 1 > 0) + { env(sponsor::set(sponsor, 0, sponsorReserveCount - 1, XRP(1)), sponsor::sponseeAcc(sponsee)); - } else { + } + else + { // just create sponsor object env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), sponsor::sponseeAcc(sponsee)); -} + } env.close(); } callback(env, submit(insufficientReserveResult)); @@ -2086,9 +2096,11 @@ class Sponsor_test : public beast::unit_test::suite } } - if (expected) { + if (expected) + { (*expected)(); - } else + } + else { BEAST_EXPECT(ownerCount(env, sponsee) - sponseeOwnerCountBefore == reserveCount); BEAST_EXPECT( @@ -5920,5 +5932,4 @@ BEAST_DEFINE_TESTSUITE(Sponsor, app, xrpl); BEAST_DEFINE_TESTSUITE(SponsorTxCosigning, app, xrpl); BEAST_DEFINE_TESTSUITE(SponsorTxPrefunded, app, xrpl); -} // namespace xrpl::test - +} // namespace xrpl::test diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 57a9cc32e68..c10f36e233c 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -1,23 +1,20 @@ #include - -#include -#include #include #include #include + #include #include #include #include #include +#include +#include + #include #include - - - - namespace xrpl::test::jtx::sponsor { Json::Value @@ -130,7 +127,4 @@ ledgerEntry(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& spon return env.rpc("json", "ledger_entry", to_string(jvParams)); } -} // namespace xrpl::test::jtx::sponsor - - - +} // namespace xrpl::test::jtx::sponsor diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index 56512c30b16..0ebc17fd915 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -71,7 +71,8 @@ class sponsored_owners std::uint32_t value_; public: - sponsored_owners(Account account, std::uint32_t value) : account_(std::move(account)), value_(value) + sponsored_owners(Account account, std::uint32_t value) + : account_(std::move(account)), value_(value) { } @@ -87,7 +88,7 @@ class sponsoring_owners std::uint32_t value_; public: - sponsoring_owners(Account account, std::uint32_t value) + sponsoring_owners(Account account, std::uint32_t value) : account_(std::move(account)), value_(value) { } @@ -104,7 +105,7 @@ class sponsoring_account_count std::uint32_t value_; public: - sponsoring_account_count(Account account, std::uint32_t value) + sponsoring_account_count(Account account, std::uint32_t value) : account_(std::move(account)), value_(value) { } diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index 9dc99e4e191..de1a712fc8c 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -6,10 +6,6 @@ #include - - - - namespace xrpl::test::jtx::sponsor { Json::Value @@ -47,7 +43,7 @@ struct counterpartySponsor jtx::Account sponsor_; public: - counterpartySponsor(jtx::Account account) : sponsor_(std::move(account)) + counterpartySponsor(jtx::Account account) : sponsor_(std::move(account)) { } @@ -61,7 +57,7 @@ struct sponseeAcc jtx::Account sponsee_; public: - sponseeAcc(jtx::Account account) : sponsee_(std::move(account)) + sponseeAcc(jtx::Account account) : sponsee_(std::move(account)) { } @@ -76,7 +72,7 @@ struct as std::uint32_t flags; public: - as(jtx::Account account, std::uint32_t flags = 0) : sponsor_(std::move(account)), flags(flags) + as(jtx::Account account, std::uint32_t flags = 0) : sponsor_(std::move(account)), flags(flags) { } @@ -87,7 +83,4 @@ struct as Json::Value ledgerEntry(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& sponsee); -} // namespace xrpl::test::jtx::sponsor - - - +} // namespace xrpl::test::jtx::sponsor diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index d68dcd3f4af..a85a536d38b 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -26,12 +26,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include From 84bffad2bf2b86f3b43a5595e5453da6cc84568f Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Tue, 28 Apr 2026 14:55:34 -0400 Subject: [PATCH 214/249] merge_fix --- src/test/app/Sponsor_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 4990cbf679b..db7f70764c3 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -15,12 +15,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include From 9b4ab67864a74da499a4373030f8b54805217be3 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Fri, 24 Apr 2026 12:15:42 -0400 Subject: [PATCH 215/249] Fix delegable test --- src/test/app/Delegate_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index c9ed44473b8..84fbd4b9903 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -1892,7 +1892,7 @@ class Delegate_test : public beast::unit_test::suite // DO NOT modify expectedDelegableCount unless all scenarios, including // edge cases, have been fully tested and verified. // ==================================================================== - std::size_t const expectedDelegableCount = 75; + std::size_t const expectedDelegableCount = 77; BEAST_EXPECTS( delegableCount == expectedDelegableCount, From 462a0b23383396db88f9215eba2d16694a77ff5e Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Wed, 18 Mar 2026 13:56:12 -0400 Subject: [PATCH 216/249] Read/const_ref --- include/xrpl/ledger/View.h | 4 ++-- .../xrpl/ledger/helpers/AccountRootHelpers.h | 12 +++++------ .../xrpl/ledger/helpers/CredentialHelpers.h | 2 +- include/xrpl/ledger/helpers/SponsorHelpers.h | 20 +++++++++---------- src/libxrpl/ledger/View.cpp | 8 ++++---- .../ledger/helpers/AccountRootHelpers.cpp | 14 ++++++------- .../ledger/helpers/CredentialHelpers.cpp | 2 +- 7 files changed, 29 insertions(+), 33 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index aaa4bddbe47..e75df60de5c 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -135,9 +135,9 @@ TER checkInsufficientReserve( ReadView const& view, STTx const& tx, - std::shared_ptr accSle, + SLE::const_ref accSle, STAmount const& accBalance, - std::shared_ptr const& sponsorSle, + SLE::const_ref sponsorSle, std::int32_t ownerCountDelta, std::int32_t accountCountDelta = 0); diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index bfc43f2b316..edf2f8b94b5 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -38,8 +38,8 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, void adjustOwnerCount( ApplyView& view, - std::shared_ptr const& accountSle, - std::shared_ptr const& sponsorSle, + SLE::ref accountSle, + SLE::ref sponsorSle, std::int32_t amount, beast::Journal j); @@ -54,7 +54,7 @@ adjustOwnerCount( adjustOwnerCount( view, view.peek(keylet::account(account)), - sponsor ? view.peek(keylet::account(*sponsor)) : std::shared_ptr(), + sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(), amount, j); } @@ -93,9 +93,7 @@ getPseudoAccountFields(); - null pointer */ [[nodiscard]] bool -isPseudoAccount( - std::shared_ptr sleAcct, - std::set const& pseudoFieldFilter = {}); +isPseudoAccount(SLE::const_ref sleAcct, std::set const& pseudoFieldFilter = {}); /** Convenience overload that reads the account from the view. */ [[nodiscard]] inline bool @@ -115,7 +113,7 @@ isPseudoAccount( * before using a field. The amendment check is **not** performed in * createPseudoAccount. */ -[[nodiscard]] Expected, TER> +[[nodiscard]] Expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); /** Checks the destination and tag. diff --git a/include/xrpl/ledger/helpers/CredentialHelpers.h b/include/xrpl/ledger/helpers/CredentialHelpers.h index c60bd6c7fa5..8e94af695d2 100644 --- a/include/xrpl/ledger/helpers/CredentialHelpers.h +++ b/include/xrpl/ledger/helpers/CredentialHelpers.h @@ -74,7 +74,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + SLE::const_ref sleDst, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/SponsorHelpers.h b/include/xrpl/ledger/helpers/SponsorHelpers.h index 74cbbd069c2..190c1fbeb5d 100644 --- a/include/xrpl/ledger/helpers/SponsorHelpers.h +++ b/include/xrpl/ledger/helpers/SponsorHelpers.h @@ -34,7 +34,7 @@ getTxReserveSponsorAccountID(STTx const& tx) return {}; } -inline std::shared_ptr +inline SLE::pointer getTxReserveSponsor(ApplyView& view, STTx const& tx) { auto const sponsorID = getTxReserveSponsorAccountID(tx); @@ -43,7 +43,7 @@ getTxReserveSponsor(ApplyView& view, STTx const& tx) return {}; } -inline std::shared_ptr +inline SLE::const_pointer getTxReserveSponsor(ReadView const& view, STTx const& tx) { auto const sponsorID = getTxReserveSponsorAccountID(tx); @@ -54,7 +54,7 @@ getTxReserveSponsor(ReadView const& view, STTx const& tx) inline std::optional getLedgerEntryReserveSponsorAccountID( - std::shared_ptr const& sle, + SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor) { if (sle->isFieldPresent(field)) @@ -62,10 +62,10 @@ getLedgerEntryReserveSponsorAccountID( return {}; } -inline std::shared_ptr +inline SLE::pointer getLedgerEntryReserveSponsor( ApplyView& view, - std::shared_ptr const& sle, + SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor) { auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); @@ -74,10 +74,10 @@ getLedgerEntryReserveSponsor( return {}; } -inline std::shared_ptr +inline SLE::const_pointer getLedgerEntryReserveSponsor( ReadView const& view, - std::shared_ptr const& sle, + SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor) { auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field); @@ -88,8 +88,8 @@ getLedgerEntryReserveSponsor( inline void addSponsorToLedgerEntry( - std::shared_ptr const& sle, - std::shared_ptr const& sponsorSle, + SLE::ref sle, + SLE::const_ref sponsorSle, SF_ACCOUNT const& field = sfSponsor) { XRPL_ASSERT( @@ -101,7 +101,7 @@ addSponsorToLedgerEntry( } inline void -removeSponsorFromLedgerEntry(std::shared_ptr const& sle, SF_ACCOUNT const& field = sfSponsor) +removeSponsorFromLedgerEntry(SLE::ref sle, SF_ACCOUNT const& field = sfSponsor) { XRPL_ASSERT( (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 0856f061ec9..c0ede08415f 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -301,7 +301,7 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) } uint32_t -ownerCount(std::shared_ptr const& sponsorSle) +ownerCount(SLE::const_ref sponsorSle) { auto const ownerCount = sponsorSle->getFieldU32(sfOwnerCount); auto const sponsoredOwnerCount = sponsorSle->getFieldU32(sfSponsoredOwnerCount); @@ -311,7 +311,7 @@ ownerCount(std::shared_ptr const& sponsorSle) } XRPAmount -calculateReserve(std::shared_ptr const& sle, Fees const& fees) +calculateReserve(SLE::const_ref sle, Fees const& fees) { XRPL_ASSERT(sle->getType() == ltACCOUNT_ROOT, "xrpl::calculateReserve : valid sle type"); @@ -327,9 +327,9 @@ TER checkInsufficientReserve( ReadView const& view, STTx const& tx, - std::shared_ptr accSle, + SLE::const_ref accSle, STAmount const& accBalance, - std::shared_ptr const& sponsorSle, + SLE::const_ref sponsorSle, std::int32_t ownerCountDelta, std::int32_t accountCountDelta) { diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index b24b4123dec..056fbd16ea1 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -134,10 +134,10 @@ transferRate(ReadView const& view, AccountID const& issuer) return parityRate; } -void +static void adjustSponsorOwnerCountHlp( ApplyView& view, - std::shared_ptr const& sle, + SLE::ref sle, SField const& sfield, std::int32_t amount, beast::Journal j) @@ -160,8 +160,8 @@ adjustSponsorOwnerCountHlp( void adjustOwnerCount( ApplyView& view, - std::shared_ptr const& accountSle, - std::shared_ptr const& sponsorSle, + SLE::ref accountSle, + SLE::ref sponsorSle, std::int32_t amount, beast::Journal j) { @@ -258,9 +258,7 @@ getPseudoAccountFields() } [[nodiscard]] bool -isPseudoAccount( - std::shared_ptr sleAcct, - std::set const& pseudoFieldFilter) +isPseudoAccount(SLE::const_ref sleAcct, std::set const& pseudoFieldFilter) { auto const& fields = getPseudoAccountFields(); @@ -274,7 +272,7 @@ isPseudoAccount( }) > 0; } -Expected, TER> +Expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField) { [[maybe_unused]] diff --git a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp index 57dbf6fe94f..7f336c35684 100644 --- a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -348,7 +348,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + SLE::const_ref sleDst, beast::Journal j) { // If depositPreauth is enabled, then an account that requires From ee8d9ca73eeb403964bf5a5a2e7c2c7cad2ddbd6 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Fri, 24 Apr 2026 15:30:05 -0400 Subject: [PATCH 217/249] More universal adjustSponsorOwnerCountHlp --- .../ledger/helpers/AccountRootHelpers.cpp | 69 +++++++------------ 1 file changed, 24 insertions(+), 45 deletions(-) diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 056fbd16ea1..0eff1009ead 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -138,22 +138,17 @@ static void adjustSponsorOwnerCountHlp( ApplyView& view, SLE::ref sle, - SField const& sfield, - std::int32_t amount, - beast::Journal j) + SF_UINT32 const& sfield, + AccountID const& accID, + std::int32_t adjustment, + beast::Journal j, + bool callHook = true) { - auto const accID = sle->getAccountID(sfAccount); - std::uint32_t const current{(sle)->getFieldU32(sfield)}; - std::uint32_t const adjusted = confineOwnerCount(current, amount, accID, j); - view.adjustOwnerCountHook(accID, current, adjusted); - if (adjusted == 0) - { - sle->makeFieldAbsent(sfield); - } - else - { - sle->setFieldU32(sfield, adjusted); - } + std::uint32_t const current = sle->at(sfield); + std::uint32_t const adjusted = confineOwnerCount(current, adjustment, accID, j); + if (callHook) + view.adjustOwnerCountHook(accID, current, adjusted); + sle->at(sfield) = adjusted; view.update(sle); } @@ -162,50 +157,34 @@ adjustOwnerCount( ApplyView& view, SLE::ref accountSle, SLE::ref sponsorSle, - std::int32_t amount, + std::int32_t adjustment, beast::Journal j) { if (!accountSle) return; - XRPL_ASSERT(amount, "xrpl::adjustOwnerCount : nonzero amount input"); + XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input"); + auto const accountID = accountSle->getAccountID(sfAccount); if (sponsorSle) { - adjustSponsorOwnerCountHlp(view, sponsorSle, sfSponsoringOwnerCount, amount, j); - adjustSponsorOwnerCountHlp(view, accountSle, sfSponsoredOwnerCount, amount, j); - - auto const account = accountSle->getAccountID(sfAccount); - auto const sponsorAccountID = (sponsorSle)->getAccountID(sfAccount); + auto const sponsorID = sponsorSle->getAccountID(sfAccount); - auto sponsorObjSle = view.peek(keylet::sponsor(sponsorAccountID, account)); + adjustSponsorOwnerCountHlp( + view, accountSle, sfSponsoredOwnerCount, accountID, adjustment, j); + adjustSponsorOwnerCountHlp( + view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j); + auto sponsorObjSle = view.peek(keylet::sponsor(sponsorID, accountID)); if (sponsorObjSle) { - // pre funded // update the pre-funded ReserveCount on Sponsorship ledger object - - std::uint32_t const currentReserveCount = sponsorObjSle->getFieldU32(sfReserveCount); - // Reserve count moves opposite to amount: +amount => consume reserve (-), -amount => - // payback (+) - std::uint32_t const adjusted = - confineOwnerCount(currentReserveCount, -amount, sponsorAccountID, j); - if (adjusted == 0) - { - sponsorObjSle->makeFieldAbsent(sfReserveCount); - } - else - { - sponsorObjSle->setFieldU32(sfReserveCount, adjusted); - } - view.update(sponsorObjSle); + // Reserve count moves opposite to adjustment: +adjustment => consume reserve (-), + // -adjustment => payback (+) + adjustSponsorOwnerCountHlp( + view, sponsorObjSle, sfReserveCount, sponsorID, -adjustment, j, false); } } - std::uint32_t const current{accountSle->getFieldU32(sfOwnerCount)}; - AccountID const id = (*accountSle)[sfAccount]; - std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j); - view.adjustOwnerCountHook(id, current, adjusted); - accountSle->at(sfOwnerCount) = adjusted; - view.update(accountSle); + adjustSponsorOwnerCountHlp(view, accountSle, sfOwnerCount, accountID, adjustment, j); } AccountID From c5941d6b14869810ce735f23df1557b6b6e67269 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:34:06 -0400 Subject: [PATCH 218/249] Unify accountReserve, ownerCount, ownerReserve --- include/xrpl/ledger/View.h | 16 -- .../xrpl/ledger/helpers/AccountRootHelpers.h | 46 ++++ include/xrpl/ledger/helpers/EscrowHelpers.h | 6 +- include/xrpl/protocol/Fees.h | 25 --- src/libxrpl/ledger/View.cpp | 79 ------- .../ledger/helpers/AccountRootHelpers.cpp | 197 ++++++++++++++++-- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 6 +- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 2 +- .../ledger/helpers/RippleStateHelpers.cpp | 4 +- src/libxrpl/tx/Transactor.cpp | 2 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 4 +- .../Sponsor/SponsorshipTransfer.cpp | 10 +- .../tx/transactors/account/SignerListSet.cpp | 2 +- .../tx/transactors/bridge/XChainBridge.cpp | 8 +- .../tx/transactors/check/CheckCash.cpp | 2 +- .../tx/transactors/check/CheckCreate.cpp | 3 +- .../credentials/CredentialAccept.cpp | 4 +- .../credentials/CredentialCreate.cpp | 4 +- .../tx/transactors/delegate/DelegateSet.cpp | 4 +- src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 9 +- src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 7 +- .../tx/transactors/dex/AMMWithdraw.cpp | 7 +- .../tx/transactors/dex/OfferCreate.cpp | 2 +- src/libxrpl/tx/transactors/did/DIDSet.cpp | 4 +- .../tx/transactors/escrow/EscrowCreate.cpp | 5 +- .../tx/transactors/lending/LoanBrokerSet.cpp | 6 +- .../tx/transactors/lending/LoanSet.cpp | 2 +- .../tx/transactors/nft/NFTokenAcceptOffer.cpp | 3 +- .../tx/transactors/nft/NFTokenMint.cpp | 5 +- .../tx/transactors/oracle/OracleSet.cpp | 2 +- .../tx/transactors/payment/DepositPreauth.cpp | 8 +- .../tx/transactors/payment/Payment.cpp | 2 +- .../payment_channel/PaymentChannelCreate.cpp | 5 +- .../payment_channel/PaymentChannelFund.cpp | 5 +- .../PermissionedDomainSet.cpp | 4 +- .../tx/transactors/system/TicketCreate.cpp | 2 +- .../token/MPTokenIssuanceCreate.cpp | 2 +- src/libxrpl/tx/transactors/token/TrustSet.cpp | 19 +- .../tx/transactors/vault/VaultCreate.cpp | 4 +- src/test/app/AMMExtendedMPT_test.cpp | 3 +- src/test/app/AMMExtended_test.cpp | 3 +- src/test/app/CheckMPT_test.cpp | 3 +- src/test/app/Check_test.cpp | 3 +- src/test/app/Credentials_test.cpp | 5 +- src/test/app/Delegate_test.cpp | 17 +- src/test/app/DepositAuth_test.cpp | 5 +- src/test/app/FlowMPT_test.cpp | 3 +- src/test/app/Flow_test.cpp | 3 +- src/test/app/LoanBroker_test.cpp | 3 +- src/test/app/Loan_test.cpp | 28 +-- src/test/app/OfferMPT_test.cpp | 18 +- src/test/app/Offer_test.cpp | 17 +- src/test/app/Oracle_test.cpp | 9 +- src/test/app/Sponsor_test.cpp | 5 +- src/test/app/Ticket_test.cpp | 9 +- src/test/app/TrustSet_test.cpp | 3 +- src/test/app/Vault_test.cpp | 4 +- src/test/app/XChain_test.cpp | 5 +- src/test/jtx/impl/AMMTest.cpp | 3 +- src/test/ledger/PaymentSandbox_test.cpp | 3 +- src/test/rpc/AccountTx_test.cpp | 3 +- 61 files changed, 406 insertions(+), 276 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index e75df60de5c..4958a89d8c7 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -125,22 +125,6 @@ areCompatible( beast::Journal::Stream& s, char const* reason); -uint32_t -ownerCount(SLE::const_ref sponsorSle); - -XRPAmount -calculateReserve(SLE::const_ref sle, Fees const& fees); - -TER -checkInsufficientReserve( - ReadView const& view, - STTx const& tx, - SLE::const_ref accSle, - STAmount const& accBalance, - SLE::const_ref sponsorSle, - std::int32_t ownerCountDelta, - std::int32_t accountCountDelta = 0); - //------------------------------------------------------------------------------ // // Modifiers diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index edf2f8b94b5..a4cb4fbb467 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -34,6 +34,52 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer); [[nodiscard]] XRPAmount xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j); +/** Returns the account reserve, in drops. + Actual owner count can be adjusted by delta in ownerCountAdj + The reserve is calculated as + (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) * + increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base" +*/ +XRPAmount +accountReserve( + ReadView const& view, + std::shared_ptr const& sle, + beast::Journal j, + std::int32_t ownerCountAdj = 0, + std::int32_t reserveCountAdj = 0); + +inline XRPAmount +accountReserve( + ReadView const& view, + AccountID const& id, + beast::Journal j, + std::int32_t ownerCountAdj = 0, + std::int32_t reserveCountAdj = 0) +{ + return accountReserve(view, view.read(keylet::account(id)), j, ownerCountAdj, reserveCountAdj); +} + +XRPAmount +baseAccountReserve(ReadView const& view, std::int32_t ownerCount); + +TER +checkInsufficientReserve( + ReadView const& view, + STTx const& tx, + SLE::const_ref accSle, + STAmount const& accBalance, + SLE::const_ref sponsorSle, + std::int32_t ownerCountDelta, + std::int32_t accountCountDelta = 0, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); + +std::uint32_t +ownerCount( + ReadView const& view, + std::shared_ptr const& sle, + beast::Journal j, + std::int32_t ownerCountAdj = 0); + /** Adjust the owner count up or down. */ void adjustOwnerCount( diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h index 42f682b1c56..f3bc996b5ae 100644 --- a/include/xrpl/ledger/helpers/EscrowHelpers.h +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -63,7 +63,8 @@ escrowUnlockApplyHelper( std::shared_ptr sponsorSle = {}; if (sponsorAccountID) sponsorSle = view.peek(keylet::account(*sponsorAccountID)); - if (auto const ret = checkInsufficientReserve(view, tx, sleDest, xrpBalance, sponsorSle, 1); + if (auto const ret = + checkInsufficientReserve(view, tx, sleDest, xrpBalance, sponsorSle, 1, 0, journal); !isTesSuccess(ret)) { JLOG(journal.trace()) << "Trust line does not exist. " @@ -192,7 +193,8 @@ escrowUnlockApplyHelper( std::shared_ptr sponsorSle = {}; if (sponsorAccountID) sponsorSle = view.peek(keylet::account(*sponsorAccountID)); - if (auto const ret = checkInsufficientReserve(view, tx, sleDest, xrpBalance, sponsorSle, 1); + if (auto const ret = + checkInsufficientReserve(view, tx, sleDest, xrpBalance, sponsorSle, 1, 0, journal); !isTesSuccess(ret)) return ret; diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index a7141ce9d9c..d5681e66c4b 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -33,31 +33,6 @@ struct Fees : base(base_), reserve(reserve_), increment(increment_) { } - - /** Returns the account reserve given the owner count, in drops. - - The reserve is calculated as the reserve base plus - the reserve increment times the number of increments. - */ - [[nodiscard]] XRPAmount - accountReserve( - std::size_t ownerCount, - std::size_t sponsoredOwnerCount = 0, - std::size_t sponsoringOwnerCount = 0, - bool isAccountSponsored = false, - std::size_t sponsoringAccountCount = 0) const - { - auto const accountReserveUnits = (isAccountSponsored ? 0 : 1) + sponsoringAccountCount; - - XRPL_ASSERT( - ownerCount >= sponsoredOwnerCount, - "xrpl::Fees::accountReserve : OwnerCount must be greater than or equal to " - "SponsoredOwnerCount"); - - auto const ownerReserveUnits = (ownerCount - sponsoredOwnerCount) + sponsoringOwnerCount; - - return (reserve * accountReserveUnits) + (increment * ownerReserveUnits); - } }; } // namespace xrpl diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index c0ede08415f..5103458848d 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -300,85 +300,6 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) return std::nullopt; } -uint32_t -ownerCount(SLE::const_ref sponsorSle) -{ - auto const ownerCount = sponsorSle->getFieldU32(sfOwnerCount); - auto const sponsoredOwnerCount = sponsorSle->getFieldU32(sfSponsoredOwnerCount); - auto const sponsoringOwnerCount = sponsorSle->getFieldU32(sfSponsoringOwnerCount); - - return ownerCount + sponsoringOwnerCount - sponsoredOwnerCount; -} - -XRPAmount -calculateReserve(SLE::const_ref sle, Fees const& fees) -{ - XRPL_ASSERT(sle->getType() == ltACCOUNT_ROOT, "xrpl::calculateReserve : valid sle type"); - - return fees.accountReserve( - sle->getFieldU32(sfOwnerCount), - sle->getFieldU32(sfSponsoredOwnerCount), - sle->getFieldU32(sfSponsoringOwnerCount), - sle->isFieldPresent(sfSponsor), - sle->getFieldU32(sfSponsoringAccountCount)); -} - -TER -checkInsufficientReserve( - ReadView const& view, - STTx const& tx, - SLE::const_ref accSle, - STAmount const& accBalance, - SLE::const_ref sponsorSle, - std::int32_t ownerCountDelta, - std::int32_t accountCountDelta) -{ - if (sponsorSle) - { - auto const isCoSigning = isSponsorReserveCoSigning(tx); - - auto const sle = view.read( - keylet::sponsor(sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount))); - - if (!isCoSigning && !sle) - { - // prefunded sponsor should have a sponsorship entry - return tecINTERNAL; // LCOV_EXCL_LINE - } - - if (sle) - { - auto const reserveCountAllowed = sle->getFieldU32(sfReserveCount); - if (reserveCountAllowed < ownerCountDelta) - return tecINSUFFICIENT_RESERVE; - } - - auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance); - STAmount const sponsorReserve{view.fees().accountReserve( - sponsorSle->getFieldU32(sfOwnerCount), - sponsorSle->getFieldU32(sfSponsoredOwnerCount), - sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ownerCountDelta, - sponsorSle->isFieldPresent(sfSponsor), - sponsorSle->getFieldU32(sfSponsoringAccountCount) + accountCountDelta)}; - - if (sponsorBalance < sponsorReserve) - return tecINSUFFICIENT_RESERVE; - } - else - { - STAmount const reserve{view.fees().accountReserve( - accSle->getFieldU32(sfOwnerCount) + ownerCountDelta, - accSle->getFieldU32(sfSponsoredOwnerCount), - accSle->getFieldU32(sfSponsoringOwnerCount), - accSle->isFieldPresent(sfSponsor), - accSle->getFieldU32(sfSponsoringAccountCount) + accountCountDelta)}; - - if (accBalance < reserve) - return tecINSUFFICIENT_RESERVE; - } - return tesSUCCESS; -} - //------------------------------------------------------------------------------ // // Modifiers diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 0eff1009ead..dfcd980f035 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +85,110 @@ confineOwnerCount( return adjusted; } +static std::uint32_t +ownerCountHlp( + ReadView const& view, + std::shared_ptr const& sle, + std::int32_t adjustment, + bool reportConfine, + beast::Journal j) +{ + AccountID const id = sle->getAccountID(sfAccount); + std::uint32_t const savedCount = sle->at(sfOwnerCount); + std::uint32_t const hookedCount = view.ownerCountHook(id, savedCount); + + std::uint32_t const sponsoredCount = sle->at(sfSponsoredOwnerCount); + std::uint32_t const sponsoringCount = sle->at(sfSponsoringOwnerCount); + XRPL_ASSERT( + hookedCount >= sponsoredCount, + "xrpl::ownerCountHlp : OwnerCount must be greater than or equal to " + "SponsoredOwnerCount"); + + std::int64_t deltaCount = + static_cast(adjustment) - sponsoredCount + sponsoringCount; + if (deltaCount > std::numeric_limits::max()) + { + deltaCount = std::numeric_limits::max(); + JLOG(j.fatal()) << "Account " << id << " delta count exceeds max, " + << "adjustment: " << adjustment << ", sponsoredCount: " << sponsoredCount + << ", sponsoringOwnerCount: " << sponsoringCount; + } + else if (deltaCount < std::numeric_limits::min()) + { + deltaCount = std::numeric_limits::min(); + JLOG(j.fatal()) << "Account " << id << " delta count exceeds min, " + << "adjustment: " << adjustment << ", sponsoredCount: " << sponsoredCount + << ", sponsoringCount: " << sponsoringCount; + } + + std::uint32_t const confinedCount = reportConfine + ? confineOwnerCount(hookedCount, deltaCount, id, j) + : confineOwnerCount(hookedCount, deltaCount); + + return confinedCount; +} + +static std::uint32_t +reserveCountHlp(std::shared_ptr const& sle, std::int32_t adjustment, beast::Journal j) +{ + bool const isSponsored = sle->isFieldPresent(sfSponsor); + std::uint32_t const sponsoringCount = sle->getFieldU32(sfSponsoringAccountCount); + std::uint32_t const reserveCount = (isSponsored ? 0 : 1) + sponsoringCount; + + std::uint32_t adjusted{reserveCount + adjustment}; + if (adjustment > 0) + { + // Overflow is well defined on unsigned + if (adjusted < reserveCount) + { + JLOG(j.fatal()) << "Reserve count exceeds max!"; + adjusted = std::numeric_limits::max(); + } + } + else + { + // Underflow is well defined on unsigned + if (adjusted > reserveCount) + { + JLOG(j.fatal()) << "Reserve count set below 0!"; + adjusted = 0; + } + } + return adjusted; +} + +static inline XRPAmount +baseReserveHlp(ReadView const& view, std::uint32_t ownerCount, std::uint32_t reserveCount) +{ + auto const& fees = view.fees(); + return (fees.reserve * reserveCount) + (fees.increment * ownerCount); +} + +static XRPAmount +reserveHlp( + ReadView const& view, + std::shared_ptr const& sle, + std::uint32_t ownerCount, + std::uint32_t reserveCount) +{ + // Pseudo-accounts have no reserve requirement + if (isPseudoAccount(sle)) + return XRPAmount(0); + + auto const reserve = baseReserveHlp(view, ownerCount, reserveCount); + return reserve; +} + +std::uint32_t +ownerCount( + ReadView const& view, + std::shared_ptr const& sle, + beast::Journal j, + std::int32_t adjustment) +{ + return ownerCountHlp(view, sle, adjustment, true, j); +} + XRPAmount xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j) { @@ -90,23 +196,9 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, if (sle == nullptr) return beast::zero; - // Return balance minus reserve - std::uint32_t const ownerCount = - confineOwnerCount(view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); - - std::uint32_t const sponsoredOwnerCount = sle->getFieldU32(sfSponsoredOwnerCount); - std::uint32_t const sponsoringOwnerCount = sle->getFieldU32(sfSponsoringOwnerCount); - bool const isAccountSponsored = sle->isFieldPresent(sfSponsor); - std::uint32_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount); - - // Pseudo-accounts have no reserve requirement - auto const reserve = isPseudoAccount(sle) ? XRPAmount{0} - : view.fees().accountReserve( - ownerCount, - sponsoredOwnerCount, - sponsoringOwnerCount, - isAccountSponsored, - sponsoringAccountCount); + std::uint32_t const ownerCount = ownerCountHlp(view, sle, ownerCountAdj, false, j); + std::uint32_t const reserveCount = reserveCountHlp(sle, 0, j); + auto const reserve = reserveHlp(view, sle, ownerCount, reserveCount); auto const fullBalance = sle->getFieldAmount(sfBalance); @@ -187,6 +279,77 @@ adjustOwnerCount( adjustSponsorOwnerCountHlp(view, accountSle, sfOwnerCount, accountID, adjustment, j); } +[[nodiscard]] XRPAmount +accountReserve( + ReadView const& view, + std::shared_ptr const& sle, + beast::Journal j, + std::int32_t ownerCountAdj, + std::int32_t reserveCountAdj) +{ + XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::accountReserve : valid sle type"); + + std::uint32_t const ownerCount = ownerCountHlp(view, sle, ownerCountAdj, true, j); + std::uint32_t const reserveCount = reserveCountHlp(sle, reserveCountAdj, j); + + return reserveHlp(view, sle, ownerCount, reserveCount); +} + +XRPAmount +baseAccountReserve(ReadView const& view, std::int32_t ownerCount) +{ + auto const reserve = baseReserveHlp(view, ownerCount, 1); + return reserve; +} + +TER +checkInsufficientReserve( + ReadView const& view, + STTx const& tx, + SLE::const_ref accSle, + STAmount const& accBalance, + SLE::const_ref sponsorSle, + std::int32_t ownerCountDelta, + std::int32_t accountCountDelta, + beast::Journal j) +{ + if (sponsorSle) + { + auto const isCoSigning = isSponsorReserveCoSigning(tx); + + auto const sle = view.read( + keylet::sponsor(sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount))); + + // prefunded sponsor should have a sponsorship entry + if (!isCoSigning && !sle) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (sle) + { + auto const reserveCountAllowed = sle->getFieldU32(sfReserveCount); + if (reserveCountAllowed < ownerCountDelta) + return tecINSUFFICIENT_RESERVE; + } + + auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance); + STAmount const sponsorReserve = + accountReserve(view, sponsorSle, j, ownerCountDelta, accountCountDelta); + + if (sponsorBalance < sponsorReserve) + return tecINSUFFICIENT_RESERVE; + } + else + { + STAmount const reserve = + accountReserve(view, accSle, j, ownerCountDelta, accountCountDelta); + if (accBalance < reserve) + return tecINSUFFICIENT_RESERVE; + } + return tesSUCCESS; +} + +// ---------------------------------------------------- + AccountID pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey) { diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index e28581a3051..39542063bf2 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -202,10 +202,10 @@ authorizeMPToken( // items. This is similar to the reserve requirements of trust lines. // If PreFunded Sponsor, it must be checked whether sufficient // ReserveCount exists. - if (ownerCount(sponsor ? sponsor : sleAcct) >= 2 || isSponsoredAndPreFunded) + if (ownerCount(view, sponsor ? sponsor : sleAcct, journal) >= 2 || isSponsoredAndPreFunded) { - if (auto const ret = - checkInsufficientReserve(view, tx, sleAcct, priorBalance, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + view, tx, sleAcct, priorBalance, sponsor, 1, 0, journal); !isTesSuccess(ret)) return ret; } diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index 4ba0513cf06..92a1ad99086 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -1001,7 +1001,7 @@ tokenOfferCreateApply( Keylet const acctKeylet = keylet::account(acctID); auto const acct = view.read(acctKeylet); auto const sponsor = getTxReserveSponsor(view, tx); - if (auto const ret = checkInsufficientReserve(view, tx, acct, priorBalance, sponsor, 1); + if (auto const ret = checkInsufficientReserve(view, tx, acct, priorBalance, sponsor, 1, 0, j); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index 30a588daac8..b62c5ed78cf 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -677,7 +677,9 @@ addEmptyHolding( priorBalance, sponsorAccountID ? view.read(keylet::account(*sponsorAccountID)) : std::shared_ptr(), - 1); + 1, + 0, + journal); !isTesSuccess(ret)) return tecNO_LINE_INSUF_RESERVE; diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index f6d2d7c619e..77a96f42b60 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -518,7 +518,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (payer.type == FeePayerType::SponsorCoSigned) { - STAmount const sponsorReserve = calculateReserve(payerSle, ctx.view.fees()); + STAmount const sponsorReserve = accountReserve(ctx.view, payerSle, ctx.j); maxSpendable = payerSle->getFieldAmount(sfBalance).xrp() - sponsorReserve.xrp(); } else diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 426fdfc3a8b..be87900c845 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -257,7 +257,7 @@ SponsorshipSet::doApply() sponsorAccSle, STAmount{(*sponsorAccSle)[sfBalance]}.xrp(), reserveSponsorAccSle, - 1); + 1, 0, ctx_.journal); !isTesSuccess(ret)) return tecUNFUNDED; @@ -323,7 +323,7 @@ SponsorshipSet::doApply() sponsorAccSle, STAmount{(*sponsorAccSle)[sfBalance]}.xrp(), reserveSponsorAccSle, - 0); + 0, 0, ctx_.journal); !isTesSuccess(ret)) return tecUNFUNDED; } diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 0acfaa6f2d9..8f07ed6bb53 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -269,7 +270,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (!newSponsor) return tecNO_PERMISSION; - // check object is already sponsored + // check object is already ctx.sponsored if (!sle->isFieldPresent(sponsorField)) return tecNO_PERMISSION; } @@ -295,7 +296,9 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) sponseeSle, sponseeSle->getFieldAmount(sfBalance), newSponsor, - ownerCountDelta); + ownerCountDelta, + 0, + ctx.j); !isTesSuccess(ter)) return ter; } @@ -345,7 +348,8 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) sponseeSle->getFieldAmount(sfBalance), newSponsor, 0, - 1); + 1, + ctx.j); !isTesSuccess(ter)) return ter; } diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index a61f2d0a719..f74ed8aff3b 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -326,7 +326,7 @@ SignerListSet::replaceSignerList() // with CreateTicket. auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - ctx_.view(), ctx_.tx, sle, preFeeBalance_, sponsor, addedOwnerCount); + ctx_.view(), ctx_.tx, sle, preFeeBalance_, sponsor, addedOwnerCount, 0, ctx_.journal); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index d418366359b..b6d9109226d 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -438,7 +438,7 @@ transferHelper( return tecINTERNAL; // LCOV_EXCL_LINE { - auto const reserve = calculateReserve(sleSrc, psb.fees()); + auto const reserve = accountReserve(psb, sleSrc, j, 0, 0); auto const availableBalance = [&]() -> STAmount { STAmount curBal = (*sleSrc)[sfBalance]; @@ -1033,7 +1033,7 @@ applyCreateAccountAttestations( // Check reserve auto const balance = (*sleDoor)[sfBalance]; // Door account should not have a sponsor - if (auto const ret = checkInsufficientReserve(psb, tx, sleDoor, balance, {}, 1); + if (auto const ret = checkInsufficientReserve(psb, tx, sleDoor, balance, {}, 1, 0, j); !isTesSuccess(ret)) return Unexpected(ret); // tecINSUFFICIENT_RESERVE } @@ -1444,7 +1444,7 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) auto const balance = (*sleAcc)[sfBalance]; auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); if (auto const ret = - checkInsufficientReserve(ctx.view, ctx.tx, sleAcc, balance, sponsor, 1); + checkInsufficientReserve(ctx.view, ctx.tx, sleAcc, balance, sponsor, 1, 0, ctx.j); !isTesSuccess(ret)) return ret; } @@ -1995,7 +1995,7 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) auto const balance = (*sleAcc)[sfBalance]; auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); if (auto const ret = - checkInsufficientReserve(ctx.view, ctx.tx, sleAcc, balance, sponsor, 1); + checkInsufficientReserve(ctx.view, ctx.tx, sleAcc, balance, sponsor, 1, 0, ctx.j); !isTesSuccess(ret)) return ret; } diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index 77de70963a1..7865a64f2d5 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -395,7 +395,7 @@ CheckCash::doApply() // Can the account cover the trust line's or MPT reserve? if (auto const ret = checkInsufficientReserve( - psb, ctx_.tx, sleDst, preFeeBalance_, sponsorSle, 1); + psb, ctx_.tx, sleDst, preFeeBalance_, sponsorSle, 1, 0, j_); !isTesSuccess(ret)) { JLOG(j_.trace()) << "Trust line does not exist. " diff --git a/src/libxrpl/tx/transactors/check/CheckCreate.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp index a2eb0624230..d5a01383399 100644 --- a/src/libxrpl/tx/transactors/check/CheckCreate.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -186,7 +186,8 @@ CheckCreate::doApply() // check the starting balance because we want to allow dipping into the // reserve to pay fees. auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); - if (auto const ret = checkInsufficientReserve(view(), ctx_.tx, sle, preFeeBalance_, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sle, preFeeBalance_, sponsor, 1, 0, ctx_.journal); !isTesSuccess(ret)) return ret; // Note that we use the value from the sequence or ticket as the diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index c6d930155b9..4d4b94c99d2 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -98,8 +98,8 @@ CredentialAccept::doApply() return tefINTERNAL; // LCOV_EXCL_LINE auto const newSponsor = getTxReserveSponsor(view(), ctx_.tx); - if (auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sleSubject, preFeeBalance_, newSponsor, 1); + if (auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sleSubject, preFeeBalance_, newSponsor, 1, 0, ctx_.journal); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index 3e69879ca1c..a4a630e19e8 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -133,8 +133,8 @@ CredentialCreate::doApply() return tefINTERNAL; // LCOV_EXCL_LINE auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); - if (auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sleIssuer, preFeeBalance_, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sleIssuer, preFeeBalance_, sponsor, 1, 0, ctx_.journal); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 157aabe5a27..d5fe2cdc8bc 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -98,8 +98,8 @@ DelegateSet::doApply() return tecINTERNAL; // LCOV_EXCL_LINE auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); - if (auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sleOwner, preFeeBalance_, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sleOwner, preFeeBalance_, sponsor, 1, 0, ctx_.journal); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index 2d36ddc274a..2c26e62a4c0 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -151,7 +151,14 @@ AMMCreate::preclaim(PreclaimContext const& ctx) // Insufficient reserve auto const accountSle = ctx.view.read(keylet::account(accountID)); if (auto const ret = checkInsufficientReserve( - ctx.view, ctx.tx, accountSle, accountSle->getFieldAmount(sfBalance), sponsorSle, 1); + ctx.view, + ctx.tx, + accountSle, + accountSle->getFieldAmount(sfBalance), + sponsorSle, + 1, + 0, + ctx.j); !isTesSuccess(ret)) { JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves"; diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index 05d36f21cf4..83940135295 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -256,7 +256,8 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) accountSle->getFieldAmount(sfBalance) - deposit, sponsorSle, 1, - !sle); + !sle, + ctx.j); sponsorSle && !isTesSuccess(ret)) return tecINSUF_RESERVE_LINE; @@ -390,7 +391,9 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) accountSle, accountSle->getFieldAmount(sfBalance), sponsor, - 1); + 1, + 0, + ctx.j); !isTesSuccess(ret)) { JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves"; diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index 77f5168ccf3..4f8cb149437 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -648,7 +648,8 @@ AMMWithdraw::withdraw( auto const sponsorSle = getTxReserveSponsor(view, tx); auto const balance = (*sleAccount)[sfBalance]->xrp(); - std::uint32_t const count = ownerCount(sponsorSle ? sponsorSle : sleAccount); + std::uint32_t const count = + ownerCount(view, sponsorSle ? sponsorSle : sleAccount, journal); // See also TrustSet::doApply() and MPTokenAuthorize::authorize() if (count >= 2) { @@ -659,7 +660,9 @@ AMMWithdraw::withdraw( std::max(priorBalance, balance), sponsor ? view.read(keylet::account(*sponsor)) : std::shared_ptr(), - 1); + 1, + 0, + journal); !isTesSuccess(ret)) return ret; } diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index e146248a4ca..6fbfc0c15fc 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -809,7 +809,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) { auto const sponsor = getTxReserveSponsor(sb, ctx_.tx); if (auto const ret = - checkInsufficientReserve(sb, ctx_.tx, sleCreator, preFeeBalance_, sponsor, 1); + checkInsufficientReserve(sb, ctx_.tx, sleCreator, preFeeBalance_, sponsor, 1, 0, j_); !isTesSuccess(ret)) { // If we are here, the signing account had an insufficient reserve diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index 9c1f185015a..07427dc4a45 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -74,8 +74,8 @@ addSLE(ApplyContext& ctx, std::shared_ptr const& sle, AccountID const& owne // Check reserve availability for new object creation auto const sponsor = getTxReserveSponsor(ctx.view(), ctx.tx); auto const balance = STAmount((*sleAccount)[sfBalance]).xrp(); - if (auto const ret = - checkInsufficientReserve(ctx.view(), ctx.tx, sleAccount, balance, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + ctx.view(), ctx.tx, sleAccount, balance, sponsor, 1, 0, ctx.journal); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 7d81d6923bf..73da485e494 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -422,7 +422,8 @@ EscrowCreate::doApply() auto const balance = sle->getFieldAmount(sfBalance).xrp(); auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); - if (auto const ret = checkInsufficientReserve(ctx_.view(), ctx_.tx, sle, balance, sponsor, 1); + if (auto const ret = + checkInsufficientReserve(ctx_.view(), ctx_.tx, sle, balance, sponsor, 1, 0, j_); !isTesSuccess(ret)) return ret; @@ -430,7 +431,7 @@ EscrowCreate::doApply() if (isXRP(amount)) { if (auto const ret = checkInsufficientReserve( - ctx_.view(), ctx_.tx, sle, balance - STAmount(amount).xrp(), {}, 1); + ctx_.view(), ctx_.tx, sle, balance - STAmount(amount).xrp(), {}, 1, 0, j_); !isTesSuccess(ret)) return tecUNFUNDED; } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index 3a3e8111b98..5a2a7e28eea 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -239,15 +239,15 @@ LoanBrokerSet::doApply() auto const sponsor = getTxReserveSponsor(view, tx); - if (auto const ret = - checkInsufficientReserve(view, tx, owner, preFeeBalance_, {}, sponsor ? 1 : 2); + if (auto const ret = checkInsufficientReserve( + view, tx, owner, preFeeBalance_, {}, sponsor ? 1 : 2, 0, j_); !isTesSuccess(ret)) return ret; if (sponsor) { if (auto const ret = - checkInsufficientReserve(view, tx, owner, preFeeBalance_, sponsor, 1); + checkInsufficientReserve(view, tx, owner, preFeeBalance_, sponsor, 1, 0, j_); !isTesSuccess(ret)) return ret; } diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index 38d11ae830c..3618abe7147 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -509,7 +509,7 @@ LoanSet::doApply() auto const balance = account_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp(); if (auto const ret = - checkInsufficientReserve(view, tx, borrowerSle, balance, sponsorSle, 1); + checkInsufficientReserve(view, tx, borrowerSle, balance, sponsorSle, 1, 0, j_); !isTesSuccess(ret)) return ret; } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index b73132a6c48..c8e6b7972b3 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -399,7 +400,7 @@ NFTokenAcceptOffer::transferNFToken( auto const sponsorSle = account_ == buyer ? getTxReserveSponsor(ctx_.view(), ctx_.tx) : std::shared_ptr(); if (auto const ret = checkInsufficientReserve( - ctx_.view(), ctx_.tx, sleBuyer, buyerBalance, sponsorSle, 0); + ctx_.view(), ctx_.tx, sleBuyer, buyerBalance, sponsorSle, 0, 0, j_); !isTesSuccess(ret)) return ret; } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index f6e48b6663b..023aaea5c28 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -347,7 +348,9 @@ NFTokenMint::doApply() view().read(keylet::account(account_)), preFeeBalance_, sponsor, - 0); + 0, + 0, + j_); !isTesSuccess(ret)) return ret; } diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index cd5bec2fba8..f9be120e5c9 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -191,7 +191,7 @@ OracleSet::preclaim(PreclaimContext const& ctx) auto const& balance = sleSetter->getFieldAmount(sfBalance); auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); if (auto const ret = - checkInsufficientReserve(ctx.view, ctx.tx, sleSetter, balance, sponsor, adjustReserve); + checkInsufficientReserve(ctx.view, ctx.tx, sleSetter, balance, sponsor, adjustReserve,0, ctx.j); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 581bc16f13e..9b1077596e2 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -163,8 +163,8 @@ DepositPreauth::doApply() // check the starting balance because we want to allow dipping into the // reserve to pay fees. auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); - if (auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sleOwner, preFeeBalance_, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sleOwner, preFeeBalance_, sponsor, 1, 0, j_); !isTesSuccess(ret)) return ret; @@ -209,8 +209,8 @@ DepositPreauth::doApply() // check the starting balance because we want to allow dipping into the // reserve to pay fees. auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); - if (auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sleOwner, preFeeBalance_, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sleOwner, preFeeBalance_, sponsor, 1, 0, j_); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index da934890245..8d8a1303115 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -650,7 +650,7 @@ Payment::doApply() // the number of reserves in this ledger for this account that require a // reserve. - auto const reserve = calculateReserve(sleSrc, view().fees()); + auto const reserve = accountReserve(view(), sleSrc, j_); // In a delegated payment, the fee payer is the delegated account, // not the source account (account_). diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp index 0cc5a448ee1..927afbe9517 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -81,12 +81,13 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx) { auto const balance = (*sle)[sfBalance]; auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); - if (auto const ret = checkInsufficientReserve(ctx.view, ctx.tx, sle, balance, sponsor, 1); + if (auto const ret = + checkInsufficientReserve(ctx.view, ctx.tx, sle, balance, sponsor, 1, 0, ctx.j); !isTesSuccess(ret)) return ret; if (auto const ret = checkInsufficientReserve( - ctx.view, ctx.tx, sle, balance - ctx.tx[sfAmount], sponsor, 1); + ctx.view, ctx.tx, sle, balance - ctx.tx[sfAmount], sponsor, 1, 0, ctx.j); !isTesSuccess(ret)) return tecUNFUNDED; } diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp index 532c8104815..84b14900b1f 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -86,12 +87,12 @@ PaymentChannelFund::doApply() auto const balance = (*sle)[sfBalance]; auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); if (auto const ret = - checkInsufficientReserve(ctx_.view(), ctx_.tx, sle, balance, sponsor, 0); + checkInsufficientReserve(ctx_.view(), ctx_.tx, sle, balance, sponsor, 0, 0, j_); !isTesSuccess(ret)) return ret; if (auto const ret = checkInsufficientReserve( - ctx_.view(), ctx_.tx, sle, balance - ctx_.tx[sfAmount], {}, 0); + ctx_.view(), ctx_.tx, sle, balance - ctx_.tx[sfAmount], {}, 0, 0, j_); !isTesSuccess(ret)) return tecUNFUNDED; } diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index 116cf661b9c..4fa80740e36 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -109,8 +109,8 @@ PermissionedDomainSet::doApply() // Check reserve availability for new object creation auto const balance = STAmount((*ownerSle)[sfBalance]).xrp(); auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - if (auto const ret = - checkInsufficientReserve(ctx_.view(), ctx_.tx, ownerSle, balance, sponsor, 1); + if (auto const ret = checkInsufficientReserve( + ctx_.view(), ctx_.tx, ownerSle, balance, sponsor, 1, 0, j_); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp index d521996096e..23933392236 100644 --- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -79,7 +79,7 @@ TicketCreate::doApply() std::uint32_t const ticketCount = ctx_.tx[sfTicketCount]; auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleAccountRoot, preFeeBalance_, sponsor, ticketCount); + view(), ctx_.tx, sleAccountRoot, preFeeBalance_, sponsor, ticketCount, 0, j_); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index c5f5f1407f0..63114567168 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -112,7 +112,7 @@ MPTokenIssuanceCreate::create( if (args.priorBalance) { if (auto const ret = - checkInsufficientReserve(view, tx, acct, *(args.priorBalance), sponsor, 1); + checkInsufficientReserve(view, tx, acct, *(args.priorBalance), sponsor, 1, 0, journal); !isTesSuccess(ret)) return Unexpected(ret); // tecINSUFFICIENT_RESERVE } diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index b57b733d5ac..da808f6458f 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -369,11 +369,11 @@ TrustSet::doApply() // could use the extra XRP for their own purposes. auto const txSponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); - std::shared_ptr txSponsorSle = {}; + std::shared_ptr txSponsorSle; if (txSponsorAcc) txSponsorSle = view().peek(keylet::account(*txSponsorAcc)); - std::uint32_t const uOwnerCount = ownerCount(txSponsorSle ? txSponsorSle : sle); + std::uint32_t const uOwnerCount = ownerCount(view(), txSponsorSle ? txSponsorSle : sle, j_); bool const isSponsoredAndPreFunded = txSponsorSle && !isSponsorReserveCoSigning(ctx_.tx); // If PreFunded Sponsor, it must be checked whether sufficient @@ -582,7 +582,7 @@ TrustSet::doApply() // For PreFunded sponsors, we need to check if there are sufficient reserves before // calling adjustOwnerCount(). if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleLowAccount, preFeeBalance_, txSponsorSle, 1); + view(), ctx_.tx, sleLowAccount, preFeeBalance_, txSponsorSle, 1, 0, j_); isSponsoredAndPreFunded && !isTesSuccess(ret)) return tecINSUF_RESERVE_LINE; @@ -611,7 +611,7 @@ TrustSet::doApply() // For PreFunded sponsors, we need to check if there are sufficient reserves before // calling adjustOwnerCount(). if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleHighAccount, preFeeBalance_, txSponsorSle, 1); + view(), ctx_.tx, sleHighAccount, preFeeBalance_, txSponsorSle, 1, 0, j_); isSponsoredAndPreFunded && !isTesSuccess(ret)) return tecINSUF_RESERVE_LINE; @@ -645,8 +645,8 @@ TrustSet::doApply() } // Reserve is not scaled by load. else if ( - auto const ret = - checkInsufficientReserve(view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0); + auto const ret = checkInsufficientReserve( + view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0, 0, j_); !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " @@ -677,12 +677,7 @@ TrustSet::doApply() } else if ( auto const ret = checkInsufficientReserve( - ctx_.view(), - ctx_.tx, - sle, - preFeeBalance_, - txSponsorSle, - 1); + ctx_.view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 1, 0, j_); !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index 1b420971063..b7aa6fa5130 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -161,7 +161,7 @@ VaultCreate::doApply() adjustOwnerCount(view(), owner, sponsor, 2, j_); addSponsorToLedgerEntry(vault, sponsor); if (auto const ret = - checkInsufficientReserve(view(), tx, owner, preFeeBalance_, sponsor, 0); + checkInsufficientReserve(view(), tx, owner, preFeeBalance_, sponsor, 0, 0, j_); !isTesSuccess(ret)) return ret; } @@ -169,7 +169,7 @@ VaultCreate::doApply() { // after Sponsor Amendment, check insufficient reserve first if (auto const ret = - checkInsufficientReserve(view(), tx, owner, preFeeBalance_, sponsor, 2); + checkInsufficientReserve(view(), tx, owner, preFeeBalance_, sponsor, 2, 0, j_); !isTesSuccess(ret)) return ret; adjustOwnerCount(view(), owner, sponsor, 2, j_); diff --git a/src/test/app/AMMExtendedMPT_test.cpp b/src/test/app/AMMExtendedMPT_test.cpp index 44ced2edc25..25660d29a06 100644 --- a/src/test/app/AMMExtendedMPT_test.cpp +++ b/src/test/app/AMMExtendedMPT_test.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -463,7 +464,7 @@ struct AMMExtendedMPT_test : public jtx::AMMTest // Provide micro amounts to compensate for fees to make results round // nice. auto const starting_xrp = - XRP(100) + env.current()->fees().accountReserve(2) + env.current()->fees().base * 3; + XRP(100) + baseAccountReserve(*env.current(), 2) + env.current()->fees().base * 3; env.fund(starting_xrp, gw, alice); env.fund(XRP(2'000), bob); diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index c94f0f405fa..d8cdf1ae12f 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -541,7 +542,7 @@ struct AMMExtended_test : public jtx::AMMTest // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) + // 1 for payment == 4 auto const starting_xrp = - XRP(100) + env.current()->fees().accountReserve(3) + env.current()->fees().base * 4; + XRP(100) + baseAccountReserve(*env.current(), 3) + env.current()->fees().base * 4; env.fund(starting_xrp, gw1, gw2, gw3, alice); env.fund(XRP(2'000), bob); diff --git a/src/test/app/CheckMPT_test.cpp b/src/test/app/CheckMPT_test.cpp index fb9d413e6ea..0ad0c4e8b3a 100644 --- a/src/test/app/CheckMPT_test.cpp +++ b/src/test/app/CheckMPT_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -408,7 +409,7 @@ class CheckMPT_test : public beast::unit_test::suite // Insufficient reserve. Account const cheri{"cheri"}; - env.fund(env.current()->fees().accountReserve(1) - drops(1), cheri); + env.fund(baseAccountReserve(*env.current(), 1) - drops(1), cheri); env(check::create(cheri, bob, USD(50)), fee(drops(env.current()->fees().base)), diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index f2c7ab3a38c..2fe68a75e51 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -470,7 +471,7 @@ class Check_test : public beast::unit_test::suite // Insufficient reserve. Account const cheri{"cheri"}; - env.fund(env.current()->fees().accountReserve(1) - drops(1), cheri); + env.fund(baseAccountReserve(*env.current(), 1) - drops(1), cheri); env.close(); env(check::create(cheri, bob, USD(50)), diff --git a/src/test/app/Credentials_test.cpp b/src/test/app/Credentials_test.cpp index 3c9a658acaf..558ab25d1da 100644 --- a/src/test/app/Credentials_test.cpp +++ b/src/test/app/Credentials_test.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -633,8 +634,8 @@ struct Credentials_test : public beast::unit_test::suite { Env env{*this, features}; - env.fund(drops(env.current()->fees().accountReserve(1)), issuer); - env.fund(drops(env.current()->fees().accountReserve(0)), subject); + env.fund(drops(baseAccountReserve(*env.current(), 1)), issuer); + env.fund(drops(baseAccountReserve(*env.current(), 0)), subject); env.close(); { diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 84fbd4b9903..a632391d6f4 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -251,7 +252,7 @@ class Delegate_test : public beast::unit_test::suite Account const bob{"bob"}; auto const txFee = env.current()->fees().base; - env.fund(env.current()->fees().accountReserve(0) + txFee, alice); + env.fund(baseAccountReserve(*env.current(), 0) + txFee, alice); env.fund(XRP(100000), bob); env.close(); @@ -268,7 +269,7 @@ class Delegate_test : public beast::unit_test::suite auto const txFee = env.current()->fees().base; - env.fund(env.current()->fees().accountReserve(1) + (txFee * 4), alice); + env.fund(baseAccountReserve(*env.current(), 1) + (txFee * 4), alice); env.fund(XRP(100000), bob, carol); env.close(); @@ -294,8 +295,8 @@ class Delegate_test : public beast::unit_test::suite Account const alice{"alice"}; Account const bob{"bob"}; - env.fund(drops(env.current()->fees().accountReserve(1)), alice); - env.fund(drops(env.current()->fees().accountReserve(2)), bob); + env.fund(drops(baseAccountReserve(*env.current(), 1)), alice); + env.fund(drops(baseAccountReserve(*env.current(), 2)), bob); env.close(); // alice gives bob permission @@ -398,7 +399,7 @@ class Delegate_test : public beast::unit_test::suite Account const carol{"carol"}; auto const baseFee = env.current()->fees().base; - auto const reserve = env.current()->fees().accountReserve(1); + auto const reserve = baseAccountReserve(*env.current(), 1); auto const paymentAmount = XRP(1); auto const highFee = reserve + baseFee; BEAST_EXPECT(highFee > reserve); @@ -464,9 +465,9 @@ class Delegate_test : public beast::unit_test::suite Account const carol{"carol"}; auto const baseFee = env.current()->fees().base; - auto const baseReserve = env.current()->fees().accountReserve(0); + auto const baseReserve = baseAccountReserve(*env.current(), 0); - env.fund(env.current()->fees().accountReserve(1) + baseFee + XRP(1), alice); + env.fund(baseAccountReserve(*env.current(), 1) + baseFee + XRP(1), alice); env.fund(baseReserve, bob); env.fund(XRP(1000), carol); env.close(); @@ -499,7 +500,7 @@ class Delegate_test : public beast::unit_test::suite Account const carol{"carol"}; auto const baseFee = env.current()->fees().base; - auto const reserve = env.current()->fees().accountReserve(1); + auto const reserve = baseAccountReserve(*env.current(), 1); // Alice is funded with (reserve + baseFee): after DelegateSet she has // exactly 'reserve', which is insufficient to send XRP(10) while keeping diff --git a/src/test/app/DepositAuth_test.cpp b/src/test/app/DepositAuth_test.cpp index c9773cba8bc..eab96f96b8a 100644 --- a/src/test/app/DepositAuth_test.cpp +++ b/src/test/app/DepositAuth_test.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +52,7 @@ namespace xrpl::test { static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return baseAccountReserve(*env.current(), count); } // Helper function that returns true if acct has the lsfDepositAuth flag set. @@ -1008,7 +1009,7 @@ struct DepositPreauth_test : public beast::unit_test::suite { // not enough reserve Account const john{"john"}; - env.fund(env.current()->fees().accountReserve(0), john); + env.fund(baseAccountReserve(*env.current(), 0), john); env.close(); auto jv = deposit::authCredentials(john, {{issuer, credType}}); env(jv, ter(tecINSUFFICIENT_RESERVE)); diff --git a/src/test/app/FlowMPT_test.cpp b/src/test/app/FlowMPT_test.cpp index a94d48beb45..958aa1c01ba 100644 --- a/src/test/app/FlowMPT_test.cpp +++ b/src/test/app/FlowMPT_test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -726,7 +727,7 @@ struct FlowMPT_test : public beast::unit_test::suite static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return baseAccountReserve(*env.current(), count); } // Helper function that returns the Offers on an account. diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index 8f096a970f3..a3f4e5f59ef 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -707,7 +708,7 @@ struct Flow_test : public beast::unit_test::suite static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return baseAccountReserve(*env.current(), count); } // Helper function that returns the Offers on an account. diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index 55818b131cb..59ae83264e2 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1093,7 +1094,7 @@ class LoanBroker_test : public beast::unit_test::suite } auto const amt = - env.balance(alice) - env.current()->fees().accountReserve(env.ownerCount(alice)); + env.balance(alice) - accountReserve(*env.current(), alice.id(), env.journal); env(pay(alice, issuer, amt)); // preclaim:: tecINSUFFICIENT_RESERVE diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 3fe727e3689..336482d8557 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -669,11 +670,11 @@ class Loan_test : public beast::unit_test::suite case AssetType::MPT: { // Enough to cover initial fees if (!env.le(keylet::account(issuer))) - env.fund(env.current()->fees().accountReserve(10) * 10, issuer); + env.fund(baseAccountReserve(*env.current(), 10) * 10, issuer); if (!env.le(keylet::account(lender))) - env.fund(env.current()->fees().accountReserve(10) * 10, noripple(lender)); + env.fund(baseAccountReserve(*env.current(), 10) * 10, noripple(lender)); if (!env.le(keylet::account(borrower))) - env.fund(env.current()->fees().accountReserve(10) * 10, noripple(borrower)); + env.fund(baseAccountReserve(*env.current(), 10) * 10, noripple(borrower)); MPTTester mptt{env, issuer, mptInitNoFund}; mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); @@ -757,11 +758,11 @@ class Loan_test : public beast::unit_test::suite using namespace jtx; // Enough to cover initial fees - env.fund(env.current()->fees().accountReserve(10) * 10, issuer); + env.fund(baseAccountReserve(*env.current(), 10) * 10, issuer); if (lender != issuer) - env.fund(env.current()->fees().accountReserve(10) * 10, noripple(lender)); + env.fund(baseAccountReserve(*env.current(), 10) * 10, noripple(lender)); if (borrower != issuer && borrower != lender) - env.fund(env.current()->fees().accountReserve(10) * 10, noripple(borrower)); + env.fund(baseAccountReserve(*env.current(), 10) * 10, noripple(borrower)); describeLoan(env, brokerParams, loanParams, assetType, issuer, lender, borrower); @@ -838,11 +839,10 @@ class Loan_test : public beast::unit_test::suite // Add extra for transaction fees and reserves, if appropriate, or a // tiny amount for the extra paid in each transaction auto const totalNeeded = state.totalValue + (serviceFee * state.paymentRemaining) + - (broker.asset.native() - ? Number( - baseFee * state.paymentRemaining + - env.current()->fees().accountReserve(env.ownerCount(borrower))) - : broker.asset(15).number()); + (broker.asset.native() ? Number( + baseFee * state.paymentRemaining + + accountReserve(*env.current(), borrower.id(), env.journal)) + : broker.asset(15).number()); auto const shortage = totalNeeded - borrowerBalance.number(); @@ -3048,7 +3048,7 @@ class Loan_test : public beast::unit_test::suite auto const [acctReserve, incReserve] = [this]() -> std::pair { Env const env{*this, testable_amendments()}; return { - env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(), + baseAccountReserve(*env.current(), 0).drops() / DROPS_PER_XRP.drops(), env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()}; }(); @@ -4505,8 +4505,8 @@ class Loan_test : public beast::unit_test::suite BrokerInfo const& brokerInfo, jtx::fee const& loanSetFee, Number const& debtMaximumRequest) { - auto const amt = env.balance(borrower) - - env.current()->fees().accountReserve(env.ownerCount(borrower)); + auto const amt = + env.balance(borrower) - accountReserve(*env.current(), borrower.id(), env.journal); env(pay(borrower, issuer, amt)); // tecINSUFFICIENT_RESERVE diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index b07fe043d27..49558d91cfd 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -58,7 +59,7 @@ class OfferMPT_test : public beast::unit_test::suite static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return baseAccountReserve(*env.current(), count); } static std::uint32_t @@ -1787,7 +1788,7 @@ class OfferMPT_test : public beast::unit_test::suite // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) + // 1 for payment == 4 auto const base = env.current()->fees().base; - auto const starting_xrp = XRP(100) + env.current()->fees().accountReserve(3) + base * 4; + auto const starting_xrp = XRP(100) + baseAccountReserve(*env.current(), 3) + base * 4; env.fund(starting_xrp, gw1, gw2, gw3, alice, bob); env.close(); @@ -1807,7 +1808,7 @@ class OfferMPT_test : public beast::unit_test::suite env(offer(alice, USD1(200), XRP(200))); BEAST_EXPECT(env.balance(alice, USD1) == USD1(100)); - BEAST_EXPECT(env.balance(alice) == STAmount(env.current()->fees().accountReserve(3))); + BEAST_EXPECT(env.balance(alice) == STAmount(baseAccountReserve(*env.current(), 3))); BEAST_EXPECT(env.balance(bob, USD1) == USD1(400)); }; @@ -1859,7 +1860,7 @@ class OfferMPT_test : public beast::unit_test::suite auto const bob = Account{"bob"}; auto const starting_xrp = - XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + XRP(100) + baseAccountReserve(*env.current(), 1) + env.current()->fees().base * 2; env.fund(starting_xrp, gw, alice, bob); @@ -1878,7 +1879,7 @@ class OfferMPT_test : public beast::unit_test::suite jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(1)).getText()); + STAmount(baseAccountReserve(*env.current(), 1)).getText()); jrr = ledgerEntryMPT(env, bob, USD); BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] == "400"); @@ -1898,7 +1899,7 @@ class OfferMPT_test : public beast::unit_test::suite auto const bob = Account{"bob"}; auto const starting_xrp = - XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + XRP(100) + baseAccountReserve(*env.current(), 1) + env.current()->fees().base * 2; env.fund(starting_xrp, gw, alice, bob); @@ -1919,7 +1920,7 @@ class OfferMPT_test : public beast::unit_test::suite jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(1)).getText()); + STAmount(baseAccountReserve(*env.current(), 1)).getText()); jrr = ledgerEntryMPT(env, bob, USD); BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] == "300"); @@ -1939,8 +1940,7 @@ class OfferMPT_test : public beast::unit_test::suite Env env{*this, features}; auto const base = env.current()->fees().base; - auto const starting_xrp = - XRP(100.1) + env.current()->fees().accountReserve(1) + base * 2; + auto const starting_xrp = XRP(100.1) + baseAccountReserve(*env.current(), 1) + base * 2; env.fund(starting_xrp, gw, alice, bob); env.close(); diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index 0c00c91f63b..117826f6030 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -61,7 +62,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return baseAccountReserve(*env.current(), count); } static std::uint32_t @@ -1932,7 +1933,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) + // 1 for payment == 4 auto const starting_xrp = - XRP(100) + env.current()->fees().accountReserve(3) + env.current()->fees().base * 4; + XRP(100) + baseAccountReserve(*env.current(), 3) + env.current()->fees().base * 4; env.fund(starting_xrp, gw1, gw2, gw3, alice, bob); env.close(); @@ -1955,7 +1956,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(3)).getText()); + STAmount(baseAccountReserve(*env.current(), 3)).getText()); jrr = ledgerEntryState(env, bob, gw1, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400"); @@ -2033,7 +2034,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite auto const USD = gw["USD"]; auto const starting_xrp = - XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + XRP(100) + baseAccountReserve(*env.current(), 1) + env.current()->fees().base * 2; env.fund(starting_xrp, gw, alice, bob); env.close(); @@ -2054,7 +2055,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(1)).getText()); + STAmount(baseAccountReserve(*env.current(), 1)).getText()); jrr = ledgerEntryState(env, bob, gw, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400"); @@ -2075,7 +2076,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite auto const USD = gw["USD"]; auto const starting_xrp = - XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + XRP(100) + baseAccountReserve(*env.current(), 1) + env.current()->fees().base * 2; env.fund(starting_xrp, gw, alice, bob); env.close(); @@ -2098,7 +2099,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(1)).getText()); + STAmount(baseAccountReserve(*env.current(), 1)).getText()); jrr = ledgerEntryState(env, bob, gw, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-300"); @@ -2120,7 +2121,7 @@ class OfferBaseUtil_test : public beast::unit_test::suite auto const XXX = gw["XXX"]; auto const starting_xrp = - XRP(100.1) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + XRP(100.1) + baseAccountReserve(*env.current(), 1) + env.current()->fees().base * 2; env.fund(starting_xrp, gw, alice, bob); env.close(); diff --git a/src/test/app/Oracle_test.cpp b/src/test/app/Oracle_test.cpp index 71875b429e5..0e3cd77cc9e 100644 --- a/src/test/app/Oracle_test.cpp +++ b/src/test/app/Oracle_test.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -61,7 +62,7 @@ struct Oracle_test : public beast::unit_test::suite // Insufficient reserve { Env env(*this); - env.fund(env.current()->fees().accountReserve(0), owner); + env.fund(baseAccountReserve(*env.current(), 0), owner); Oracle const oracle( env, {.owner = owner, @@ -71,8 +72,7 @@ struct Oracle_test : public beast::unit_test::suite // Insufficient reserve if the data series extends to greater than 5 { Env env(*this); - env.fund( - env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner); + env.fund(baseAccountReserve(*env.current(), 1) + env.current()->fees().base * 2, owner); Oracle oracle( env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); BEAST_EXPECT(oracle.exists()); @@ -640,8 +640,7 @@ struct Oracle_test : public beast::unit_test::suite { Env env(*this); auto const baseFee = static_cast(env.current()->fees().base.drops()); - env.fund( - env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner); + env.fund(baseAccountReserve(*env.current(), 1) + env.current()->fees().base * 2, owner); Oracle oracle(env, {.owner = owner, .fee = baseFee}); oracle.set(UpdateArg{.series = {{"XRP", "USD", 742, 2}}, .fee = baseFee}); } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index db7f70764c3..edc1e7daf4d 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -78,7 +79,7 @@ accountReserve(jtx::Env& env, std::uint32_t count = 1) static STAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return baseAccountReserve(*env.current(), count); } static void @@ -1808,7 +1809,7 @@ class Sponsor_test : public beast::unit_test::suite // Account is not sponsored by normal Sponsor specification { - env(pay(alice, bob, drops(env.current()->fees().accountReserve(0))), + env(pay(alice, bob, drops(baseAccountReserve(*env.current(), 0))), sponsor::as(sponsor, spfSponsorReserve), sig(sfSponsorSignature, sponsor)); env.close(); diff --git a/src/test/app/Ticket_test.cpp b/src/test/app/Ticket_test.cpp index 0dc9f0155ff..3aed6da18ea 100644 --- a/src/test/app/Ticket_test.cpp +++ b/src/test/app/Ticket_test.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -503,7 +504,7 @@ class Ticket_test : public beast::unit_test::suite Account const alice{"alice"}; // Fund alice not quite enough to make the reserve for a Ticket. - env.fund(env.current()->fees().accountReserve(1) - drops(1), alice); + env.fund(baseAccountReserve(*env.current(), 1) - drops(1), alice); env.close(); env(ticket::create(alice, 1), ter(tecINSUFFICIENT_RESERVE)); @@ -511,7 +512,7 @@ class Ticket_test : public beast::unit_test::suite env.require(owners(alice, 0), tickets(alice, 0)); // Give alice enough to exactly meet the reserve for one Ticket. - env(pay(env.master, alice, env.current()->fees().accountReserve(1) - env.balance(alice))); + env(pay(env.master, alice, baseAccountReserve(*env.current(), 1) - env.balance(alice))); env.close(); env(ticket::create(alice, 1)); @@ -524,7 +525,7 @@ class Ticket_test : public beast::unit_test::suite env( pay(env.master, alice, - env.current()->fees().accountReserve(250) - drops(1) - env.balance(alice))); + baseAccountReserve(*env.current(), 250) - drops(1) - env.balance(alice))); env.close(); // alice doesn't quite have the reserve for a total of 250 @@ -535,7 +536,7 @@ class Ticket_test : public beast::unit_test::suite // Give alice enough so she can make the reserve for all 250 // Tickets. - env(pay(env.master, alice, env.current()->fees().accountReserve(250) - env.balance(alice))); + env(pay(env.master, alice, baseAccountReserve(*env.current(), 250) - env.balance(alice))); env.close(); std::uint32_t const ticketSeq{env.seq(alice) + 1}; diff --git a/src/test/app/TrustSet_test.cpp b/src/test/app/TrustSet_test.cpp index 6d24b695c73..6d2233e9e56 100644 --- a/src/test/app/TrustSet_test.cpp +++ b/src/test/app/TrustSet_test.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -191,7 +192,7 @@ class TrustSet_test : public beast::unit_test::suite auto const txFee = env.current()->fees().base; auto const baseReserve = env.current()->fees().reserve; - auto const threelineReserve = env.current()->fees().accountReserve(3); + auto const threelineReserve = baseAccountReserve(*env.current(), 3); env.fund(XRP(10000), gwA, gwB, assistor); diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 0e6b680ff3a..f01e68fc3ef 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -1874,7 +1874,7 @@ class Vault_test : public beast::unit_test::suite auto const [acctReserve, incReserve] = [this]() -> std::pair { Env const env{*this, testable_amendments()}; return { - env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(), + baseAccountReserve(*env.current(), 0).drops() / DROPS_PER_XRP.drops(), env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()}; }(); @@ -2834,7 +2834,7 @@ class Vault_test : public beast::unit_test::suite auto const [acctReserve, incReserve] = [this]() -> std::pair { Env const env{*this, testable_amendments()}; return { - env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(), + baseAccountReserve(*env.current(), 0).drops() / DROPS_PER_XRP.drops(), env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()}; }(); diff --git a/src/test/app/XChain_test.cpp b/src/test/app/XChain_test.cpp index 3f49ab42bbb..de5ddbf8914 100644 --- a/src/test/app/XChain_test.cpp +++ b/src/test/app/XChain_test.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -141,7 +142,7 @@ struct SEnv XRPAmount reserve(std::uint32_t count) { - return env_.current()->fees().accountReserve(count); + return baseAccountReserve(*env_.current(), count); } XRPAmount @@ -371,7 +372,7 @@ struct XChain_test : public beast::unit_test::suite, public jtx::XChainBridgeObj XRPAmount reserve(std::uint32_t count) { - return XEnv(*this).env_.current()->fees().accountReserve(count); + return baseAccountReserve(*XEnv(*this).env_.current(), count); } XRPAmount diff --git a/src/test/jtx/impl/AMMTest.cpp b/src/test/jtx/impl/AMMTest.cpp index 24d831be7d1..2ff261afe26 100644 --- a/src/test/jtx/impl/AMMTest.cpp +++ b/src/test/jtx/impl/AMMTest.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -197,7 +198,7 @@ AMMTestBase::testAMM(std::function const& cb, TestAM XRPAmount AMMTest::reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return baseAccountReserve(*env.current(), count); } XRPAmount diff --git a/src/test/ledger/PaymentSandbox_test.cpp b/src/test/ledger/PaymentSandbox_test.cpp index 0dc2cb6e74f..b78f3eb42a8 100644 --- a/src/test/ledger/PaymentSandbox_test.cpp +++ b/src/test/ledger/PaymentSandbox_test.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -317,7 +318,7 @@ class PaymentSandbox_test : public beast::unit_test::suite }; auto reserve = [](jtx::Env& env, std::uint32_t count) -> XRPAmount { - return env.current()->fees().accountReserve(count); + return baseAccountReserve(*env.current(), count); }; Env env(*this, features); diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 9265d816ed8..68c5fe36278 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -775,7 +776,7 @@ class AccountTx_test : public beast::unit_test::suite // All it takes is a large enough XRP payment to resurrect // becky's account. Try too small a payment. - env(pay(alice, becky, drops(env.current()->fees().accountReserve(0)) - XRP(1)), + env(pay(alice, becky, drops(baseAccountReserve(*env.current(), 0)) - XRP(1)), ter(tecNO_DST_INSUF_XRP)); env.close(); From 3ad93af34dfc376ff8b3cf6c777f1711ec39d44e Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 29 Apr 2026 08:34:50 +0900 Subject: [PATCH 219/249] fix: Sponsor's MaxFee cap is bypassed in reset() path, allowing sponsee to drain entire pre-funded FeeAmount in a single tec-failing transaction --- src/libxrpl/tx/Transactor.cpp | 6 ++++++ src/test/app/Sponsor_test.cpp | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 77a96f42b60..d1e4d72c820 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -1243,6 +1243,12 @@ Transactor::reset(XRPAmount fee) auto const balance = payerSle->getFieldAmount(payer.balanceField).xrp(); + if (payer.type == FeePayerType::SponsorPreFunded && payerSle->isFieldPresent(sfMaxFee)) + { + auto const cap = payerSle->getFieldAmount(sfMaxFee).xrp(); + fee = std::min(fee, cap); + } + // balance should have already been checked in checkFee / preFlight. XRPL_ASSERT( balance != beast::zero && (!view().open() || balance >= fee), diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index edc1e7daf4d..987fdbbb696 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include #include #include +#include #include #include @@ -1695,6 +1697,42 @@ class Sponsor_test : public beast::unit_test::suite } } + // MaxFee cap is enforced in reset() for tec-failing transactions. + // On a closed ledger view (!view.open()), checkFee returns tecINSUFF_FEE when + // fee > MaxFee (not terINSUF_FEE_B), triggering reset() + { + Env env{*this, testable_amendments()}; + Account const alice("alice"); + Account const carol("sponsor"); + + env.fund(XRP(10000), alice, carol); + env.close(); + + // FeeAmount=1000 drops, MaxFee=10 drops + env(sponsor::set_fee(carol, 0, drops(1000), drops(10)), sponsor::sponseeAcc(alice)); + env.close(); + + // Apply directly against the closed ledger view (open_ = false) so that + // checkFee returns tecINSUFF_FEE and reset() is invoked. + OpenView overlay(&*env.closed()); + + auto jt = env.jt( + noop(alice), + fee(drops(1000)), + seq(env.seq(alice)), + sponsor::as(carol, spfSponsorFee)); + + auto const result = xrpl::apply(env.app(), overlay, *jt.stx, tapNONE, env.journal); + BEAST_EXPECT(result.ter == tecINSUFF_FEE); + BEAST_EXPECT(result.applied); + + // Only MaxFee (10 drops) must be deducted, not the full 1000 drops. + auto const sle = overlay.read(keylet::sponsor(carol.id(), alice.id())); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->isFieldPresent(sfFeeAmount)); + BEAST_EXPECT(sle->getFieldAmount(sfFeeAmount) == drops(990)); // 1000 - MaxFee(10) + } + // test lsfSponsorshipRequireSignForFee { Env env{*this, testable_amendments()}; From 688e568e397cb763733b5984e5d144e0546a5912 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 30 Apr 2026 09:58:36 +0900 Subject: [PATCH 220/249] fix: getLedgerEntryOwner missing ltLOAN_BROKER and ltLOAN prevents object sponsorship --- .../Sponsor/SponsorshipTransfer.cpp | 3 + src/test/app/Sponsor_test.cpp | 66 +++++++++++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 8f07ed6bb53..695d1b7c1c6 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -118,6 +118,7 @@ getLedgerEntryOwner(ReadView const& view, T const& sle, AccountID const& account case ltORACLE: case ltPERMISSIONED_DOMAIN: case ltVAULT: + case ltLOAN_BROKER: return sle->getAccountID(sfOwner); case ltCHECK: case ltDID: @@ -134,6 +135,8 @@ getLedgerEntryOwner(ReadView const& view, T const& sle, AccountID const& account return sle->getAccountID(sfAccount); case ltMPTOKEN_ISSUANCE: return sle->getAccountID(sfIssuer); + case ltLOAN: + return sle->getAccountID(sfBorrower); case ltSIGNER_LIST: { auto const signerList = view.read(keylet::signers(account)); if (!signerList) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 987fdbbb696..2f2bdf3d95c 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -5129,11 +5129,12 @@ class Sponsor_test : public beast::unit_test::suite Account const bob("bob"); Account const issuer("issuer"); Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); // LoanBrokerSet / LoanBrokerDelete { Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, sponsor); + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); PrettyAsset const asset{xrpIssue(), 1'000'000}; @@ -5167,14 +5168,41 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // LoanBrokerDelete auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice) - 1); + + if (cosigning) + { + // transfer sponsor + env(sponsor::transfer(alice, tfSponsorshipReassign, brokerKeylet.key), + sponsor::as(sponsor2, spfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env.close(); + + // transfer sponsor + env(sponsor::transfer(alice, tfSponsorshipReassign, brokerKeylet.key), + sponsor::as(sponsor2, spfSponsorReserve)); + env.close(); + } + + BEAST_EXPECT( + ownerCount(env, alice) == + 5); // LoanBroker, PseudoAccount(LB), (Vault, PseudoAccount(Vault), MPToken(Vault)) + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // LoanBrokerDelete env(loanBroker::del(alice, brokerKeylet.key, 0)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 3); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } // LoanBrokerConverDeposit/Withdraw/Clawback @@ -5258,7 +5286,7 @@ class Sponsor_test : public beast::unit_test::suite // LoanSet { Env env{*this, testable_amendments()}; - env.fund(XRP(1000000), alice, bob, issuer, sponsor); + env.fund(XRP(1000000), alice, bob, issuer, sponsor, sponsor2); env.close(); MPTTester mptt{env, issuer, mptInitNoFund}; @@ -5346,6 +5374,36 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(ownerCount(env, alice) == 2); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + // before transfer + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + if (cosigning) + { + // transfer sponsor + env(sponsor::transfer(alice, tfSponsorshipReassign, loanKeylet.key), + sponsor::as(sponsor2, spfSponsorReserve), + sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env.close(); + + // transfer sponsor + env(sponsor::transfer(alice, tfSponsorshipReassign, loanKeylet.key), + sponsor::as(sponsor2, spfSponsorReserve)); + env.close(); + } + + // after transfer + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + // LoanDelete env(loan::del(alice, loanKeylet.key), sponsor::as(sponsor, spfSponsorReserve), From d057f07d0e458c2335c5e9e434ca182ae418745c Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 30 Apr 2026 10:05:28 +0900 Subject: [PATCH 221/249] fix: setSponsorFieldU32 silently clamps to 0 on underflow instead of failing --- .../tx/transactors/Sponsor/SponsorshipTransfer.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 695d1b7c1c6..43289e71816 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -411,7 +411,13 @@ SponsorshipTransfer::doApply() auto const setSponsorFieldU32 = [](auto const& sle, auto const& field, auto const& delta) { int32_t const newValue = static_cast(sle->getFieldU32(field)) + delta; - if (newValue <= 0) + + if (newValue < 0) + { + UNREACHABLE("xrpl::SponsorshipTransfer::doApply : Invalid sponsor field value"); + return; + } + else if (newValue == 0) { sle->makeFieldAbsent(field); } From 33be9abbefa168fa33a642e88194d144e05b1e8d Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 30 Apr 2026 10:45:30 +0900 Subject: [PATCH 222/249] clang-format --- include/xrpl/ledger/helpers/AccountRootHelpers.h | 2 +- include/xrpl/ledger/helpers/SponsorHelpers.h | 4 +--- src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp | 8 ++++++-- src/libxrpl/tx/transactors/dex/OfferCreate.cpp | 4 ++-- src/libxrpl/tx/transactors/nft/NFTokenMint.cpp | 2 +- src/libxrpl/tx/transactors/oracle/OracleSet.cpp | 4 ++-- .../tx/transactors/token/MPTokenIssuanceCreate.cpp | 4 ++-- src/test/app/AMMExtended_test.cpp | 2 +- src/test/app/DepositAuth_test.cpp | 2 +- src/test/app/LoanBroker_test.cpp | 2 +- src/test/app/Ticket_test.cpp | 2 +- src/test/app/TrustSet_test.cpp | 2 +- 12 files changed, 20 insertions(+), 18 deletions(-) diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index a4cb4fbb467..6cf04d7e8cb 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -68,7 +68,7 @@ checkInsufficientReserve( STTx const& tx, SLE::const_ref accSle, STAmount const& accBalance, - SLE::const_ref sponsorSle, + SLE::const_ref sponsorSle, std::int32_t ownerCountDelta, std::int32_t accountCountDelta = 0, beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); diff --git a/include/xrpl/ledger/helpers/SponsorHelpers.h b/include/xrpl/ledger/helpers/SponsorHelpers.h index 190c1fbeb5d..fc065194867 100644 --- a/include/xrpl/ledger/helpers/SponsorHelpers.h +++ b/include/xrpl/ledger/helpers/SponsorHelpers.h @@ -53,9 +53,7 @@ getTxReserveSponsor(ReadView const& view, STTx const& tx) } inline std::optional -getLedgerEntryReserveSponsorAccountID( - SLE::const_ref sle, - SF_ACCOUNT const& field = sfSponsor) +getLedgerEntryReserveSponsorAccountID(SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor) { if (sle->isFieldPresent(field)) return sle->getAccountID(field); diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index be87900c845..3d0bee2c4c6 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -257,7 +257,9 @@ SponsorshipSet::doApply() sponsorAccSle, STAmount{(*sponsorAccSle)[sfBalance]}.xrp(), reserveSponsorAccSle, - 1, 0, ctx_.journal); + 1, + 0, + ctx_.journal); !isTesSuccess(ret)) return tecUNFUNDED; @@ -323,7 +325,9 @@ SponsorshipSet::doApply() sponsorAccSle, STAmount{(*sponsorAccSle)[sfBalance]}.xrp(), reserveSponsorAccSle, - 0, 0, ctx_.journal); + 0, + 0, + ctx_.journal); !isTesSuccess(ret)) return tecUNFUNDED; } diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index ab75cac38f9..30477dca38f 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -823,8 +823,8 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) { auto const sponsor = getTxReserveSponsor(sb, ctx_.tx); - if (auto const ret = - checkInsufficientReserve(sb, ctx_.tx, sleCreator, preFeeBalance_, sponsor, 1, 0, j_); + if (auto const ret = checkInsufficientReserve( + sb, ctx_.tx, sleCreator, preFeeBalance_, sponsor, 1, 0, j_); !isTesSuccess(ret)) { // If we are here, the signing account had an insufficient reserve diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index 023aaea5c28..7e889759659 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -4,10 +4,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index f9be120e5c9..82121612d52 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -190,8 +190,8 @@ OracleSet::preclaim(PreclaimContext const& ctx) auto const& balance = sleSetter->getFieldAmount(sfBalance); auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); - if (auto const ret = - checkInsufficientReserve(ctx.view, ctx.tx, sleSetter, balance, sponsor, adjustReserve,0, ctx.j); + if (auto const ret = checkInsufficientReserve( + ctx.view, ctx.tx, sleSetter, balance, sponsor, adjustReserve, 0, ctx.j); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 63114567168..92f44d8ed62 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -111,8 +111,8 @@ MPTokenIssuanceCreate::create( !isPseudoAccount((acct)) ? getTxReserveSponsor(view, tx) : std::shared_ptr(); if (args.priorBalance) { - if (auto const ret = - checkInsufficientReserve(view, tx, acct, *(args.priorBalance), sponsor, 1, 0, journal); + if (auto const ret = checkInsufficientReserve( + view, tx, acct, *(args.priorBalance), sponsor, 1, 0, journal); !isTesSuccess(ret)) return Unexpected(ret); // tecINSUFFICIENT_RESERVE } diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index 6047880e723..6ffaf6f0620 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -35,9 +35,9 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/src/test/app/DepositAuth_test.cpp b/src/test/app/DepositAuth_test.cpp index eab96f96b8a..2effc70061d 100644 --- a/src/test/app/DepositAuth_test.cpp +++ b/src/test/app/DepositAuth_test.cpp @@ -26,8 +26,8 @@ #include #include #include -#include #include +#include #include #include #include diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index a6cff0530a9..bf6588e006d 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -26,9 +26,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/src/test/app/Ticket_test.cpp b/src/test/app/Ticket_test.cpp index 3aed6da18ea..33e8a9985f0 100644 --- a/src/test/app/Ticket_test.cpp +++ b/src/test/app/Ticket_test.cpp @@ -22,8 +22,8 @@ #include #include #include -#include #include +#include #include #include #include diff --git a/src/test/app/TrustSet_test.cpp b/src/test/app/TrustSet_test.cpp index 6d2233e9e56..2844362827d 100644 --- a/src/test/app/TrustSet_test.cpp +++ b/src/test/app/TrustSet_test.cpp @@ -14,8 +14,8 @@ #include #include #include -#include #include +#include #include #include #include From 0c683bfd66c5fb270ae831d67df0aa25549595b0 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 30 Apr 2026 10:47:16 +0900 Subject: [PATCH 223/249] levelization --- .github/scripts/levelization/results/ordering.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/scripts/levelization/results/ordering.txt b/.github/scripts/levelization/results/ordering.txt index 7c43864c540..c2000d17681 100644 --- a/.github/scripts/levelization/results/ordering.txt +++ b/.github/scripts/levelization/results/ordering.txt @@ -50,7 +50,6 @@ libxrpl.tx > xrpl.protocol libxrpl.tx > xrpl.server libxrpl.tx > xrpl.tx test.app > test.jtx -test.app > test.toplevel test.app > test.unit_test test.app > xrpl.basics test.app > xrpl.core From 86c90c8e5cf8ac100e2bd03ea901a67a1d2bde5a Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 30 Apr 2026 11:58:26 +0900 Subject: [PATCH 224/249] clang-tidy --- include/xrpl/tx/invariants/SponsorshipInvariant.h | 4 ++-- src/libxrpl/ledger/View.cpp | 1 - src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 1 - src/libxrpl/ledger/helpers/RippleStateHelpers.cpp | 1 - src/libxrpl/ledger/helpers/TokenHelpers.cpp | 1 + src/libxrpl/tx/Transactor.cpp | 1 - src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp | 1 - src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp | 3 +-- src/libxrpl/tx/transactors/account/SignerListSet.cpp | 1 - src/libxrpl/tx/transactors/bridge/XChainBridge.cpp | 1 - src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp | 1 - src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp | 1 - src/libxrpl/tx/transactors/delegate/DelegateSet.cpp | 1 - src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp | 1 - src/libxrpl/tx/transactors/did/DIDSet.cpp | 1 - src/libxrpl/tx/transactors/oracle/OracleSet.cpp | 1 - src/libxrpl/tx/transactors/payment/DepositPreauth.cpp | 1 - src/libxrpl/tx/transactors/payment/Payment.cpp | 1 - .../tx/transactors/payment_channel/PaymentChannelFund.cpp | 1 - .../transactors/permissioned_domain/PermissionedDomainSet.cpp | 1 - src/libxrpl/tx/transactors/system/TicketCreate.cpp | 1 - src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp | 1 - src/libxrpl/tx/transactors/token/TrustSet.cpp | 1 - src/test/app/Sponsor_test.cpp | 2 ++ 24 files changed, 6 insertions(+), 24 deletions(-) diff --git a/include/xrpl/tx/invariants/SponsorshipInvariant.h b/include/xrpl/tx/invariants/SponsorshipInvariant.h index fbe3cd2aaad..8bae9d8d479 100644 --- a/include/xrpl/tx/invariants/SponsorshipInvariant.h +++ b/include/xrpl/tx/invariants/SponsorshipInvariant.h @@ -28,7 +28,7 @@ class SponsorshipOwnerCountsMatch void visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); - bool + [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; @@ -49,7 +49,7 @@ class SponsorshipAccountCountMatchesField void visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); - bool + [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; }; diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 5103458848d..ae91da5742c 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index 92a1ad99086..856b07e7b03 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index b62c5ed78cf..49f07d9396a 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index ec56717f619..330d51098c5 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index d1e4d72c820..3ab2474008a 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -12,7 +12,6 @@ #include // IWYU pragma: keep #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 3d0bee2c4c6..8787e447295 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 43289e71816..92b72e1645c 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -417,7 +416,7 @@ SponsorshipTransfer::doApply() UNREACHABLE("xrpl::SponsorshipTransfer::doApply : Invalid sponsor field value"); return; } - else if (newValue == 0) + if (newValue == 0) { sle->makeFieldAbsent(field); } diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index 9cf316d0230..78c1da0efef 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index b6d9109226d..af6412abfbf 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index 4d4b94c99d2..35f20bedbaf 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index a4a630e19e8..da0c9a5e0b9 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include // IWYU pragma: keep #include diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 1eb7e56085d..c0ddb51518f 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index 4f8cb149437..feea1d34a23 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index 07427dc4a45..8ad6dbe5685 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index 82121612d52..e9dc1b619f1 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 9b1077596e2..e3bf39f1de1 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 8d8a1303115..91ecba3f53c 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp index 84b14900b1f..8ab1d0abf37 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index 4fa80740e36..f10c3901da9 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp index 23933392236..5491a27d568 100644 --- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 92f44d8ed62..285b4f95586 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index da808f6458f..b1e419d5dc7 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 2f2bdf3d95c..702679bfcfa 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,7 @@ #include #include #include +#include #include #include #include From bca6d8e515dae48c8f9514a9a9f186d49cbdb381 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 30 Apr 2026 22:15:05 +0900 Subject: [PATCH 225/249] fix not to payback ReserveCount --- src/libxrpl/ledger/helpers/AccountRootHelpers.cpp | 3 +-- src/test/app/Sponsor_test.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index dfcd980f035..e19a49286a9 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -267,11 +267,10 @@ adjustOwnerCount( view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j); auto sponsorObjSle = view.peek(keylet::sponsor(sponsorID, accountID)); - if (sponsorObjSle) + if (sponsorObjSle && adjustment > 0) { // update the pre-funded ReserveCount on Sponsorship ledger object // Reserve count moves opposite to adjustment: +adjustment => consume reserve (-), - // -adjustment => payback (+) adjustSponsorOwnerCountHlp( view, sponsorObjSle, sfReserveCount, sponsorID, -adjustment, j, false); } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 702679bfcfa..cccbb71ef21 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -725,7 +725,7 @@ class Sponsor_test : public beast::unit_test::suite sle = env.le(keylet::sponsor(sponsor, alice)); BEAST_EXPECT(sle); - BEAST_EXPECT(sle->at(sfReserveCount) == 100); // paybacked + BEAST_EXPECT(sle->at(sfReserveCount) == 99); // not paybacked BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99)); } From eb88c0ef6b8c0485ac3a4285b28bd60e3243da6b Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:15:30 -0400 Subject: [PATCH 226/249] Fix clang-format, some refactoring --- .../xrpl/ledger/helpers/AccountRootHelpers.h | 8 ++-- include/xrpl/protocol/LedgerFormats.h | 4 +- .../xrpl/tx/transactors/oracle/OracleSet.h | 7 ++- .../ledger/helpers/AccountRootHelpers.cpp | 16 +++---- src/libxrpl/tx/Transactor.cpp | 46 +++++++++++-------- .../tx/transactors/oracle/OracleSet.cpp | 6 --- 6 files changed, 44 insertions(+), 43 deletions(-) diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index 6cf04d7e8cb..92259913a2f 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -40,15 +40,15 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) * increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base" */ -XRPAmount +[[nodiscard]] XRPAmount accountReserve( ReadView const& view, - std::shared_ptr const& sle, + SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj = 0, std::int32_t reserveCountAdj = 0); -inline XRPAmount +[[nodiscard]] inline XRPAmount accountReserve( ReadView const& view, AccountID const& id, @@ -76,7 +76,7 @@ checkInsufficientReserve( std::uint32_t ownerCount( ReadView const& view, - std::shared_ptr const& sle, + SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj = 0); diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index 78b0e751e91..92ef289ac8d 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -137,7 +137,7 @@ enum LedgerEntryType : std::uint16_t { LSF_FLAG(lsfDisallowIncomingPayChan, 0x10000000) /* True, reject new paychans */ \ LSF_FLAG(lsfDisallowIncomingTrustline, 0x20000000) /* True, reject new trustlines (only if no issued assets) */ \ LSF_FLAG(lsfAllowTrustLineLocking, 0x40000000) /* True, enable trustline locking */ \ - LSF_FLAG(lsfAllowTrustLineClawback, 0x80000000)) /* True, enable clawback */ \ + LSF_FLAG(lsfAllowTrustLineClawback, 0x80000000)) /* True, enable clawback */ \ \ LEDGER_OBJECT(Offer, \ LSF_FLAG(lsfPassive, 0x00010000) \ @@ -203,7 +203,7 @@ enum LedgerEntryType : std::uint16_t { LSF_FLAG(lsfLoanImpaired, 0x00020000) \ LSF_FLAG(lsfLoanOverpayment, 0x00040000)) /* True, loan allows overpayments */ \ \ - LEDGER_OBJECT(Sponsorship, \ + LEDGER_OBJECT(Sponsorship, \ LSF_FLAG(lsfSponsorshipRequireSignForFee, 0x00010000) \ LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000)) diff --git a/include/xrpl/tx/transactors/oracle/OracleSet.h b/include/xrpl/tx/transactors/oracle/OracleSet.h index 30e15c9d3cf..8ce8a819235 100644 --- a/include/xrpl/tx/transactors/oracle/OracleSet.h +++ b/include/xrpl/tx/transactors/oracle/OracleSet.h @@ -22,8 +22,11 @@ class OracleSet : public Transactor { } - static uint32_t - calculateOracleReserve(std::size_t count); + inline static uint32_t + calculateOracleReserve(std::size_t count) + { + return count > 5 ? 2 : 1; + } static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index e19a49286a9..2e64ac6de4e 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -88,7 +88,7 @@ confineOwnerCount( static std::uint32_t ownerCountHlp( ReadView const& view, - std::shared_ptr const& sle, + SLE::const_ref sle, std::int32_t adjustment, bool reportConfine, beast::Journal j) @@ -129,7 +129,7 @@ ownerCountHlp( } static std::uint32_t -reserveCountHlp(std::shared_ptr const& sle, std::int32_t adjustment, beast::Journal j) +reserveCountHlp(SLE::const_ref sle, std::int32_t adjustment, beast::Journal j) { bool const isSponsored = sle->isFieldPresent(sfSponsor); std::uint32_t const sponsoringCount = sle->getFieldU32(sfSponsoringAccountCount); @@ -167,7 +167,7 @@ baseReserveHlp(ReadView const& view, std::uint32_t ownerCount, std::uint32_t res static XRPAmount reserveHlp( ReadView const& view, - std::shared_ptr const& sle, + SLE::const_ref sle, std::uint32_t ownerCount, std::uint32_t reserveCount) { @@ -180,11 +180,7 @@ reserveHlp( } std::uint32_t -ownerCount( - ReadView const& view, - std::shared_ptr const& sle, - beast::Journal j, - std::int32_t adjustment) +ownerCount(ReadView const& view, SLE::const_ref sle, beast::Journal j, std::int32_t adjustment) { return ownerCountHlp(view, sle, adjustment, true, j); } @@ -278,10 +274,10 @@ adjustOwnerCount( adjustSponsorOwnerCountHlp(view, accountSle, sfOwnerCount, accountID, adjustment, j); } -[[nodiscard]] XRPAmount +XRPAmount accountReserve( ReadView const& view, - std::shared_ptr const& sle, + SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj, std::int32_t reserveCountAdj) diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 3ab2474008a..ddb25e6f7fa 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -167,19 +167,9 @@ preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::J } // namespace detail -/** Performs early sanity checks on the account and fee fields */ -NotTEC -Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) +static NotTEC +preflight1Sponsor(PreflightContext const& ctx, AccountID const& id) { - if (ctx.tx.isFieldPresent(sfDelegate)) - { - if (!ctx.rules.enabled(featurePermissionDelegationV1_1)) - return temDISABLED; - - if (ctx.tx[sfDelegate] == ctx.tx[sfAccount]) - return temBAD_SIGNER; - } - bool const hasSponsor = ctx.tx.isFieldPresent(sfSponsor); bool const hasSponsorFlags = ctx.tx.isFieldPresent(sfSponsorFlags); bool const hasSponsorSig = ctx.tx.isFieldPresent(sfSponsorSignature); @@ -223,6 +213,29 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return temINVALID_FLAG; } + if (hasSponsor && ctx.tx.getAccountID(sfSponsor) == id) + { + JLOG(ctx.j.debug()) << "preflight1: Sponsor account cannot be the " + "same as the transaction originator"; + return temMALFORMED; + } + + return tesSUCCESS; +} + +/** Performs early sanity checks on the account and fee fields */ +NotTEC +Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) +{ + if (ctx.tx.isFieldPresent(sfDelegate)) + { + if (!ctx.rules.enabled(featurePermissionDelegationV1_1)) + return temDISABLED; + + if (ctx.tx[sfDelegate] == ctx.tx[sfAccount]) + return temBAD_SIGNER; + } + if (auto const ret = preflight0(ctx, flagMask)) return ret; @@ -261,13 +274,8 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) !ctx.rules.enabled(featureBatch), "Inner batch transaction must have a parent batch ID."); - // Sponsor checks - if (hasSponsor && ctx.tx.getAccountID(sfSponsor) == id) - { - JLOG(ctx.j.debug()) << "preflight1: Sponsor account cannot be the " - "same as the transaction originator"; - return temMALFORMED; - } + if (auto const ter = preflight1Sponsor(ctx, id); !isTesSuccess(ter)) + return ter; return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index e9dc1b619f1..bb7eeb44681 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -59,12 +59,6 @@ OracleSet::preflight(PreflightContext const& ctx) return tesSUCCESS; } -uint32_t -OracleSet::calculateOracleReserve(std::size_t count) -{ - return count > 5 ? 2 : 1; -} - TER OracleSet::preclaim(PreclaimContext const& ctx) { From 2b4ce0fc218069489b247659fc64e5a20d70e78a Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:56:44 -0400 Subject: [PATCH 227/249] Merge fixes --- src/test/app/Delegate_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index feca3a41c8b..b7d1c46f463 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -2042,7 +2042,7 @@ class Delegate_test : public beast::unit_test::suite // DO NOT modify expectedDelegableCount unless all scenarios, including // edge cases, have been fully tested and verified. // ==================================================================== - std::size_t const expectedDelegableCount = 77; + std::size_t const expectedDelegableCount = 53; BEAST_EXPECTS( delegableCount == expectedDelegableCount, From d792e03468fb1ccd994e82dee280490175991480 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:47:15 -0400 Subject: [PATCH 228/249] adjustOwnerCountObj, more checks, some fixes --- .../xrpl/ledger/helpers/AccountRootHelpers.h | 20 +++++++ .../ledger/helpers/AccountRootHelpers.cpp | 25 ++++++++- .../ledger/helpers/CredentialHelpers.cpp | 5 +- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 3 +- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 52 +++---------------- src/libxrpl/ledger/helpers/OfferHelpers.cpp | 3 +- .../ledger/helpers/PaymentChannelHelpers.cpp | 3 +- src/libxrpl/tx/Transactor.cpp | 3 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 3 +- .../tx/transactors/account/SignerListSet.cpp | 5 +- .../tx/transactors/bridge/XChainBridge.cpp | 5 +- .../tx/transactors/check/CheckCancel.cpp | 4 +- .../tx/transactors/check/CheckCash.cpp | 6 +-- .../credentials/CredentialAccept.cpp | 5 +- .../tx/transactors/delegate/DelegateSet.cpp | 3 +- src/libxrpl/tx/transactors/did/DIDDelete.cpp | 4 +- .../tx/transactors/escrow/EscrowCancel.cpp | 4 +- .../tx/transactors/escrow/EscrowFinish.cpp | 5 +- .../transactors/lending/LoanBrokerDelete.cpp | 5 +- .../tx/transactors/lending/LoanDelete.cpp | 3 +- .../tx/transactors/oracle/OracleDelete.cpp | 5 +- .../tx/transactors/oracle/OracleSet.cpp | 6 +-- .../tx/transactors/payment/DepositPreauth.cpp | 4 +- .../PermissionedDomainDelete.cpp | 3 +- .../token/MPTokenIssuanceDestroy.cpp | 5 +- .../tx/transactors/vault/VaultDelete.cpp | 3 +- 26 files changed, 80 insertions(+), 112 deletions(-) diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index 92259913a2f..6151ffe83f6 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -105,6 +105,26 @@ adjustOwnerCount( j); } +void +adjustOwnerCountObj( + ApplyView& view, + SLE::ref accountSle, + SLE::ref objectSle, + std::int32_t amount, + beast::Journal j); + +inline void +adjustOwnerCountObj( + ApplyView& view, + AccountID const& account, + SLE::ref objectSle, + std::int32_t amount, + beast::Journal j) +{ + SLE::ref accountSle = view.peek(keylet::account(account)); + return adjustOwnerCountObj(view, accountSle, objectSle, amount, j); +} + /** Returns IOU issuer transfer fee as Rate. Rate specifies * the fee as fractions of 1 billion. For example, 1% transfer rate * is represented as 1,010,000,000. diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 2e64ac6de4e..66a6d02fe8b 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -248,13 +248,19 @@ adjustOwnerCount( std::int32_t adjustment, beast::Journal j) { - if (!accountSle) - return; + XRPL_ASSERT(accountSle, "xrpl::adjustOwnerCount : valid account sle"); + auto const sleType = accountSle->getType(); + bool const validType = sponsorSle ? sleType == ltACCOUNT_ROOT + : sleType == ltLOAN_BROKER || sleType == ltACCOUNT_ROOT; + XRPL_ASSERT(validType, "xrpl::adjustOwnerCount : valid account sle type"); XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input"); auto const accountID = accountSle->getAccountID(sfAccount); if (sponsorSle) { + XRPL_ASSERT( + sponsorSle->getType() == ltACCOUNT_ROOT, + "xrpl::adjustOwnerCount : valid sponsor sle type"); auto const sponsorID = sponsorSle->getAccountID(sfAccount); adjustSponsorOwnerCountHlp( @@ -274,6 +280,21 @@ adjustOwnerCount( adjustSponsorOwnerCountHlp(view, accountSle, sfOwnerCount, accountID, adjustment, j); } +void +adjustOwnerCountObj( + ApplyView& view, + SLE::ref accountSle, + SLE::ref objectSle, + std::int32_t amount, + beast::Journal j) +{ + XRPL_ASSERT(objectSle, "xrpl::adjustOwnerCount : valid object sle"); + XRPL_ASSERT( + objectSle->getType() != ltACCOUNT_ROOT, "xrpl::adjustOwnerCount : valid object sle type"); + SLE::ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle); + adjustOwnerCount(view, accountSle, sponsorSle, amount, j); +} + XRPAmount accountReserve( ReadView const& view, diff --git a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp index 7f336c35684..32279514a54 100644 --- a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -94,10 +94,7 @@ deleteSLE(ApplyView& view, std::shared_ptr const& sleCredential, beast::Jou } if (isOwner) - { - auto const sponsorSle = getLedgerEntryReserveSponsor(view, sleCredential); - adjustOwnerCount(view, sleAccount, sponsorSle, -1, j); - } + adjustOwnerCountObj(view, sleAccount, sleCredential, -1, j); return tesSUCCESS; }; diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index 39542063bf2..43f5590098e 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -180,8 +180,7 @@ authorizeMPToken( keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false)) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsor = getLedgerEntryReserveSponsor(view, sleMpt); - adjustOwnerCount(view, sleAcct, sponsor, -1, journal); + adjustOwnerCountObj(view, sleAcct, sleMpt, -1, journal); view.erase(sleMpt); return tesSUCCESS; diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index 856b07e7b03..f5a6681fa6b 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -451,6 +451,7 @@ removeToken( auto const prev = loadPage(curr, sfPreviousPageMin); auto const next = loadPage(curr, sfNextPageMin); + auto nullJ = beast::Journal{beast::Journal::getNullSink()}; if (!arr.empty()) { // The current page isn't empty. Update it and then try to consolidate @@ -460,26 +461,10 @@ removeToken( view.update(curr); if (prev && mergePages(view, prev, curr)) - { - auto const sponsor = getLedgerEntryReserveSponsor(view, prev); - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - sponsor, - -1, - beast::Journal{beast::Journal::getNullSink()}); - } + adjustOwnerCountObj(view, owner, prev, -1, nullJ); if (next && mergePages(view, curr, next)) - { - auto const sponsor = getLedgerEntryReserveSponsor(view, curr); - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - sponsor, - -1, - beast::Journal{beast::Journal::getNullSink()}); - } + adjustOwnerCountObj(view, owner, curr, -1, nullJ); return tesSUCCESS; } @@ -513,13 +498,7 @@ removeToken( curr->makeFieldAbsent(sfPreviousPageMin); } - auto const sponsor = getLedgerEntryReserveSponsor(view, prev); - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - sponsor, - -1, - beast::Journal{beast::Journal::getNullSink()}); + adjustOwnerCountObj(view, owner, prev, -1, nullJ); view.update(curr); view.erase(prev); @@ -555,13 +534,7 @@ removeToken( view.update(next); } - auto const sponsor = getLedgerEntryReserveSponsor(view, curr); - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - sponsor, - -1, - beast::Journal{beast::Journal::getNullSink()}); + adjustOwnerCountObj(view, owner, curr, -1, nullJ); view.erase(curr); @@ -579,12 +552,7 @@ removeToken( view.peek(Keylet(ltNFTOKEN_PAGE, prev->key())), view.peek(Keylet(ltNFTOKEN_PAGE, next->key())))) { - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - getLedgerEntryReserveSponsor(view, prev), - -1, - beast::Journal{beast::Journal::getNullSink()}); + adjustOwnerCountObj(view, owner, prev, -1, nullJ); } return tesSUCCESS; @@ -701,13 +669,7 @@ deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer) false)) return false; - auto const sponsor = getLedgerEntryReserveSponsor(view, offer); - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - sponsor, - -1, - beast::Journal{beast::Journal::getNullSink()}); + adjustOwnerCountObj(view, owner, offer, -1, beast::Journal{beast::Journal::getNullSink()}); view.erase(offer); return true; diff --git a/src/libxrpl/ledger/helpers/OfferHelpers.cpp b/src/libxrpl/ledger/helpers/OfferHelpers.cpp index 8a5800dc566..71930ddf5a9 100644 --- a/src/libxrpl/ledger/helpers/OfferHelpers.cpp +++ b/src/libxrpl/ledger/helpers/OfferHelpers.cpp @@ -57,8 +57,7 @@ offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) } } - auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, view.peek(keylet::account(owner)), sponsor, -1, j); + adjustOwnerCountObj(view, owner, sle, -1, j); view.erase(sle); diff --git a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp index 59ee9c92d8d..cfa226c3d74 100644 --- a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp +++ b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp @@ -58,8 +58,7 @@ closeChannel( XRPL_ASSERT( (*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount"); (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; - auto const sponsor = getLedgerEntryReserveSponsor(view, slep); - adjustOwnerCount(view, sle, sponsor, -1, j); + adjustOwnerCountObj(view, sle, slep, -1, j); view.update(sle); // Remove PayChan from ledger diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index ddb25e6f7fa..e5eba0d0950 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -757,8 +757,7 @@ Transactor::ticketDelete( } // Update the Ticket owner's reserve. - auto const sponsor = getLedgerEntryReserveSponsor(view, sleTicket); - adjustOwnerCount(view, sleAccount, sponsor, -1, j); + adjustOwnerCountObj(view, sleAccount, sleTicket, -1, j); // Remove Ticket from ledger. view.erase(sleTicket); diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 8787e447295..ebda6f0b8ee 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -202,8 +202,7 @@ SponsorshipSet::doApply() if (!sponsorObjSle) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), sponsorObjSle); - adjustOwnerCount(ctx_.view(), sponsorAccSle, sponsor, -1, ctx_.journal); + adjustOwnerCountObj(ctx_.view(), sponsorAccSle, sponsorObjSle, -1, ctx_.journal); ctx_.view().dirRemove( keylet::ownerDir(sponsorAccountID), diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index 78c1da0efef..aa60204a6a2 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -216,9 +216,8 @@ removeSignersFromLedger( // LCOV_EXCL_STOP } - auto const sponsor = getLedgerEntryReserveSponsor(view, signers); - adjustOwnerCount( - view, view.peek(accountKeylet), sponsor, removeFromOwnerCount, registry.getJournal("View")); + adjustOwnerCountObj( + view, view.peek(accountKeylet), signers, removeFromOwnerCount, registry.getJournal("View")); view.erase(signers); diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index af6412abfbf..edf244f9330 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -726,11 +726,10 @@ finalizeClaimHelper( return result; } + adjustOwnerCountObj(outerSb, sleOwner, sleClaimID, -1, j); + // Remove the claim id from the ledger outerSb.erase(sleClaimID); - - auto const sponsor = getLedgerEntryReserveSponsor(outerSb, sleClaimID); - adjustOwnerCount(outerSb, sleOwner, sponsor, -1, j); } } diff --git a/src/libxrpl/tx/transactors/check/CheckCancel.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp index 6f738864e72..0d92c176dad 100644 --- a/src/libxrpl/tx/transactors/check/CheckCancel.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -94,9 +94,7 @@ CheckCancel::doApply() } // If we succeeded, update the check owner's reserve. - auto const sleSrc = view().peek(keylet::account(srcId)); - auto const sponsor = getLedgerEntryReserveSponsor(view(), sleCheck); - adjustOwnerCount(view(), sleSrc, sponsor, -1, viewJ); + adjustOwnerCountObj(view(), srcId, sleCheck, -1, viewJ); // Remove check from ledger. view().erase(sleCheck); diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index 050525d39b1..72d1177b1eb 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -303,7 +303,7 @@ CheckCash::doApply() // LCOV_EXCL_STOP } - auto const sponsorSle = getLedgerEntryReserveSponsor(psb, sleCheck); + auto const sponsorCheckSle = getLedgerEntryReserveSponsor(psb, sleCheck); // Preclaim already checked that source has at least the requested // funds. @@ -333,7 +333,7 @@ CheckCash::doApply() // from src's directory, we allow them to send that additional // incremental reserve amount in the transfer. Hence the -1 // argument. - STAmount const srcLiquid{xrpLiquid(psb, srcId, sponsorSle ? 0 : -1, viewJ)}; + STAmount const srcLiquid{xrpLiquid(psb, srcId, sponsorCheckSle ? 0 : -1, viewJ)}; // Now, how much do they need in order to be successful? STAmount const xrpDeliver{ @@ -588,7 +588,7 @@ CheckCash::doApply() // If we succeeded, update the check owner's reserve. - adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), sponsorSle, -1, viewJ); + adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), sponsorCheckSle, -1, viewJ); // Remove check from ledger. psb.erase(sleCheck); diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index 35f20bedbaf..4bd5d3b2d52 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -104,8 +104,7 @@ CredentialAccept::doApply() auto const credType(ctx_.tx[sfCredentialType]); Keylet const credentialKey = keylet::credential(account_, issuer, credType); - auto const sleCred = view().peek(credentialKey); // Checked in preclaim() - auto const currentSponsor = getLedgerEntryReserveSponsor(view(), sleCred); + auto const sleCred = view().peek(credentialKey); // Checked in preclaim() if (checkExpired(sleCred, view().header().parentCloseTime)) { @@ -118,7 +117,7 @@ CredentialAccept::doApply() sleCred->setFieldU32(sfFlags, lsfAccepted); view().update(sleCred); - adjustOwnerCount(view(), sleIssuer, currentSponsor, -1, j_); + adjustOwnerCountObj(view(), sleIssuer, sleCred, -1, j_); removeSponsorFromLedgerEntry(sleCred); adjustOwnerCount(view(), sleSubject, newSponsor, 1, j_); addSponsorToLedgerEntry(sleCred, newSponsor); diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index c0ddb51518f..8742ec2bbe6 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -168,8 +168,7 @@ DelegateSet::deleteDelegate(ApplyView& view, std::shared_ptr const& sle, be if (!sleOwner) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, sleOwner, sponsor, -1, j); + adjustOwnerCountObj(view, sleOwner, sle, -1, j); view.erase(sle); diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp index b6d7ef94b49..a06191f321f 100644 --- a/src/libxrpl/tx/transactors/did/DIDDelete.cpp +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -57,9 +57,7 @@ DIDDelete::deleteSLE( if (!sleOwner) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, sleOwner, sponsor, -1, j); - view.update(sleOwner); + adjustOwnerCountObj(view, sleOwner, sle, -1, j); // Remove object from ledger view.erase(sle); diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp index cd69438a462..901a5801715 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -212,9 +212,7 @@ EscrowCancel::doApply() } } - auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep); - adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); - ctx_.view().update(sle); + adjustOwnerCountObj(ctx_.view(), sle, slep, -1, ctx_.journal); // Remove escrow from ledger ctx_.view().erase(slep); diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index 8e5c60ce8b3..1714847118e 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -392,10 +392,7 @@ EscrowFinish::doApply() ctx_.view().update(sled); // Adjust source owner count - auto const sle = ctx_.view().peek(keylet::account(account)); - auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep); - adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal); - ctx_.view().update(sle); + adjustOwnerCountObj(ctx_.view(), account, slep, -1, ctx_.journal); // Remove escrow from ledger ctx_.view().erase(slep); diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index ef7e99c355f..810ba403cf9 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -181,17 +181,14 @@ LoanBrokerDelete::doApply() // Decreases the owner count by two: one for the LoanBroker object, and // one for the pseudo-account. // LoanBroker object can be sponsored - auto const sponsor = getLedgerEntryReserveSponsor(view(), broker); - adjustOwnerCount(view(), owner, sponsor, -1, j_); + adjustOwnerCountObj(view(), owner, broker, -1, j_); // pseudo-account cannot be sponsored adjustOwnerCount(view(), owner, {}, -1, j_); } view().erase(brokerPseudoSLE); - view().erase(broker); - associateAsset(*broker, vaultAsset); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index 0a90b0f5fc7..97761458fc2 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -130,8 +130,7 @@ LoanDelete::doApply() } } // Decrement the borrower's owner count - auto const sponsor = getLedgerEntryReserveSponsor(view, loanSle); - adjustOwnerCount(view, borrowerSle, sponsor, -1, j_); + adjustOwnerCountObj(view, borrowerSle, loanSle, -1, j_); // Delete the Loan object view.erase(loanSle); diff --git a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index bc46107cf98..ddb848a1181 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -72,10 +72,7 @@ OracleDelete::deleteOracle( return tecINTERNAL; // LCOV_EXCL_LINE auto const count = sle->getFieldArray(sfPriceDataSeries).size() > 5 ? -2 : -1; - - auto const sponsor = getLedgerEntryReserveSponsor(view, sle); - adjustOwnerCount(view, sleOwner, sponsor, count, j); - + adjustOwnerCountObj(view, sleOwner, sle, count, j); view.erase(sle); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index bb7eeb44681..ef39d3aab67 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -281,11 +281,10 @@ OracleSet::doApply() // the sponsor decrease current sponsored owner count. // Otherwise, the sponsorship will be deleted. - auto const currentSponsorSle = getLedgerEntryReserveSponsor(ctx_.view(), sle); auto const newSponsorSle = getTxReserveSponsor(ctx_.view(), ctx_.tx); // decrease current sponsored owner count - adjustOwnerCount(ctx_.view(), accountSle, currentSponsorSle, -oldCount, ctx_.journal); + adjustOwnerCountObj(ctx_.view(), accountSle, sle, -oldCount, ctx_.journal); removeSponsorFromLedgerEntry(sle); // increase new owner count adjustOwnerCount(ctx_.view(), accountSle, newSponsorSle, newCount, ctx_.journal); @@ -294,8 +293,7 @@ OracleSet::doApply() else if (adjust < 0) { // decrease owner count - auto const sponsorSle = getLedgerEntryReserveSponsor(ctx_.view(), sle); - adjustOwnerCount(ctx_.view(), accountSle, sponsorSle, adjust, ctx_.journal); + adjustOwnerCountObj(ctx_.view(), accountSle, sle, adjust, ctx_.journal); } ctx_.view().update(sle); diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index e3bf39f1de1..9cb434cdfeb 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -288,9 +288,7 @@ DepositPreauth::removeFromLedger(ApplyView& view, uint256 const& preauthIndex, b if (!sleOwner) return tefINTERNAL; // LCOV_EXCL_LINE - auto const sponsor = getLedgerEntryReserveSponsor(view, slePreauth); - adjustOwnerCount(view, sleOwner, sponsor, -1, j); - + adjustOwnerCountObj(view, sleOwner, slePreauth, -1, j); // Remove DepositPreauth from ledger. view.erase(slePreauth); diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index 40e18596975..7d70cf507a3 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -68,8 +68,7 @@ PermissionedDomainDelete::doApply() XRPL_ASSERT( ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0, "xrpl::PermissionedDomainDelete::doApply : nonzero owner count"); - auto const sponsor = getLedgerEntryReserveSponsor(view(), slePd); - adjustOwnerCount(view(), ownerSle, sponsor, -1, ctx_.journal); + adjustOwnerCountObj(view(), ownerSle, slePd, -1, ctx_.journal); view().erase(slePd); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index 35fc414f6ac..fb2ca35ef20 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -52,10 +52,9 @@ MPTokenIssuanceDestroy::doApply() if (!view().dirRemove(keylet::ownerDir(account_), (*mpt)[sfOwnerNode], mpt->key(), false)) return tefBAD_LEDGER; // LCOV_EXCL_LINE - auto const sponsor = getLedgerEntryReserveSponsor(view(), mpt); - adjustOwnerCount(view(), view().peek(keylet::account(account_)), sponsor, -1, j_); - + adjustOwnerCountObj(view(), account_, mpt, -1, j_); view().erase(mpt); + return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index 7920b9bdfa7..b392cf745c0 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -207,8 +207,7 @@ VaultDelete::doApply() } // We are destroying Vault and PseudoAccount, hence decrease by 2 - auto const vaultSponsor = getLedgerEntryReserveSponsor(view(), vault); - adjustOwnerCount(view(), owner, vaultSponsor, -2, j_); + adjustOwnerCountObj(view(), owner, vault, -2, j_); // Destroy the vault. view().erase(vault); From c72fd5953956efda11e8b23c1d9e5fd245b0f2c3 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 May 2026 10:51:53 +0900 Subject: [PATCH 229/249] clang-tidy --- include/xrpl/ledger/helpers/AccountRootHelpers.h | 2 +- include/xrpl/tx/transactors/oracle/OracleSet.h | 2 +- src/libxrpl/ledger/helpers/CredentialHelpers.cpp | 1 - src/libxrpl/ledger/helpers/OfferHelpers.cpp | 1 - src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp | 1 - src/libxrpl/tx/Transactor.cpp | 1 - src/libxrpl/tx/transactors/check/CheckCancel.cpp | 1 - src/libxrpl/tx/transactors/did/DIDDelete.cpp | 1 - src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp | 1 - src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp | 1 - src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp | 1 - src/libxrpl/tx/transactors/lending/LoanDelete.cpp | 1 - src/libxrpl/tx/transactors/oracle/OracleDelete.cpp | 1 - .../permissioned_domain/PermissionedDomainDelete.cpp | 1 - src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp | 1 - src/libxrpl/tx/transactors/vault/VaultDelete.cpp | 1 - 16 files changed, 2 insertions(+), 16 deletions(-) diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index 6151ffe83f6..7eb1e545608 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -122,7 +122,7 @@ adjustOwnerCountObj( beast::Journal j) { SLE::ref accountSle = view.peek(keylet::account(account)); - return adjustOwnerCountObj(view, accountSle, objectSle, amount, j); + adjustOwnerCountObj(view, accountSle, objectSle, amount, j); } /** Returns IOU issuer transfer fee as Rate. Rate specifies diff --git a/include/xrpl/tx/transactors/oracle/OracleSet.h b/include/xrpl/tx/transactors/oracle/OracleSet.h index 8ce8a819235..47ddd7b891f 100644 --- a/include/xrpl/tx/transactors/oracle/OracleSet.h +++ b/include/xrpl/tx/transactors/oracle/OracleSet.h @@ -22,7 +22,7 @@ class OracleSet : public Transactor { } - inline static uint32_t + static uint32_t calculateOracleReserve(std::size_t count) { return count > 5 ? 2 : 1; diff --git a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp index 32279514a54..e4a5da31296 100644 --- a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/ledger/helpers/OfferHelpers.cpp b/src/libxrpl/ledger/helpers/OfferHelpers.cpp index 71930ddf5a9..870bfdc32ae 100644 --- a/src/libxrpl/ledger/helpers/OfferHelpers.cpp +++ b/src/libxrpl/ledger/helpers/OfferHelpers.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include // IWYU pragma: keep diff --git a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp index cfa226c3d74..a47998b05e7 100644 --- a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp +++ b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index e5eba0d0950..a4a83ae6733 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/check/CheckCancel.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp index 0d92c176dad..ceca4055407 100644 --- a/src/libxrpl/tx/transactors/check/CheckCancel.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp index a06191f321f..7061122b68a 100644 --- a/src/libxrpl/tx/transactors/did/DIDDelete.cpp +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp index 901a5801715..0a33a23ab80 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index 1714847118e..5bbdd4dc546 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 810ba403cf9..7dd82a118f9 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index 97761458fc2..5bde1ef8668 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include // IWYU pragma: keep diff --git a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index ddb848a1181..6c23295eb58 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index 7d70cf507a3..bc040c8cc20 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index fb2ca35ef20..f7240a17249 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index b392cf745c0..8976add578a 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include From 77ebc2921e871b953b0eaecea37c06ecc81f19fe Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 May 2026 10:55:47 +0900 Subject: [PATCH 230/249] clang-format --- src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index 4bd5d3b2d52..3a377a7f6d2 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -104,7 +104,7 @@ CredentialAccept::doApply() auto const credType(ctx_.tx[sfCredentialType]); Keylet const credentialKey = keylet::credential(account_, issuer, credType); - auto const sleCred = view().peek(credentialKey); // Checked in preclaim() + auto const sleCred = view().peek(credentialKey); // Checked in preclaim() if (checkExpired(sleCred, view().header().parentCloseTime)) { From 405b3148422f0d3acf5cfa8b833ebb9620b59b22 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 May 2026 12:47:45 +0900 Subject: [PATCH 231/249] fix clang-tidy error --- src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp | 1 + src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp | 2 ++ src/test/app/Sponsor_test.cpp | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index ebda6f0b8ee..ee479a20fc9 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -287,6 +287,7 @@ SponsorshipSet::doApply() return tecDIR_FULL; // LCOV_EXCL_LINE (*newSle)[sfSponseeNode] = *sponseePage; + // NOLINTNEXTLINE(readability-suspicious-call-argument) adjustOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, ctx_.journal); addSponsorToLedgerEntry(newSle, reserveSponsorAccSle); diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 92b72e1645c..bd971bb95e3 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -292,6 +292,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } // check new sponsor have sufficient balance + // NOLINTNEXTLINE(readability-suspicious-call-argument) if (auto const ter = checkInsufficientReserve( ctx.view, ctx.tx, @@ -343,6 +344,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) // In the case of removing an account sponsor, accSle should have no sfSponsor set // (AccountReserve = 0). However, by setting accountCountDelta = 1 here, we are able to // calculate the actual required Account Reserve. + // NOLINTNEXTLINE(readability-suspicious-call-argument) if (auto const ter = checkInsufficientReserve( ctx.view, ctx.tx, diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index cccbb71ef21..a0af9d17578 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2535,7 +2535,8 @@ class Sponsor_test : public beast::unit_test::suite } } - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == maxDeletableAMMTrustLines * 2 + 10); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == ((maxDeletableAMMTrustLines * 2) + 10)); // The trustlines are partially deleted. amm.withdrawAll(gw); From b76652bdf47f242f662d6c6182afeaa11284ecb3 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Fri, 1 May 2026 13:11:54 -0400 Subject: [PATCH 232/249] XRPL_ASSERT -> Throw --- .../ledger/helpers/AccountRootHelpers.cpp | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index ac625cde1d7..d6ddbda118c 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -99,10 +99,13 @@ ownerCountHlp( std::uint32_t const sponsoredCount = sle->at(sfSponsoredOwnerCount); std::uint32_t const sponsoringCount = sle->at(sfSponsoringOwnerCount); - XRPL_ASSERT( - hookedCount >= sponsoredCount, - "xrpl::ownerCountHlp : OwnerCount must be greater than or equal to " - "SponsoredOwnerCount"); + + if (hookedCount < sponsoredCount) + { + Throw( + "xrpl::ownerCountHlp : OwnerCount must be greater than or equal to " + "SponsoredOwnerCount"); + } std::int64_t deltaCount = static_cast(adjustment) - sponsoredCount + sponsoringCount; @@ -223,7 +226,7 @@ transferRate(ReadView const& view, AccountID const& issuer) } static void -adjustSponsorOwnerCountHlp( +adjustOwnerCountHlp( ApplyView& view, SLE::ref sle, SF_UINT32 const& sfield, @@ -248,37 +251,39 @@ adjustOwnerCount( std::int32_t adjustment, beast::Journal j) { - XRPL_ASSERT(accountSle, "xrpl::adjustOwnerCount : valid account sle"); + if (!accountSle) + Throw("xrpl::adjustOwnerCount : valid account sle"); + auto const sleType = accountSle->getType(); - [[maybe_unused]] bool const validType = sponsorSle - ? sleType == ltACCOUNT_ROOT - : sleType == ltLOAN_BROKER || sleType == ltACCOUNT_ROOT; - XRPL_ASSERT(validType, "xrpl::adjustOwnerCount : valid account sle type"); + bool const validType = sponsorSle ? sleType == ltACCOUNT_ROOT + : sleType == ltLOAN_BROKER || sleType == ltACCOUNT_ROOT; + if (!validType) + Throw("xrpl::adjustOwnerCount : valid account sle type"); + XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input"); + if (adjustment == 0) + return; auto const accountID = accountSle->getAccountID(sfAccount); if (sponsorSle) { - XRPL_ASSERT( - sponsorSle->getType() == ltACCOUNT_ROOT, - "xrpl::adjustOwnerCount : valid sponsor sle type"); + if (sponsorSle->getType() != ltACCOUNT_ROOT) + Throw("xrpl::adjustOwnerCount : valid sponsor sle type"); auto const sponsorID = sponsorSle->getAccountID(sfAccount); - adjustSponsorOwnerCountHlp( - view, accountSle, sfSponsoredOwnerCount, accountID, adjustment, j); - adjustSponsorOwnerCountHlp( - view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j); + adjustOwnerCountHlp(view, accountSle, sfSponsoredOwnerCount, accountID, adjustment, j); + adjustOwnerCountHlp(view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j); auto sponsorObjSle = view.peek(keylet::sponsor(sponsorID, accountID)); if (sponsorObjSle && adjustment > 0) { // update the pre-funded ReserveCount on Sponsorship ledger object // Reserve count moves opposite to adjustment: +adjustment => consume reserve (-), - adjustSponsorOwnerCountHlp( + adjustOwnerCountHlp( view, sponsorObjSle, sfReserveCount, sponsorID, -adjustment, j, false); } } - adjustSponsorOwnerCountHlp(view, accountSle, sfOwnerCount, accountID, adjustment, j); + adjustOwnerCountHlp(view, accountSle, sfOwnerCount, accountID, adjustment, j); } void @@ -289,9 +294,11 @@ adjustOwnerCountObj( std::int32_t amount, beast::Journal j) { - XRPL_ASSERT(objectSle, "xrpl::adjustOwnerCount : valid object sle"); - XRPL_ASSERT( - objectSle->getType() != ltACCOUNT_ROOT, "xrpl::adjustOwnerCount : valid object sle type"); + if (!objectSle) + Throw("xrpl::adjustOwnerCount : valid object sle"); + if (objectSle->getType() == ltACCOUNT_ROOT) + Throw("xrpl::adjustOwnerCount : valid object sle type"); + SLE::ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle); adjustOwnerCount(view, accountSle, sponsorSle, amount, j); } @@ -304,7 +311,10 @@ accountReserve( std::int32_t ownerCountAdj, std::int32_t reserveCountAdj) { - XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::accountReserve : valid sle type"); + if (!sle) + Throw("xrpl::accountReserve : valid sle"); + if (sle->getType() != ltACCOUNT_ROOT) + Throw("xrpl::accountReserve : valid sle type"); std::uint32_t const ownerCount = ownerCountHlp(view, sle, ownerCountAdj, true, j); std::uint32_t const reserveCount = reserveCountHlp(sle, reserveCountAdj, j); From 9d4c4968ad7d5d4b2af684b30c7413b48512b58a Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 1 May 2026 13:08:25 +0900 Subject: [PATCH 233/249] fix tidy error --- src/libxrpl/ledger/helpers/AccountRootHelpers.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 66a6d02fe8b..ac625cde1d7 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -250,8 +250,9 @@ adjustOwnerCount( { XRPL_ASSERT(accountSle, "xrpl::adjustOwnerCount : valid account sle"); auto const sleType = accountSle->getType(); - bool const validType = sponsorSle ? sleType == ltACCOUNT_ROOT - : sleType == ltLOAN_BROKER || sleType == ltACCOUNT_ROOT; + [[maybe_unused]] bool const validType = sponsorSle + ? sleType == ltACCOUNT_ROOT + : sleType == ltLOAN_BROKER || sleType == ltACCOUNT_ROOT; XRPL_ASSERT(validType, "xrpl::adjustOwnerCount : valid account sle type"); XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input"); From 960aaec91e83d980d75999e4089b3a494d513741 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Tue, 5 May 2026 17:32:26 -0400 Subject: [PATCH 234/249] merge fixes --- .../xrpl/protocol/detail/transactions.macro | 22 +- .../tx/transactors/sponsor/SponsorshipSet.h | 2 +- .../transactors/sponsor/SponsorshipTransfer.h | 2 +- src/libxrpl/protocol/InnerObjectFormats.cpp | 8 +- src/libxrpl/tx/Transactor.cpp | 4 +- .../PermissionedDomainInvariant.cpp | 2 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 10 +- src/test/app/AMMMPT_test.cpp | 2 +- src/test/app/AccountSet_test.cpp | 16 +- src/test/app/DepositAuth_test.cpp | 62 +- src/test/app/FlowMPT_test.cpp | 42 +- src/test/app/Invariants_test.cpp | 63 +- src/test/app/Loan_test.cpp | 8 +- src/test/app/MPToken_test.cpp | 4 +- src/test/app/NFToken_test.cpp | 16 +- src/test/app/OfferMPT_test.cpp | 16 +- src/test/app/Offer_test.cpp | 12 +- src/test/app/Oracle_test.cpp | 1 - src/test/app/Sponsor_test.cpp | 1934 ++++++++--------- src/test/jtx/impl/owners.cpp | 6 +- src/test/jtx/impl/sponsor.cpp | 48 +- src/test/jtx/owners.h | 12 +- src/test/jtx/sponsor.h | 28 +- src/test/rpc/AccountObjects_test.cpp | 32 +- src/test/rpc/AccountTx_test.cpp | 28 +- src/test/rpc/Simulate_test.cpp | 10 +- .../transactions/SponsorshipSetTests.cpp | 52 +- .../transactions/SponsorshipTransferTests.cpp | 46 +- .../rpc/handlers/account/AccountObjects.cpp | 2 +- 29 files changed, 1226 insertions(+), 1264 deletions(-) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index ee4c162dd91..62108aa51a7 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -1081,12 +1081,12 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay, # include #endif TRANSACTION(ttSPONSORSHIP_TRANSFER, 85, SponsorshipTransfer, - Delegation::delegable, + Delegation::Delegable, featureSponsor, - noPriv, + NoPriv, ({ - {sfObjectID, soeOPTIONAL}, - {sfSponsee, soeOPTIONAL}, + {sfObjectID, SoeOptional}, + {sfSponsee, SoeOptional}, })) /** This transaction create sponsorship object */ @@ -1094,15 +1094,15 @@ TRANSACTION(ttSPONSORSHIP_TRANSFER, 85, SponsorshipTransfer, # include #endif TRANSACTION(ttSPONSORSHIP_SET, 86, SponsorshipSet, - Delegation::delegable, + Delegation::Delegable, featureSponsor, - noPriv, + NoPriv, ({ - {sfCounterpartySponsor, soeOPTIONAL}, - {sfSponsee, soeOPTIONAL}, - {sfFeeAmount, soeOPTIONAL}, - {sfMaxFee, soeOPTIONAL}, - {sfReserveCount, soeOPTIONAL}, + {sfCounterpartySponsor, SoeOptional}, + {sfSponsee, SoeOptional}, + {sfFeeAmount, SoeOptional}, + {sfMaxFee, SoeOptional}, + {sfReserveCount, SoeOptional}, })) /** This system-generated transaction type is used to update the status of the various amendments. diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h index f1e7ac84f2d..5ca90f7963e 100644 --- a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h @@ -7,7 +7,7 @@ namespace xrpl { class SponsorshipSet : public Transactor { public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + static constexpr ConsequencesFactoryType kCONSEQUENCES_FACTORY{Normal}; explicit SponsorshipSet(ApplyContext& ctx) : Transactor(ctx) { diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h b/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h index f23acf69288..78888f022df 100644 --- a/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h @@ -7,7 +7,7 @@ namespace xrpl { class SponsorshipTransfer : public Transactor { public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + static constexpr ConsequencesFactoryType kCONSEQUENCES_FACTORY{Normal}; explicit SponsorshipTransfer(ApplyContext& ctx) : Transactor(ctx) { diff --git a/src/libxrpl/protocol/InnerObjectFormats.cpp b/src/libxrpl/protocol/InnerObjectFormats.cpp index daf664fb13e..6fe3977b86d 100644 --- a/src/libxrpl/protocol/InnerObjectFormats.cpp +++ b/src/libxrpl/protocol/InnerObjectFormats.cpp @@ -161,12 +161,12 @@ InnerObjectFormats::InnerObjectFormats() {sfSigners, SoeOptional}, }); - add(sfSponsorSignature.jsonName.c_str(), + add(sfSponsorSignature.jsonName.cStr(), sfSponsorSignature.getCode(), { - {sfSigningPubKey, soeOPTIONAL}, - {sfTxnSignature, soeOPTIONAL}, - {sfSigners, soeOPTIONAL}, + {sfSigningPubKey, SoeOptional}, + {sfTxnSignature, SoeOptional}, + {sfSigners, SoeOptional}, }); } diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 917a423cdcb..e72de61407b 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -501,7 +501,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) return terNO_ACCOUNT; } - XRPAmount maxSpendable = beast::zero; + XRPAmount maxSpendable = beast::kZERO; if (payer.type == FeePayerType::SponsorPreFunded) { @@ -1272,7 +1272,7 @@ Transactor::reset(XRPAmount fee) // then the ledger is corrupted. Rather than make things worse we // reject the transaction. auto const feeAmountAfter = balance - fee; - if (feeAmountAfter == beast::zero && payer.balanceField == sfFeeAmount) + if (feeAmountAfter == beast::kZERO && payer.balanceField == sfFeeAmount) { // Because ltSponsorship.sfFeeAmount is soeOptional payerSle->makeFieldAbsent(payer.balanceField); diff --git a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp index 71154b403e4..b037ce94c83 100644 --- a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp @@ -158,7 +158,7 @@ ValidPermissionedDomain::finalize( if (sleStatus_.empty()) return true; - if (sleStatus_[0].isDelete_) + if (sleStatus_[0].isDelete) { JLOG(j.fatal()) << "Invariant failed: domain object " "deleted by SponsorshipTransfer"; diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index ee479a20fc9..32a06b8e9ad 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -58,11 +58,11 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if ((flags & tfDeleteObject) != 0u) { // can not combine with any modification flags when deleting - constexpr std::uint32_t modifyFlags = tfSponsorshipSetRequireSignForFee | + constexpr std::uint32_t kMODIFY_FLAGS = tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForFee | tfSponsorshipClearRequireSignForReserve; - if ((flags & modifyFlags) != 0u) + if ((flags & kMODIFY_FLAGS) != 0u) return temINVALID_FLAG; // can not include these fields when deleting @@ -298,14 +298,14 @@ SponsorshipSet::doApply() // Update if (feeAmount) { - auto const currentFeeAmount = (*sponsorObjSle)[~sfFeeAmount].value_or(XRPAmount(0)); + auto const currentFeeAmount = (*sponsorObjSle)[~sfFeeAmount].valueOr(XRPAmount(0)); auto feeAmountDelta = XRPAmount(*feeAmount - currentFeeAmount); - if (feeAmountDelta > beast::zero && feeAmountDelta > (*sponsorAccSle)[sfBalance]) + if (feeAmountDelta > beast::kZERO && feeAmountDelta > (*sponsorAccSle)[sfBalance]) return tecUNFUNDED; // transfer feeAmount to ledger entry - if (feeAmountDelta != beast::zero) + if (feeAmountDelta != beast::kZERO) { (*sponsorAccSle)[sfBalance] -= feeAmountDelta; diff --git a/src/test/app/AMMMPT_test.cpp b/src/test/app/AMMMPT_test.cpp index 47f08386aac..b97c023c159 100644 --- a/src/test/app/AMMMPT_test.cpp +++ b/src/test/app/AMMMPT_test.cpp @@ -7042,7 +7042,7 @@ struct AMMMPT_test : public jtx::AMMTest } // This test validates both invariant changes work together for - // the specific case of MPT/MPT pools with > maxDeletableAMMTrustLines. + // the specific case of MPT/MPT pools with > kMAX_DELETABLE_AMM_TRUST_LINES. { Env env( *this, diff --git a/src/test/app/AccountSet_test.cpp b/src/test/app/AccountSet_test.cpp index b445eb02721..63ea0c561a5 100644 --- a/src/test/app/AccountSet_test.cpp +++ b/src/test/app/AccountSet_test.cpp @@ -444,20 +444,20 @@ class AccountSet_test : public beast::unit_test::Suite env.close(); env(did::set(gw), - did::uri("uri"), - sponsor::as(alice, spfSponsorReserve), - sig(sfSponsorSignature, alice)); + did::Uri("uri"), + sponsor::As(alice, spfSponsorReserve), + Sig(sfSponsorSignature, alice)); env.close(); env(did::set(alice), - did::uri("uri"), - sponsor::as(gw, spfSponsorReserve), - sig(sfSponsorSignature, gw)); + did::Uri("uri"), + sponsor::As(gw, spfSponsorReserve), + Sig(sfSponsorSignature, gw)); env.close(); env(sponsor::transfer(alice, tfSponsorshipCreate), - sponsor::as(gw, spfSponsorReserve), - sig(sfSponsorSignature, gw)); + sponsor::As(gw, spfSponsorReserve), + Sig(sfSponsorSignature, gw)); env.close(); } diff --git a/src/test/app/DepositAuth_test.cpp b/src/test/app/DepositAuth_test.cpp index 432b59986af..dcb2cfafaad 100644 --- a/src/test/app/DepositAuth_test.cpp +++ b/src/test/app/DepositAuth_test.cpp @@ -615,7 +615,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite TER const expectTer(!supportsCredentials ? TER(temDISABLED) : TER(tesSUCCESS)); - env(deposit::authCredentials(becky, {{carol, credType}}), Ter(expectTer)); + env(deposit::authCredentials(becky, {{.issuer=carol, .credType=credType}}), Ter(expectTer)); env.close(); // gw accept credentials @@ -745,7 +745,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite env.close(); // Setup DepositPreauth object failed - amendent is not supported - env(deposit::authCredentials(bob, {{issuer, credType}}), Ter(temDISABLED)); + env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}), Ter(temDISABLED)); env.close(); // But can create old DepositPreauth @@ -783,10 +783,10 @@ struct DepositPreauth_test : public beast::unit_test::Suite // Bob will accept payments from accounts with credentials signed // by 'issuer' - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}})); env.close(); - auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}}); + auto const jDP = ledgerEntryDepositPreauth(env, bob, {{.issuer=issuer, .credType=credType}}); BEAST_EXPECT( jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) && jDP[jss::result].isMember(jss::node) && @@ -859,11 +859,11 @@ struct DepositPreauth_test : public beast::unit_test::Suite } // Bob setup DepositPreauth object, duplicates is not allowed - env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType}}), + env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}, {.issuer=issuer, .credType=credType}}), Ter(temMALFORMED)); // Bob setup DepositPreauth object - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}})); env.close(); { @@ -929,35 +929,35 @@ struct DepositPreauth_test : public beast::unit_test::Suite { // both included [AuthorizeCredentials UnauthorizeCredentials] - auto jv = deposit::authCredentials(bob, {{issuer, credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}); jv[sfUnauthorizeCredentials.jsonName] = json::ArrayValue; env(jv, Ter(temMALFORMED)); } { // both included [Unauthorize, AuthorizeCredentials] - auto jv = deposit::authCredentials(bob, {{issuer, credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}); jv[sfUnauthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } { // both included [Authorize, AuthorizeCredentials] - auto jv = deposit::authCredentials(bob, {{issuer, credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}); jv[sfAuthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } { // both included [Unauthorize, UnauthorizeCredentials] - auto jv = deposit::unauthCredentials(bob, {{issuer, credType}}); + auto jv = deposit::unauthCredentials(bob, {{.issuer=issuer, .credType=credType}}); jv[sfUnauthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } { // both included [Authorize, UnauthorizeCredentials] - auto jv = deposit::unauthCredentials(bob, {{issuer, credType}}); + auto jv = deposit::unauthCredentials(bob, {{.issuer=issuer, .credType=credType}}); jv[sfAuthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } @@ -984,7 +984,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite { // empty credential type - auto jv = deposit::authCredentials(bob, {{issuer, {}}}); + auto jv = deposit::authCredentials(bob, {{.issuer=issuer, .credType={}}}); env(jv, Ter(temMALFORMED)); } @@ -994,14 +994,14 @@ struct DepositPreauth_test : public beast::unit_test::Suite i("i"); auto const& z = credType; auto jv = deposit::authCredentials( - bob, {{a, z}, {b, z}, {c, z}, {d, z}, {e, z}, {f, z}, {g, z}, {h, z}, {i, z}}); + bob, {{.issuer=a, .credType=z}, {.issuer=b, .credType=z}, {.issuer=c, .credType=z}, {.issuer=d, .credType=z}, {.issuer=e, .credType=z}, {.issuer=f, .credType=z}, {.issuer=g, .credType=z}, {.issuer=h, .credType=z}, {.issuer=i, .credType=z}}); env(jv, Ter(temARRAY_TOO_LARGE)); } { // Can't create with non-existing issuer Account const rick{"rick"}; - auto jv = deposit::authCredentials(bob, {{rick, credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer=rick, .credType=credType}}); env(jv, Ter(tecNO_ISSUER)); env.close(); } @@ -1011,21 +1011,21 @@ struct DepositPreauth_test : public beast::unit_test::Suite Account const john{"john"}; env.fund(baseAccountReserve(*env.current(), 0), john); env.close(); - auto jv = deposit::authCredentials(john, {{issuer, credType}}); + auto jv = deposit::authCredentials(john, {{.issuer=issuer, .credType=credType}}); env(jv, Ter(tecINSUFFICIENT_RESERVE)); } { // NO deposit object exists - env(deposit::unauthCredentials(bob, {{issuer, credType}}), Ter(tecNO_ENTRY)); + env(deposit::unauthCredentials(bob, {{.issuer=issuer, .credType=credType}}), Ter(tecNO_ENTRY)); } // Create DepositPreauth object { - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}})); env.close(); - auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}}); + auto const jDP = ledgerEntryDepositPreauth(env, bob, {{.issuer=issuer, .credType=credType}}); BEAST_EXPECT( jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) && @@ -1046,14 +1046,14 @@ struct DepositPreauth_test : public beast::unit_test::Suite } // can't create duplicate - env(deposit::authCredentials(bob, {{issuer, credType}}), Ter(tecDUPLICATE)); + env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}), Ter(tecDUPLICATE)); } // Delete DepositPreauth object { - env(deposit::unauthCredentials(bob, {{issuer, credType}})); + env(deposit::unauthCredentials(bob, {{.issuer=issuer, .credType=credType}})); env.close(); - auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}}); + auto const jDP = ledgerEntryDepositPreauth(env, bob, {{.issuer=issuer, .credType=credType}}); BEAST_EXPECT( jDP.isObject() && jDP.isMember(jss::result) && jDP[jss::result].isMember(jss::error) && @@ -1120,7 +1120,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite env(fset(bob, asfDepositAuth)); env.close(); // Bob setup DepositPreauth object - env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType2}})); + env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}, {.issuer=issuer, .credType=credType2}})); env.close(); { @@ -1229,7 +1229,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite env(fset(bob, asfDepositAuth)); env.close(); // Bob setup DepositPreauth object - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}})); env.close(); auto const seq = env.seq(alice); @@ -1287,14 +1287,14 @@ struct DepositPreauth_test : public beast::unit_test::Suite env.fund(XRP(5000), stock, alice, bob); std::vector credentials = { - {"a", "a"}, - {"b", "b"}, - {"c", "c"}, - {"d", "d"}, - {"e", "e"}, - {"f", "f"}, - {"g", "g"}, - {"h", "h"}}; + {.issuer="a", .credType="a"}, + {.issuer="b", .credType="b"}, + {.issuer="c", .credType="c"}, + {.issuer="d", .credType="d"}, + {.issuer="e", .credType="e"}, + {.issuer="f", .credType="f"}, + {.issuer="g", .credType="g"}, + {.issuer="h", .credType="h"}}; for (auto const& c : credentials) env.fund(XRP(5000), c.issuer); diff --git a/src/test/app/FlowMPT_test.cpp b/src/test/app/FlowMPT_test.cpp index d5f0e8fc46f..7a6d28618ed 100644 --- a/src/test/app/FlowMPT_test.cpp +++ b/src/test/app/FlowMPT_test.cpp @@ -1794,7 +1794,7 @@ struct FlowMPT_test : public beast::unit_test::Suite // but OutstandingAmount is 300USD because gw's sell offer is balanced out by // gw's buy offer. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 400, 400, 400, 400, 300, 100, 100, 100, 1100, 0, false}, + { .maxAmt=400, .sendMax=400, .dstTrustLimit=400, .dstExpectEUR=400, .outstandingUSD=300, .expEdBuyUSD=100, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: alice, carol, bob, gw are consumed. // Buy USD: john, gw, dan, ed (partially) are consumed. // gw's sell USD is partially consumed because there is available balance (50USD). @@ -1803,32 +1803,32 @@ struct FlowMPT_test : public beast::unit_test::Suite // gw's offer is removed from the order book because it's partially consumed and // the remaining offer is unfunded. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 350, 400, 400, 350, 250, 50, 100, 100, 1050, 0, false}, + { .maxAmt=350, .sendMax=400, .dstTrustLimit=400, .dstExpectEUR=350, .outstandingUSD=250, .expEdBuyUSD=50, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: alice, carol, bob are consumed; gw's is unfunded // since OutstandingAmount is initially at MaximumAmount. // Buy USD: john, gw, dan are consumed; ed's remains on the order // book since 300USD is the sell limit. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 400, 400, 300, 200, 0, 100, 100, 1000, 0, false}, + { .maxAmt=300, .sendMax=400, .dstTrustLimit=400, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. bill's trustline limit sets the output to 300USD. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 400, 300, 300, 200, 0, 100, 100, 1000, 0, false}, + { .maxAmt=300, .sendMax=400, .dstTrustLimit=300, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: alice, carol, bob are consumed; gw's removed from // the order book since it's unfunded. // Buy USD: john, gw, dan are consumed; ed's remains on the order // book since 300USD is the limit. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 400, 300, 300, 200, 0, 100, 100, 1000, 0, true}, + { .maxAmt=300, .sendMax=400, .dstTrustLimit=300, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: alice, carol are consumed; gw's removed from // the order book in rev pass since it's unfunded; bob's // remains on the order book. // Buy USD: john, gw; ed's, dan's remains on the order // book since 300USD is the limit. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 200, 300, 200, 200, 0, 0, 0, 1000, 0, false}, + { .maxAmt=300, .sendMax=200, .dstTrustLimit=300, .dstExpectEUR=200, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=0, .expBobSellUSD=0, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as three tests above since limited by buy 300USD (gw offer is unfunded) //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 380, 400, 300, 200, 0, 100, 100, 1000, 0, false}, + { .maxAmt=300, .sendMax=380, .dstTrustLimit=400, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, }; // clang-format on for (auto const& t : tests) @@ -1915,26 +1915,26 @@ struct FlowMPT_test : public beast::unit_test::Suite // Gw gets 300USD from alice; carol and bob buy 200USD, // therefore OutstandingAmount is 200. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 100, 1300, 200, 100, 900, 0, false}, + { .maxAmt=300, .sendMax=300, .gwOffer=100, .dstExpectXRP=1300, .outstandingUSD=200, .expBobBuyUSD=100, .expGwXRP=900, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. Gw offer location in the order book doesn't matter //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 100, 1300, 200, 100, 900, 0, true}, + { .maxAmt=300, .sendMax=300, .gwOffer=100, .dstExpectXRP=1300, .outstandingUSD=200, .expBobBuyUSD=100, .expGwXRP=900, .expOffersGw=0, .lastGwBuyUSD=true}, // Buy USD: carol, gw are consumed. bob's offer remains on the order book. // Gw gets 300USD from alice; carol buys 100USD, // therefore OutstandingAmount is 100. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 200, 1300, 100, 0, 800, 0, false}, + { .maxAmt=300, .sendMax=300, .gwOffer=200, .dstExpectXRP=1300, .outstandingUSD=100, .expBobBuyUSD=0, .expGwXRP=800, .expOffersGw=0, .lastGwBuyUSD=false}, // Buy USD: carol, bob are consumed; gw's is partially consumed (100/100) since it's last. // Gw gets 300USD from alice; carol and bob buy 200USD, // therefore OutstandingAmount is 200. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 200, 1300, 200, 100, 900, 1, true}, + { .maxAmt=300, .sendMax=300, .gwOffer=200, .dstExpectXRP=1300, .outstandingUSD=200, .expBobBuyUSD=100, .expGwXRP=900, .expOffersGw=1, .lastGwBuyUSD=true}, // Buy USD: carol, bob are consumed; gw's is partially consumed (50/50) since it's last // and sendMax limits the output. // Gw gets 250USD from alice; carol and bob buy 200USD, alice has 50USD left, // therefore OutstandingAmount is 200. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 250, 200, 1250, 250, 100, 950, 1, true}, + { .maxAmt=300, .sendMax=250, .gwOffer=200, .dstExpectXRP=1250, .outstandingUSD=250, .expBobBuyUSD=100, .expGwXRP=950, .expOffersGw=1, .lastGwBuyUSD=true}, }; // clang-format on for (auto const& t : tests) @@ -2027,10 +2027,10 @@ struct FlowMPT_test : public beast::unit_test::Suite // Sell USD: carol, gw, bob are consumed. // ed buys 300USD from carol, gw, bob therefore OutstandingAmount is 300. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 0, 100, 300, 300, 700, 100, 1100, 0, false}, + { .maxAmt=300, .sendMax=300, .initDst=0, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=700, .expBobSellUSD=100, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. Gw offer location in the order book doesn't matter //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 0, 100, 300, 300, 700, 100, 1100, 0, true}, + { .maxAmt=300, .sendMax=300, .initDst=0, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=700, .expBobSellUSD=100, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: carol, bob are consumed, gw is partially consumed. // ed buys 200 from carol and bob and 50 from gw because gw can only issue 50 // (300(max) - 200(carol+bob) - 50(ed)). ed buys 250 from carol, gw, bob and has 50 initially, @@ -2038,33 +2038,33 @@ struct FlowMPT_test : public beast::unit_test::Suite // gw's offer is removed from the order book because it's partially consumed and the remaining // offer is unfunded. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 50, 100, 300, 300, 750, 100, 1050, 0, false}, + { .maxAmt=300, .sendMax=300, .initDst=50, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. Gw offer location in the order book doesn't matter. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 50, 100, 300, 300, 750, 100, 1050, 0, true}, + { .maxAmt=300, .sendMax=300, .initDst=50, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=true}, // Same as above. Gw offer size doesn't matter. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 50, 200, 300, 300, 750, 100, 1050, 0, true}, + { .maxAmt=300, .sendMax=300, .initDst=50, .gwOffer=200, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: carol, gw are consumed, bob is partially consumed. // ed buys 200 from carol and gw and 50 form bob because of sendMax limit. bob keeps 50, // therefore OutstandingAmount is 300. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 0, 100, 250, 300, 750, 50, 1100, 0, false}, + { .maxAmt=300, .sendMax=250, .initDst=0, .gwOffer=100, .dstExpectUSD=250, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=50, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: carol, bob are consumed, gw is partially consumed because of sendMax limit. // ed buys 200 from carol and bob and 50 from gw. Therefore, OutstandingAmount is 250. // gw's offer remains on the order book because it's partially consumed and has more funds. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 0, 100, 250, 250, 750, 100, 1050, 1, true}, + { .maxAmt=300, .sendMax=250, .initDst=0, .gwOffer=100, .dstExpectUSD=250, .outstandingUSD=250, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=1, .lastGwBuyUSD=true}, // Sell USD: carol, bob are consumed, gw is partially consumed because of sendMax limit, also // there is only 50 available to issue. ed buys 200 from carol and bob and 50 from gw, plus // he has initially 50, therefore OutstandingAmount is 300. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 50, 100, 300, 300, 750, 100, 1050, 0, true}, + { .maxAmt=300, .sendMax=250, .initDst=50, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: carol, bob are consumed, gw is not consumed because there is not available funds // to issue. ed buys 200 from carol and bob and, plus he has initially 100, // therefore OutstandingAmount is 300. gw offer is removed because it's unfunded. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 100, 100, 300, 300, 800, 100, 1000, 0, true}, + { .maxAmt=300, .sendMax=250, .initDst=100, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=800, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=true}, }; // clang-format on for (auto const& t : tests) diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 6f58fbf104b..32160cb910d 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -344,12 +344,11 @@ class Invariants_test : public beast::unit_test::Suite doInvariantCheck( {{"account deletion left behind a sponsorship field"}}, - [&](Account const& A1, Account const& A2, ApplyContext& ac) { - auto const a1 = A1.id(); - auto const sleA1 = ac.view().peek(keylet::account(a1)); + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sleA1 = ac.view().peek(keylet::account(a1.id())); if (!sleA1) return false; - sleA1->at(sfBalance) = beast::zero; + sleA1->at(sfBalance) = beast::kZERO; sleA1->setFieldU32(sfSponsoredOwnerCount, 1); ac.view().erase(sleA1); @@ -361,12 +360,11 @@ class Invariants_test : public beast::unit_test::Suite doInvariantCheck( {{"account deletion left behind a sponsorship field"}}, - [&](Account const& A1, Account const& A2, ApplyContext& ac) { - auto const a1 = A1.id(); - auto const sleA1 = ac.view().peek(keylet::account(a1)); + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sleA1 = ac.view().peek(keylet::account(a1.id())); if (!sleA1) return false; - sleA1->at(sfBalance) = beast::zero; + sleA1->at(sfBalance) = beast::kZERO; sleA1->setFieldU32(sfSponsoringOwnerCount, 1); ac.view().erase(sleA1); @@ -378,12 +376,12 @@ class Invariants_test : public beast::unit_test::Suite doInvariantCheck( {{"account deletion left behind a sponsorship field"}}, - [&](Account const& A1, Account const& A2, ApplyContext& ac) { - auto const a1 = A1.id(); - auto const sleA1 = ac.view().peek(keylet::account(a1)); + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const a1Id = a1.id(); + auto const sleA1 = ac.view().peek(keylet::account(a1Id)); if (!sleA1) return false; - sleA1->at(sfBalance) = beast::zero; + sleA1->at(sfBalance) = beast::kZERO; sleA1->setFieldU32(sfSponsoringAccountCount, 1); ac.view().erase(sleA1); @@ -395,13 +393,12 @@ class Invariants_test : public beast::unit_test::Suite doInvariantCheck( {{"account deletion left behind a sponsorship field"}}, - [&](Account const& A1, Account const& A2, ApplyContext& ac) { - auto const a1 = A1.id(); - auto const sleA1 = ac.view().peek(keylet::account(a1)); + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sleA1 = ac.view().peek(keylet::account(a1.id())); if (!sleA1) return false; - sleA1->at(sfBalance) = beast::zero; - sleA1->setAccountID(sfSponsor, A2.id()); + sleA1->at(sfBalance) = beast::kZERO; + sleA1->setAccountID(sfSponsor, a2.id()); ac.view().erase(sleA1); @@ -1868,7 +1865,7 @@ class Invariants_test : public beast::unit_test::Suite for (std::size_t n = 0; n < numCreds; ++n) { auto credType = "cred_type" + std::to_string(n); - credentials.push_back({a2, credType}); + credentials.push_back({.issuer = a2, .credType = credType}); } std::uint32_t const seq = env.seq(a1); @@ -4455,13 +4452,13 @@ class Invariants_test : public beast::unit_test::Suite using namespace std::string_literals; testcase << "Sponsorship"; { - auto const expect_message = + auto const expectMessage = "SponsoredOwnerCount does not equal " "SponsoringOwnerCount delta."; doInvariantCheck( - {{expect_message}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - auto const sle = ac.view().peek(keylet::account(A1.id())); + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); if (!sle) return false; sle->setFieldU32(sfSponsoredOwnerCount, 1); @@ -4470,8 +4467,8 @@ class Invariants_test : public beast::unit_test::Suite }); doInvariantCheck( - {{expect_message}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - auto const sle = ac.view().peek(keylet::account(A1.id())); + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); if (!sle) return false; sle->setFieldU32(sfSponsoringOwnerCount, 1); @@ -4481,19 +4478,19 @@ class Invariants_test : public beast::unit_test::Suite } { - auto const expect_message = + auto const expectMessage = "OwnerCount must be greater than or equal to SponsoredOwnerCount."; doInvariantCheck( - {{expect_message}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - auto const sle = ac.view().peek(keylet::account(A1.id())); + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); if (!sle) return false; sle->setFieldU32(sfOwnerCount, 0); sle->setFieldU32(sfSponsoredOwnerCount, 1); ac.view().update(sle); - auto const sle2 = ac.view().peek(keylet::account(A2.id())); + auto const sle2 = ac.view().peek(keylet::account(a2.id())); if (!sle2) return false; sle2->setFieldU32(sfSponsoringOwnerCount, 1); @@ -4503,13 +4500,13 @@ class Invariants_test : public beast::unit_test::Suite } { - auto const expect_message = + auto const expectMessage = "Invariant failed: Net delta of SponsoringAccountCount does " "not match net delta of sfSponsor presence."; doInvariantCheck( - {{expect_message}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - auto const sle = ac.view().peek(keylet::account(A1.id())); + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); if (!sle) return false; sle->setFieldU32(sfSponsoringAccountCount, 1); @@ -4518,11 +4515,11 @@ class Invariants_test : public beast::unit_test::Suite }); doInvariantCheck( - {{expect_message}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - auto const sle = ac.view().peek(keylet::account(A1.id())); + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); if (!sle) return false; - sle->setAccountID(sfSponsor, A2.id()); + sle->setAccountID(sfSponsor, a2.id()); ac.view().update(sle); return true; }); diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 461a1db8a59..c37349080c8 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -5189,7 +5189,7 @@ class Loan_test : public beast::unit_test::Suite auto const loanSequence = brokerStateBefore->at(sfLoanSequence); auto const keylet = keylet::loan(broker.brokerID, loanSequence); - auto const closeStartDate = (parentCloseTime() / 10 + 1) * 10; + auto const closeStartDate = ((parentCloseTime() / 10) + 1) * 10; auto const grace = 5'000; auto const interval = kMAX_TIME - closeStartDate - grace; auto const total = 1; @@ -5218,7 +5218,7 @@ class Loan_test : public beast::unit_test::Suite env(pay(issuer, borrower, iouAsset(Number{1'055'524'81, -2}))); // Start date when the ledger is closed will be larger - auto const closeStartDate = (parentCloseTime() / 10 + 1) * 10; + auto const closeStartDate = ((parentCloseTime() / 10) + 1) * 10; auto const grace = 5'000; auto const maxLoanTime = kMAX_TIME - closeStartDate - grace; auto const total = [&]() { @@ -6807,7 +6807,7 @@ class Loan_test : public beast::unit_test::Suite auto credType = "credential1"; - pdomain::Credentials const credentials1{{issuer, credType}}; + pdomain::Credentials const credentials1{{.issuer=issuer, .credType=credType}}; env(pdomain::setTx(issuer, credentials1)); env.close(); @@ -6910,7 +6910,7 @@ class Loan_test : public beast::unit_test::Suite auto credType = "credential1"; - pdomain::Credentials const credentials1{{issuer, credType}}; + pdomain::Credentials const credentials1{{.issuer=issuer, .credType=credType}}; env(pdomain::setTx(issuer, credentials1)); env.close(); diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index c217dd7274f..1cb2e9c49ab 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -1775,7 +1775,7 @@ class MPToken_test : public beast::unit_test::Suite env.close(); // Bob authorize credentials - env(deposit::authCredentials(bob, {{dpIssuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer = dpIssuer, .credType = credType}})); env.close(); // alice try to send 100 MPT to bob, not authorized @@ -2098,7 +2098,7 @@ class MPToken_test : public beast::unit_test::Suite jv[jss::Account] = alice.human(); jv[sfSponsee.fieldName] = carol.human(); jv[sfFeeAmount.fieldName] = mpt.getJson(JsonOptions::KNone); - test(jv, sfFeeAmount.fieldName); + test(jv, sfFeeAmount.fieldName.c_str()); } } BEAST_EXPECT(txWithAmounts.empty()); diff --git a/src/test/app/NFToken_test.cpp b/src/test/app/NFToken_test.cpp index bac3cd015fb..671167781de 100644 --- a/src/test/app/NFToken_test.cpp +++ b/src/test/app/NFToken_test.cpp @@ -446,20 +446,20 @@ class NFTokenBaseUtil_test : public beast::unit_test::Suite env.close(); env(did::set(alice), - did::uri("uri"), - sponsor::as(bob, spfSponsorReserve), - sig(sfSponsorSignature, bob)); + did::Uri("uri"), + sponsor::As(bob, spfSponsorReserve), + Sig(sfSponsorSignature, bob)); env.close(); env(did::set(bob), - did::uri("uri"), - sponsor::as(alice, spfSponsorReserve), - sig(sfSponsorSignature, alice)); + did::Uri("uri"), + sponsor::As(alice, spfSponsorReserve), + Sig(sfSponsorSignature, alice)); env.close(); env(sponsor::transfer(bob, tfSponsorshipCreate), - sponsor::as(alice, spfSponsorReserve), - sig(sfSponsorSignature, alice)); + sponsor::As(alice, spfSponsorReserve), + Sig(sfSponsorSignature, alice)); env.close(); } diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index 4e7db580461..66b5bdcccea 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -4600,14 +4600,14 @@ class OfferMPT_test : public beast::unit_test::Suite // IOU/IOU, XRP/IOU, IOU/XRP offers have TickSize logic unchanged // IOU/MPT, MPT/IOU have TickSize logic applied to adjust IOU only std::vector const tests = { - {getIOU, getIOU, 10, 30}, - {getIOU, getXRP, 10, 30'000'000}, - {getXRP, getIOU, 10'000'000, 30}, - {getMPT, getXRP, 10'000'000, 30'000'000}, - {getXRP, getMPT, 10'000'000, 30'000'000}, - {getIOU, getMPT, 10, 30'000'000}, - {getMPT, getIOU, 10'000'000, 30}, - {getMPT, getMPT, 10'000'000, 30'000'000}}; + {.toAsset1=getIOU, .toAsset2=getIOU, .val1=10, .val2=30}, + {.toAsset1=getIOU, .toAsset2=getXRP, .val1=10, .val2=30'000'000}, + {.toAsset1=getXRP, .toAsset2=getIOU, .val1=10'000'000, .val2=30}, + {.toAsset1=getMPT, .toAsset2=getXRP, .val1=10'000'000, .val2=30'000'000}, + {.toAsset1=getXRP, .toAsset2=getMPT, .val1=10'000'000, .val2=30'000'000}, + {.toAsset1=getIOU, .toAsset2=getMPT, .val1=10, .val2=30'000'000}, + {.toAsset1=getMPT, .toAsset2=getIOU, .val1=10'000'000, .val2=30}, + {.toAsset1=getMPT, .toAsset2=getMPT, .val1=10'000'000, .val2=30'000'000}}; for (TestInfo const& t : tests) { Env env{*this, features}; diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index 54846d7ed19..5629ca34308 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -3923,10 +3923,10 @@ class OfferBaseUtil_test : public beast::unit_test::Suite // clang-format off TestData const tests[]{ // btcStart --------------------- actor[0] --------------------- -------------------- actor[1] ------------------- - {.self=0, .leg0=0, .leg1=1, .btcStart=btc(20), .actors={{"ann", 0, drops(3900000'000000 - (4 * baseFee)), btc(20.0), usd(3000)}, {"abe", 0, drops(4100000'000000 - (3 * baseFee)), btc( 0), usd(750)}}}, // no BTC xfer fee - {.self=0, .leg0=1, .leg1=0, .btcStart=btc(20), .actors={{"bev", 0, drops(4100000'000000 - (4 * baseFee)), btc( 7.5), usd(2000)}, {"bob", 0, drops(3900000'000000 - (3 * baseFee)), btc(10), usd( 0)}}}, // no USD xfer fee - {.self=0, .leg0=0, .leg1=0, .btcStart=btc(20), .actors={{"cam", 0, drops(4000000'000000 - (5 * baseFee)), btc(20.0), usd(2000)} }}, // no xfer fee - {.self=0, .leg0=1, .leg1=0, .btcStart=btc( 5), .actors={{"deb", 1, drops(4040000'000000 - (4 * baseFee)), btc( 0.0), usd(2000)}, {"dan", 1, drops(3960000'000000 - (3 * baseFee)), btc( 4), usd( 0)}}}, // no USD xfer fee + {.self=0, .leg0=0, .leg1=1, .btcStart=btc(20), .actors={{.acct="ann", .offers=0, .xrp=drops(3900000'000000 - (4 * baseFee)), .btc=btc(20.0), .usd=usd(3000)}, {.acct="abe", .offers=0, .xrp=drops(4100000'000000 - (3 * baseFee)), .btc=btc( 0), .usd=usd(750)}}}, // no BTC xfer fee + {.self=0, .leg0=1, .leg1=0, .btcStart=btc(20), .actors={{.acct="bev", .offers=0, .xrp=drops(4100000'000000 - (4 * baseFee)), .btc=btc( 7.5), .usd=usd(2000)}, {.acct="bob", .offers=0, .xrp=drops(3900000'000000 - (3 * baseFee)), .btc=btc(10), .usd=usd( 0)}}}, // no USD xfer fee + {.self=0, .leg0=0, .leg1=0, .btcStart=btc(20), .actors={{.acct="cam", .offers=0, .xrp=drops(4000000'000000 - (5 * baseFee)), .btc=btc(20.0), .usd=usd(2000)} }}, // no xfer fee + {.self=0, .leg0=1, .leg1=0, .btcStart=btc( 5), .actors={{.acct="deb", .offers=1, .xrp=drops(4040000'000000 - (4 * baseFee)), .btc=btc( 0.0), .usd=usd(2000)}, {.acct="dan", .offers=1, .xrp=drops(3960000'000000 - (3 * baseFee)), .btc=btc( 4), .usd=usd( 0)}}}, // no USD xfer fee }; // clang-format on @@ -4071,8 +4071,8 @@ class OfferBaseUtil_test : public beast::unit_test::Suite // clang-format off TestData const tests[]{ // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] -------------------- - {.self=0, .leg0=0, .leg1=1, .btcStart=btc(5), .actors={{"gay", 1, drops(3950000'000000 - (4 * baseFee)), btc(5), usd(2500)}, {"gar", 1, drops(4050000'000000 - (3 * baseFee)), btc(0), usd(1375)}}}, // no BTC xfer fee - {.self=0, .leg0=0, .leg1=0, .btcStart=btc(5), .actors={{"hye", 2, drops(4000000'000000 - (5 * baseFee)), btc(5), usd(2000)} }} // no xfer fee + {.self=0, .leg0=0, .leg1=1, .btcStart=btc(5), .actors={{.acct="gay", .offers=1, .xrp=drops(3950000'000000 - (4 * baseFee)), .btc=btc(5), .usd=usd(2500)}, {.acct="gar", .offers=1, .xrp=drops(4050000'000000 - (3 * baseFee)), .btc=btc(0), .usd=usd(1375)}}}, // no BTC xfer fee + {.self=0, .leg0=0, .leg1=0, .btcStart=btc(5), .actors={{.acct="hye", .offers=2, .xrp=drops(4000000'000000 - (5 * baseFee)), .btc=btc(5), .usd=usd(2000)} }} // no xfer fee }; // clang-format on diff --git a/src/test/app/Oracle_test.cpp b/src/test/app/Oracle_test.cpp index d6655e59568..08fe75c3122 100644 --- a/src/test/app/Oracle_test.cpp +++ b/src/test/app/Oracle_test.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index a0af9d17578..2ddd953ba50 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -22,20 +22,15 @@ #include #include #include -#include #include #include -#include #include #include #include #include -#include #include #include -#include - #include #include #include @@ -44,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -53,7 +47,6 @@ #include #include #include -#include #include #include #include @@ -62,6 +55,13 @@ #include #include +#include "test/jtx/seq.h" +#include "test/jtx/txflags.h" +#include "xrpl/basics/Slice.h" +#include "xrpl/ledger/ApplyView.h" +#include "xrpl/protocol/Protocol.h" +#include "xrpld/core/Config.h" + #include #include #include @@ -99,19 +99,19 @@ adjustAccountXRPBalance(jtx::Env& env, jtx::Account const& account, STAmount con if (currentBalance > balanceTo) { env(pay(account, env.master, currentBalance - (balanceTo)), - fee(XRP(1)), - sponsor::as(env.master, spfSponsorFee), - sig(sfSponsorSignature, env.master)); + Fee(XRP(1)), + sponsor::As(env.master, spfSponsorFee), + Sig(sfSponsorSignature, env.master)); } else { - env(pay(env.master, account, balanceTo - currentBalance), fee(baseFee)); + env(pay(env.master, account, balanceTo - currentBalance), Fee(baseFee)); } env.close(); } -class Sponsor_test : public beast::unit_test::suite +class Sponsor_test : public beast::unit_test::Suite { public: void @@ -119,7 +119,7 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Disabled"); using namespace test::jtx; - Env env{*this, testable_amendments() - featureSponsor}; + Env env{*this, testableAmendments() - featureSponsor}; Account const alice("alice"); Account const sponsor("sponsor"); env.fund(XRP(10000), alice, sponsor); @@ -128,16 +128,16 @@ class Sponsor_test : public beast::unit_test::suite auto const jt = noop(alice); auto jt1 = jt; jt1[sfSponsor.jsonName] = sponsor.human(); - env(jt1, ter(temDISABLED)); - env(jt, sig(sfSponsorSignature, sponsor), ter(temDISABLED)); + env(jt1, Ter(temDISABLED)); + env(jt, Sig(sfSponsorSignature, sponsor), Ter(temDISABLED)); auto jt2 = jt; jt2[sfSponsorFlags.jsonName] = spfSponsorFee | spfSponsorReserve; - env(jt2, ter(temDISABLED)); + env(jt2, Ter(temDISABLED)); // check Sponsor transactions - env(sponsor::transfer(alice, 0), ter(temDISABLED)); - env(sponsor::set(sponsor, 0), ter(temDISABLED)); + env(sponsor::transfer(alice, 0), Ter(temDISABLED)); + env(sponsor::set(sponsor, 0), Ter(temDISABLED)); } void @@ -145,14 +145,14 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Invalid SponsorshipSet"); using namespace test::jtx; - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); Account const noFunded("noFunded"); Account const gw("gw"); - auto const USD = gw["USD"]; + auto const usd = gw["usd"]; env.fund(XRP(10000), alice, sponsor, gw); env.close(); @@ -163,21 +163,21 @@ class Sponsor_test : public beast::unit_test::suite // Invalid flags { env(sponsor::set(sponsor, ~tfSponsorshipSetMask - tfInnerBatchTxn), - sponsor::sponseeAcc(alice), - ter(temINVALID_FLAG)); + sponsor::SponseeAcc(alice), + Ter(temINVALID_FLAG)); env(sponsor::set( sponsor, tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee), - sponsor::sponseeAcc(alice), - ter(temINVALID_FLAG)); + sponsor::SponseeAcc(alice), + Ter(temINVALID_FLAG)); env(sponsor::set( sponsor, tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForReserve), - sponsor::sponseeAcc(alice), - ter(temINVALID_FLAG)); + sponsor::SponseeAcc(alice), + Ter(temINVALID_FLAG)); for (auto flag : {tfSponsorshipSetRequireSignForFee, @@ -186,110 +186,110 @@ class Sponsor_test : public beast::unit_test::suite tfSponsorshipClearRequireSignForReserve}) { env(sponsor::set(sponsor, tfDeleteObject | flag), - sponsor::sponseeAcc(alice), - ter(temINVALID_FLAG)); + sponsor::SponseeAcc(alice), + Ter(temINVALID_FLAG)); } } // invalid SponsorAccount / Sponsee // Account = Sponsor env(sponsor::set(alice, tfDeleteObject), - sponsor::counterpartySponsor(alice), - ter(temMALFORMED)); + sponsor::CounterpartySponsor(alice), + Ter(temMALFORMED)); // Account = Sponsee - env(sponsor::set(alice, tfDeleteObject), sponsor::sponseeAcc(alice), ter(temMALFORMED)); + env(sponsor::set(alice, tfDeleteObject), sponsor::SponseeAcc(alice), Ter(temMALFORMED)); // Both Sponsor and Sponsee are specified env(sponsor::set(alice, 0), - sponsor::counterpartySponsor(sponsor), - sponsor::sponseeAcc(alice), - ter(temMALFORMED)); + sponsor::CounterpartySponsor(sponsor), + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); // Invalid feeAmount - for (auto const& amt : {XRP(-1), USD(1)}) + for (auto const& amt : {XRP(-1), usd(1)}) { - env(sponsor::set_fee(sponsor, 0, amt), sponsor::sponseeAcc(alice), ter(temBAD_AMOUNT)); + env(sponsor::set_fee(sponsor, 0, amt), sponsor::SponseeAcc(alice), Ter(temBAD_AMOUNT)); } // Invalid MaxFee - for (auto const& amt : {XRP(-1), USD(1)}) + for (auto const& amt : {XRP(-1), usd(1)}) { env(sponsor::set_fee(sponsor, 0, XRP(1), amt), - sponsor::sponseeAcc(alice), - ter(temBAD_AMOUNT)); + sponsor::SponseeAcc(alice), + Ter(temBAD_AMOUNT)); } // Invalid Delete operation env(sponsor::set_reserve(sponsor, tfDeleteObject, 1), - sponsor::sponseeAcc(alice), - ter(temMALFORMED)); + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); env(sponsor::set_fee(sponsor, tfDeleteObject, XRP(1)), - sponsor::sponseeAcc(alice), - ter(temMALFORMED)); + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); env(sponsor::set_max_fee(sponsor, tfDeleteObject, XRP(1)), - sponsor::sponseeAcc(alice), - ter(temMALFORMED)); + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); // Invalid SponsorAccount with non-Delete operation env(sponsor::set_reserve(sponsor, 0, 100), - sponsor::counterpartySponsor(alice), - ter(temMALFORMED)); + sponsor::CounterpartySponsor(alice), + Ter(temMALFORMED)); env(sponsor::set_fee(sponsor, 0, XRP(1), XRP(1)), - sponsor::counterpartySponsor(alice), - ter(temMALFORMED)); + sponsor::CounterpartySponsor(alice), + Ter(temMALFORMED)); // // preclaim // // Invalid Sponsee - env(sponsor::set(sponsor, 0), sponsor::sponseeAcc(noFunded), ter(tecNO_DST)); + env(sponsor::set(sponsor, 0), sponsor::SponseeAcc(noFunded), Ter(tecNO_DST)); env.close(); // Invalid Sponsor env(sponsor::set(sponsor, tfDeleteObject), - sponsor::counterpartySponsor(noFunded), - ter(tecNO_DST)); + sponsor::CounterpartySponsor(noFunded), + Ter(tecNO_DST)); env.close(); // Invalid Delete operation (sponsorship not found) - env(sponsor::set(sponsor, tfDeleteObject), sponsor::sponseeAcc(alice), ter(tecNO_ENTRY)); + env(sponsor::set(sponsor, tfDeleteObject), sponsor::SponseeAcc(alice), Ter(tecNO_ENTRY)); env.close(); - // insufficient balance to sponsor fee + // insufficient balance to sponsor Fee adjustAccountXRPBalance(env, sponsor, env.current()->fees().reserve); - env(sponsor::set_fee(sponsor, 0, XRP(4)), sponsor::sponseeAcc(alice), ter(tecUNFUNDED)); + env(sponsor::set_fee(sponsor, 0, XRP(4)), sponsor::SponseeAcc(alice), Ter(tecUNFUNDED)); env.close(); // insufficent reserve to create sponsorship adjustAccountXRPBalance(env, sponsor, XRP(100) + XRP(1) + reserve(env, 1) - drops(1)); env(sponsor::set(sponsor, 0, 100, XRP(100)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tecUNFUNDED)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tecUNFUNDED)); env.close(); // FeeAmount + Fee > Balance /// Balance = 1000XRP, FeeAmount = 1001XRP adjustAccountXRPBalance(env, sponsor, XRP(1000)); env(sponsor::set_fee(sponsor, 0, XRP(1001)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tecUNFUNDED)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tecUNFUNDED)); env.close(); /// Balance = 1000XRP, FeeAmount = 999XRP, Fee=2XRP adjustAccountXRPBalance(env, sponsor, XRP(1000)); env(sponsor::set_fee(sponsor, 0, XRP(999)), - sponsor::sponseeAcc(alice), - fee(XRP(2)), - ter(tecUNFUNDED)); + sponsor::SponseeAcc(alice), + Fee(XRP(2)), + Ter(tecUNFUNDED)); env.close(); // create sponsor to use above tests - // need feeAmount(1000) + fee(1) + reserve(~250) = ~1251 + // need feeAmount(1000) + Fee(1) + reserve(~250) = ~1251 adjustAccountXRPBalance(env, sponsor, XRP(1000) + XRP(1) + reserve(env, 1)); env(sponsor::set(sponsor, 0, 100, XRP(1000)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); env.close(); // delta-based balance check @@ -298,27 +298,27 @@ class Sponsor_test : public beast::unit_test::suite // Decreasing feeAmount should succeed (refund, negative delta) adjustAccountXRPBalance(env, sponsor, XRP(500)); env(sponsor::set_fee(sponsor, 0, XRP(800)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); env.close(); // balance was 500, delta = 800-1000 = -200 (refund), balance = 500+200-1 = 699 // Increasing feeAmount within delta budget should succeed adjustAccountXRPBalance(env, sponsor, XRP(500)); env(sponsor::set_fee(sponsor, 0, XRP(850)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); env.close(); // balance was 500, delta = 850-800 = 50, balance = 500-50-1 = 449 // Increasing feeAmount where delta exceeds balance should fail adjustAccountXRPBalance(env, sponsor, XRP(310)); env(sponsor::set_fee(sponsor, 0, XRP(1200)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tecUNFUNDED)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tecUNFUNDED)); env.close(); // Increasing feeAmount to reach insufficient reserve @@ -326,9 +326,9 @@ class Sponsor_test : public beast::unit_test::suite env.le(keylet::sponsor(sponsor.id(), alice.id()))->getFieldAmount(sfFeeAmount).xrp(); adjustAccountXRPBalance(env, sponsor, XRP(310)); env(sponsor::set_fee(sponsor, 0, currentFeeAmount + XRP(309)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tecUNFUNDED)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tecUNFUNDED)); env.close(); } @@ -337,7 +337,7 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Pseudo account sponsorship"); using namespace test::jtx; - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const gw("gw"); @@ -361,15 +361,15 @@ class Sponsor_test : public beast::unit_test::suite // Sponsee is a pseudo account -> tecNO_PERMISSION env(sponsor::set(sp, 0, 100, XRP(100)), - sponsor::sponseeAcc(pseudoAcc), - ter(tecNO_PERMISSION)); + sponsor::SponseeAcc(pseudoAcc), + Ter(tecNO_PERMISSION)); env.close(); // Sponsor is a pseudo account -> tecNO_PERMISSION // (submitted by bob with counterpartySponsor pointing to pseudo account) env(sponsor::set(bob, tfDeleteObject), - sponsor::counterpartySponsor(pseudoAcc), - ter(tecNO_PERMISSION)); + sponsor::CounterpartySponsor(pseudoAcc), + Ter(tecNO_PERMISSION)); env.close(); } @@ -378,7 +378,7 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Single signing"); using namespace test::jtx; - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const sponsor("sponsor"); Account const invalid("invalid"); @@ -391,25 +391,25 @@ class Sponsor_test : public beast::unit_test::suite tx[sfSponsor.jsonName] = sponsor.human(); tx[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = strHex(sponsor.pk().slice()); - env(tx, fee(XRP(1)), sponsor::as(sponsor, spfSponsorReserve), ter(telENV_RPC_FAILED)); + env(tx, Fee(XRP(1)), sponsor::As(sponsor, spfSponsorReserve), Ter(telENV_RPC_FAILED)); // Invalid signature tx[sfSponsorSignature.jsonName][sfTxnSignature.jsonName] = "DEADBEEF"; - env(tx, fee(XRP(1)), sponsor::as(sponsor, spfSponsorReserve), ter(telENV_RPC_FAILED)); + env(tx, Fee(XRP(1)), sponsor::As(sponsor, spfSponsorReserve), Ter(telENV_RPC_FAILED)); // Signer account doesn't exist env(noop(alice), - fee(XRP(1)), - sponsor::as(invalid, spfSponsorReserve), - sig(sfSponsorSignature, invalid), - ter(terNO_ACCOUNT)); + Fee(XRP(1)), + sponsor::As(invalid, spfSponsorReserve), + Sig(sfSponsorSignature, invalid), + Ter(terNO_ACCOUNT)); // Success env(noop(alice), - fee(XRP(1)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tesSUCCESS)); + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); } void @@ -417,7 +417,7 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Multi signing"); using namespace test::jtx; - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const sponsor("sponsor"); Account const invalid("invalid"); @@ -437,20 +437,20 @@ class Sponsor_test : public beast::unit_test::suite signers1[sfAccount.jsonName] = signer1.human(); signers1[sfSigningPubKey.jsonName] = strHex(signer1.pk().slice()); signers1[sfTxnSignature.jsonName] = "DEADBEEF"; - env(tx, fee(XRP(1)), sponsor::as(sponsor, spfSponsorReserve), ter(telENV_RPC_FAILED)); + env(tx, Fee(XRP(1)), sponsor::As(sponsor, spfSponsorReserve), Ter(telENV_RPC_FAILED)); // Signer account doesn't exist env(noop(alice), - fee(XRP(1)), - sponsor::as(invalid, spfSponsorReserve), - msig(sfSponsorSignature, {signer1}), - ter(tefNOT_MULTI_SIGNING)); + Fee(XRP(1)), + sponsor::As(invalid, spfSponsorReserve), + Msig(sfSponsorSignature, {signer1}), + Ter(tefNOT_MULTI_SIGNING)); env(noop(alice), - fee(XRP(1)), - sponsor::as(sponsor, spfSponsorReserve), - msig(sfSponsorSignature, {signer1}), - ter(tesSUCCESS)); + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorReserve), + Msig(sfSponsorSignature, {signer1}), + Ter(tesSUCCESS)); env.close(); env(signers(sponsor, 2, {{signer1, 1}, {signer2, 1}})); @@ -459,16 +459,16 @@ class Sponsor_test : public beast::unit_test::suite // test calculateBaseFee for multisigned sponsor auto const baseFee = env.current()->fees().base; env(noop(alice), - fee(baseFee + 2 * baseFee - 1), - sponsor::as(sponsor, spfSponsorReserve), - msig(sfSponsorSignature, {signer1, signer2}), - ter(telINSUF_FEE_P)); + Fee(baseFee + 2 * baseFee - 1), + sponsor::As(sponsor, spfSponsorReserve), + Msig(sfSponsorSignature, {signer1, signer2}), + Ter(telINSUF_FEE_P)); env(noop(alice), - fee(baseFee + 2 * baseFee), - sponsor::as(sponsor, spfSponsorReserve), - msig(sfSponsorSignature, {signer1, signer2}), - ter(tesSUCCESS)); + Fee(baseFee + 2 * baseFee), + sponsor::As(sponsor, spfSponsorReserve), + Msig(sfSponsorSignature, {signer1, signer2}), + Ter(tesSUCCESS)); } void @@ -476,7 +476,7 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Invalid Sponsor Field"); using namespace test::jtx; - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const sponsor("sponsor"); Account const noFunded("noFunded"); @@ -484,36 +484,36 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // Invalid Sponsor Account (Account = Sponsor.Account) - env(noop(alice), sponsor::as(alice, spfSponsorFee), ter(temMALFORMED)); + env(noop(alice), sponsor::As(alice, spfSponsorFee), Ter(temMALFORMED)); // Invalid Sponsor Account // (SponsorSignature is specified but Sponsor.Account is not specified) - env(noop(alice), sig(sfSponsorSignature, sponsor), ter(temMALFORMED)); + env(noop(alice), Sig(sfSponsorSignature, sponsor), Ter(temMALFORMED)); // Invalid Sponsor Account (Sponsor.Account doesn't exist) - env(noop(alice), sponsor::as(noFunded, spfSponsorReserve), ter(terNO_SPONSORSHIP)); + env(noop(alice), sponsor::As(noFunded, spfSponsorReserve), Ter(terNO_SPONSORSHIP)); env(noop(alice), - sponsor::as(noFunded, spfSponsorReserve), - sig(sfSponsorSignature, noFunded), - ter(terNO_ACCOUNT)); + sponsor::As(noFunded, spfSponsorReserve), + Sig(sfSponsorSignature, noFunded), + Ter(terNO_ACCOUNT)); // Invalid Flags env(noop(alice), - sponsor::as(sponsor, (spfSponsorFee | spfSponsorReserve) + 1), - ter(temINVALID_FLAG)); + sponsor::As(sponsor, (spfSponsorFee | spfSponsorReserve) + 1), + Ter(temINVALID_FLAG)); // SponsorFlags=0 with valid sponsor (no sponsorship purpose) - env(noop(alice), sponsor::as(sponsor, 0), ter(temINVALID_FLAG)); + env(noop(alice), sponsor::As(sponsor, 0), Ter(temINVALID_FLAG)); // no SponsorFlag with valid sponsor auto tx = noop(alice); tx[sfSponsor.jsonName] = sponsor.human(); - env(tx, ter(temINVALID_FLAG)); + env(tx, Ter(temINVALID_FLAG)); // Invalid Flags without sponsor tx = noop(alice); tx[sfSponsorFlags.jsonName] = spfSponsorFee | spfSponsorReserve; - env(tx, ter(temINVALID_FLAG)); + env(tx, Ter(temINVALID_FLAG)); } void @@ -521,7 +521,7 @@ class Sponsor_test : public beast::unit_test::suite { testcase("Simple SponsorshipSet"); using namespace test::jtx; - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const sponsor("sponsor"); env.fund(XRP(10000), alice, sponsor); @@ -535,9 +535,9 @@ class Sponsor_test : public beast::unit_test::suite 100, XRP(100), XRP(1)), - fee(XRP(1)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); + Fee(XRP(1)), + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); env.close(); auto sle = env.le(keylet::sponsor(sponsor, alice)); @@ -551,9 +551,9 @@ class Sponsor_test : public beast::unit_test::suite // update sponsorship (decrement) env(sponsor::set(sponsor, 0, 50, XRP(50), XRP(0.5)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); env.close(); sle = env.le(keylet::sponsor(sponsor, alice)); @@ -565,9 +565,9 @@ class Sponsor_test : public beast::unit_test::suite // update sponsorship (increment) env(sponsor::set(sponsor, 0, 200, XRP(200), XRP(2)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); env.close(); sle = env.le(keylet::sponsor(sponsor, alice)); @@ -578,7 +578,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(sponsor) == XRP(10000) - sle->at(sfFeeAmount) - XRP(3)); // delete from sponsor - env(sponsor::del(sponsor), sponsor::sponseeAcc(alice), fee(XRP(1)), ter(tesSUCCESS)); + env(sponsor::del(sponsor), sponsor::SponseeAcc(alice), Fee(XRP(1)), Ter(tesSUCCESS)); env.close(); BEAST_EXPECT(env.balance(sponsor) == XRP(10000) - XRP(4)); @@ -589,19 +589,19 @@ class Sponsor_test : public beast::unit_test::suite 100, XRP(100), XRP(1)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); env.close(); // delete from sponsee - env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); env.close(); BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))); // create sponsorship with zero value env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), - sponsor::sponseeAcc(alice), - fee(XRP(1))); + sponsor::SponseeAcc(alice), + Fee(XRP(1))); env.close(); sle = env.le(keylet::sponsor(sponsor, alice)); @@ -615,8 +615,8 @@ class Sponsor_test : public beast::unit_test::suite // update sponsorship with non-zero value env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), - sponsor::sponseeAcc(alice), - fee(XRP(1))); + sponsor::SponseeAcc(alice), + Fee(XRP(1))); env.close(); sle = env.le(keylet::sponsor(sponsor, alice)); @@ -627,8 +627,8 @@ class Sponsor_test : public beast::unit_test::suite // update sponsorship with zero value env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), - sponsor::sponseeAcc(alice), - fee(XRP(1))); + sponsor::SponseeAcc(alice), + Fee(XRP(1))); env.close(); sle = env.le(keylet::sponsor(sponsor, alice)); @@ -641,49 +641,49 @@ class Sponsor_test : public beast::unit_test::suite { // Update Sponsorship (FeeAmount) // set empty FeeAmount - env(sponsor::set_reserve(sponsor, 0, 100), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::SponseeAcc(alice), Ter(tesSUCCESS)); env.close(); // add FeeAmount env(sponsor::set_fee(sponsor, 0, XRP(100)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); env.close(); - env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); env.close(); } { // Update Sponsorship (ReserveCount) // set empty ReserveCount env(sponsor::set_fee(sponsor, 0, XRP(100)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); env.close(); // add ReserveCount - env(sponsor::set_reserve(sponsor, 0, 100), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::SponseeAcc(alice), Ter(tesSUCCESS)); env.close(); - env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); env.close(); } { // delete Sponsorship (only with FeeAmount) env(sponsor::set_fee(sponsor, 0, XRP(100)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); env.close(); - env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); env.close(); } { // delete Sponsorship (only with ReserveCount) - env(sponsor::set_reserve(sponsor, 0, 100), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::SponseeAcc(alice), Ter(tesSUCCESS)); env.close(); - env(sponsor::del(alice), sponsor::counterpartySponsor(sponsor), ter(tesSUCCESS)); + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); env.close(); } } @@ -698,21 +698,21 @@ class Sponsor_test : public beast::unit_test::suite { // both pre-funded and co-signed,pre-funded value is used - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, sponsor); env.close(); env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); env.close(); env(did::set(alice), - did::uri("uri"), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), - sig(sfSponsorSignature, sponsor), - fee(XRP(1)), - ter(tesSUCCESS)); + did::Uri("uri"), + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Fee(XRP(1)), + Ter(tesSUCCESS)); env.close(); auto sle = env.le(keylet::sponsor(sponsor, alice)); @@ -720,7 +720,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sle->at(sfReserveCount) == 99); BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99)); - env(did::del(alice), ter(tesSUCCESS)); + env(did::del(alice), Ter(tesSUCCESS)); env.close(); sle = env.le(keylet::sponsor(sponsor, alice)); @@ -731,29 +731,29 @@ class Sponsor_test : public beast::unit_test::suite { // if pre-funded value is not enough, error - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, sponsor); env.close(); env(sponsor::set(sponsor, 0, 10, XRP(10), XRP(100)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); env.close(); - // fee insufficient + // Fee insufficient env(ticket::create(alice, 1), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), - sig(sfSponsorSignature, sponsor), - fee(XRP(11)), - ter(terINSUF_FEE_B)); + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Fee(XRP(11)), + Ter(terINSUF_FEE_B)); env.close(); // reserve insufficient env(ticket::create(alice, 11), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), - sig(sfSponsorSignature, sponsor), - fee(XRP(1)), - ter(tecINSUFFICIENT_RESERVE)); + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Fee(XRP(1)), + Ter(tecINSUFFICIENT_RESERVE)); env.close(); } } @@ -766,7 +766,7 @@ class Sponsor_test : public beast::unit_test::suite { // invalid fields - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor1("sponsor1"); @@ -776,7 +776,7 @@ class Sponsor_test : public beast::unit_test::suite env(sponsor::transfer( alice, (tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd) + 1), - ter(temINVALID_FLAG)); + Ter(temINVALID_FLAG)); // invalid combination of flags for (auto flag : { @@ -785,40 +785,40 @@ class Sponsor_test : public beast::unit_test::suite tfSponsorshipReassign | tfSponsorshipEnd, tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd, }) - env(sponsor::transfer(alice, flag), ter(temINVALID_FLAG)); + env(sponsor::transfer(alice, flag), Ter(temINVALID_FLAG)); // invalid tfSponsorshipCreate // no sponsor field present - env(sponsor::transfer(alice, tfSponsorshipCreate), ter(temINVALID_FLAG)); + env(sponsor::transfer(alice, tfSponsorshipCreate), Ter(temINVALID_FLAG)); // sponsee field present env(sponsor::transfer(alice, tfSponsorshipCreate), - sponsor::sponseeAcc(bob), - sponsor::as(sponsor1, spfSponsorReserve), - ter(temMALFORMED)); + sponsor::SponseeAcc(bob), + sponsor::As(sponsor1, spfSponsorReserve), + Ter(temMALFORMED)); // invalid tfSponsorshipReassign // no sponsor field present - env(sponsor::transfer(alice, tfSponsorshipReassign), ter(temINVALID_FLAG)); + env(sponsor::transfer(alice, tfSponsorshipReassign), Ter(temINVALID_FLAG)); // sponsee field present env(sponsor::transfer(alice, tfSponsorshipReassign), - sponsor::sponseeAcc(bob), - sponsor::as(sponsor1, spfSponsorReserve), - ter(temMALFORMED)); + sponsor::SponseeAcc(bob), + sponsor::As(sponsor1, spfSponsorReserve), + Ter(temMALFORMED)); // invalid tfSponsorshipEnd // sponsor field present env(sponsor::transfer(alice, tfSponsorshipEnd), - sponsor::as(sponsor1, spfSponsorReserve), - ter(temINVALID_FLAG)); + sponsor::As(sponsor1, spfSponsorReserve), + Ter(temINVALID_FLAG)); // account = sponsee env(sponsor::transfer(alice, tfSponsorshipEnd), - sponsor::sponseeAcc(alice), - ter(temMALFORMED)); + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); } { // Invalid SponsorshipEnd permission (sponsor object/sponsor account) - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -828,32 +828,32 @@ class Sponsor_test : public beast::unit_test::suite { // sponsor object env(did::set(alice), - did::uri("uri"), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + did::Uri("uri"), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); auto const keylet = keylet::did(alice); env(sponsor::transfer(bob, tfSponsorshipEnd, keylet.key), - sponsor::sponseeAcc(alice), - ter(tecNO_PERMISSION)); + sponsor::SponseeAcc(alice), + Ter(tecNO_PERMISSION)); } { // sponsor object env(sponsor::transfer(alice, tfSponsorshipCreate), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); env(sponsor::transfer(bob, tfSponsorshipEnd), - sponsor::sponseeAcc(alice), - ter(tecNO_PERMISSION)); + sponsor::SponseeAcc(alice), + Ter(tecNO_PERMISSION)); } } { // sponsor account - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor1("sponsor1"); @@ -862,23 +862,23 @@ class Sponsor_test : public beast::unit_test::suite // sfSponsor provided but sfSponsorSignature not provided env(sponsor::transfer(alice, tfSponsorshipCreate), - sponsor::as(sponsor1, spfSponsorReserve), - ter(temMALFORMED)); + sponsor::As(sponsor1, spfSponsorReserve), + Ter(temMALFORMED)); env.close(); adjustAccountXRPBalance(env, sponsor1, accountReserve(env, 2) - drops(1)); env(sponsor::transfer(alice, tfSponsorshipCreate), - sponsor::as(sponsor1, spfSponsorReserve), - sig(sfSponsorSignature, sponsor1), - ter(tecINSUFFICIENT_RESERVE)); + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor1, accountReserve(env, 2)); env(sponsor::transfer(alice, tfSponsorshipCreate), - sponsor::as(sponsor1, spfSponsorReserve), - sig(sfSponsorSignature, sponsor1)); + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -895,16 +895,16 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 2) - drops(1)); env(sponsor::transfer(alice, tfSponsorshipReassign), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2), - ter(tecINSUFFICIENT_RESERVE)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 2)); env(sponsor::transfer(alice, tfSponsorshipReassign), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -925,14 +925,14 @@ class Sponsor_test : public beast::unit_test::suite // sponsor 2 accounts adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 3)); env(sponsor::transfer(bob, tfSponsorshipCreate), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); // dissolve sponsors adjustAccountXRPBalance(env, alice, accountReserve(env, 1) - drops(1)); - env(sponsor::transfer(alice, tfSponsorshipEnd), ter(tecINSUFFICIENT_RESERVE)); + env(sponsor::transfer(alice, tfSponsorshipEnd), Ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, alice, accountReserve(env, 1)); @@ -970,12 +970,12 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(!sle4->isFieldPresent(sfSponsor)); // not sponsored - env(sponsor::transfer(bob, tfSponsorshipEnd), ter(tecNO_PERMISSION)); + env(sponsor::transfer(bob, tfSponsorshipEnd), Ter(tecNO_PERMISSION)); env.close(); } { // dissolve account sponsorship from sponsor - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -983,14 +983,14 @@ class Sponsor_test : public beast::unit_test::suite env.close(); env(sponsor::transfer(alice, tfSponsorshipCreate), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(env.le(alice)->getAccountID(sfSponsor) == sponsor.id()); BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1); - env(sponsor::transfer(sponsor, tfSponsorshipEnd), sponsor::sponseeAcc(alice)); + env(sponsor::transfer(sponsor, tfSponsorshipEnd), sponsor::SponseeAcc(alice)); env.close(); BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfSponsor)); @@ -999,7 +999,7 @@ class Sponsor_test : public beast::unit_test::suite { // sponsor object (co-signing) - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor1("sponsor1"); @@ -1018,9 +1018,9 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), - sponsor::as(sponsor1, spfSponsorReserve), - sig(sfSponsorSignature, sponsor1), - ter(tecINSUFFICIENT_RESERVE)); + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecINSUFFICIENT_RESERVE)); env.close(); env(pay(alice, sponsor1, drops(1))); @@ -1028,22 +1028,22 @@ class Sponsor_test : public beast::unit_test::suite // Invalid ObjectID (not found) env(sponsor::transfer(alice, tfSponsorshipCreate, keylet::check(alice, 0).key), - sponsor::as(sponsor1, spfSponsorReserve), - sig(sfSponsorSignature, sponsor1), - ter(tecNO_ENTRY)); + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecNO_ENTRY)); env.close(); // Invalid Owner env(sponsor::transfer(bob, tfSponsorshipCreate, checkId), - sponsor::as(sponsor1, spfSponsorReserve), - sig(sfSponsorSignature, sponsor1), - ter(tecNO_PERMISSION)); + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecNO_PERMISSION)); env.close(); // Valid Owner env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), - sponsor::as(sponsor1, spfSponsorReserve), - sig(sfSponsorSignature, sponsor1)); + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1059,16 +1059,16 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2), - ter(tecINSUFFICIENT_RESERVE)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecINSUFFICIENT_RESERVE)); env(pay(alice, sponsor2, drops(1))); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -1087,7 +1087,7 @@ class Sponsor_test : public beast::unit_test::suite // dissolve sponsor adjustAccountXRPBalance(env, alice, reserve(env, 1) - drops(1)); - env(sponsor::transfer(alice, tfSponsorshipEnd, checkId), ter(tecINSUFFICIENT_RESERVE)); + env(sponsor::transfer(alice, tfSponsorshipEnd, checkId), Ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, alice, reserve(env, 1)); @@ -1096,11 +1096,11 @@ class Sponsor_test : public beast::unit_test::suite auto const ticketSeq = env.seq(alice); env(ticket::create(alice, 1)); env.close(); - auto ticketId = keylet::ticket(alice, ticketSeq + 1).key; + auto ticketId = keylet::TicketT()(alice, ticketSeq + 1).key; BEAST_EXPECT(env.le(keylet::unchecked(ticketId))); - env(sponsor::transfer(alice, tfSponsorshipEnd, ticketId), ter(tecNO_PERMISSION)); + env(sponsor::transfer(alice, tfSponsorshipEnd, ticketId), Ter(tecNO_PERMISSION)); env.close(); - env(noop(alice), ticket::use(ticketSeq + 1)); + env(noop(alice), ticket::Use(ticketSeq + 1)); env.close(); adjustAccountXRPBalance(env, alice, reserve(env, 1)); @@ -1124,7 +1124,7 @@ class Sponsor_test : public beast::unit_test::suite } { // sponsor object (pre-funded + no ltSponsorship entry) - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor1("sponsor1"); @@ -1140,25 +1140,25 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), - sponsor::as(sponsor1, spfSponsorReserve), - ter(terNO_SPONSORSHIP)); + sponsor::As(sponsor1, spfSponsorReserve), + Ter(terNO_SPONSORSHIP)); env.close(); - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), - sponsor::as(sponsor1, spfSponsorReserve), - ter(terNO_SPONSORSHIP)); + sponsor::As(sponsor1, spfSponsorReserve), + Ter(terNO_SPONSORSHIP)); env.close(); } { // sponsor object (pre-funded) - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor1("sponsor1"); @@ -1174,18 +1174,18 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); // insufficient reserve count - env(sponsor::set_fee(sponsor1, 0, XRP(100)), sponsor::sponseeAcc(alice)); + env(sponsor::set_fee(sponsor1, 0, XRP(100)), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), - sponsor::as(sponsor1, spfSponsorReserve), - ter(tecINSUFFICIENT_RESERVE)); + sponsor::As(sponsor1, spfSponsorReserve), + Ter(tecINSUFFICIENT_RESERVE)); env.close(); - env(sponsor::set_reserve(sponsor1, 0, 100), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor1, 0, 100), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), - sponsor::as(sponsor1, spfSponsorReserve)); + sponsor::As(sponsor1, spfSponsorReserve)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -1202,11 +1202,11 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 99); // transfer sponsor - env(sponsor::set_reserve(sponsor2, 0, 100), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 100), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); @@ -1250,7 +1250,7 @@ class Sponsor_test : public beast::unit_test::suite { // Dissolve object sponsorship from sponsor(no-ltSponsorship) - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -1265,8 +1265,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT( @@ -1276,10 +1276,10 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // not the owner of the object - env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), ter(tecNO_PERMISSION)); + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), Ter(tecNO_PERMISSION)); env.close(); - env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), sponsor::sponseeAcc(alice)); + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), sponsor::SponseeAcc(alice)); env.close(); BEAST_EXPECT(!env.le(keylet::unchecked(checkId))->isFieldPresent(sfSponsor)); @@ -1290,7 +1290,7 @@ class Sponsor_test : public beast::unit_test::suite { // Dissolve object sponsorship from sponsor (with ltSponsorship) - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -1305,11 +1305,11 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); - env(sponsor::set_reserve(sponsor, 0, 100), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::SponseeAcc(alice)); env.close(); BEAST_EXPECT( @@ -1321,10 +1321,10 @@ class Sponsor_test : public beast::unit_test::suite env.le(keylet::sponsor(sponsor, alice))->getFieldU32(sfReserveCount) == 100); // not the owner of the object - env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), ter(tecNO_PERMISSION)); + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), Ter(tecNO_PERMISSION)); env.close(); - env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), sponsor::sponseeAcc(alice)); + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), sponsor::SponseeAcc(alice)); env.close(); BEAST_EXPECT(!env.le(keylet::unchecked(checkId))->isFieldPresent(sfSponsor)); @@ -1346,17 +1346,17 @@ class Sponsor_test : public beast::unit_test::suite for (bool const isIssuerHigh : {false, true}) { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, bob, sponsor); env.close(); auto const& issuer = isIssuerHigh ? highAcc : lowAcc; auto const& user = isIssuerHigh ? lowAcc : highAcc; - auto const USD = issuer["USD"]; - auto const currency = USD.currency; + auto const usd = issuer["usd"]; + auto const currency = usd.currency; - env(trust(user, issuer["USD"](100))); + env(trust(user, issuer["usd"](100))); env.close(); auto const trustId = keylet::line(user, issuer, currency); @@ -1364,8 +1364,8 @@ class Sponsor_test : public beast::unit_test::suite // transfer sponsor env(sponsor::transfer(user, tfSponsorshipCreate, trustId.key), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(env.le(trustId)); @@ -1390,7 +1390,7 @@ class Sponsor_test : public beast::unit_test::suite { // invalid transfer - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -1418,9 +1418,9 @@ class Sponsor_test : public beast::unit_test::suite for (auto const& keylet : keylets) { env(sponsor::transfer(alice, tfSponsorshipCreate, keylet.key), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecNO_PERMISSION)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecNO_PERMISSION)); } } } @@ -1434,7 +1434,7 @@ class Sponsor_test : public beast::unit_test::suite { // co-signing - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -1444,17 +1444,17 @@ class Sponsor_test : public beast::unit_test::suite { // Fee should be checked before permission check, // otherwise tecNO_SPONSOR_PERMISSION returned when permission - // check fails could cause context reset to pay fee because it + // check fails could cause context reset to pay Fee because it // is tec error auto aliceBalance = env.balance(alice); auto bobBalance = env.balance(bob); auto sponsorBalance = env.balance(sponsor); env(pay(alice, bob, XRP(100)), - fee(XRP(2000)), - sponsor::as(sponsor, spfSponsorFee), - sig(sfSponsorSignature, sponsor), - ter(terNO_ACCOUNT)); + Fee(XRP(2000)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(terNO_ACCOUNT)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); BEAST_EXPECT(env.balance(bob) == bobBalance); @@ -1465,7 +1465,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); { - // Sponsor pays the fee + // Sponsor pays the Fee auto aliceBalance = env.balance(alice); auto bobBalance = env.balance(bob); auto sponsorBalance = env.balance(sponsor); @@ -1473,9 +1473,9 @@ class Sponsor_test : public beast::unit_test::suite auto const sendAmt = XRP(100); auto const feeAmt = XRP(10); env(pay(alice, bob, sendAmt), - fee(feeAmt), - sponsor::as(sponsor, spfSponsorFee), - sig(sfSponsorSignature, sponsor)); + Fee(feeAmt), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt); BEAST_EXPECT(env.balance(bob) == bobBalance + sendAmt); @@ -1483,16 +1483,16 @@ class Sponsor_test : public beast::unit_test::suite } { - // insufficient balance to pay fee + // insufficient balance to pay Fee auto aliceBalance = env.balance(alice); auto bobBalance = env.balance(bob); auto sponsorBalance = env.balance(sponsor); env(pay(alice, bob, XRP(100)), - fee(XRP(2000)), - sponsor::as(sponsor, spfSponsorFee), - sig(sfSponsorSignature, sponsor), - ter(terINSUF_FEE_B)); + Fee(XRP(2000)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(terINSUF_FEE_B)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); BEAST_EXPECT(env.balance(bob) == bobBalance); @@ -1500,7 +1500,7 @@ class Sponsor_test : public beast::unit_test::suite } { - // fee is paid by Sponsor + // Fee is paid by Sponsor // on context reset (tec error) auto aliceBalance = env.balance(alice); auto bobBalance = env.balance(bob); @@ -1508,10 +1508,10 @@ class Sponsor_test : public beast::unit_test::suite auto const feeAmt = XRP(10); env(pay(alice, bob, XRP(20000)), - fee(feeAmt), - sponsor::as(sponsor, spfSponsorFee), - sig(sfSponsorSignature, sponsor), - ter(tecUNFUNDED_PAYMENT)); + Fee(feeAmt), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tecUNFUNDED_PAYMENT)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); @@ -1526,24 +1526,24 @@ class Sponsor_test : public beast::unit_test::suite auto const feeAmt = XRP(4); env(noop(alice), - fee(env.current()->fees().base), - sponsor::as(sponsor, spfSponsorFee), - sig(sfSponsorSignature, sponsor), - ter(terINSUF_FEE_B)); + Fee(env.current()->fees().base), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(terINSUF_FEE_B)); env.close(); env(noop(alice), - fee(XRP(10)), - sponsor::as(sponsor, spfSponsorFee), - sig(sfSponsorSignature, sponsor), - ter(terINSUF_FEE_B)); + Fee(XRP(10)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(terINSUF_FEE_B)); env.close(); } } { // pre funded - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -1557,27 +1557,27 @@ class Sponsor_test : public beast::unit_test::suite { // Fee should be checked before permission check, // otherwise tecNO_SPONSOR_PERMISSION returned when permission - // check fails could cause context reset to pay fee because it + // check fails could cause context reset to pay Fee because it // is tec error auto aliceBalance = env.balance(alice); auto bobBalance = env.balance(bob); auto sponsorBalance = env.balance(sponsor); env(pay(alice, bob, XRP(100)), - fee(XRP(2000)), - sponsor::as(sponsor, spfSponsorFee), - ter(terNO_SPONSORSHIP)); + Fee(XRP(2000)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terNO_SPONSORSHIP)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); BEAST_EXPECT(env.balance(bob) == bobBalance); BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); } - env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::sponseeAcc(alice)); + env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::SponseeAcc(alice)); env.close(); { - // Sponsor pays the fee + // Sponsor pays the Fee auto aliceBalance = env.balance(alice); auto bobBalance = env.balance(bob); auto sponsorBalance = env.balance(sponsor); @@ -1585,7 +1585,7 @@ class Sponsor_test : public beast::unit_test::suite auto const sendAmt = XRP(100); auto const feeAmt = XRP(10); - env(pay(alice, bob, sendAmt), fee(feeAmt), sponsor::as(sponsor, spfSponsorFee)); + env(pay(alice, bob, sendAmt), Fee(feeAmt), sponsor::As(sponsor, spfSponsorFee)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt); @@ -1595,7 +1595,7 @@ class Sponsor_test : public beast::unit_test::suite } { - // insufficient balance to pay fee + // insufficient balance to pay Fee { // > FeeAmount auto aliceBalance = env.balance(alice); @@ -1604,9 +1604,9 @@ class Sponsor_test : public beast::unit_test::suite auto sponsorFee = sponsorFeeBalance(sponsor, alice); env(pay(alice, bob, XRP(100)), - fee(XRP(90) + drops(1)), - sponsor::as(sponsor, spfSponsorFee), - ter(terINSUF_FEE_B)); + Fee(XRP(90) + drops(1)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terINSUF_FEE_B)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); @@ -1622,9 +1622,9 @@ class Sponsor_test : public beast::unit_test::suite auto sponsorBalance = env.balance(sponsor); env(pay(alice, bob, XRP(100)), - fee(XRP(90)), - sponsor::as(sponsor, spfSponsorFee), - ter(tesSUCCESS)); + Fee(XRP(90)), + sponsor::As(sponsor, spfSponsorFee), + Ter(tesSUCCESS)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100)); @@ -1635,9 +1635,9 @@ class Sponsor_test : public beast::unit_test::suite } // reset FeeAmount and MaxFee - env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); + env(sponsor::del(sponsor), sponsor::SponseeAcc(alice)); env.close(); - env(sponsor::set_fee(sponsor, 0, XRP(10), XRP(1)), sponsor::sponseeAcc(alice)); + env(sponsor::set_fee(sponsor, 0, XRP(10), XRP(1)), sponsor::SponseeAcc(alice)); env.close(); { @@ -1648,9 +1648,9 @@ class Sponsor_test : public beast::unit_test::suite auto sponsorFee = sponsorFeeBalance(sponsor, alice); env(pay(alice, bob, XRP(100)), - fee(XRP(1) + drops(1)), - sponsor::as(sponsor, spfSponsorFee), - ter(terINSUF_FEE_B)); + Fee(XRP(1) + drops(1)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terINSUF_FEE_B)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); @@ -1661,7 +1661,7 @@ class Sponsor_test : public beast::unit_test::suite } { - // fee is paid by Sponsor + // Fee is paid by Sponsor // on context reset (tec error) auto aliceBalance = env.balance(alice); auto bobBalance = env.balance(bob); @@ -1670,9 +1670,9 @@ class Sponsor_test : public beast::unit_test::suite auto const feeAmt = XRP(1); env(pay(alice, bob, XRP(20000)), - fee(feeAmt), - sponsor::as(sponsor, spfSponsorFee), - ter(tecUNFUNDED_PAYMENT)); + Fee(feeAmt), + sponsor::As(sponsor, spfSponsorFee), + Ter(tecUNFUNDED_PAYMENT)); env.close(); BEAST_EXPECT(env.balance(alice) == aliceBalance); @@ -1681,19 +1681,19 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsorFeeBalance(sponsor, alice) == sponsorFee - feeAmt); } - // make sfFeeAmount absent if tec error and all fee is paid + // make sfFeeAmount absent if tec error and all Fee is paid { // reset FeeAmount and MaxFee - env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); - env(sponsor::set_fee(sponsor, 0, XRP(10)), sponsor::sponseeAcc(alice)); + env(sponsor::del(sponsor), sponsor::SponseeAcc(alice)); + env(sponsor::set_fee(sponsor, 0, XRP(10)), sponsor::SponseeAcc(alice)); env.close(); BEAST_EXPECT(env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount)); auto sponsorAvailableFee = sponsorFeeBalance(sponsor, alice); env(check::cancel(alice, uint256(1)), - fee(sponsorAvailableFee), - sponsor::as(sponsor, spfSponsorFee), - ter(tecNO_ENTRY)); + Fee(sponsorAvailableFee), + sponsor::As(sponsor, spfSponsorFee), + Ter(tecNO_ENTRY)); env.close(); BEAST_EXPECT(!env.le(keylet::sponsor(sponsor, alice))->isFieldPresent(sfFeeAmount)); } @@ -1701,9 +1701,9 @@ class Sponsor_test : public beast::unit_test::suite // MaxFee cap is enforced in reset() for tec-failing transactions. // On a closed ledger view (!view.open()), checkFee returns tecINSUFF_FEE when - // fee > MaxFee (not terINSUF_FEE_B), triggering reset() + // Fee > MaxFee (not terINSUF_FEE_B), triggering reset() { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const carol("sponsor"); @@ -1711,7 +1711,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(); // FeeAmount=1000 drops, MaxFee=10 drops - env(sponsor::set_fee(carol, 0, drops(1000), drops(10)), sponsor::sponseeAcc(alice)); + env(sponsor::set_fee(carol, 0, drops(1000), drops(10)), sponsor::SponseeAcc(alice)); env.close(); // Apply directly against the closed ledger view (open_ = false) so that @@ -1720,11 +1720,11 @@ class Sponsor_test : public beast::unit_test::suite auto jt = env.jt( noop(alice), - fee(drops(1000)), - seq(env.seq(alice)), - sponsor::as(carol, spfSponsorFee)); + Fee(drops(1000)), + Seq(env.seq(alice)), + sponsor::As(carol, spfSponsorFee)); - auto const result = xrpl::apply(env.app(), overlay, *jt.stx, tapNONE, env.journal); + auto const result = xrpl::apply(env.app(), overlay, *jt.stx, TapNone, env.journal); BEAST_EXPECT(result.ter == tecINSUFF_FEE); BEAST_EXPECT(result.applied); @@ -1737,7 +1737,7 @@ class Sponsor_test : public beast::unit_test::suite // test lsfSponsorshipRequireSignForFee { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -1746,13 +1746,13 @@ class Sponsor_test : public beast::unit_test::suite // set flag env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), - sponsor::sponseeAcc(alice)); + sponsor::SponseeAcc(alice)); env.close(); env(pay(alice, bob, XRP(100)), - fee(XRP(10)), - sponsor::as(sponsor, spfSponsorFee), - ter(terNO_SPONSORSHIP)); + Fee(XRP(10)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terNO_SPONSORSHIP)); env.close(); BEAST_EXPECT( @@ -1760,7 +1760,7 @@ class Sponsor_test : public beast::unit_test::suite // clear flag env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), - sponsor::sponseeAcc(alice)); + sponsor::SponseeAcc(alice)); env.close(); // Payment is re-applied @@ -1769,7 +1769,7 @@ class Sponsor_test : public beast::unit_test::suite // RequireSignForFee: co-signing should succeed { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -1778,22 +1778,22 @@ class Sponsor_test : public beast::unit_test::suite // set flag env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), - sponsor::sponseeAcc(alice)); + sponsor::SponseeAcc(alice)); env.close(); // pre-funded (no sig) should fail env(pay(alice, bob, XRP(100)), - fee(XRP(1)), - sponsor::as(sponsor, spfSponsorFee), - ter(terNO_SPONSORSHIP)); + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terNO_SPONSORSHIP)); env.close(); // co-signing (with sig) should succeed env(pay(alice, bob, XRP(100)), - fee(XRP(1)), - sponsor::as(sponsor, spfSponsorFee), - sig(sfSponsorSignature, sponsor), - ter(tesSUCCESS)); + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); env.close(); BEAST_EXPECT( @@ -1815,18 +1815,18 @@ class Sponsor_test : public beast::unit_test::suite Account const charlie("charlie"); Account const dave("dave"); Account const gw("gw"); - auto const USD = gw["USD"]; + auto const usd = gw["usd"]; { // Disabled - Env env{*this, testable_amendments() - featureSponsor}; + Env env{*this, testableAmendments() - featureSponsor}; env.fund(XRP(10000), alice, sponsor); env.close(); - env(pay(alice, bob, XRP(100)), txflags(tfSponsorCreatedAccount), ter(temDISABLED)); + env(pay(alice, bob, XRP(100)), Txflags(tfSponsorCreatedAccount), Ter(temDISABLED)); env.close(); } - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, sponsor, sponsor2, sponsor3); env.close(); @@ -1838,20 +1838,20 @@ class Sponsor_test : public beast::unit_test::suite }) { env(pay(alice, bob, XRP(100)), - txflags(tfSponsorCreatedAccount | flag), - ter(temINVALID_FLAG)); + Txflags(tfSponsorCreatedAccount | flag), + Ter(temINVALID_FLAG)); env.close(); } // Invalid amount(iou) - env(pay(alice, bob, USD(100)), txflags(tfSponsorCreatedAccount), ter(temBAD_AMOUNT)); + env(pay(alice, bob, usd(100)), Txflags(tfSponsorCreatedAccount), Ter(temBAD_AMOUNT)); env.close(); // Account is not sponsored by normal Sponsor specification { env(pay(alice, bob, drops(baseAccountReserve(*env.current(), 0))), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); auto const bobSle = env.le(keylet::account(bob)); @@ -1864,22 +1864,22 @@ class Sponsor_test : public beast::unit_test::suite { // to funded account env(pay(sponsor2, bob, drops(1)), - txflags(tfSponsorCreatedAccount), - fee(XRP(1)), - ter(tecNO_SPONSOR_PERMISSION)); + Txflags(tfSponsorCreatedAccount), + Fee(XRP(1)), + Ter(tecNO_SPONSOR_PERMISSION)); env.close(); BEAST_EXPECT(env.balance(sponsor2) == XRP(9999)); // to non-funded account / insufficient balance for reserve env(pay(sponsor2, charlie, XRP(9999) - env.current()->fees().reserve + drops(1)), - txflags(tfSponsorCreatedAccount), - ter(tecUNFUNDED_PAYMENT)); + Txflags(tfSponsorCreatedAccount), + Ter(tecUNFUNDED_PAYMENT)); env.close(); // to non-funded account auto const sponsor2BalanceBefore = env.balance(sponsor2); - env(pay(sponsor2, charlie, drops(1)), txflags(tfSponsorCreatedAccount), fee(XRP(1))); + env(pay(sponsor2, charlie, drops(1)), Txflags(tfSponsorCreatedAccount), Fee(XRP(1))); env.close(); auto const charlieSle = env.le(keylet::account(charlie)); @@ -1887,7 +1887,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(charlieSle->getAccountID(sfSponsor) == sponsor2.id()); BEAST_EXPECT(sponsoredOwnerCount(env, charlie) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); - // verify sponsor balance decreased by payment + fee + // verify sponsor balance decreased by payment + Fee BEAST_EXPECT(env.balance(sponsor2) == sponsor2BalanceBefore - drops(1) - XRP(1)); } { @@ -1898,16 +1898,16 @@ class Sponsor_test : public beast::unit_test::suite auto const requireBalance = accountReserve(env, 2) + sendAmount; adjustAccountXRPBalance(env, sponsor3, requireBalance - drops(1)); env(pay(sponsor3, dave, sendAmount), - txflags(tfSponsorCreatedAccount), - fee(XRP(1)), - ter(tecUNFUNDED_PAYMENT)); + Txflags(tfSponsorCreatedAccount), + Fee(XRP(1)), + Ter(tecUNFUNDED_PAYMENT)); env.close(); adjustAccountXRPBalance(env, sponsor3, requireBalance); env(pay(sponsor3, dave, sendAmount), - txflags(tfSponsorCreatedAccount), - fee(XRP(1)), - ter(tesSUCCESS)); + Txflags(tfSponsorCreatedAccount), + Fee(XRP(1)), + Ter(tesSUCCESS)); env.close(); } } @@ -1919,7 +1919,7 @@ class Sponsor_test : public beast::unit_test::suite { testcase("SponsorshipRequireSignForReserve"); - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -1928,13 +1928,13 @@ class Sponsor_test : public beast::unit_test::suite // set flag env(sponsor::set_reserve(sponsor, tfSponsorshipSetRequireSignForReserve, 10), - sponsor::sponseeAcc(alice)); + sponsor::SponseeAcc(alice)); env.close(); env(check::create(alice, bob, XRP(100)), - fee(XRP(10)), - sponsor::as(sponsor, spfSponsorReserve), - ter(terNO_SPONSORSHIP)); + Fee(XRP(10)), + sponsor::As(sponsor, spfSponsorReserve), + Ter(terNO_SPONSORSHIP)); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -1942,7 +1942,7 @@ class Sponsor_test : public beast::unit_test::suite // clear flag env(sponsor::set_reserve(sponsor, tfSponsorshipClearRequireSignForReserve, 1), - sponsor::sponseeAcc(alice)); + sponsor::SponseeAcc(alice)); env.close(); // CheckCreate is re-applied @@ -1954,7 +1954,7 @@ class Sponsor_test : public beast::unit_test::suite { testcase("SponsorshipRequireSignForFee"); - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); @@ -1963,13 +1963,13 @@ class Sponsor_test : public beast::unit_test::suite // set flag env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), - sponsor::sponseeAcc(alice)); + sponsor::SponseeAcc(alice)); env.close(); env(check::create(alice, bob, XRP(100)), - fee(XRP(10)), - sponsor::as(sponsor, spfSponsorFee), - ter(terNO_SPONSORSHIP)); + Fee(XRP(10)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terNO_SPONSORSHIP)); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT( @@ -1977,7 +1977,7 @@ class Sponsor_test : public beast::unit_test::suite // clear flag env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), - sponsor::sponseeAcc(alice)); + sponsor::SponseeAcc(alice)); env.close(); // CheckCreate is re-applied @@ -1991,7 +1991,7 @@ class Sponsor_test : public beast::unit_test::suite { testcase("SponsorReserveSimple"); using namespace test::jtx; - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const alice("alice"); Account const sponsor("sponsor"); @@ -2004,36 +2004,36 @@ class Sponsor_test : public beast::unit_test::suite adjustAccountXRPBalance(env, sponsor, reserve(env, 99)); env(ticket::create(alice, 100), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 100)); env(ticket::create(alice, 100), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tesSUCCESS)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); env.close(); } else { - env(sponsor::set_reserve(sponsor, 0, 250), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, 0, 250), sponsor::SponseeAcc(alice)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 99 + 1 /* sponsor object*/)); env(ticket::create(alice, 100), - sponsor::as(sponsor, spfSponsorReserve), - ter(tecINSUFFICIENT_RESERVE)); + sponsor::As(sponsor, spfSponsorReserve), + Ter(tecINSUFFICIENT_RESERVE)); env.close(); adjustAccountXRPBalance(env, sponsor, reserve(env, 100 + 1 /* sponsor object*/)); env(ticket::create(alice, 100), - sponsor::as(sponsor, spfSponsorReserve), - ter(tesSUCCESS)); + sponsor::As(sponsor, spfSponsorReserve), + Ter(tesSUCCESS)); env.close(); } } @@ -2059,21 +2059,21 @@ class Sponsor_test : public beast::unit_test::suite auto const sponseeSponsoringOwnerCountBefore = sponsoringOwnerCount(env, sponsee); auto const sponsorSponsoringOwnerCountBefore = sponsoringOwnerCount(env, sponsor); - std::optional sponsorSig = - cosigning ? std::optional(sig(sfSponsorSignature, sponsor)) : std::nullopt; + std::optional sponsorSig = + cosigning ? std::optional(Sig(sfSponsorSignature, sponsor)) : std::nullopt; auto const sponsorCurrentOwnerCount = ownerCount(env, sponsor) - sponsoredOwnerCount(env, sponsor) + sponsoringOwnerCount(env, sponsor); - auto submit = [&](TER _ter) { - return [&, _ter](Json::Value const& jv, auto const&... fN) { + auto submit = [&](TER ter) { + return [&, ter](json::Value const& jv, auto const&... fN) { if (sponsorSig) { - env(jv, fN..., sponsor::as(sponsor, spfSponsorReserve), *sponsorSig, ter(_ter)); + env(jv, fN..., sponsor::As(sponsor, spfSponsorReserve), *sponsorSig, Ter(ter)); } else { - env(jv, fN..., sponsor::as(sponsor, spfSponsorReserve), ter(_ter)); + env(jv, fN..., sponsor::As(sponsor, spfSponsorReserve), Ter(ter)); } }; }; @@ -2092,20 +2092,20 @@ class Sponsor_test : public beast::unit_test::suite // cleanup previous sponsorship if (env.le(keylet::sponsor(sponsor, sponsee))) { - env(sponsor::del(sponsor), sponsor::sponseeAcc(sponsee)); + env(sponsor::del(sponsor), sponsor::SponseeAcc(sponsee)); env.close(); } if (sponsorReserveCount - 1 > 0) { env(sponsor::set(sponsor, 0, sponsorReserveCount - 1, XRP(1)), - sponsor::sponseeAcc(sponsee)); + sponsor::SponseeAcc(sponsee)); } else { // just create sponsor object env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), - sponsor::sponseeAcc(sponsee)); + sponsor::SponseeAcc(sponsee)); } env.close(); } @@ -2123,9 +2123,9 @@ class Sponsor_test : public beast::unit_test::suite else { // reset sponsorship - env(sponsor::del(sponsor), sponsor::sponseeAcc(sponsee)); + env(sponsor::del(sponsor), sponsor::SponseeAcc(sponsee)); env(sponsor::set(sponsor, 0, sponsorReserveCount, XRP(1)), - sponsor::sponseeAcc(sponsee)); + sponsor::SponseeAcc(sponsee)); env.close(); } callback(env, submit(tesSUCCESS)); @@ -2134,7 +2134,7 @@ class Sponsor_test : public beast::unit_test::suite if (!cosigning) { // cleanup sponsorship - env(sponsor::del(sponsor), sponsor::sponseeAcc(sponsee)); + env(sponsor::del(sponsor), sponsor::SponseeAcc(sponsee)); env.close(); } } @@ -2167,18 +2167,18 @@ class Sponsor_test : public beast::unit_test::suite Account const gw("gw"); Account const sponsor("sponsor"); - auto const USD = gw["USD"]; - auto const EUR = gw["EUR"]; + auto const usd = gw["usd"]; + auto const eur = gw["eur"]; auto const ammCreate = [&](Env& env, Account const& account, STAmount const& amount1, STAmount const& amount2) { - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::AMMCreate; jv[jss::Account] = account.human(); - jv[jss::Amount] = amount1.getJson(JsonOptions::none); - jv[jss::Amount2] = amount2.getJson(JsonOptions::none); + jv[jss::Amount] = amount1.getJson(JsonOptions::KNone); + jv[jss::Amount2] = amount2.getJson(JsonOptions::KNone); jv[jss::TradingFee] = 0; jv[jss::Fee] = std::to_string(env.current()->fees().increment.drops()); return jv; @@ -2188,13 +2188,13 @@ class Sponsor_test : public beast::unit_test::suite Account const& account, STAmount const& amount1, STAmount const& amount2) { - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::AMMDeposit; jv[jss::Account] = account.human(); - jv[jss::Asset] = STIssue(sfAsset, amount1.asset()).getJson(JsonOptions::none); - jv[jss::Asset2] = STIssue(sfAsset, amount2.asset()).getJson(JsonOptions::none); - jv[jss::Amount] = amount1.value().getJson(JsonOptions::none); - jv[jss::Amount2] = amount2.value().getJson(JsonOptions::none); + jv[jss::Asset] = STIssue(sfAsset, amount1.asset()).getJson(JsonOptions::KNone); + jv[jss::Asset2] = STIssue(sfAsset, amount2.asset()).getJson(JsonOptions::KNone); + jv[jss::Amount] = amount1.value().getJson(JsonOptions::KNone); + jv[jss::Amount2] = amount2.value().getJson(JsonOptions::KNone); jv[jss::Flags] = tfTwoAsset; return jv; }; @@ -2203,16 +2203,16 @@ class Sponsor_test : public beast::unit_test::suite // AMMCreate // - sponsor LPToken // - doesn't sponsor AMM object - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, gw, sponsor); env.close(); - env(trust(alice, USD(10000))); - env(trust(alice, EUR(10000))); + env(trust(alice, usd(10000))); + env(trust(alice, eur(10000))); env.close(); - env(pay(gw, alice, USD(1000))); - env(pay(gw, alice, EUR(1000))); + env(pay(gw, alice, usd(1000))); + env(pay(gw, alice, eur(1000))); env.close(); testEachSponsorship( @@ -2224,62 +2224,62 @@ class Sponsor_test : public beast::unit_test::suite 1, tecINSUF_RESERVE_LINE, [&](Env& env, auto const& submit) { - submit(ammCreate(env, alice, USD(100), EUR(100))); + submit(ammCreate(env, alice, usd(100), eur(100))); }, [&]() { - auto const amm = env.current()->read(keylet::amm(USD.issue(), EUR.issue())); + auto const amm = env.current()->read(keylet::amm(usd.issue(), eur.issue())); auto const ammAccount = Account("amm", amm->getAccountID(sfAccount)); - BEAST_EXPECT(ownerCount(env, alice) == 3); // RippleState (USD,EUR/LP Token) - BEAST_EXPECT(ownerCount(env, ammAccount) == 2); // USD, EUR + BEAST_EXPECT(ownerCount(env, alice) == 3); // RippleState (usd,eur/LP Token) + BEAST_EXPECT(ownerCount(env, ammAccount) == 2); // usd, eur BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); // LPToken BEAST_EXPECT(sponsoredOwnerCount(env, ammAccount) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken BEAST_EXPECT( - !env.le(keylet::amm(USD.issue(), EUR.issue()))->isFieldPresent(sfSponsor)); + !env.le(keylet::amm(usd.issue(), eur.issue()))->isFieldPresent(sfSponsor)); }); - auto const ammKeylet = keylet::amm(USD.issue(), EUR.issue()); + auto const ammKeylet = keylet::amm(usd.issue(), eur.issue()); if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipCreate, ammKeylet.key), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecNO_PERMISSION)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecNO_PERMISSION)); env.close(); } else { - env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice)); - env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(alice)); env(sponsor::transfer(alice, tfSponsorshipCreate, ammKeylet.key), - sponsor::as(sponsor, spfSponsorReserve), - ter(tecNO_PERMISSION)); + sponsor::As(sponsor, spfSponsorReserve), + Ter(tecNO_PERMISSION)); env.close(); } } { // AMMDeposit // - sponsor new LPToken - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, bob, gw, sponsor); env.close(); - env(trust(alice, USD(10000))); - env(trust(alice, EUR(10000))); - env(trust(bob, USD(10000))); - env(trust(bob, EUR(10000))); + env(trust(alice, usd(10000))); + env(trust(alice, eur(10000))); + env(trust(bob, usd(10000))); + env(trust(bob, eur(10000))); env.close(); - env(pay(gw, alice, USD(1000))); - env(pay(gw, alice, EUR(1000))); - env(pay(gw, bob, USD(1000))); - env(pay(gw, bob, EUR(1000))); + env(pay(gw, alice, usd(1000))); + env(pay(gw, alice, eur(1000))); + env(pay(gw, bob, usd(1000))); + env(pay(gw, bob, eur(1000))); env.close(); - env(ammCreate(env, alice, USD(100), EUR(100))); + env(ammCreate(env, alice, usd(100), eur(100))); env.close(); - BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState (USD,EUR) + BEAST_EXPECT(ownerCount(env, bob) == 2); // RippleState (usd,eur) testEachSponsorship( env, @@ -2290,45 +2290,45 @@ class Sponsor_test : public beast::unit_test::suite 1, tecINSUF_RESERVE_LINE, [&](Env& env, auto const& submit) { - submit(ammDeposit(env, bob, USD(100), EUR(100))); + submit(ammDeposit(env, bob, usd(100), eur(100))); }); } { // AMMDeposit single-asset XRP: reserve sponsor covers LP trustline reserve // but depositor's own liquid XRP is insufficient for the deposit → tecUNFUNDED_AMM - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, bob, gw, sponsor); env.close(); - env(trust(bob, USD(10000))); - env(trust(alice, USD(10000))); + env(trust(bob, usd(10000))); + env(trust(alice, usd(10000))); env.close(); - env(pay(gw, bob, USD(1000))); + env(pay(gw, bob, usd(1000))); env.close(); - AMM const amm(env, bob, XRP(1000), USD(100)); + AMM const amm(env, bob, XRP(1000), usd(100)); - // alice has 1 owner object (USD trust line); give her reserve + 5 XRP liquid + // alice has 1 owner object (usd trust line); give her reserve + 5 XRP liquid adjustAccountXRPBalance(env, alice, reserve(env, ownerCount(env, alice)) + XRP(5)); auto const jv = AMM::depositJv( {.account = alice, .asset1In = XRP(10), - .assets = std::make_pair(Asset{xrpIssue()}, Asset{USD.issue()})}); + .assets = std::make_pair(Asset{xrpIssue()}, Asset{usd.issue()})}); if (cosigning) { env(jv, - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUF_RESERVE_LINE)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecINSUF_RESERVE_LINE)); } else { - env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(alice)); env.close(); - env(jv, sponsor::as(sponsor, spfSponsorReserve), ter(tecINSUF_RESERVE_LINE)); - env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); + env(jv, sponsor::As(sponsor, spfSponsorReserve), Ter(tecINSUF_RESERVE_LINE)); + env(sponsor::del(sponsor), sponsor::SponseeAcc(alice)); } env.close(); @@ -2339,37 +2339,37 @@ class Sponsor_test : public beast::unit_test::suite { // Single Asset Withdraw // - sponsor new RippleState - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, bob, gw, sponsor); env.close(); - env(trust(alice, USD(10000))); - env(trust(alice, EUR(10000))); + env(trust(alice, usd(10000))); + env(trust(alice, eur(10000))); env.close(); - env(pay(gw, alice, USD(1000))); - env(pay(gw, alice, EUR(1000))); + env(pay(gw, alice, usd(1000))); + env(pay(gw, alice, eur(1000))); env.close(); - env(ammCreate(env, alice, USD(1000), EUR(1000)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(ammCreate(env, alice, usd(1000), eur(1000)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); - env(trust(alice, USD(0))); - env(trust(alice, EUR(0))); + env(trust(alice, usd(0))); + env(trust(alice, eur(0))); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // LPToken BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); // LPToken BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // LPToken - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::AMMWithdraw; jv[jss::Account] = alice.human(); - jv[jss::Asset] = STIssue(sfAsset, USD.issue()).getJson(JsonOptions::none); - jv[jss::Asset2] = STIssue(sfAsset, EUR.issue()).getJson(JsonOptions::none); - jv[jss::Amount] = USD(100).value().getJson(JsonOptions::none); + jv[jss::Asset] = STIssue(sfAsset, usd.issue()).getJson(JsonOptions::KNone); + jv[jss::Asset2] = STIssue(sfAsset, eur.issue()).getJson(JsonOptions::KNone); + jv[jss::Amount] = usd(100).value().getJson(JsonOptions::KNone); jv[jss::Flags] = tfSingleAsset; env(ticket::create(sponsor, 1)); // adjust for free @@ -2389,36 +2389,36 @@ class Sponsor_test : public beast::unit_test::suite // Double Asset Withdraw // - sponsor new RippleState * 2 // - remove sponsored LPToken - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, bob, gw, sponsor); env.close(); - env(trust(alice, USD(10000))); - env(trust(alice, EUR(10000))); + env(trust(alice, usd(10000))); + env(trust(alice, eur(10000))); env.close(); - env(pay(gw, alice, USD(1000))); - env(pay(gw, alice, EUR(1000))); + env(pay(gw, alice, usd(1000))); + env(pay(gw, alice, eur(1000))); env.close(); - env(ammCreate(env, alice, USD(1000), EUR(1000)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(ammCreate(env, alice, usd(1000), eur(1000)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); - env(trust(alice, USD(0))); - env(trust(alice, EUR(0))); + env(trust(alice, usd(0))); + env(trust(alice, eur(0))); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // LPToken BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::AMMWithdraw; jv[jss::Account] = alice.human(); - jv[jss::Asset] = STIssue(sfAsset, USD.issue()).getJson(JsonOptions::none); - jv[jss::Asset2] = STIssue(sfAsset, EUR.issue()).getJson(JsonOptions::none); + jv[jss::Asset] = STIssue(sfAsset, usd.issue()).getJson(JsonOptions::KNone); + jv[jss::Asset2] = STIssue(sfAsset, eur.issue()).getJson(JsonOptions::KNone); jv[jss::Flags] = tfWithdrawAll; env(ticket::create(sponsor, 1)); // adjust for free trustline @@ -2434,7 +2434,7 @@ class Sponsor_test : public beast::unit_test::suite tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { submit(jv); }, [&]() { - // LPToken deleted, USD, EUR created + // LPToken deleted, usd, eur created BEAST_EXPECT(ownerCount(env, alice) == 2); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); @@ -2446,30 +2446,30 @@ class Sponsor_test : public beast::unit_test::suite // - doesn't sponsor holder's new RippleState // - remove sponsored LPToken Account const gw2("gw2"); - auto const EUR2 = gw2["EUR"]; + auto const eur2 = gw2["eur"]; - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, gw, gw2, sponsor); env.close(); env(fset(gw, asfAllowTrustLineClawback)); env.close(); - env(trust(alice, USD(10000))); - env(trust(alice, EUR2(10000))); + env(trust(alice, usd(10000))); + env(trust(alice, eur2(10000))); env.close(); - env(pay(gw, alice, USD(100))); - env(pay(gw2, alice, EUR2(100))); + env(pay(gw, alice, usd(100))); + env(pay(gw2, alice, eur2(100))); env.close(); - env(ammCreate(env, alice, USD(100), EUR2(100)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(ammCreate(env, alice, usd(100), eur2(100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); - env(trust(alice, USD(0))); - env(trust(alice, EUR2(0))); + env(trust(alice, usd(0))); + env(trust(alice, eur2(0))); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); // LPToken @@ -2477,21 +2477,21 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); { // doesn't sponsor holder's new RippleState - env(amm::ammClawback(gw, alice, USD, EUR2, USD(10)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(amm::ammClawback(gw, alice, usd, eur2, usd(10)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // LPToken, EUR2 + BEAST_EXPECT(ownerCount(env, alice) == 2); // LPToken, eur2 BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); } { // remove sponsored LPToken - env(amm::ammClawback(gw, alice, USD, EUR2, std::nullopt)); + env(amm::ammClawback(gw, alice, usd, eur2, std::nullopt)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); // EUR2 + BEAST_EXPECT(ownerCount(env, alice) == 1); // eur2 BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } @@ -2505,45 +2505,45 @@ class Sponsor_test : public beast::unit_test::suite cfg->FEES.reference_fee = XRPAmount(1); return cfg; }), - testable_amendments()); + testableAmendments()); env.fund(XRP(20'000), alice, gw, sponsor); env.close(); - env(trust(alice, USD(10'000))); + env(trust(alice, usd(10'000))); env.close(); - env(pay(gw, alice, USD(10'000))); + env(pay(gw, alice, usd(10'000))); env.close(); - AMM amm(env, gw, XRP(10'000), USD(10'000)); - for (auto i = 0; i < (maxDeletableAMMTrustLines * 2) + 10; ++i) + AMM amm(env, gw, XRP(10'000), usd(10'000)); + for (auto i = 0; i < (kMAX_DELETABLE_AMM_TRUST_LINES * 2) + 10; ++i) { Account const a{std::to_string(i)}; env.fund(XRP(1'000), a); if (cosigning) { env(trust(a, STAmount{amm.lptIssue(), 10'000}), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); } else { - env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(a)); + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(a)); env.close(); env(trust(a, STAmount{amm.lptIssue(), 10'000}), - sponsor::as(sponsor, spfSponsorReserve)); + sponsor::As(sponsor, spfSponsorReserve)); env.close(); } } BEAST_EXPECT( - sponsoringOwnerCount(env, sponsor) == ((maxDeletableAMMTrustLines * 2) + 10)); + sponsoringOwnerCount(env, sponsor) == ((kMAX_DELETABLE_AMM_TRUST_LINES * 2) + 10)); // The trustlines are partially deleted. amm.withdrawAll(gw); BEAST_EXPECT(amm.ammExists()); // AMMDelete has to be called twice to delete AMM. - amm.ammDelete(alice, ter(tecINCOMPLETE)); + amm.ammDelete(alice, Ter(tecINCOMPLETE)); BEAST_EXPECT(amm.ammExists()); // Deletes remaining trustlines and deletes AMM. @@ -2567,10 +2567,10 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - auto const USD = gw["USD"]; + auto const usd = gw["usd"]; { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, bob, sponsor, sponsor2); env.close(); @@ -2602,18 +2602,18 @@ class Sponsor_test : public beast::unit_test::suite { // transfer sponsor env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); // transfer sponsor env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -2636,7 +2636,7 @@ class Sponsor_test : public beast::unit_test::suite } { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, bob, sponsor); env.close(); @@ -2672,13 +2672,13 @@ class Sponsor_test : public beast::unit_test::suite // RippleState sponsor (CheckCashMakesTrustLine) { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, bob, gw, sponsor, sponsor2); env.close(); - env.trust(USD(100), alice); + env.trust(usd(100), alice); env.close(); - env(pay(gw, alice, USD(100))); + env(pay(gw, alice, usd(100))); env.close(); // CheckCreat = 0e -> CheckCash @@ -2693,7 +2693,7 @@ class Sponsor_test : public beast::unit_test::suite tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { seq2 = env.seq(alice); - submit(check::create(alice, bob, USD(1))); + submit(check::create(alice, bob, usd(1))); }); BEAST_EXPECT(ownerCount(env, bob) == 0); @@ -2711,7 +2711,7 @@ class Sponsor_test : public beast::unit_test::suite 1, 1, tecNO_LINE_INSUF_RESERVE, - [&](Env& env, auto const& submit) { submit(check::cash(bob, keylet.key, USD(1))); }, + [&](Env& env, auto const& submit) { submit(check::cash(bob, keylet.key, usd(1))); }, [&]() { BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState @@ -2733,11 +2733,11 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor1("sponsor1"); Account const sponsor2("sponsor2"); - auto USD = gw["USD"]; - auto EUR = gw["EUR"]; + auto usd = gw["usd"]; + auto eur = gw["eur"]; { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, gw, sponsor1, sponsor2); env.close(); @@ -2754,7 +2754,7 @@ class Sponsor_test : public beast::unit_test::suite tecINSUF_RESERVE_OFFER, [&](Env& env, auto const& submit) { seq = env.seq(alice); - submit(offer(alice, USD(1), XRP(1))); + submit(offer(alice, usd(1), XRP(1))); }); // transfer sponsor @@ -2762,17 +2762,17 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -2784,7 +2784,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor2.id()); // OfferCancel - env(offer_cancel(alice, seq)); + env(offerCancel(alice, seq)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); @@ -2795,7 +2795,7 @@ class Sponsor_test : public beast::unit_test::suite } { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, gw, sponsor1, sponsor2); env.close(); @@ -2812,27 +2812,27 @@ class Sponsor_test : public beast::unit_test::suite tecINSUF_RESERVE_OFFER, [&](Env& env, auto const& submit) { seq = env.seq(alice); - submit(offer(alice, USD(1), XRP(1))); + submit(offer(alice, usd(1), XRP(1))); }); // OfferCreate with Cancel (new sponsor) auto const seq2 = env.seq(alice); if (cosigning) { - env(offer(alice, USD(1), XRP(1)), - json(jss::OfferSequence, seq), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + env(offer(alice, usd(1), XRP(1)), + Json(jss::OfferSequence, seq), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); - env(offer(alice, USD(1), XRP(1)), - json(jss::OfferSequence, seq), - sponsor::as(sponsor2, spfSponsorReserve)); + env(offer(alice, usd(1), XRP(1)), + Json(jss::OfferSequence, seq), + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -2843,7 +2843,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); // OfferCreate with Cancel (no sponsor) - env(offer(alice, USD(1), XRP(1)), json(jss::OfferSequence, seq2)); + env(offer(alice, usd(1), XRP(1)), Json(jss::OfferSequence, seq2)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2855,16 +2855,16 @@ class Sponsor_test : public beast::unit_test::suite // test Offer Execution doesn't sponsor new trustline { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(10000), alice, bob, gw, sponsor1, sponsor2); env.close(); - env(trust(alice, USD(100))); - env(trust(bob, EUR(100))); + env(trust(alice, usd(100))); + env(trust(bob, eur(100))); env.close(); - env(pay(gw, alice, USD(100))); - env(pay(gw, bob, EUR(100))); + env(pay(gw, alice, usd(100))); + env(pay(gw, bob, eur(100))); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -2873,17 +2873,17 @@ class Sponsor_test : public beast::unit_test::suite // OfferCreate if (cosigning) { - env(offer(alice, EUR(1), USD(1)), - sponsor::as(sponsor1, spfSponsorReserve), - sig(sfSponsorSignature, sponsor1)); + env(offer(alice, eur(1), usd(1)), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1)); env.close(); } else { - env(sponsor::set_reserve(sponsor1, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor1, 0, 1), sponsor::SponseeAcc(alice)); env.close(); - env(offer(alice, EUR(1), USD(1)), sponsor::as(sponsor1, spfSponsorReserve)); + env(offer(alice, eur(1), usd(1)), sponsor::As(sponsor1, spfSponsorReserve)); env.close(); } @@ -2900,17 +2900,17 @@ class Sponsor_test : public beast::unit_test::suite // OfferCreate (cross offer) if (cosigning) { - env(offer(bob, USD(1), EUR(1)), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + env(offer(bob, usd(1), eur(1)), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(bob)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(bob)); env.close(); - env(offer(bob, USD(1), EUR(1)), sponsor::as(sponsor2, spfSponsorReserve)); + env(offer(bob, usd(1), eur(1)), sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -2937,7 +2937,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, sponsor, sponsor2); env.close(); @@ -2957,24 +2957,24 @@ class Sponsor_test : public beast::unit_test::suite submit(ticket::create(alice, 250)); }); - auto const keylet = keylet::ticket(alice, ticketSeq); + auto const keylet = keylet::TicketT()(alice, ticketSeq); BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id()); // transfer sponsor if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -2986,7 +2986,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor2.id()); // use a Ticket - env(noop(alice), ticket::use(ticketSeq)); + env(noop(alice), ticket::Use(ticketSeq)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 249); @@ -3011,7 +3011,7 @@ class Sponsor_test : public beast::unit_test::suite // CredentialsCreate { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), issuer, subject, sponsor, sponsor2); env.close(); @@ -3024,7 +3024,7 @@ class Sponsor_test : public beast::unit_test::suite 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(credentials::create(subject, issuer, credType), credentials::uri("uri")); + submit(credentials::create(subject, issuer, credType), credentials::Uri("uri")); }); BEAST_EXPECT(ownerCount(env, subject) == 0); @@ -3035,17 +3035,17 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(issuer, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(issuer)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(issuer)); env.close(); env(sponsor::transfer(issuer, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -3080,17 +3080,17 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(subject, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(subject)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(subject)); env.close(); env(sponsor::transfer(subject, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -3107,7 +3107,7 @@ class Sponsor_test : public beast::unit_test::suite } { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), issuer, subject, sponsor); env.close(); @@ -3152,7 +3152,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); @@ -3174,17 +3174,17 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -3213,7 +3213,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, sponsor, sponsor2); env.close(); @@ -3233,17 +3233,17 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } @@ -3273,7 +3273,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, sponsor, sponsor2); env.close(); @@ -3286,23 +3286,23 @@ class Sponsor_test : public beast::unit_test::suite 1, 1, tecINSUFFICIENT_RESERVE, - [&](Env& env, auto const& submit) { submit(did::set(alice), did::uri("uri")); }); + [&](Env& env, auto const& submit) { submit(did::set(alice), did::Uri("uri")); }); // transfer sponsor auto const keylet = keylet::did(alice); if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -3335,7 +3335,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); { // Native Escrow - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; auto const baseFee = env.current()->fees().base; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); @@ -3355,8 +3355,8 @@ class Sponsor_test : public beast::unit_test::suite seq = env.seq(alice); submit( escrow::create(alice, bob, XRP(100)), - escrow::condition(escrow::cb1), - escrow::cancel_time(env.now() + 100s)); + escrow::kCONDITION(escrow::kCB1), + escrow::kCANCEL_TIME(env.now() + 100s)); }); BEAST_EXPECT( env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); @@ -3365,17 +3365,17 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::escrow(alice, seq).key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::escrow(alice, seq).key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -3389,9 +3389,9 @@ class Sponsor_test : public beast::unit_test::suite // EscrowFinish env(escrow::finish(bob, alice, seq), - escrow::condition(escrow::cb1), - escrow::fulfillment(escrow::fb1), - fee(baseFee * 150)); + escrow::kCONDITION(escrow::kCB1), + escrow::kFULFILLMENT(escrow::kFB1), + Fee(baseFee * 150)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); @@ -3401,10 +3401,10 @@ class Sponsor_test : public beast::unit_test::suite } Account const gw("gw"); - auto const USD = gw["USD"]; + auto const usd = gw["usd"]; { // IOU Escrow - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; auto const baseFee = env.current()->fees().base; env.fund(XRP(1000000), alice, bob, gw, sponsor, sponsor2); @@ -3413,9 +3413,9 @@ class Sponsor_test : public beast::unit_test::suite env(fset(gw, asfAllowTrustLineLocking)); env.close(); - env.trust(USD(1000000), alice); + env.trust(usd(1000000), alice); env.close(); - env(pay(gw, alice, USD(10000))); + env(pay(gw, alice, usd(10000))); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -3433,9 +3433,9 @@ class Sponsor_test : public beast::unit_test::suite [&](Env& env, auto const& submit) { seq = env.seq(alice); submit( - escrow::create(alice, bob, USD(100)), - escrow::condition(escrow::cb1), - escrow::cancel_time(env.now() + 100s)); + escrow::create(alice, bob, usd(100)), + escrow::kCONDITION(escrow::kCB1), + escrow::kCANCEL_TIME(env.now() + 100s)); }); BEAST_EXPECT( @@ -3453,9 +3453,9 @@ class Sponsor_test : public beast::unit_test::suite [&](Env& env, auto const& submit) { submit( escrow::finish(bob, alice, seq), - escrow::condition(escrow::cb1), - escrow::fulfillment(escrow::fb1), - fee(baseFee * 150)); + escrow::kCONDITION(escrow::kCB1), + escrow::kFULFILLMENT(escrow::kFB1), + Fee(baseFee * 150)); }); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -3463,12 +3463,12 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT( - env.le(keylet::line(bob, gw, USD.currency))->getAccountID(sfHighSponsor) == + env.le(keylet::line(bob, gw, usd.currency))->getAccountID(sfHighSponsor) == sponsor2.id()); } { // MPT Escrow - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), bob, sponsor); env.close(); @@ -3476,15 +3476,15 @@ class Sponsor_test : public beast::unit_test::suite mptGw.create( {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); mptGw.authorize({.account = alice}); - auto const MPT = mptGw["MPT"]; - env(pay(gw, alice, MPT(10'000))); + auto const mpt = mptGw["MPT"]; + env(pay(gw, alice, mpt(10'000))); env.close(); // create Escrow from alice to bob auto const seq = env.seq(alice); - env(escrow::create(alice, bob, MPT(100)), - escrow::condition(escrow::cb1), - escrow::cancel_time(env.now() + 100s)); + env(escrow::create(alice, bob, mpt(100)), + escrow::kCONDITION(escrow::kCB1), + escrow::kCANCEL_TIME(env.now() + 100s)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -3493,11 +3493,11 @@ class Sponsor_test : public beast::unit_test::suite // finish Escrow env(escrow::finish(bob, alice, seq), - escrow::condition(escrow::cb1), - escrow::fulfillment(escrow::fb1), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - fee(XRP(1))); + escrow::kCONDITION(escrow::kCB1), + escrow::kFULFILLMENT(escrow::kFB1), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Fee(XRP(1))); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -3518,12 +3518,12 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); // MPTokenIssuanceCreate - Json::Value jv = {}; + json::Value jv = {}; jv[sfAccount] = alice.human(); jv[sfTransactionType] = jss::MPTokenIssuanceCreate; MPTID mptid; @@ -3546,17 +3546,17 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, mptIssuanceKeylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, mptIssuanceKeylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -3591,17 +3591,17 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(bob, tfSponsorshipReassign, mptTokenKeylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(bob)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(bob)); env.close(); env(sponsor::transfer(bob, tfSponsorshipReassign, mptTokenKeylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -3641,12 +3641,12 @@ class Sponsor_test : public beast::unit_test::suite } { // check INSUFFICIENT_RESERVE for MPToken - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor); env.close(); // MPTokenAuthorize - Json::Value jv = {}; + json::Value jv = {}; jv[sfAccount] = alice.human(); jv[sfTransactionType] = jss::MPTokenIssuanceCreate; auto const mptid = makeMptID(env.seq(alice), alice.id()); @@ -3670,21 +3670,21 @@ class Sponsor_test : public beast::unit_test::suite { adjustAccountXRPBalance(env, sponsor, reserve(env, 3) - drops(1)); env(jv, - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tecINSUFFICIENT_RESERVE)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecINSUFFICIENT_RESERVE)); env.close(); } else { - env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), sponsor::sponseeAcc(bob)); + env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), sponsor::SponseeAcc(bob)); env.close(); - env(jv, sponsor::as(sponsor, spfSponsorReserve), ter(tecINSUFFICIENT_RESERVE)); + env(jv, sponsor::As(sponsor, spfSponsorReserve), Ter(tecINSUFFICIENT_RESERVE)); env.close(); } - env(noop(sponsor), ticket::use(ticketSeq)); + env(noop(sponsor), ticket::Use(ticketSeq)); env.close(); // pass (free mptoken) @@ -3692,16 +3692,16 @@ class Sponsor_test : public beast::unit_test::suite { adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); env(jv, - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tesSUCCESS)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); env.close(); } else { - env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(bob)); + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(bob)); env.close(); - env(jv, sponsor::as(sponsor, spfSponsorReserve), ter(tesSUCCESS)); + env(jv, sponsor::As(sponsor, spfSponsorReserve), Ter(tesSUCCESS)); env.close(); } } @@ -3718,7 +3718,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); @@ -3739,21 +3739,21 @@ class Sponsor_test : public beast::unit_test::suite }); // transfer sponsor - auto const keylet = keylet::nftpage_max(alice); + auto const keylet = keylet::nftpageMax(alice); if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); } // NFTokenBurn env(token::burn(alice, nftId)); @@ -3774,13 +3774,13 @@ class Sponsor_test : public beast::unit_test::suite 2, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(token::mint(alice), token::amount(XRP(100))); + submit(token::mint(alice), token::Amount(XRP(100))); }); } { // multiple nft page process - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor); env.close(); @@ -3793,17 +3793,17 @@ class Sponsor_test : public beast::unit_test::suite for (auto i = 0; i < nftCount; i++) { env(token::mint(alice), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); } } else { - env(sponsor::set_reserve(sponsor, 0, 8), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, 0, 8), sponsor::SponseeAcc(alice)); env.close(); for (auto i = 0; i < nftCount; i++) { - env(token::mint(alice), sponsor::as(sponsor, spfSponsorReserve)); + env(token::mint(alice), sponsor::As(sponsor, spfSponsorReserve)); } } env.close(); @@ -3840,13 +3840,13 @@ class Sponsor_test : public beast::unit_test::suite { // Mint + CreateOffer + CancelOffer - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); // Mint uint256 const nftId{token::getNextID(env, alice, taxon, tfTransferable)}; - env(token::mint(alice, taxon), txflags(tfTransferable)); + env(token::mint(alice, taxon), Txflags(tfTransferable)); env.close(); // NFTokenOfferCreate @@ -3863,8 +3863,8 @@ class Sponsor_test : public beast::unit_test::suite offerIndex1 = keylet::nftoffer(alice, env.seq(alice)).key; submit( token::createOffer(alice, nftId, XRP(1)), - token::destination(bob), - txflags(tfSellNFToken)); + token::Destination(bob), + Txflags(tfSellNFToken)); }); uint256 offerIndex2; @@ -3880,25 +3880,25 @@ class Sponsor_test : public beast::unit_test::suite offerIndex2 = keylet::nftoffer(alice, env.seq(alice)).key; submit( token::createOffer(alice, nftId, XRP(1)), - token::destination(bob), - txflags(tfSellNFToken)); + token::Destination(bob), + Txflags(tfSellNFToken)); }); // transfer sponsor if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, offerIndex1), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, offerIndex1), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -3919,13 +3919,13 @@ class Sponsor_test : public beast::unit_test::suite { // Mint + CreateSellOffer + AcceptSellOffer - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor); env.close(); // Mint uint256 const nftId{token::getNextID(env, alice, taxon, tfTransferable)}; - env(token::mint(alice, taxon), txflags(tfTransferable)); + env(token::mint(alice, taxon), Txflags(tfTransferable)); env.close(); // NFTokenOfferCreate @@ -3942,8 +3942,8 @@ class Sponsor_test : public beast::unit_test::suite offerIndex = keylet::nftoffer(alice, env.seq(alice)).key; submit( token::createOffer(alice, nftId, XRP(1)), - token::destination(bob), - txflags(tfSellNFToken)); + token::Destination(bob), + Txflags(tfSellNFToken)); }); // NFTokenOfferAccept @@ -3959,13 +3959,13 @@ class Sponsor_test : public beast::unit_test::suite { // Mint + CreateBuyOffer + AcceptBuyOffer - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor); env.close(); // Mint uint256 const nftId{token::getNextID(env, alice, taxon, tfTransferable)}; - env(token::mint(alice, taxon), txflags(tfTransferable)); + env(token::mint(alice, taxon), Txflags(tfTransferable)); env.close(); // NFTokenOfferCreate @@ -3982,8 +3982,8 @@ class Sponsor_test : public beast::unit_test::suite offerIndex = keylet::nftoffer(bob, env.seq(bob)).key; submit( token::createOffer(bob, nftId, XRP(1)), - token::owner(alice), - token::destination(alice)); + token::Owner(alice), + token::Destination(alice)); }); // NFTokenOfferAccept @@ -3998,13 +3998,13 @@ class Sponsor_test : public beast::unit_test::suite } { // Broker - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, broker, sponsor, sponsor2); env.close(); // Mint uint256 const nftId{token::getNextID(env, alice, taxon, tfTransferable)}; - env(token::mint(alice, taxon), txflags(tfTransferable)); + env(token::mint(alice, taxon), Txflags(tfTransferable)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 1); @@ -4023,8 +4023,8 @@ class Sponsor_test : public beast::unit_test::suite buyOfferIndex = keylet::nftoffer(bob, env.seq(bob)).key; submit( token::createOffer(bob, nftId, XRP(1)), - token::owner(alice), - token::destination(broker)); + token::Owner(alice), + token::Destination(broker)); }); // NFTokenOfferCreate (SellOffer) @@ -4041,8 +4041,8 @@ class Sponsor_test : public beast::unit_test::suite sellOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key; submit( token::createOffer(alice, nftId, XRP(1)), - txflags(tfSellNFToken), - token::destination(broker)); + Txflags(tfSellNFToken), + token::Destination(broker)); }); // NFTokenOfferAccept @@ -4070,7 +4070,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor2("sponsor2"); { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); @@ -4095,17 +4095,17 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, chan), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, chan), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -4116,7 +4116,7 @@ class Sponsor_test : public beast::unit_test::suite env.close(env.now() + settleDelay); // PayChanClaim (delete PayChan) - env(paychan::claim(bob, chan), txflags(tfClose)); + env(paychan::claim(bob, chan), Txflags(tfClose)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); @@ -4135,12 +4135,12 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, sponsor, sponsor2); env.close(); // PermissionedDomainSet - pdomain::Credentials credentials{{alice, "first credential"}}; + pdomain::Credentials credentials{{.issuer = alice, .credType = "first credential"}}; uint32_t seq = 0; testEachSponsorship( env, @@ -4161,16 +4161,16 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -4207,16 +4207,16 @@ class Sponsor_test : public beast::unit_test::suite auto const oracleSet = [](Env& env, Account const& account, uint8_t dataSeriesSize) { auto const now = env.timeKeeper().now(); - env.close(now + oracle::testStartTime - epoch_offset); - Json::Value jv; + env.close(now + oracle::kTEST_START_TIME - kEPOCH_OFFSET); + json::Value jv; jv[jss::TransactionType] = jss::OracleSet; jv[jss::Account] = to_string(account); jv[jss::OracleDocumentID] = 1; jv[jss::LastUpdateTime] = to_string( duration_cast(env.current()->header().closeTime.time_since_epoch()) .count() + - epoch_offset.count() + 100); - jv[jss::PriceDataSeries] = Json::arrayValue; + kEPOCH_OFFSET.count() + 100); + jv[jss::PriceDataSeries] = json::ArrayValue; jv[jss::Provider] = strHex(std::string{"provider"}); jv[jss::AssetClass] = strHex(std::string{"currency"}); @@ -4235,11 +4235,11 @@ class Sponsor_test : public beast::unit_test::suite DataSeries const actualSeries(series.begin(), series.begin() + dataSeriesSize); - Json::Value dataSeries(Json::arrayValue); + json::Value dataSeries(json::ArrayValue); for (auto const& data : actualSeries) { - Json::Value priceData; - Json::Value price; + json::Value priceData; + json::Value price; price[jss::BaseAsset] = std::get<0>(data); price[jss::QuoteAsset] = std::get<1>(data); price[jss::AssetPrice] = std::get<2>(data); @@ -4252,7 +4252,7 @@ class Sponsor_test : public beast::unit_test::suite }; auto const oracleDelete = [&](Account const& account) { - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::OracleDelete; jv[jss::Account] = to_string(account); jv[jss::OracleDocumentID] = 1; @@ -4260,7 +4260,7 @@ class Sponsor_test : public beast::unit_test::suite }; { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, sponsor, sponsor2); env.close(); @@ -4281,16 +4281,16 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -4325,16 +4325,16 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 2), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 2), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -4468,17 +4468,17 @@ class Sponsor_test : public beast::unit_test::suite { env(sponsor::transfer( alice, tfSponsorshipReassign, keylet::oracle(alice, 1).key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, ocount), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, ocount), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer( alice, tfSponsorshipReassign, keylet::oracle(alice, 1).key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -4512,7 +4512,7 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); Account const sponsor2("sponsor2"); - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); @@ -4533,28 +4533,28 @@ class Sponsor_test : public beast::unit_test::suite // invalid signer list owner 1 // account doesn't have signer list but specified signer list exists env(sponsor::transfer(bob, tfSponsorshipReassign, keylet::signers(alice).key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2), - ter(tecNO_PERMISSION)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); // invalid signer list owner 2 // account has signer list and specified signer list exists env(signers(bob, 1, {{alice, 1}})); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::signers(bob).key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2), - ter(tecNO_PERMISSION)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::signers(alice).key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::signers(alice).key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -4564,7 +4564,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); // Delete - env(signers(alice, none)); + env(signers(alice, NoneT())); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); @@ -4597,15 +4597,15 @@ class Sponsor_test : public beast::unit_test::suite // create and delete for (bool const isIssuerHigh : {false, true}) { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, charlie, sponsor, sponsor2); env.close(); auto const& issuer = isIssuerHigh ? highAcc : lowAcc; auto const& user = isIssuerHigh ? lowAcc : highAcc; - auto const USD = issuer["USD"]; - auto const currency = USD.currency; + auto const usd = issuer["usd"]; + auto const currency = usd.currency; // create TrustLine if (cosigning) @@ -4623,7 +4623,7 @@ class Sponsor_test : public beast::unit_test::suite 1, 1, tecNO_LINE_INSUF_RESERVE, - [&](Env& env, auto const& submit) { submit(trust(user, USD(100))); }); + [&](Env& env, auto const& submit) { submit(trust(user, usd(100))); }); auto const keylet = keylet::line(user, issuer, currency); @@ -4631,30 +4631,30 @@ class Sponsor_test : public beast::unit_test::suite { // invalid owner env(sponsor::transfer(charlie, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2), - ter(tecNO_PERMISSION)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); // invalid reserve owner env(sponsor::transfer(issuer, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2), - ter(tecNO_PERMISSION)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); env(sponsor::transfer(user, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(user)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(user)); env.close(); env(sponsor::transfer(user, tfSponsorshipReassign, keylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } // delete TrustLine - env(trust(user, USD(0))); + env(trust(user, usd(0))); env.close(); BEAST_EXPECT(ownerCount(env, user) == 0); @@ -4667,18 +4667,18 @@ class Sponsor_test : public beast::unit_test::suite // update for (bool const isIssuerHigh : {false, true}) { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); auto const& issuer = isIssuerHigh ? highAcc : lowAcc; auto const& user = isIssuerHigh ? lowAcc : highAcc; - auto const USD = issuer["USD"]; - auto const currency = USD.currency; + auto const usd = issuer["usd"]; + auto const currency = usd.currency; // create TrustLine from issuer - env(trust(issuer, user["USD"](100))); + env(trust(issuer, user["usd"](100))); env.close(); BEAST_EXPECT(env.le(keylet::line(user, issuer, currency))); @@ -4698,13 +4698,13 @@ class Sponsor_test : public beast::unit_test::suite 1, 1, tecINSUF_RESERVE_LINE, - [&](Env& env, auto const& submit) { submit(trust(user, USD(100))); }); + [&](Env& env, auto const& submit) { submit(trust(user, usd(100))); }); auto const line = env.le(keylet::line(user, issuer, currency)); validateSponsoredTrustline(line, isIssuerHigh, sponsor); // update TrustLine from user to clear reserve - env(trust(user, USD(0))); + env(trust(user, usd(0))); env.close(); BEAST_EXPECT(ownerCount(env, user) == 0); @@ -4713,28 +4713,28 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(env.le(keylet::line(user, issuer, currency))); // remove TrustLine from issuer - env(trust(issuer, user["USD"](0))); + env(trust(issuer, user["usd"](0))); env.close(); BEAST_EXPECT(!env.le(keylet::line(user, issuer, currency))); } // both High and Low sponsored { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor); env.close(); // create TrustLines - env(trust(alice, bob["USD"](100)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(trust(alice, bob["usd"](100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); - env(trust(bob, alice["USD"](100)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(trust(bob, alice["usd"](100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); - auto sle = env.le(keylet::line(alice, bob, alice["USD"].currency)); + auto sle = env.le(keylet::line(alice, bob, alice["usd"].currency)); BEAST_EXPECT(sle); BEAST_EXPECT(sle->isFlag(lsfHighReserve)); BEAST_EXPECT(sle->isFlag(lsfLowReserve)); @@ -4748,12 +4748,12 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); // clear TrustLines - env(trust(alice, bob["USD"](0))); + env(trust(alice, bob["usd"](0))); env.close(); - env(trust(bob, alice["USD"](0))); + env(trust(bob, alice["usd"](0))); env.close(); - sle = env.le(keylet::line(alice, bob, alice["USD"].currency)); + sle = env.le(keylet::line(alice, bob, alice["usd"].currency)); BEAST_EXPECT(!sle); BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -4778,7 +4778,7 @@ class Sponsor_test : public beast::unit_test::suite // VaultCreate { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, gw, sponsor); env.close(); @@ -4805,7 +4805,7 @@ class Sponsor_test : public beast::unit_test::suite } // VaultDeposit { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, gw, sponsor); env.close(); @@ -4841,7 +4841,7 @@ class Sponsor_test : public beast::unit_test::suite { // RippleState Vault { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, gw, sponsor); env.close(); @@ -4913,7 +4913,7 @@ class Sponsor_test : public beast::unit_test::suite // VaultClawback { // remove sponsored shares MPToken - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, gw, sponsor); env.close(); @@ -4951,8 +4951,8 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); // MPToken(share) env(vault.clawback({.issuer = gw, .id = keylet.key, .holder = bob, .amount = asset(0)}), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState @@ -4961,7 +4961,7 @@ class Sponsor_test : public beast::unit_test::suite } // VaultDelete { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, gw, sponsor); env.close(); @@ -4970,7 +4970,7 @@ class Sponsor_test : public beast::unit_test::suite Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); - env(tx, sponsor::as(sponsor, spfSponsorReserve), sig(sfSponsorSignature, sponsor)); + env(tx, sponsor::As(sponsor, spfSponsorReserve), Sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 3); // Vault, PseudoAccount, MPToken(share) @@ -4997,7 +4997,7 @@ class Sponsor_test : public beast::unit_test::suite Account const signer("signer"); Account const sponsor("sponsor"); - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor, doorA); env.close(); @@ -5017,7 +5017,7 @@ class Sponsor_test : public beast::unit_test::suite 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(bridge_create(doorA, jvb, XRP(1), XRP(1))); + submit(bridgeCreate(doorA, jvb, XRP(1), XRP(1))); }); } // XChainCreateClaimID @@ -5031,7 +5031,7 @@ class Sponsor_test : public beast::unit_test::suite 1, tecINSUFFICIENT_RESERVE, [&](Env& env, auto const& submit) { - submit(xchain_create_claim_id(alice, jvb, XRP(1), bob)); + submit(xchainCreateClaimId(alice, jvb, XRP(1), bob)); }); } // XChainCommit @@ -5042,21 +5042,21 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { - env(xchain_commit(alice, jvb, 1, XRP(100), bob), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(xchainCommit(alice, jvb, 1, XRP(100), bob), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); } else { - env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(alice)); env.close(); - env(xchain_commit(alice, jvb, 1, XRP(100), bob), - sponsor::as(sponsor, spfSponsorReserve)); + env(xchainCommit(alice, jvb, 1, XRP(100), bob), + sponsor::As(sponsor, spfSponsorReserve)); env.close(); - env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); + env(sponsor::del(sponsor), sponsor::SponseeAcc(alice)); env.close(); } @@ -5073,21 +5073,21 @@ class Sponsor_test : public beast::unit_test::suite if (cosigning) { - env(claim_attestation(alice, jvb, bob, XRP(1), bob, false, 1, bob, signer), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + env(claimAttestation(alice, jvb, bob, XRP(1), bob, false, 1, bob, signer), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); } else { - env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(alice)); env.close(); - env(claim_attestation(alice, jvb, bob, XRP(1), bob, false, 1, bob, signer), - sponsor::as(sponsor, spfSponsorReserve)); + env(claimAttestation(alice, jvb, bob, XRP(1), bob, false, 1, bob, signer), + sponsor::As(sponsor, spfSponsorReserve)); env.close(); - env(sponsor::del(sponsor), sponsor::sponseeAcc(alice)); + env(sponsor::del(sponsor), sponsor::SponseeAcc(alice)); env.close(); } @@ -5100,11 +5100,11 @@ class Sponsor_test : public beast::unit_test::suite { // prepare for claim { - env(xchain_create_claim_id(alice, jvb, XRP(1), bob), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); - env(xchain_commit(alice, jvb, 2, XRP(100))); // omit destination - env(claim_attestation( + env(xchainCreateClaimId(alice, jvb, XRP(1), bob), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env(xchainCommit(alice, jvb, 2, XRP(100))); // omit destination + env(claimAttestation( alice, jvb, bob, XRP(100), bob, false, 2, std::nullopt, signer)); env.close(); } @@ -5113,7 +5113,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); - env(xchain_claim(alice, jvb, 2, XRP(100), bob)); + env(xchainClaim(alice, jvb, 2, XRP(100), bob)); env.close(); // XChainOwnedClaimID deleted @@ -5136,7 +5136,7 @@ class Sponsor_test : public beast::unit_test::suite // LoanBrokerSet / LoanBrokerDelete { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); @@ -5177,18 +5177,18 @@ class Sponsor_test : public beast::unit_test::suite { // transfer sponsor env(sponsor::transfer(alice, tfSponsorshipReassign, brokerKeylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); // transfer sponsor env(sponsor::transfer(alice, tfSponsorshipReassign, brokerKeylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -5210,11 +5210,11 @@ class Sponsor_test : public beast::unit_test::suite // LoanBrokerConverDeposit/Withdraw/Clawback { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000), alice, bob, issuer, sponsor); env.close(); - MPTTester mptt{env, issuer, mptInitNoFund}; + MPTTester mptt{env, issuer, kMPT_INIT_NO_FUND}; mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); env.close(); PrettyAsset const asset = mptt["MPT"]; @@ -5241,8 +5241,8 @@ class Sponsor_test : public beast::unit_test::suite // LoanBrokerCoverDeposit // doesn't sponsor anything env(loanBroker::coverDeposit(alice, brokerKeylet.key, asset(100)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 6); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); @@ -5276,10 +5276,10 @@ class Sponsor_test : public beast::unit_test::suite // LoanBrokerCoverClawback // doesn't sponsor anything env(loanBroker::coverClawback(issuer), - loanBroker::loanBrokerID(brokerKeylet.key), - amount(asset(1)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + loanBroker::kLOAN_BROKER_ID(brokerKeylet.key), + kAMOUNT(asset(1)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 6); @@ -5288,11 +5288,11 @@ class Sponsor_test : public beast::unit_test::suite } // LoanSet { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, issuer, sponsor, sponsor2); env.close(); - MPTTester mptt{env, issuer, mptInitNoFund}; + MPTTester mptt{env, issuer, kMPT_INIT_NO_FUND}; mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); env.close(); PrettyAsset const asset = mptt["MPT"]; @@ -5334,8 +5334,8 @@ class Sponsor_test : public beast::unit_test::suite [&](Env& env, auto const& submit) { submit( loan::set(alice, brokerKeylet.key, 10), - sig(sfCounterpartySignature, bob), - fee(XRP(1))); + Sig(sfCounterpartySignature, bob), + Fee(XRP(1))); }); broker = env.le(brokerKeylet); // broker'object doesn't sponsored @@ -5352,8 +5352,8 @@ class Sponsor_test : public beast::unit_test::suite // LoanManage env(loan::manage(bob, loanKeylet.key, lsfLoanImpaired), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); // doesn't sponsor anything @@ -5364,8 +5364,8 @@ class Sponsor_test : public beast::unit_test::suite // LoanPay env(loan::pay(alice, loanKeylet.key, asset(10)), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); // doesn't sponsor anything @@ -5386,18 +5386,18 @@ class Sponsor_test : public beast::unit_test::suite { // transfer sponsor env(sponsor::transfer(alice, tfSponsorshipReassign, loanKeylet.key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); } else { - env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::sponseeAcc(alice)); + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); env.close(); // transfer sponsor env(sponsor::transfer(alice, tfSponsorshipReassign, loanKeylet.key), - sponsor::as(sponsor2, spfSponsorReserve)); + sponsor::As(sponsor2, spfSponsorReserve)); env.close(); } @@ -5409,8 +5409,8 @@ class Sponsor_test : public beast::unit_test::suite // LoanDelete env(loan::del(alice, loanKeylet.key), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); // Sponsored ltLoan is deleted @@ -5434,14 +5434,14 @@ class Sponsor_test : public beast::unit_test::suite { // Delete Sponsor/Sponsee Account with ltSponsorship (tecHAS_OBLIGATIONS) - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor); env.close(); // set sponsor env(sponsor::set(sponsor, 0, 100, XRP(100)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); env.close(); incLgrSeqForAccDel(env, sponsor); @@ -5452,26 +5452,26 @@ class Sponsor_test : public beast::unit_test::suite // AccountDelete auto const requiredFee = drops(env.current()->fees().increment); - env(acctdelete(alice, bob), fee(requiredFee), ter(tecHAS_OBLIGATIONS)); - env(acctdelete(sponsor, bob), fee(requiredFee), ter(tecHAS_OBLIGATIONS)); + env(acctdelete(alice, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); + env(acctdelete(sponsor, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); } { // Delete SponsoredAccount - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.memoize(alice); env.fund(XRP(1000000), bob, sponsor); env.close(); // create SponsoredAccount - env(pay(sponsor, alice, XRP(10000)), txflags(tfSponsorCreatedAccount)); + env(pay(sponsor, alice, XRP(10000)), Txflags(tfSponsorCreatedAccount)); env.close(); incLgrSeqForAccDel(env, alice); // AccountDelete: destination = non-sponsor auto const requiredFee = drops(env.current()->fees().increment); - env(acctdelete(alice, bob), fee(requiredFee), ter(tecNO_SPONSOR_PERMISSION)); + env(acctdelete(alice, bob), Fee(requiredFee), Ter(tecNO_SPONSOR_PERMISSION)); auto const sponsorSle = env.le(keylet::account(sponsor)); BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringAccountCount) == 1); @@ -5479,7 +5479,7 @@ class Sponsor_test : public beast::unit_test::suite incLgrSeqForAccDel(env, alice); // AccountDelete: destination = sponsor - env(acctdelete(alice, sponsor), fee(requiredFee), ter(tesSUCCESS)); + env(acctdelete(alice, sponsor), Fee(requiredFee), Ter(tesSUCCESS)); auto const sponsorSle2 = env.le(keylet::account(sponsor)); BEAST_EXPECT(!sponsorSle2->isFieldPresent(sfSponsoringAccountCount)); @@ -5487,30 +5487,30 @@ class Sponsor_test : public beast::unit_test::suite { // Sponsor with sfSponsoringOwnerCount cannot delete (tecHAS_OBLIGATIONS) - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; Account const gw("gw"); env.fund(XRP(1000000), alice, bob, sponsor, gw); env.close(); - auto const USD = gw["USD"]; + auto const usd = gw["usd"]; // Create sponsorship allowing reserve sponsoring env(sponsor::set(sponsor, 0, 100, XRP(100)), - sponsor::sponseeAcc(alice), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); env.close(); // Create a trust line for alice - env(trust(alice, USD(1000))); + env(trust(alice, usd(1000))); env.close(); // Transfer reserve sponsorship of trust line to sponsor - auto const trustId = keylet::line(alice, gw, USD.currency); + auto const trustId = keylet::line(alice, gw, usd.currency); BEAST_EXPECT(env.le(trustId)); env(sponsor::transfer(alice, tfSponsorshipCreate, trustId.key), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); // Verify sfSponsoringOwnerCount is set on sponsor @@ -5522,18 +5522,18 @@ class Sponsor_test : public beast::unit_test::suite // AccountDelete should fail auto const requiredFee = drops(env.current()->fees().increment); - env(acctdelete(sponsor, bob), fee(requiredFee), ter(tecHAS_OBLIGATIONS)); + env(acctdelete(sponsor, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); } { // Sponsor with sfSponsoringAccountCount cannot delete (tecHAS_OBLIGATIONS) - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.memoize(alice); env.fund(XRP(1000000), bob, sponsor); env.close(); // Create SponsoredAccount (sets sfSponsoringAccountCount on sponsor) - env(pay(sponsor, alice, XRP(10000)), txflags(tfSponsorCreatedAccount)); + env(pay(sponsor, alice, XRP(10000)), Txflags(tfSponsorCreatedAccount)); env.close(); // Verify sfSponsoringAccountCount is set on sponsor @@ -5545,7 +5545,7 @@ class Sponsor_test : public beast::unit_test::suite // AccountDelete should fail auto const requiredFee = drops(env.current()->fees().increment); - env(acctdelete(sponsor, bob), fee(requiredFee), ter(tecHAS_OBLIGATIONS)); + env(acctdelete(sponsor, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); } } @@ -5562,7 +5562,7 @@ class Sponsor_test : public beast::unit_test::suite // SponsorshipTransfer // { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, carol); env.close(); @@ -5573,41 +5573,41 @@ class Sponsor_test : public beast::unit_test::suite auto const keylet = keylet::check(alice, seq); env(sponsor::transfer(alice, tfSponsorshipCreate, keylet.key), - sponsor::as(bob, spfSponsorReserve), - sig(sfSponsorSignature, bob), - delegate::as(carol), - ter(terNO_DELEGATE_PERMISSION)); + sponsor::As(bob, spfSponsorReserve), + Sig(sfSponsorSignature, bob), + delegate::As(carol), + Ter(terNO_DELEGATE_PERMISSION)); env(delegate::set(alice, carol, {"SponsorshipTransfer"})); env.close(); env(sponsor::transfer(alice, tfSponsorshipCreate, keylet.key), - sponsor::as(bob, spfSponsorReserve), - sig(sfSponsorSignature, bob), - delegate::as(carol), - ter(tesSUCCESS)); + sponsor::As(bob, spfSponsorReserve), + Sig(sfSponsorSignature, bob), + delegate::As(carol), + Ter(tesSUCCESS)); env.close(); } // // SponsorshipSet // { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, carol); env.close(); env(sponsor::set(alice, 0, 100, XRP(100)), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(terNO_DELEGATE_PERMISSION)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(terNO_DELEGATE_PERMISSION)); env(delegate::set(alice, carol, {"SponsorshipSet"})); env.close(); env(sponsor::set(alice, 0, 100, XRP(100)), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(tesSUCCESS)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(tesSUCCESS)); env.close(); } @@ -5615,30 +5615,30 @@ class Sponsor_test : public beast::unit_test::suite // Permission SponsorFee // { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, carol); env.close(); auto const testFeePermission = [&](TER result) { // FeeAmount env(sponsor::set(alice, 0, std::nullopt, XRP(100)), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(result)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(result)); // MaxFee env(sponsor::set(alice, 0, std::nullopt, std::nullopt, XRP(100)), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(result)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(result)); // SetRequireSignForFee flag env(sponsor::set(alice, tfSponsorshipSetRequireSignForFee), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(result)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(result)); // ClearRequireSignForFee flag env(sponsor::set(alice, tfSponsorshipClearRequireSignForFee), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(result)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(result)); env.close(); }; @@ -5659,35 +5659,35 @@ class Sponsor_test : public beast::unit_test::suite // test with SponsorReserve (should failed) env(sponsor::set(alice, 0, 100, XRP(100)), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(terNO_DELEGATE_PERMISSION)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(terNO_DELEGATE_PERMISSION)); } // // Permission SponsorReserve // { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, carol); env.close(); auto const testReservePermission = [&](TER result) { // ReserveCount env(sponsor::set(alice, 0, 100), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(result)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(result)); // SetRequireSignForReserve flag env(sponsor::set(alice, tfSponsorshipSetRequireSignForReserve), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(result)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(result)); // ClearRequireSignForReserve flag env(sponsor::set(alice, tfSponsorshipClearRequireSignForReserve), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(result)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(result)); env.close(); }; @@ -5708,9 +5708,9 @@ class Sponsor_test : public beast::unit_test::suite // test with SponsorFee (should failed) env(sponsor::set(alice, 0, 100, XRP(100)), - sponsor::sponseeAcc(bob), - delegate::as(carol), - ter(terNO_DELEGATE_PERMISSION)); + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(terNO_DELEGATE_PERMISSION)); } } @@ -5724,21 +5724,21 @@ class Sponsor_test : public beast::unit_test::suite Account const sponsor("sponsor"); // - // Outer transaction + // outer transaction // { // test outer transaction with co-signing sponsor - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000), alice, bob, sponsor); env.close(); auto const seq = env.seq(alice); env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), - batch::inner(noop(alice), seq + 1), - batch::inner(ticket::create(alice, 1), seq + 2), - sponsor::as(sponsor, spfSponsorFee), - sig(sfSponsorSignature, sponsor), - ter(tesSUCCESS)); + batch::Inner(noop(alice), seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); env.close(); // does not affect reserve @@ -5746,12 +5746,12 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - // fee is paid by sponsor + // Fee is paid by sponsor BEAST_EXPECT(env.balance(alice) == XRP(1000)); BEAST_EXPECT(env.balance(sponsor) == XRP(1000 - 1)); } { - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000), alice, bob, sponsor); env.close(); @@ -5760,33 +5760,33 @@ class Sponsor_test : public beast::unit_test::suite { auto const seq = env.seq(alice); env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), - batch::inner(noop(alice), seq + 1), - batch::inner(noop(alice), seq + 2), - sponsor::as(sponsor, flags), - sig(sfSponsorSignature, sponsor), - ter(temINVALID_FLAG)); + batch::Inner(noop(alice), seq + 1), + batch::Inner(noop(alice), seq + 2), + sponsor::As(sponsor, flags), + Sig(sfSponsorSignature, sponsor), + Ter(temINVALID_FLAG)); env.close(); } } { // test outer transaction with prefunded sponsor - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000), alice, bob); env.fund(XRP(1001), sponsor); env.close(); env(sponsor::set(sponsor, 0, 100, XRP(100)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); env.close(); auto const seq = env.seq(alice); env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), - batch::inner(noop(alice), seq + 1), - batch::inner(ticket::create(alice, 1), seq + 2), - sponsor::as(sponsor, spfSponsorFee), - ter(tesSUCCESS)); + batch::Inner(noop(alice), seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + sponsor::As(sponsor, spfSponsorFee), + Ter(tesSUCCESS)); env.close(); // does not affect reserve @@ -5794,7 +5794,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - // fee is paid by sponsor object + // Fee is paid by sponsor object BEAST_EXPECT(env.balance(alice) == XRP(1000)); BEAST_EXPECT(env.balance(sponsor) == XRP(900)); @@ -5807,54 +5807,54 @@ class Sponsor_test : public beast::unit_test::suite // Inner transaction // { - // test invalid inner transaction with co-signing sponsor + // test invalid Inner transaction with co-signing sponsor Account const signerAccount("signer"); - Env env{*this, testable_amendments()}; + Env env{*this, testableAmendments()}; env.fund(XRP(1000), alice, bob, sponsor, signerAccount); env.close(); - env(signers(sponsor, 1, {signer(signerAccount, 1)})); + env(signers(sponsor, 1, {Signer(signerAccount, 1)})); env.close(); { auto jt = env.jtnofill( noop(alice), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor)); jt.jv.removeMember(sfTxnSignature.jsonName); auto const seq = env.seq(alice); - // should fail because inner transaction cannot include SponsorSignature with + // should fail because Inner transaction cannot include SponsorSignature with // TxnSignature BEAST_EXPECT(jt.jv[sfSponsorSignature.jsonName].isMember(sfTxnSignature.jsonName)); env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), - batch::inner(jt.jv, seq + 1), - batch::inner(ticket::create(alice, 1), seq + 2), - ter(temBAD_SIGNATURE)); + batch::Inner(jt.jv, seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + Ter(temBAD_SIGNATURE)); } { auto jt = env.jtnofill( noop(alice), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), - msig(sfSponsorSignature, sponsor, signerAccount)); + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Msig(sfSponsorSignature, sponsor, signerAccount)); jt.jv.removeMember(sfTxnSignature.jsonName); auto const seq = env.seq(alice); - // should fail because inner transaction cannot include SponsorSignature with + // should fail because Inner transaction cannot include SponsorSignature with // Signers BEAST_EXPECT(jt.jv[sfSponsorSignature.jsonName].isMember(sfSigners.jsonName)); env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), - batch::inner(jt.jv, seq + 1), - batch::inner(ticket::create(alice, 1), seq + 2), - ter(temBAD_SIGNER)); + batch::Inner(jt.jv, seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + Ter(temBAD_SIGNER)); } { auto jt = env.jtnofill( noop(alice), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor)); jt.jv.removeMember(sfTxnSignature.jsonName); jt.jv[sfSponsorSignature.jsonName].removeMember(sfTxnSignature.jsonName); jt.jv[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = ""; @@ -5862,36 +5862,36 @@ class Sponsor_test : public beast::unit_test::suite auto const seq = env.seq(alice); // should fail BatchSigners does have signer for SponsorSignature env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), - batch::inner(jt.jv, seq + 1), - batch::inner(ticket::create(alice, 1), seq + 2), - ter(temBAD_SIGNER)); + batch::Inner(jt.jv, seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + Ter(temBAD_SIGNER)); } } { - // test inner transaction with prefunded sponsor - Env env{*this, testable_amendments()}; + // test Inner transaction with prefunded sponsor + Env env{*this, testableAmendments()}; env.fund(XRP(1000), alice, bob); env.fund(XRP(1001), sponsor); env.close(); env(sponsor::set(sponsor, 0, 100, XRP(100)), - sponsor::sponseeAcc(alice), - fee(XRP(1)), - ter(tesSUCCESS)); + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); env.close(); BEAST_EXPECT(env.balance(sponsor) == XRP(900)); auto jt = env.jtnofill( - ticket::create(alice, 1), sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee)); + ticket::create(alice, 1), sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee)); // remove txn signature since it is filled by env.jtnofill() jt.jv.removeMember(jss::TxnSignature); auto const seq = env.seq(alice); env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), - batch::inner(noop(alice), seq + 1), - batch::inner(jt.jv, seq + 2), - ter(tesSUCCESS)); + batch::Inner(noop(alice), seq + 1), + batch::Inner(jt.jv, seq + 2), + Ter(tesSUCCESS)); env.close(); // affect sponsor reserve @@ -5899,7 +5899,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // fee is paid by outer transaction originator (alice) + // Fee is paid by outer transaction originator (alice) BEAST_EXPECT(env.balance(alice) == XRP(999)); BEAST_EXPECT(env.balance(sponsor) == XRP(900)); @@ -5911,15 +5911,15 @@ class Sponsor_test : public beast::unit_test::suite } { - // test inner transaction with co-signing sponsor - Env env{*this, testable_amendments()}; + // test Inner transaction with co-signing sponsor + Env env{*this, testableAmendments()}; env.fund(XRP(1000), alice, bob, sponsor); env.close(); auto jt = env.jtnofill( ticket::create(alice, 1), - sponsor::as(sponsor, spfSponsorReserve | spfSponsorFee), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor)); // remove txn signature since it is filled by env.jtnofill() jt.jv.removeMember(sfTxnSignature.jsonName); jt.jv[sfSponsorSignature.jsonName].removeMember(sfTxnSignature.jsonName); @@ -5927,10 +5927,10 @@ class Sponsor_test : public beast::unit_test::suite auto const seq = env.seq(alice); env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), - batch::inner(noop(alice), seq + 1), - batch::inner(jt.jv, seq + 2), - batch::sig(sponsor), - ter(tesSUCCESS)); + batch::Inner(noop(alice), seq + 1), + batch::Inner(jt.jv, seq + 2), + batch::Sig(sponsor), + Ter(tesSUCCESS)); env.close(); // affect sponsor reserve @@ -5938,7 +5938,7 @@ class Sponsor_test : public beast::unit_test::suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // fee is paid by outer transaction originator (alice) + // Fee is paid by outer transaction originator (alice) BEAST_EXPECT(env.balance(alice) == XRP(999)); BEAST_EXPECT(env.balance(sponsor) == XRP(1000)); } diff --git a/src/test/jtx/impl/owners.cpp b/src/test/jtx/impl/owners.cpp index 017389e73f3..32bd7bdc236 100644 --- a/src/test/jtx/impl/owners.cpp +++ b/src/test/jtx/impl/owners.cpp @@ -47,19 +47,19 @@ Owners::operator()(Env& env) const } void -sponsored_owners::operator()(Env& env) const +SponsoredOwners::operator()(Env& env) const { env.test.expect(env.le(account_)->getFieldU32(sfSponsoredOwnerCount) == value_); } void -sponsoring_owners::operator()(Env& env) const +SponsoringOwners::operator()(Env& env) const { env.test.expect(env.le(account_)->getFieldU32(sfSponsoringOwnerCount) == value_); } void -sponsoring_account_count::operator()(Env& env) const +SponsoringAccountCount::operator()(Env& env) const { env.test.expect(env.le(account_)->getFieldU32(sfSponsoringAccountCount) == value_); } diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index c10f36e233c..0901a0aec3a 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -6,58 +6,58 @@ #include #include -#include #include #include #include #include +#include #include #include namespace xrpl::test::jtx::sponsor { -Json::Value +json::Value set(jtx::Account const& account, uint32_t flags, std::optional reserveCount, std::optional feeAmount, std::optional maxFee) { - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); jv[sfFlags.jsonName] = flags; if (reserveCount) jv[sfReserveCount.jsonName] = *reserveCount; if (feeAmount) - jv[sfFeeAmount.jsonName] = feeAmount->getJson(JsonOptions::none); + jv[sfFeeAmount.jsonName] = feeAmount->getJson(JsonOptions::KNone); if (maxFee) - jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::none); + jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::KNone); return jv; } -Json::Value +json::Value set_fee( jtx::Account const& account, uint32_t flags, STAmount feeAmount, std::optional maxFee) { - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); jv[sfFlags.jsonName] = flags; - jv[sfFeeAmount.jsonName] = feeAmount.getJson(JsonOptions::none); + jv[sfFeeAmount.jsonName] = feeAmount.getJson(JsonOptions::KNone); if (maxFee) - jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::none); + jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::KNone); return jv; } -Json::Value +json::Value set_reserve(jtx::Account const& account, uint32_t flags, uint32_t reserveCount) { - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); jv[sfFlags.jsonName] = flags; @@ -65,31 +65,31 @@ set_reserve(jtx::Account const& account, uint32_t flags, uint32_t reserveCount) return jv; } -Json::Value +json::Value set_max_fee(jtx::Account const& account, uint32_t flags, STAmount maxFee) { - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); jv[sfFlags.jsonName] = flags; - jv[sfMaxFee.jsonName] = maxFee.getJson(JsonOptions::none); + jv[sfMaxFee.jsonName] = maxFee.getJson(JsonOptions::KNone); return jv; } -Json::Value +json::Value del(jtx::Account const& account) { - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); jv[sfFlags.jsonName] = tfDeleteObject; return jv; } -Json::Value +json::Value transfer(jtx::Account const& account, uint32_t flags, std::optional const& index) { - Json::Value jv; + json::Value jv; jv[jss::TransactionType] = jss::SponsorshipTransfer; jv[jss::Account] = account.human(); jv[sfFlags.jsonName] = flags; @@ -99,28 +99,28 @@ transfer(jtx::Account const& account, uint32_t flags, std::optional con } void -counterpartySponsor::operator()(Env& env, JTx& jt) const +CounterpartySponsor::operator()(Env& env, JTx& jt) const { jt.jv[sfCounterpartySponsor.jsonName] = sponsor_.human(); } void -sponseeAcc::operator()(Env& env, JTx& jt) const +SponseeAcc::operator()(Env& env, JTx& jt) const { jt.jv[sfSponsee.jsonName] = sponsee_.human(); } void -as::operator()(Env& env, JTx& jt) const +As::operator()(Env& env, JTx& jt) const { jt.jv[sfSponsor.jsonName] = sponsor_.human(); - jt.jv[sfSponsorFlags.jsonName] = flags; + jt.jv[sfSponsorFlags.jsonName] = flags_; } -Json::Value +json::Value ledgerEntry(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& sponsee) { - Json::Value jvParams; + json::Value jvParams; jvParams[jss::ledger_index] = jss::validated; jvParams[jss::sponsorship][jss::sponsor] = sponsor.human(); jvParams[jss::sponsorship][jss::sponsee] = sponsee.human(); diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index fdfd5c386e5..2aedf2d741b 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -64,14 +64,14 @@ class Owners }; /** Match the number of items in the account's owner directory */ -class sponsored_owners +class SponsoredOwners { private: Account account_; std::uint32_t value_; public: - sponsored_owners(Account account, std::uint32_t value) + SponsoredOwners(Account account, std::uint32_t value) : account_(std::move(account)), value_(value) { } @@ -81,14 +81,14 @@ class sponsored_owners }; /** Match the number of items in the account's owner directory */ -class sponsoring_owners +class SponsoringOwners { private: Account account_; std::uint32_t value_; public: - sponsoring_owners(Account account, std::uint32_t value) + SponsoringOwners(Account account, std::uint32_t value) : account_(std::move(account)), value_(value) { } @@ -98,14 +98,14 @@ class sponsoring_owners }; /** Match the number of items in the account's owner directory */ -class sponsoring_account_count +class SponsoringAccountCount { private: Account account_; std::uint32_t value_; public: - sponsoring_account_count(Account account, std::uint32_t value) + SponsoringAccountCount(Account account, std::uint32_t value) : account_(std::move(account)), value_(value) { } diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h index de1a712fc8c..570fe431e68 100644 --- a/src/test/jtx/sponsor.h +++ b/src/test/jtx/sponsor.h @@ -8,42 +8,42 @@ namespace xrpl::test::jtx::sponsor { -Json::Value +json::Value set(jtx::Account const& account, std::uint32_t flags, std::optional reserveCount = std::nullopt, std::optional feeAmount = std::nullopt, std::optional maxFee = std::nullopt); -Json::Value +json::Value set_fee( jtx::Account const& account, std::uint32_t flags, STAmount feeAmount, std::optional maxFee = std::nullopt); -Json::Value +json::Value set_reserve(jtx::Account const& account, std::uint32_t flags, std::uint32_t reserveCount); -Json::Value +json::Value set_max_fee(jtx::Account const& account, std::uint32_t flags, STAmount maxFee); -Json::Value +json::Value del(jtx::Account const& account); -Json::Value +json::Value transfer( jtx::Account const& account, uint32_t flags, std::optional const& index = std::nullopt); -struct counterpartySponsor +struct CounterpartySponsor { private: jtx::Account sponsor_; public: - counterpartySponsor(jtx::Account account) : sponsor_(std::move(account)) + CounterpartySponsor(jtx::Account account) : sponsor_(std::move(account)) { } @@ -51,13 +51,13 @@ struct counterpartySponsor operator()(jtx::Env&, jtx::JTx& jtx) const; }; -struct sponseeAcc +struct SponseeAcc { private: jtx::Account sponsee_; public: - sponseeAcc(jtx::Account account) : sponsee_(std::move(account)) + SponseeAcc(jtx::Account account) : sponsee_(std::move(account)) { } @@ -65,14 +65,14 @@ struct sponseeAcc operator()(jtx::Env&, jtx::JTx& jtx) const; }; -struct as +struct As { private: jtx::Account sponsor_; - std::uint32_t flags; + std::uint32_t flags_; public: - as(jtx::Account account, std::uint32_t flags = 0) : sponsor_(std::move(account)), flags(flags) + As(jtx::Account account, std::uint32_t flags = 0) : sponsor_(std::move(account)), flags_(flags) { } @@ -80,7 +80,7 @@ struct as operator()(jtx::Env&, jtx::JTx& jtx) const; }; -Json::Value +json::Value ledgerEntry(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& sponsee); } // namespace xrpl::test::jtx::sponsor diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 88d34db532a..ff74b400bbe 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -698,11 +698,11 @@ class AccountObjects_test : public beast::unit_test::Suite { std::string const credentialType1 = "credential1"; - Account issuer("issuer"); + Account const issuer("issuer"); env.fund(XRP(5000), issuer); // gw creates an PermissionedDomain. - env(pdomain::setTx(gw, {{issuer, credentialType1}})); + env(pdomain::setTx(gw, {{.issuer = issuer, .credType = credentialType1}})); env.close(); // Find the PermissionedDomain. @@ -935,13 +935,13 @@ class AccountObjects_test : public beast::unit_test::Suite { // Create a sponsorship env(sponsor::set(alice, tfSponsorshipSetRequireSignForFee, 200, XRP(100), drops(10)), - sponsor::sponseeAcc(gw)); + sponsor::SponseeAcc(gw)); env.close(); // Find the sponsorship. for (auto const& acct : {alice, gw}) { - Json::Value const resp = acctObjs(acct, jss::sponsorship); + json::Value const resp = acctObjs(acct, jss::sponsorship); BEAST_EXPECT(acctObjsIsSize(resp, 1)); auto const& sponsorship = resp[jss::result][jss::account_objects][0u]; @@ -1387,12 +1387,12 @@ class AccountObjects_test : public beast::unit_test::Suite testcase("SponsoredFilter"); using namespace jtx; - Env env(*this, testable_amendments()); + Env env(*this, testableAmendments()); Account const alice("alice"); Account const bob("bob"); Account const sponsor1("sponsor1"); Account const gw("gw"); - auto const USD = gw["USD"]; + auto const usd = gw["USD"]; env.fund(XRP(10000), alice, bob, sponsor1, gw); env.close(); @@ -1401,8 +1401,8 @@ class AccountObjects_test : public beast::unit_test::Suite auto acctObjsSponsored = [&env]( AccountID const& acct, bool sponsored, - std::optional const& type = std::nullopt) { - Json::Value params; + std::optional const& type = std::nullopt) { + json::Value params; params[jss::account] = to_string(acct); params[jss::sponsored] = sponsored; if (type) @@ -1412,11 +1412,11 @@ class AccountObjects_test : public beast::unit_test::Suite }; // Create a sponsorship (alice sponsors bob) - env(sponsor::set(alice, 0, 100, XRP(100)), sponsor::sponseeAcc(bob), fee(XRP(1))); + env(sponsor::set(alice, 0, 100, XRP(100)), sponsor::SponseeAcc(bob), Fee(XRP(1))); env.close(); // Create a trust line for bob (not sponsored) - env(trust(bob, USD(1000))); + env(trust(bob, usd(1000))); env.close(); // sponsored=true should not find any objects for bob (doesn't have any sponsored objects) @@ -1427,12 +1427,12 @@ class AccountObjects_test : public beast::unit_test::Suite } // Now sponsor bob's trust line - auto const trustId = keylet::line(bob, gw, USD.currency); + auto const trustId = keylet::line(bob, gw, usd.currency); BEAST_EXPECT(env.le(trustId)); env(sponsor::transfer(bob, tfSponsorshipCreate, trustId.key), - sponsor::as(sponsor1, spfSponsorReserve), - sig(sfSponsorSignature, sponsor1)); + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1)); env.close(); // Verify trust line has sponsor field @@ -1482,13 +1482,13 @@ class AccountObjects_test : public beast::unit_test::Suite env(token::mint(bob, 0)); env.close(); - auto const nftPageKeylet = keylet::nftpage_max(bob); + auto const nftPageKeylet = keylet::nftpageMax(bob); BEAST_EXPECT(env.le(nftPageKeylet)); // Sponsor the NFT page env(sponsor::transfer(bob, tfSponsorshipCreate, nftPageKeylet.key), - sponsor::as(sponsor1, spfSponsorReserve), - sig(sfSponsorSignature, sponsor1)); + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1)); env.close(); // Verify NFT page has sponsor field diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 732bea4614e..a65f75d95bb 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -906,8 +906,8 @@ class AccountTx_test : public beast::unit_test::Suite env.close(); // check the latest sponsorship-related txn is in account tx list - auto const checkTx = [&](Account const& account, Json::StaticString txType) { - Json::Value params; + auto const checkTx = [&](Account const& account, json::StaticString txType) { + json::Value params; params[jss::account] = account.human(); params[jss::limit] = 100; auto const jv = env.rpc("json", "account_tx", to_string(params))[jss::result]; @@ -915,33 +915,33 @@ class AccountTx_test : public beast::unit_test::Suite auto const& tx0(jv[jss::transactions][0u][jss::tx]); BEAST_EXPECT(tx0[jss::TransactionType] == txType); - std::string const txHash{env.tx()->getJson(JsonOptions::none)[jss::hash].asString()}; + std::string const txHash{env.tx()->getJson(JsonOptions::KNone)[jss::hash].asString()}; BEAST_EXPECT(tx0[jss::hash] == txHash); }; // fee sponsorship - env(noop(alice), sponsor::as(sponsor, spfSponsorFee), sig(sfSponsorSignature, sponsor)); + env(noop(alice), sponsor::As(sponsor, spfSponsorFee), Sig(sfSponsorSignature, sponsor)); env.close(); checkTx(alice, jss::AccountSet); checkTx(sponsor, jss::AccountSet); // set sponsor - env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::SponseeAcc(alice), Ter(tesSUCCESS)); env.close(); checkTx(alice, jss::SponsorshipSet); checkTx(sponsor, jss::SponsorshipSet); // create a ticket with sponsor auto const seq = env.seq(alice); - env(ticket::create(alice, 1), sponsor::as(sponsor, spfSponsorReserve)); + env(ticket::create(alice, 1), sponsor::As(sponsor, spfSponsorReserve)); env.close(); checkTx(alice, jss::TicketCreate); checkTx(sponsor, jss::TicketCreate); // transfer object sponsorship - env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::ticket(alice, seq + 1).key), - sponsor::as(sponsor2, spfSponsorReserve), - sig(sfSponsorSignature, sponsor2)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::TicketT()(alice, seq + 1).key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); env.close(); checkTx(alice, jss::SponsorshipTransfer); checkTx(sponsor, jss::SponsorshipTransfer); @@ -949,9 +949,9 @@ class AccountTx_test : public beast::unit_test::Suite // use a ticket env(noop(alice), - ticket::use(seq + 1), - sponsor::as(sponsor, spfSponsorFee), - sig(sfSponsorSignature, sponsor)); + ticket::Use(seq + 1), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor)); env.close(); checkTx(alice, jss::AccountSet); checkTx(sponsor, jss::AccountSet); @@ -959,8 +959,8 @@ class AccountTx_test : public beast::unit_test::Suite // account sponsorship env(sponsor::transfer(alice, tfSponsorshipCreate), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); env.close(); checkTx(alice, jss::SponsorshipTransfer); checkTx(sponsor, jss::SponsorshipTransfer); diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 931380c6368..8dbe3af674d 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -550,7 +550,7 @@ class Simulate_test : public beast::unit_test::Suite { // autofill sponsor signature - auto validateOutput = [&](Json::Value const& resp, Json::Value const& tx) { + auto validateOutput = [&](json::Value const& resp, json::Value const& tx) { auto result = resp[jss::result]; checkBasicReturnValidity(result, tx, 2, env.current()->fees().base); @@ -562,7 +562,7 @@ class Simulate_test : public beast::unit_test::Suite if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob))) { - Json::Value const metadata = getJsonMetadata(result); + json::Value const metadata = getJsonMetadata(result); if (BEAST_EXPECT(metadata.isMember(sfAffectedNodes.jsonName))) { @@ -596,14 +596,14 @@ class Simulate_test : public beast::unit_test::Suite env.fund(XRP(10000), sponsor); env.close(); - Json::Value tx; + json::Value tx; tx[jss::Account] = env.master.human(); tx[jss::TransactionType] = jss::AccountSet; - tx[sfDomain] = newDomain; + tx[sfDomain.jsonName] = kNEW_DOMAIN; tx[sfSponsor.jsonName] = sponsor.human(); tx[sfSponsorFlags.jsonName] = spfSponsorFee; - tx[sfSponsorSignature.jsonName] = Json::objectValue; + tx[sfSponsorSignature.jsonName] = json::ObjectValue; // test with autofill testTx(env, tx, validateOutput); diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipSetTests.cpp index 339a21cf393..c978d7ef7d6 100644 --- a/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipSetTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipSetTests.cpp @@ -1,15 +1,13 @@ // Auto-generated unit tests for transaction SponsorshipSet - -#include - -#include - +#include #include #include -#include -#include #include +#include + +#include +#include #include @@ -21,7 +19,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip) { // Generate a deterministic keypair for signing auto const [publicKey, secretKey] = - generateKeyPair(KeyType::secp256k1, generateSeed("testSponsorshipSet")); + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipSet")); // Common transaction fields auto const accountValue = calcAccountID(publicKey); @@ -35,11 +33,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip) auto const maxFeeValue = canonical_AMOUNT(); auto const reserveCountValue = canonical_UINT32(); - SponsorshipSetBuilder builder{ - accountValue, - sequenceValue, - feeValue - }; + SponsorshipSetBuilder builder{accountValue, sequenceValue, feeValue}; // Set optional fields builder.setCounterpartySponsor(counterpartySponsorValue); @@ -67,7 +61,8 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip) { auto const& expected = counterpartySponsorValue; auto const actualOpt = tx.getCounterpartySponsor(); - ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySponsor should be present"; + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfCounterpartySponsor should be present"; expectEqualField(expected, *actualOpt, "sfCounterpartySponsor"); EXPECT_TRUE(tx.hasCounterpartySponsor()); } @@ -103,7 +98,6 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip) expectEqualField(expected, *actualOpt, "sfReserveCount"); EXPECT_TRUE(tx.hasReserveCount()); } - } // 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, @@ -112,7 +106,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip) { // Generate a deterministic keypair for signing auto const [publicKey, secretKey] = - generateKeyPair(KeyType::secp256k1, generateSeed("testSponsorshipSetFromTx")); + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipSetFromTx")); // Common transaction fields auto const accountValue = calcAccountID(publicKey); @@ -127,11 +121,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip) auto const reserveCountValue = canonical_UINT32(); // Build an initial transaction - SponsorshipSetBuilder initialBuilder{ - accountValue, - sequenceValue, - feeValue - }; + SponsorshipSetBuilder initialBuilder{accountValue, sequenceValue, feeValue}; initialBuilder.setCounterpartySponsor(counterpartySponsorValue); initialBuilder.setSponsee(sponseeValue); @@ -159,7 +149,8 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip) { auto const& expected = counterpartySponsorValue; auto const actualOpt = rebuiltTx.getCounterpartySponsor(); - ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySponsor should be present"; + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfCounterpartySponsor should be present"; expectEqualField(expected, *actualOpt, "sfCounterpartySponsor"); } @@ -190,15 +181,13 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip) ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveCount should be present"; expectEqualField(expected, *actualOpt, "sfReserveCount"); } - } // 3) Verify wrapper throws when constructed from wrong transaction type. TEST(TransactionsSponsorshipSetTests, WrapperThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = - generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const [pk, sk] = generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongType")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -211,8 +200,7 @@ TEST(TransactionsSponsorshipSetTests, WrapperThrowsOnWrongTxType) TEST(TransactionsSponsorshipSetTests, BuilderThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = - generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const [pk, sk] = generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongTypeBuilder")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -226,7 +214,7 @@ TEST(TransactionsSponsorshipSetTests, OptionalFieldsReturnNullopt) { // Generate a deterministic keypair for signing auto const [publicKey, secretKey] = - generateKeyPair(KeyType::secp256k1, generateSeed("testSponsorshipSetNullopt")); + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipSetNullopt")); // Common transaction fields auto const accountValue = calcAccountID(publicKey); @@ -235,11 +223,7 @@ TEST(TransactionsSponsorshipSetTests, OptionalFieldsReturnNullopt) // Transaction-specific required field values - SponsorshipSetBuilder builder{ - accountValue, - sequenceValue, - feeValue - }; + SponsorshipSetBuilder builder{accountValue, sequenceValue, feeValue}; // Do NOT set optional fields @@ -258,4 +242,4 @@ TEST(TransactionsSponsorshipSetTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(tx.getReserveCount().has_value()); } -} +} // namespace xrpl::transactions diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp index e9fa9072a67..88f3ab3a606 100644 --- a/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp @@ -1,15 +1,13 @@ // Auto-generated unit tests for transaction SponsorshipTransfer - -#include - -#include - +#include #include #include -#include -#include #include +#include + +#include +#include #include @@ -21,7 +19,7 @@ TEST(TransactionsSponsorshipTransferTests, BuilderSettersRoundTrip) { // Generate a deterministic keypair for signing auto const [publicKey, secretKey] = - generateKeyPair(KeyType::secp256k1, generateSeed("testSponsorshipTransfer")); + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipTransfer")); // Common transaction fields auto const accountValue = calcAccountID(publicKey); @@ -32,11 +30,7 @@ TEST(TransactionsSponsorshipTransferTests, BuilderSettersRoundTrip) auto const objectIDValue = canonical_UINT256(); auto const sponseeValue = canonical_ACCOUNT(); - SponsorshipTransferBuilder builder{ - accountValue, - sequenceValue, - feeValue - }; + SponsorshipTransferBuilder builder{accountValue, sequenceValue, feeValue}; // Set optional fields builder.setObjectID(objectIDValue); @@ -73,7 +67,6 @@ TEST(TransactionsSponsorshipTransferTests, BuilderSettersRoundTrip) expectEqualField(expected, *actualOpt, "sfSponsee"); EXPECT_TRUE(tx.hasSponsee()); } - } // 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, @@ -82,7 +75,7 @@ TEST(TransactionsSponsorshipTransferTests, BuilderFromStTxRoundTrip) { // Generate a deterministic keypair for signing auto const [publicKey, secretKey] = - generateKeyPair(KeyType::secp256k1, generateSeed("testSponsorshipTransferFromTx")); + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipTransferFromTx")); // Common transaction fields auto const accountValue = calcAccountID(publicKey); @@ -94,11 +87,7 @@ TEST(TransactionsSponsorshipTransferTests, BuilderFromStTxRoundTrip) auto const sponseeValue = canonical_ACCOUNT(); // Build an initial transaction - SponsorshipTransferBuilder initialBuilder{ - accountValue, - sequenceValue, - feeValue - }; + SponsorshipTransferBuilder initialBuilder{accountValue, sequenceValue, feeValue}; initialBuilder.setObjectID(objectIDValue); initialBuilder.setSponsee(sponseeValue); @@ -133,15 +122,13 @@ TEST(TransactionsSponsorshipTransferTests, BuilderFromStTxRoundTrip) ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSponsee should be present"; expectEqualField(expected, *actualOpt, "sfSponsee"); } - } // 3) Verify wrapper throws when constructed from wrong transaction type. TEST(TransactionsSponsorshipTransferTests, WrapperThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = - generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const [pk, sk] = generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongType")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -154,8 +141,7 @@ TEST(TransactionsSponsorshipTransferTests, WrapperThrowsOnWrongTxType) TEST(TransactionsSponsorshipTransferTests, BuilderThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = - generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const [pk, sk] = generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongTypeBuilder")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -169,7 +155,7 @@ TEST(TransactionsSponsorshipTransferTests, OptionalFieldsReturnNullopt) { // Generate a deterministic keypair for signing auto const [publicKey, secretKey] = - generateKeyPair(KeyType::secp256k1, generateSeed("testSponsorshipTransferNullopt")); + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipTransferNullopt")); // Common transaction fields auto const accountValue = calcAccountID(publicKey); @@ -178,11 +164,7 @@ TEST(TransactionsSponsorshipTransferTests, OptionalFieldsReturnNullopt) // Transaction-specific required field values - SponsorshipTransferBuilder builder{ - accountValue, - sequenceValue, - feeValue - }; + SponsorshipTransferBuilder builder{accountValue, sequenceValue, feeValue}; // Do NOT set optional fields @@ -195,4 +177,4 @@ TEST(TransactionsSponsorshipTransferTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(tx.getSponsee().has_value()); } -} +} // namespace xrpl::transactions diff --git a/src/xrpld/rpc/handlers/account/AccountObjects.cpp b/src/xrpld/rpc/handlers/account/AccountObjects.cpp index 286949b857e..90a6fef4912 100644 --- a/src/xrpld/rpc/handlers/account/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/account/AccountObjects.cpp @@ -378,7 +378,7 @@ doAccountObjects(RPC::JsonContext& context) { auto const& sponsoredJv = params[jss::sponsored]; if (!sponsoredJv.isBool()) - return RPC::expected_field_error(jss::sponsored, "boolean"); + return RPC::expectedFieldError(jss::sponsored, "boolean"); sponsored = sponsoredJv.asBool(); } From 7624c18c76896dde7dbd7c109bfeed1b6930e6f3 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 6 May 2026 15:16:59 +0900 Subject: [PATCH 235/249] fix: XChainAddAccountCreateAttestation redirects relayer sponsorship to door-owned claim objects --- .../tx/transactors/bridge/XChainBridge.cpp | 10 ++++---- src/test/app/Sponsor_test.cpp | 23 +++++++++++++++++++ src/test/jtx/Env.h | 6 ++--- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 68e6c95045a..fba90245262 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -1030,7 +1030,8 @@ applyCreateAccountAttestations( // Check reserve auto const balance = (*sleDoor)[sfBalance]; - // Door account should not have a sponsor + // Don't sponsor door account objects in transactions not sent by the door account + // itself if (auto const ret = checkInsufficientReserve(psb, tx, sleDoor, balance, {}, 1, 0, j); !isTesSuccess(ret)) return Unexpected(ret); // tecINSUFFICIENT_RESERVE @@ -1138,10 +1139,9 @@ applyCreateAccountAttestations( if (!sleDoor) return tecINTERNAL; // LCOV_EXCL_LINE - // Reserve was already checked - auto const sponsor = getTxReserveSponsor(psb, tx); - adjustOwnerCount(psb, sleDoor, sponsor, 1, j); - addSponsorToLedgerEntry(createdSleClaimID, sponsor); + // Don't sponsor door account objects in transactions not sent by the door account + // itself + adjustOwnerCount(psb, sleDoor, {}, 1, j); psb.insert(createdSleClaimID); psb.update(sleDoor); } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 2ddd953ba50..fbbdeb46ec1 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -64,6 +64,7 @@ #include #include +#include #include #include #include @@ -5121,6 +5122,28 @@ class Sponsor_test : public beast::unit_test::Suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); } + // XChainCreateAccountClaimID + { + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, doorA) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, doorA) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + env(create_account_attestation( + alice, jvb, alice, XRP(20), XRP(0), bob, false, 2, bob, signer), + sponsor::as(sponsor, spfSponsorReserve), + sig(sfSponsorSignature, sponsor), + ter(tesSUCCESS)); + env.close(); + + // XChainCreateAccountClaimID not sponsored + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, doorA) == 3); + BEAST_EXPECT(sponsoredOwnerCount(env, doorA) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } } void diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index faf8424e71d..fe90c461a0a 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -533,19 +533,19 @@ class Env /** Return the number of sponsored objects owned by an account. * Returns 0 if the account does not exist. */ - std::uint32_t + [[nodiscard]] std::uint32_t sponsoredOwnerCount(Account const& account) const; /** Return the number of sponsoring objects owned by an account. * Returns 0 if the account does not exist. */ - std::uint32_t + [[nodiscard]] std::uint32_t sponsoringOwnerCount(Account const& account) const; /** Return the number of sponsoring accounts owned by an account. * Returns 0 if the account does not exist. */ - std::uint32_t + [[nodiscard]] std::uint32_t sponsoringAccountCount(Account const& account) const; /** Return an account root. From a669099f21b4c1c97e2a0ceb44d805b5d459b7b1 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 6 May 2026 15:55:10 +0900 Subject: [PATCH 236/249] fix --- src/test/app/Sponsor_test.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index fbbdeb46ec1..3cf4f055dbe 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -64,7 +64,6 @@ #include #include -#include #include #include #include @@ -5130,11 +5129,11 @@ class Sponsor_test : public beast::unit_test::Suite BEAST_EXPECT(sponsoredOwnerCount(env, doorA) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - env(create_account_attestation( + env(createAccountAttestation( alice, jvb, alice, XRP(20), XRP(0), bob, false, 2, bob, signer), - sponsor::as(sponsor, spfSponsorReserve), - sig(sfSponsorSignature, sponsor), - ter(tesSUCCESS)); + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); env.close(); // XChainCreateAccountClaimID not sponsored From bb4c443fe597e5d86dbe3144248cd309763b9e9e Mon Sep 17 00:00:00 2001 From: tequ Date: Mon, 18 May 2026 13:33:12 +0900 Subject: [PATCH 237/249] fix ReserveCount Inflation via Co-Signed + Pre-Funded Interaction fix #6864 --- .../Sponsor/SponsorshipTransfer.cpp | 31 ++++--------------- src/test/app/Sponsor_test.cpp | 6 ++-- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index bd971bb95e3..9b748abf96a 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -362,7 +362,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } TER -adjustReserveCount( +reduceReserveCount( ApplyView& view, AccountID const& account, AccountID const& sponsor, @@ -370,6 +370,9 @@ adjustReserveCount( { if (delta == 0) return tesSUCCESS; + if (delta > 0) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const sponsorKeylet = keylet::sponsor(sponsor, account); auto const sponsorSle = view.peek(sponsorKeylet); if (!sponsorSle) @@ -472,7 +475,7 @@ SponsorshipTransfer::doApply() if (!hasSignature) { // use ReserveCount for pre-funded sponsoring - if (auto const ter = adjustReserveCount( + if (auto const ter = reduceReserveCount( view(), sponseeAccountID, newSponsorAccountID, -ownerCountDelta); !isTesSuccess(ter)) return ter; @@ -509,22 +512,11 @@ SponsorshipTransfer::doApply() if (!hasSignature) { // use ReserveCount for pre-funded sponsoring - if (auto const ter = adjustReserveCount( + if (auto const ter = reduceReserveCount( view(), sponseeAccountID, newSponsorAccountID, -ownerCountDelta); !isTesSuccess(ter)) return ter; } - - // payback the reserve count if ltSponsorship exists - if (auto const sponsorSle = - view().exists(keylet::sponsor(oldSponsorAccountID, sponseeAccountID)); - sponsorSle) - { - if (auto const ter = adjustReserveCount( - view(), sponseeAccountID, oldSponsorAccountID, ownerCountDelta); - !isTesSuccess(ter)) - return ter; - } } else if ((flags & tfSponsorshipEnd) != 0u) { @@ -543,17 +535,6 @@ SponsorshipTransfer::doApply() setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); view().update(oldSponsorSle); - // payback the reserve count if ltSponsorship exists - if (auto const sponsorSle = - view().exists(keylet::sponsor(oldSponsorAccountID, sponseeAccountID)); - sponsorSle) - { - if (auto const ter = adjustReserveCount( - view(), sponseeAccountID, oldSponsorAccountID, ownerCountDelta); - !isTesSuccess(ter)) - return ter; - } - // remove sponsor from object objSle->makeFieldAbsent(sponsorField); view().update(objSle); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 3cf4f055dbe..d0bc7d734ce 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -1222,7 +1222,7 @@ class Sponsor_test : public beast::unit_test::Suite BEAST_EXPECT(checkSle->isFieldPresent(sfSponsor)); BEAST_EXPECT(checkSle->getAccountID(sfSponsor) == sponsor2.id()); sponsor1Sle = env.le(keylet::sponsor(sponsor1, alice)); - BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 100); // paybacked + BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 99); auto sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice)); BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 99); @@ -1245,7 +1245,7 @@ class Sponsor_test : public beast::unit_test::Suite checkSle = env.le(keylet::unchecked(checkId)); BEAST_EXPECT(!checkSle->isFieldPresent(sfSponsor)); sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice)); - BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 100); // paybacked + BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 99); } { @@ -1332,7 +1332,7 @@ class Sponsor_test : public beast::unit_test::Suite BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT( - env.le(keylet::sponsor(sponsor, alice))->getFieldU32(sfReserveCount) == 101); + env.le(keylet::sponsor(sponsor, alice))->getFieldU32(sfReserveCount) == 100); } { From 08357b196f11b6588649f8710d167eff9dd77ac9 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Thu, 14 May 2026 00:12:04 -0400 Subject: [PATCH 238/249] sponsor AccountID -> sponsorSle --- .../xrpl/ledger/helpers/AccountRootHelpers.h | 4 +- include/xrpl/ledger/helpers/EscrowHelpers.h | 28 +++++------ include/xrpl/ledger/helpers/MPTokenHelpers.h | 4 +- include/xrpl/ledger/helpers/NFTokenHelpers.h | 7 +-- .../xrpl/ledger/helpers/RippleStateHelpers.h | 2 +- include/xrpl/ledger/helpers/SponsorHelpers.h | 35 +++++++++++--- include/xrpl/ledger/helpers/TokenHelpers.h | 4 +- include/xrpl/protocol/Indexes.h | 2 +- include/xrpl/tx/paths/AMMOffer.h | 2 +- include/xrpl/tx/paths/Offer.h | 2 +- src/libxrpl/ledger/View.cpp | 6 ++- .../ledger/helpers/AccountRootHelpers.cpp | 10 ++-- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 33 ++++++------- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 33 ++++++------- .../ledger/helpers/RippleStateHelpers.cpp | 31 +++++------- src/libxrpl/ledger/helpers/TokenHelpers.cpp | 47 +++++++++---------- src/libxrpl/tx/Transactor.cpp | 11 +++-- .../tx/invariants/SponsorshipInvariant.cpp | 8 +--- src/libxrpl/tx/paths/BookStep.cpp | 3 +- src/libxrpl/tx/paths/MPTEndpointStep.cpp | 2 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 10 ++-- .../Sponsor/SponsorshipTransfer.cpp | 20 ++++---- .../tx/transactors/account/AccountDelete.cpp | 14 ++---- .../tx/transactors/account/SignerListSet.cpp | 17 +++++-- .../tx/transactors/bridge/XChainBridge.cpp | 32 ++++++++----- .../tx/transactors/check/CheckCash.cpp | 13 +++-- .../tx/transactors/check/CheckCreate.cpp | 10 ++-- .../credentials/CredentialAccept.cpp | 10 ++-- .../credentials/CredentialCreate.cpp | 10 ++-- .../tx/transactors/delegate/DelegateSet.cpp | 10 ++-- src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 18 ++++--- src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 26 ++++++---- .../tx/transactors/dex/AMMWithdraw.cpp | 20 ++++---- .../tx/transactors/dex/OfferCreate.cpp | 38 +++++++-------- src/libxrpl/tx/transactors/did/DIDSet.cpp | 10 ++-- .../tx/transactors/escrow/EscrowCreate.cpp | 10 ++-- .../tx/transactors/escrow/EscrowFinish.cpp | 5 ++ .../tx/transactors/lending/LoanBrokerSet.cpp | 16 ++++--- .../tx/transactors/lending/LoanSet.cpp | 8 ++-- .../tx/transactors/nft/NFTokenAcceptOffer.cpp | 14 ++++-- .../tx/transactors/nft/NFTokenMint.cpp | 12 +++-- .../tx/transactors/oracle/OracleSet.cpp | 20 +++++--- .../tx/transactors/payment/DepositPreauth.cpp | 20 ++++---- .../payment_channel/PaymentChannelCreate.cpp | 16 ++++--- .../payment_channel/PaymentChannelFund.cpp | 6 ++- .../PermissionedDomainSet.cpp | 10 ++-- .../tx/transactors/system/TicketCreate.cpp | 10 ++-- .../token/MPTokenIssuanceCreate.cpp | 17 +++++-- src/libxrpl/tx/transactors/token/TrustSet.cpp | 31 ++++++------ .../tx/transactors/vault/VaultClawback.cpp | 10 +--- .../tx/transactors/vault/VaultCreate.cpp | 16 ++++--- .../tx/transactors/vault/VaultDeposit.cpp | 8 ++-- .../tx/transactors/vault/VaultWithdraw.cpp | 6 ++- src/test/app/Sponsor_test.cpp | 9 ++-- 54 files changed, 428 insertions(+), 348 deletions(-) diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index 7eb1e545608..01b3bd5551b 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -62,7 +62,7 @@ accountReserve( XRPAmount baseAccountReserve(ReadView const& view, std::int32_t ownerCount); -TER +[[nodiscard]] TER checkInsufficientReserve( ReadView const& view, STTx const& tx, @@ -70,7 +70,7 @@ checkInsufficientReserve( STAmount const& accBalance, SLE::const_ref sponsorSle, std::int32_t ownerCountDelta, - std::int32_t accountCountDelta = 0, + std::int32_t reserveCountDelta = 0, beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); std::uint32_t diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h index be016c08771..bdfcb72e484 100644 --- a/include/xrpl/ledger/helpers/EscrowHelpers.h +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -59,12 +59,12 @@ escrowUnlockApplyHelper( if (!view.exists(trustLineKey) && createAsset) { // Can the account cover the trust line's reserve? - auto const sponsorAccountID = getTxReserveSponsorAccountID(tx); - std::shared_ptr sponsorSle = {}; - if (sponsorAccountID) - sponsorSle = view.peek(keylet::account(*sponsorAccountID)); + auto const sponsorSle = getTxReserveSponsor(view, tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + if (auto const ret = - checkInsufficientReserve(view, tx, sleDest, xrpBalance, sponsorSle, 1, 0, journal); + checkInsufficientReserve(view, tx, sleDest, xrpBalance, *sponsorSle, 1, 0, journal); !isTesSuccess(ret)) { JLOG(journal.trace()) << "Trust line does not exist. " @@ -92,7 +92,7 @@ escrowUnlockApplyHelper( Issue(currency, receiver), // limit of zero 0, // quality in 0, // quality out - sponsorAccountID, // sponsor + *sponsorSle, // sponsor journal); // journal !isTesSuccess(ter)) { @@ -189,25 +189,25 @@ escrowUnlockApplyHelper( auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver); if (!view.exists(mptKeylet) && createAsset && !receiverIssuer) { - auto const sponsorAccountID = getTxReserveSponsorAccountID(tx); - std::shared_ptr sponsorSle = {}; - if (sponsorAccountID) - sponsorSle = view.peek(keylet::account(*sponsorAccountID)); + auto const sponsorSle = getTxReserveSponsor(view, tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + if (auto const ret = - checkInsufficientReserve(view, tx, sleDest, xrpBalance, sponsorSle, 1, 0, journal); + checkInsufficientReserve(view, tx, sleDest, xrpBalance, *sponsorSle, 1, 0, journal); !isTesSuccess(ret)) return ret; - if (auto const ter = createMPToken(view, mptID, receiver, sponsorAccountID, 0); + if (auto const ter = createMPToken(view, mptID, receiver, *sponsorSle, 0); !isTesSuccess(ter)) { return ter; // LCOV_EXCL_LINE } // update owner count. - adjustOwnerCount(view, sleDest, sponsorSle, 1, journal); + adjustOwnerCount(view, sleDest, *sponsorSle, 1, journal); auto mptSle = view.peek(mptKeylet); - addSponsorToLedgerEntry(mptSle, sponsorSle); + addSponsorToLedgerEntry(mptSle, *sponsorSle); } if (!view.exists(mptKeylet) && !receiverIssuer) diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h index 8925c1fdc9c..fa79420ab6c 100644 --- a/include/xrpl/ledger/helpers/MPTokenHelpers.h +++ b/include/xrpl/ledger/helpers/MPTokenHelpers.h @@ -173,7 +173,7 @@ createMPToken( ApplyView& view, MPTID const& mptIssuanceID, AccountID const& account, - std::optional const& sponsor, + SLE::ref sponsorSle, std::uint32_t const flags); TER @@ -181,7 +181,7 @@ checkCreateMPT( xrpl::ApplyView& view, xrpl::MPTIssue const& mptIssue, xrpl::AccountID const& holder, - std::optional const& sponsor, + SLE::ref sponsorSle, beast::Journal j); //------------------------------------------------------------------------------ diff --git a/include/xrpl/ledger/helpers/NFTokenHelpers.h b/include/xrpl/ledger/helpers/NFTokenHelpers.h index 0a1ddc5e52c..b43a71518eb 100644 --- a/include/xrpl/ledger/helpers/NFTokenHelpers.h +++ b/include/xrpl/ledger/helpers/NFTokenHelpers.h @@ -40,12 +40,7 @@ findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftoken /** Insert the token in the owner's token directory. */ TER -insertToken( - ApplyView& view, - STTx const& tx, - AccountID owner, - std::optional const& sponsor, - STObject&& nft); +insertToken(ApplyView& view, STTx const& tx, AccountID owner, SLE::ref sponsorSle, STObject&& nft); /** Remove the token from the owner's token directory. */ TER diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h index 074cf750d90..d9aeb9ee59b 100644 --- a/include/xrpl/ledger/helpers/RippleStateHelpers.h +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -149,7 +149,7 @@ trustCreate( // Issuer should be the account being set. std::uint32_t uQualityIn, std::uint32_t uQualityOut, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, beast::Journal j); [[nodiscard]] TER diff --git a/include/xrpl/ledger/helpers/SponsorHelpers.h b/include/xrpl/ledger/helpers/SponsorHelpers.h index fc065194867..a086765c4fa 100644 --- a/include/xrpl/ledger/helpers/SponsorHelpers.h +++ b/include/xrpl/ledger/helpers/SponsorHelpers.h @@ -34,22 +34,36 @@ getTxReserveSponsorAccountID(STTx const& tx) return {}; } -inline SLE::pointer +inline Expected getTxReserveSponsor(ApplyView& view, STTx const& tx) { auto const sponsorID = getTxReserveSponsorAccountID(tx); if (sponsorID) - return view.peek(keylet::account(*sponsorID)); - return {}; + { + auto sle = view.peek(keylet::account(*sponsorID)); + + // already checked in Transactor::checkSponsor + if (!sle) + return Unexpected(tecINTERNAL); + return sle; + } + return SLE::pointer(); } -inline SLE::const_pointer +inline Expected getTxReserveSponsor(ReadView const& view, STTx const& tx) { auto const sponsorID = getTxReserveSponsorAccountID(tx); if (sponsorID) - return view.read(keylet::account(*sponsorID)); - return {}; + { + auto sle = view.read(keylet::account(*sponsorID)); + + // already checked in Transactor::checkSponsor + if (!sle) + return Unexpected(tecINTERNAL); + return sle; + } + return SLE::pointer(); } inline std::optional @@ -109,4 +123,13 @@ removeSponsorFromLedgerEntry(SLE::ref sle, SF_ACCOUNT const& field = sfSponsor) sle->makeFieldAbsent(field); } +// namespace sponsor +// { +// // Accessing the ledger to check if provided sponsor is valid. +// [[nodiscard]] TER +// valid(ReadView const& view, STTx const& tx, beast::Journal j) +// { +// } +// } + } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/TokenHelpers.h b/include/xrpl/ledger/helpers/TokenHelpers.h index de1a10f8b97..6830f17afb8 100644 --- a/include/xrpl/ledger/helpers/TokenHelpers.h +++ b/include/xrpl/ledger/helpers/TokenHelpers.h @@ -272,7 +272,7 @@ accountSend( AccountID const& to, STAmount const& saAmount, beast::Journal j, - std::optional const& sponsorAccountID = std::nullopt, + SLE::ref sponsorSle = {}, WaiveTransferFee waiveFee = WaiveTransferFee::No, AllowMPTOverflow allowOverflow = AllowMPTOverflow::No); @@ -290,7 +290,7 @@ accountSendMulti( Asset const& asset, MultiplePaymentDestinations const& receivers, beast::Journal j, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, WaiveTransferFee waiveFee = WaiveTransferFee::No); [[nodiscard]] TER diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 97f2f867eac..ceff580c6c4 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -151,7 +151,7 @@ static TicketT const kTICKET{}; Keylet signers(AccountID const& account) noexcept; -/** A Sponsor */ +/** A Sponsorship */ Keylet sponsor(AccountID const& sponsor, AccountID const& sponsee) noexcept; diff --git a/include/xrpl/tx/paths/AMMOffer.h b/include/xrpl/tx/paths/AMMOffer.h index 905d2235a85..4292d67015d 100644 --- a/include/xrpl/tx/paths/AMMOffer.h +++ b/include/xrpl/tx/paths/AMMOffer.h @@ -105,7 +105,7 @@ class AMMOffer { return accountSend( std::forward(args)..., - std::nullopt, + SLE::pointer(), WaiveTransferFee::Yes, AllowMPTOverflow::Yes); } diff --git a/include/xrpl/tx/paths/Offer.h b/include/xrpl/tx/paths/Offer.h index 7cb0b59e5b2..2df3b9af486 100644 --- a/include/xrpl/tx/paths/Offer.h +++ b/include/xrpl/tx/paths/Offer.h @@ -225,7 +225,7 @@ TER TOffer::send(Args&&... args) { return accountSend( - std::forward(args)..., std::nullopt, WaiveTransferFee::No, AllowMPTOverflow::Yes); + std::forward(args)..., SLE::pointer(), WaiveTransferFee::No, AllowMPTOverflow::Yes); } template diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index bd76b181157..7b286c3bf4e 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -448,12 +448,14 @@ doWithdraw( // LCOV_EXCL_STOP } - auto const sponsorAccountID = getTxReserveSponsorAccountID(tx); + auto const sponsorSle = getTxReserveSponsor(view, tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE // Move the funds directly from the broker's pseudo-account to the // dstAcct return accountSend( - view, sourceAcct, dstAcct, amount, j, sponsorAccountID, WaiveTransferFee::Yes); + view, sourceAcct, dstAcct, amount, j, *sponsorSle, WaiveTransferFee::Yes); } TER diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 4cd6e5e0bd5..9d399cb1a4e 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -337,7 +337,7 @@ checkInsufficientReserve( STAmount const& accBalance, SLE::const_ref sponsorSle, std::int32_t ownerCountDelta, - std::int32_t accountCountDelta, + std::int32_t reserveCountDelta, beast::Journal j) { if (sponsorSle) @@ -353,14 +353,14 @@ checkInsufficientReserve( if (sle) { - auto const reserveCountAllowed = sle->getFieldU32(sfReserveCount); - if (reserveCountAllowed < ownerCountDelta) + auto const ownerCountAllowed = sle->getFieldU32(sfReserveCount); + if (ownerCountAllowed < ownerCountDelta) return tecINSUFFICIENT_RESERVE; } auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance); STAmount const sponsorReserve = - accountReserve(view, sponsorSle, j, ownerCountDelta, accountCountDelta); + accountReserve(view, sponsorSle, j, ownerCountDelta, reserveCountDelta); if (sponsorBalance < sponsorReserve) return tecINSUFFICIENT_RESERVE; @@ -368,7 +368,7 @@ checkInsufficientReserve( else { STAmount const reserve = - accountReserve(view, accSle, j, ownerCountDelta, accountCountDelta); + accountReserve(view, accSle, j, ownerCountDelta, reserveCountDelta); if (accBalance < reserve) return tecINSUFFICIENT_RESERVE; } diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index 466a821612f..5eef22a4530 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -190,9 +190,11 @@ authorizeMPToken( // - add the new mptokenKey to the owner directory // - create the MPToken object for the holder - auto const sponsor = getTxReserveSponsor(view, tx); + auto const sponsorSle = getTxReserveSponsor(view, tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE - auto const isSponsoredAndPreFunded = sponsor && !isSponsorReserveCoSigning(tx); + auto const isSponsoredAndPreFunded = *sponsorSle && !isSponsorReserveCoSigning(tx); // The reserve that is required to create the MPToken. Note // that although the reserve increases with every item @@ -201,10 +203,11 @@ authorizeMPToken( // items. This is similar to the reserve requirements of trust lines. // If PreFunded Sponsor, it must be checked whether sufficient // ReserveCount exists. - if (ownerCount(view, sponsor ? sponsor : sleAcct, journal) >= 2 || isSponsoredAndPreFunded) + if (ownerCount(view, *sponsorSle ? *sponsorSle : sleAcct, journal) >= 2 || + isSponsoredAndPreFunded) { if (auto const ret = checkInsufficientReserve( - view, tx, sleAcct, priorBalance, sponsor, 1, 0, journal); + view, tx, sleAcct, priorBalance, *sponsorSle, 1, 0, journal); !isTesSuccess(ret)) return ret; } @@ -231,8 +234,8 @@ authorizeMPToken( view.insert(mptoken); // Update owner count. - adjustOwnerCount(view, sleAcct, sponsor, 1, journal); - addSponsorToLedgerEntry(mptoken, sponsor); + adjustOwnerCount(view, sleAcct, *sponsorSle, 1, journal); + addSponsorToLedgerEntry(mptoken, *sponsorSle); return tesSUCCESS; } @@ -800,7 +803,7 @@ createMPToken( ApplyView& view, MPTID const& mptIssuanceID, AccountID const& account, - std::optional const& sponsor, + SLE::ref sponsorSle, std::uint32_t const flags) { auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); @@ -817,13 +820,8 @@ createMPToken( (*mptoken)[sfFlags] = flags; (*mptoken)[sfOwnerNode] = *ownerNode; - if (sponsor) - { - auto const sponsorSle = view.peek(keylet::account(*sponsor)); - if (!sponsorSle) - return tecINTERNAL; + if (sponsorSle) addSponsorToLedgerEntry(mptoken, sponsorSle); - } view.insert(mptoken); @@ -835,7 +833,7 @@ checkCreateMPT( xrpl::ApplyView& view, xrpl::MPTIssue const& mptIssue, xrpl::AccountID const& holder, - std::optional const& sponsor, + SLE::ref sponsorSle, beast::Journal j) { if (mptIssue.getIssuer() == holder) @@ -845,7 +843,7 @@ checkCreateMPT( auto const mptokenID = keylet::mptoken(mptIssuanceID.key, holder); if (!view.exists(mptokenID)) { - if (auto const err = createMPToken(view, mptIssue.getMptID(), holder, sponsor, 0); + if (auto const err = createMPToken(view, mptIssue.getMptID(), holder, sponsorSle, 0); !isTesSuccess(err)) { return err; @@ -855,9 +853,8 @@ checkCreateMPT( { return tecINTERNAL; } - auto const sleSponsor = - sponsor ? view.peek(keylet::account(*sponsor)) : std::shared_ptr(); - adjustOwnerCount(view, sleAcct, sleSponsor, 1, j); + + adjustOwnerCount(view, sleAcct, sponsorSle, 1, j); } return tesSUCCESS; } diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index 62daf5575a7..452554b138a 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -75,14 +75,14 @@ getPageForToken( ApplyView& view, STTx const& tx, AccountID const& owner, - std::optional const& sponsor, + SLE::ref sponsorSle, uint256 const& id, std::function< TER(ApplyView&, STTx const&, std::shared_ptr const&, AccountID const&, - std::optional const&)> const& createCallback) + SLE::ref)> const& createCallback) { auto const base = keylet::nftpageMin(owner); auto const first = keylet::nftpage(base, id); @@ -102,7 +102,7 @@ getPageForToken( cp->setFieldArray(sfNFTokens, arr); view.insert(cp); - if (auto const ret = createCallback(view, tx, cp, owner, sponsor); !isTesSuccess(ret)) + if (auto const ret = createCallback(view, tx, cp, owner, sponsorSle); !isTesSuccess(ret)) return Unexpected(ret); return cp; } @@ -216,7 +216,7 @@ getPageForToken( cp->setFieldH256(sfPreviousPageMin, np->key()); view.update(cp); - if (auto const ret = createCallback(view, tx, np, owner, sponsor); ret != tesSUCCESS) + if (auto const ret = createCallback(view, tx, np, owner, sponsorSle); ret != tesSUCCESS) return Unexpected(ret); return (first.key < np->key()) ? np : cp; @@ -273,12 +273,7 @@ changeTokenURI( /** Insert the token in the owner's token directory. */ TER -insertToken( - ApplyView& view, - STTx const& tx, - AccountID owner, - std::optional const& sponsor, - STObject&& nft) +insertToken(ApplyView& view, STTx const& tx, AccountID owner, SLE::ref sponsorSle, STObject&& nft) { XRPL_ASSERT(nft.isFieldPresent(sfNFTokenID), "xrpl::nft::insertToken : has NFT token"); @@ -289,16 +284,13 @@ insertToken( view, tx, owner, - sponsor, + sponsorSle, nft[sfNFTokenID], [](ApplyView& view, STTx const& tx, std::shared_ptr const& newPage, AccountID const& owner, - std::optional const& sponsor) -> TER { - std::shared_ptr const sponsorSle = - sponsor ? view.peek(keylet::account(*sponsor)) : std::shared_ptr(); - + SLE::ref sponsorSle) -> TER { if (isReserveSponsored(tx)) { auto const ownerSle = view.read(keylet::account(owner)); @@ -962,8 +954,11 @@ tokenOfferCreateApply( { Keylet const acctKeylet = keylet::account(acctID); auto const acct = view.read(acctKeylet); - auto const sponsor = getTxReserveSponsor(view, tx); - if (auto const ret = checkInsufficientReserve(view, tx, acct, priorBalance, sponsor, 1, 0, j); + auto const sponsorSle = getTxReserveSponsor(view, tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + if (auto const ret = + checkInsufficientReserve(view, tx, acct, priorBalance, *sponsorSle, 1, 0, j); !isTesSuccess(ret)) return ret; @@ -1012,13 +1007,13 @@ tokenOfferCreateApply( if (dest) (*offer)[sfDestination] = *dest; - addSponsorToLedgerEntry(offer, sponsor); + addSponsorToLedgerEntry(offer, *sponsorSle); view.insert(offer); } // Update owner count. - adjustOwnerCount(view, view.peek(acctKeylet), sponsor, 1, j); + adjustOwnerCount(view, view.peek(acctKeylet), *sponsorSle, 1, j); return tesSUCCESS; } diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index 2f0485a88e8..d39c8134cd9 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -197,7 +197,7 @@ trustCreate( // Issuer should be the account being set. std::uint32_t uQualityIn, std::uint32_t uQualityOut, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, beast::Journal j) { JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", " @@ -283,10 +283,6 @@ trustCreate( uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); } - std::shared_ptr sponsorSle = {}; - if (sponsorAccountID) - sponsorSle = view.peek(keylet::account(*sponsorAccountID)); - sleRippleState->setFieldU32(sfFlags, uFlags); adjustOwnerCount(view, sleAccount, sponsorSle, 1, j); @@ -665,20 +661,19 @@ addEmptyHolding( // If the line already exists, don't create it again. if (view.read(index)) return tecDUPLICATE; - auto const& sponsorAccountID = - !isPseudoAccount(sleDst) ? getTxReserveSponsorAccountID(tx) : std::nullopt; + + SLE::pointer sponsorSle; + if (!isPseudoAccount(sleDst)) + { + auto sle = getTxReserveSponsor(view, tx); + if (!sle) + return sle.error(); // LCOV_EXCL_LINE + sponsorSle = std::move(*sle); + } // Can the account cover the trust line reserve ? - if (auto const ret = checkInsufficientReserve( - view, - tx, - sleDst, - priorBalance, - sponsorAccountID ? view.read(keylet::account(*sponsorAccountID)) - : std::shared_ptr(), - 1, - 0, - journal); + if (auto const ret = + checkInsufficientReserve(view, tx, sleDst, priorBalance, sponsorSle, 1, 0, journal); !isTesSuccess(ret)) return tecNO_LINE_INSUF_RESERVE; @@ -697,7 +692,7 @@ addEmptyHolding( /*saLimit=*/STAmount{Issue{currency, dstId}}, /*uQualityIn=*/0, /*uQualityOut=*/0, - sponsorAccountID, + sponsorSle, journal); } diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index cab1f4562bb..f63123518f4 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -533,7 +533,7 @@ directSendNoFeeIOU( AccountID const& uReceiverID, STAmount const& saAmount, bool bCheckIssuer, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, beast::Journal j) { AccountID const& issuer = saAmount.getIssuer(); @@ -670,7 +670,7 @@ directSendNoFeeIOU( saReceiverLimit, 0, 0, - sponsorAccountID, + sponsorSle, j); } @@ -685,7 +685,7 @@ directSendNoLimitIOU( STAmount const& saAmount, STAmount& saActual, beast::Journal j, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, WaiveTransferFee waiveFee) { auto const& issuer = saAmount.getIssuer(); @@ -699,7 +699,7 @@ directSendNoLimitIOU( { // Direct send: redeeming IOUs and/or sending own IOUs. auto const ter = - directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, false, sponsorAccountID, j); + directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, false, sponsorSle, j); if (!isTesSuccess(ter)) return ter; saActual = saAmount; @@ -717,13 +717,11 @@ directSendNoLimitIOU( << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() << " cost=" << saActual.getFullText(); - TER terResult = - directSendNoFeeIOU(view, issuer, uReceiverID, saAmount, true, sponsorAccountID, j); + TER terResult = directSendNoFeeIOU(view, issuer, uReceiverID, saAmount, true, sponsorSle, j); if (tesSUCCESS == terResult) { - terResult = - directSendNoFeeIOU(view, uSenderID, issuer, saActual, true, sponsorAccountID, j); + terResult = directSendNoFeeIOU(view, uSenderID, issuer, saActual, true, sponsorSle, j); } return terResult; @@ -740,7 +738,7 @@ directSendNoLimitMultiIOU( MultiplePaymentDestinations const& receivers, STAmount& actual, beast::Journal j, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, WaiveTransferFee waiveFee) { auto const& issuer = issue.getIssuer(); @@ -768,8 +766,8 @@ directSendNoLimitMultiIOU( if (senderID == issuer || receiverID == issuer || issuer == noAccount()) { // Direct send: redeeming IOUs and/or sending own IOUs. - if (auto const ter = directSendNoFeeIOU( - view, senderID, receiverID, amount, false, sponsorAccountID, j)) + if (auto const ter = + directSendNoFeeIOU(view, senderID, receiverID, amount, false, sponsorSle, j)) return ter; actual += amount; // Do not add amount to takeFromSender, because directSendNoFeeIOU took @@ -793,14 +791,14 @@ directSendNoLimitMultiIOU( << " cost=" << actual.getFullText(); if (TER const terResult = - directSendNoFeeIOU(view, issuer, receiverID, amount, true, sponsorAccountID, j)) + directSendNoFeeIOU(view, issuer, receiverID, amount, true, sponsorSle, j)) return terResult; } if (senderID != issuer && takeFromSender) { - if (TER const terResult = directSendNoFeeIOU( - view, senderID, issuer, takeFromSender, true, sponsorAccountID, j)) + if (TER const terResult = + directSendNoFeeIOU(view, senderID, issuer, takeFromSender, true, sponsorSle, j)) return terResult; } @@ -814,7 +812,7 @@ accountSendIOU( AccountID const& uReceiverID, STAmount const& saAmount, beast::Journal j, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, WaiveTransferFee waiveFee) { if (view.rules().enabled(fixAMMv1_1)) @@ -847,7 +845,7 @@ accountSendIOU( << to_string(uReceiverID) << " : " << saAmount.getFullText(); return directSendNoLimitIOU( - view, uSenderID, uReceiverID, saAmount, saActual, j, sponsorAccountID, waiveFee); + view, uSenderID, uReceiverID, saAmount, saActual, j, sponsorSle, waiveFee); } /* XRP send which does not check reserve and can do pure adjustment. @@ -933,7 +931,7 @@ accountSendMultiIOU( Issue const& issue, MultiplePaymentDestinations const& receivers, beast::Journal j, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, WaiveTransferFee waiveFee) { XRPL_ASSERT_PARTS( @@ -946,7 +944,7 @@ accountSendMultiIOU( << receivers.size() << " IOUs"; return directSendNoLimitMultiIOU( - view, senderID, issue, receivers, actual, j, sponsorAccountID, waiveFee); + view, senderID, issue, receivers, actual, j, sponsorSle, waiveFee); } /* XRP send which does not check reserve and can do pure adjustment. @@ -1371,8 +1369,7 @@ directSendNoFee( { return saAmount.asset().visit( [&](Issue const&) { - return directSendNoFeeIOU( - view, uSenderID, uReceiverID, saAmount, bCheckIssuer, std::nullopt, j); + return directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, {}, j); }, [&](MPTIssue const&) { XRPL_ASSERT(!bCheckIssuer, "xrpl::directSendNoFee : not checking issuer"); @@ -1387,14 +1384,13 @@ accountSend( AccountID const& uReceiverID, STAmount const& saAmount, beast::Journal j, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, WaiveTransferFee waiveFee, AllowMPTOverflow allowOverflow) { return saAmount.asset().visit( [&](Issue const&) { - return accountSendIOU( - view, uSenderID, uReceiverID, saAmount, j, sponsorAccountID, waiveFee); + return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, sponsorSle, waiveFee); }, [&](MPTIssue const&) { return accountSendMPT( @@ -1409,15 +1405,14 @@ accountSendMulti( Asset const& asset, MultiplePaymentDestinations const& receivers, beast::Journal j, - std::optional const& sponsorAccountID, + SLE::ref sponsorSle, WaiveTransferFee waiveFee) { XRPL_ASSERT_PARTS( receivers.size() > 1, "xrpl::accountSendMulti", "multiple recipients provided"); return asset.visit( [&](Issue const& issue) { - return accountSendMultiIOU( - view, senderID, issue, receivers, j, sponsorAccountID, waiveFee); + return accountSendMultiIOU(view, senderID, issue, receivers, j, sponsorSle, waiveFee); }, [&](MPTIssue const& issue) { return accountSendMultiMPT(view, senderID, issue, receivers, j, waiveFee); diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index e72de61407b..aa22de651b5 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -368,26 +368,29 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) if (!tx.isFieldPresent(sfSponsor)) return tesSUCCESS; + if (auto const sponsorSle = getTxReserveSponsor(view, tx); !sponsorSle) + return terNO_ACCOUNT; + auto const hasSponsorSignature = tx.isFieldPresent(sfSponsorSignature); if (hasSponsorSignature) return tesSUCCESS; - auto const sponsorSle = + auto const sponsorshipSle = view.read(keylet::sponsor(tx.getAccountID(sfSponsor), tx.getAccountID(sfAccount))); // sponsorship object missing for pre-funded tx - if (!sponsorSle) + if (!sponsorshipSle) return terNO_SPONSORSHIP; auto const sponsorFlags = tx.getFieldU32(sfSponsorFlags); if (((sponsorFlags & spfSponsorFee) != 0u) && - sponsorSle->isFlag(lsfSponsorshipRequireSignForFee)) + sponsorshipSle->isFlag(lsfSponsorshipRequireSignForFee)) return terNO_SPONSORSHIP; if (((sponsorFlags & spfSponsorReserve) != 0u) && - sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve)) + sponsorshipSle->isFlag(lsfSponsorshipRequireSignForReserve)) return terNO_SPONSORSHIP; return tesSUCCESS; diff --git a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp index 028b3b4fded..1dee7f2dac5 100644 --- a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp +++ b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp @@ -13,7 +13,6 @@ #include #include -#include namespace xrpl { @@ -40,11 +39,6 @@ SponsorshipOwnerCountsMatch::visitEntry( return sle->getFieldU32(sfOwnerCount); return 0; }; - auto getSponsoredOwnerCount = [](std::shared_ptr const& sle) -> std::uint32_t { - if (sle && sle->getType() == ltACCOUNT_ROOT) - return sle->getFieldU32(sfSponsoredOwnerCount); - return 0; - }; auto getSponsoredObjectOwnerCount = [&](std::shared_ptr const& sle) -> std::uint32_t { @@ -97,7 +91,7 @@ SponsorshipOwnerCountsMatch::visitEntry( deltaSponsoredObjectOwnerCount_ += (afterSponsoredObjectOwnerCount - beforeSponsoredObjectOwnerCount); - if (getOwnerCount(after) < getSponsoredOwnerCount(after)) + if (getOwnerCount(after) < getSponsored(after)) invalidOwnerCountLessThanSponsoredOwnerCount_ += 1; } diff --git a/src/libxrpl/tx/paths/BookStep.cpp b/src/libxrpl/tx/paths/BookStep.cpp index ddcdba51f02..5985c0ab579 100644 --- a/src/libxrpl/tx/paths/BookStep.cpp +++ b/src/libxrpl/tx/paths/BookStep.cpp @@ -731,8 +731,7 @@ BookStep::forEachOffer( // Create MPToken for the offer's owner. No need to check // for the reserve since the offer is removed if it is consumed. // Therefore, the owner count remains the same. - if (auto const err = - checkCreateMPT(sb, assetIn.get(), owner, std::nullopt, j_); + if (auto const err = checkCreateMPT(sb, assetIn.get(), owner, {}, j_); !isTesSuccess(err)) { return true; diff --git a/src/libxrpl/tx/paths/MPTEndpointStep.cpp b/src/libxrpl/tx/paths/MPTEndpointStep.cpp index 4cf39fa96a3..3b92691e7ff 100644 --- a/src/libxrpl/tx/paths/MPTEndpointStep.cpp +++ b/src/libxrpl/tx/paths/MPTEndpointStep.cpp @@ -408,7 +408,7 @@ MPTEndpointOfferCrossingStep::checkCreateMPT(ApplyView& view, xrpl::DebtDirectio // for the reserve since the offer doesn't go on the books // if crossed. Insufficient reserve is allowed if the offer // crossed. See CreateOffer::applyGuts() for reserve check. - if (auto const err = xrpl::checkCreateMPT(view, mptIssue_, dst_, std::nullopt, j_); + if (auto const err = xrpl::checkCreateMPT(view, mptIssue_, dst_, {}, j_); !isTesSuccess(err)) { JLOG(j_.trace()) << "MPTEndpointStep::checkCreateMPT: failed create MPT"; diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 32a06b8e9ad..0c329f52eeb 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -232,6 +232,8 @@ SponsorshipSet::doApply() auto const reserveCount = ctx_.tx[~sfReserveCount]; auto reserveSponsorAccSle = getTxReserveSponsor(view(), ctx_.tx); + if (!reserveSponsorAccSle) + return reserveSponsorAccSle.error(); // LCOV_EXCL_LINE if (!sponsorObjSle) { @@ -254,7 +256,7 @@ SponsorshipSet::doApply() ctx_.tx, sponsorAccSle, STAmount{(*sponsorAccSle)[sfBalance]}.xrp(), - reserveSponsorAccSle, + *reserveSponsorAccSle, 1, 0, ctx_.journal); @@ -288,8 +290,8 @@ SponsorshipSet::doApply() (*newSle)[sfSponseeNode] = *sponseePage; // NOLINTNEXTLINE(readability-suspicious-call-argument) - adjustOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, ctx_.journal); - addSponsorToLedgerEntry(newSle, reserveSponsorAccSle); + adjustOwnerCount(view(), sponsorAccSle, *reserveSponsorAccSle, 1, ctx_.journal); + addSponsorToLedgerEntry(newSle, *reserveSponsorAccSle); ctx_.view().insert(newSle); return tesSUCCESS; @@ -323,7 +325,7 @@ SponsorshipSet::doApply() ctx_.tx, sponsorAccSle, STAmount{(*sponsorAccSle)[sfBalance]}.xrp(), - reserveSponsorAccSle, + *reserveSponsorAccSle, 0, 0, ctx_.journal); diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index bd971bb95e3..69b93b6255e 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -233,7 +233,9 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { auto const index = ctx.tx[~sfObjectID]; auto const flags = ctx.tx.getFlags(); - auto const newSponsor = getTxReserveSponsor(ctx.view, ctx.tx); + auto const newSponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + if (!newSponsorSle) + return newSponsorSle.error(); // LCOV_EXCL_LINE bool const isObjectSponsor = index != std::nullopt; @@ -260,7 +262,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if ((flags & tfSponsorshipCreate) != 0u) { - if (!newSponsor) + if (!*newSponsorSle) return tecNO_PERMISSION; // check object is not sponsored yet @@ -269,7 +271,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } else if ((flags & tfSponsorshipReassign) != 0u) { - if (!newSponsor) + if (!*newSponsorSle) return tecNO_PERMISSION; // check object is already ctx.sponsored @@ -278,7 +280,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } else if ((flags & tfSponsorshipEnd) != 0u) { - if (newSponsor) + if (*newSponsorSle) return tecNO_PERMISSION; // check object is sponsored @@ -298,7 +300,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) ctx.tx, sponseeSle, sponseeSle->getFieldAmount(sfBalance), - newSponsor, + *newSponsorSle, ownerCountDelta, 0, ctx.j); @@ -309,7 +311,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { if ((flags & tfSponsorshipCreate) != 0u) { - if (!newSponsor) + if (!*newSponsorSle) return tecNO_PERMISSION; // check account is not sponsored yet @@ -318,7 +320,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } else if ((flags & tfSponsorshipReassign) != 0u) { - if (!newSponsor) + if (!*newSponsorSle) return tecNO_PERMISSION; // check account is already sponsored @@ -327,7 +329,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } else if ((flags & tfSponsorshipEnd) != 0u) { - if (newSponsor) + if (*newSponsorSle) return tecNO_PERMISSION; // check account is sponsored @@ -350,7 +352,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) ctx.tx, sponseeSle, sponseeSle->getFieldAmount(sfBalance), - newSponsor, + *newSponsorSle, 0, 1, ctx.j); diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index f25579ff942..80c46e287fa 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -420,24 +420,16 @@ AccountDelete::doApply() { // sanity check // Since sfSponsoringAccountCount is set to soeDEFAULT, the field will not be - // populated with a value of 0. + // present with a value of 0. return tefINTERNAL; // LCOV_EXCL_LINE } - - if (sponsoringAccountCount == 1) - { - sponsorSle->makeFieldAbsent(sfSponsoringAccountCount); - } - else - { - sponsorSle->setFieldU32(sfSponsoringAccountCount, sponsoringAccountCount - 1); - } + sponsorSle->at(sfSponsoringAccountCount) = sponsoringAccountCount - 1; view().update(sponsorSle); // Following line might look redundant, but without it, sfSponsor // would end up remaining in after-ltAccountRoot during the // InvariantCheck. - (*src).makeFieldAbsent(sfSponsor); + src->makeFieldAbsent(sfSponsor); } XRPL_ASSERT( diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index 5a5f6222927..e6b75ed6639 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -322,9 +322,18 @@ SignerListSet::replaceSignerList() // We check the reserve against the starting balance because we want to // allow dipping into the reserve to pay fees. This behavior is consistent // with TicketCreate. - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - ctx_.view(), ctx_.tx, sle, preFeeBalance_, sponsor, kADDED_OWNER_COUNT, 0, ctx_.journal); + ctx_.view(), + ctx_.tx, + sle, + preFeeBalance_, + *sponsorSle, + kADDED_OWNER_COUNT, + 0, + ctx_.journal); !isTesSuccess(ret)) return ret; @@ -348,8 +357,8 @@ SignerListSet::replaceSignerList() // If we succeeded, the new entry counts against the // creator's reserve. - adjustOwnerCount(view(), sle, sponsor, kADDED_OWNER_COUNT, viewJ); - addSponsorToLedgerEntry(signerList, sponsor); + adjustOwnerCount(view(), sle, *sponsorSle, kADDED_OWNER_COUNT, viewJ); + addSponsorToLedgerEntry(signerList, *sponsorSle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index fba90245262..0f37ad0781f 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -1440,9 +1440,11 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) return terNO_ACCOUNT; auto const balance = (*sleAcc)[sfBalance]; - auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); - if (auto const ret = - checkInsufficientReserve(ctx.view, ctx.tx, sleAcc, balance, sponsor, 1, 0, ctx.j); + auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + if (auto const ret = checkInsufficientReserve( + ctx.view, ctx.tx, sleAcc, balance, *sponsorSle, 1, 0, ctx.j); !isTesSuccess(ret)) return ret; } @@ -1486,9 +1488,11 @@ XChainCreateBridge::doApply() (*sleBridge)[sfOwnerNode] = *page; } - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal); - addSponsorToLedgerEntry(sleBridge, sponsor); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + adjustOwnerCount(ctx_.view(), sleAcct, *sponsorSle, 1, ctx_.journal); + addSponsorToLedgerEntry(sleBridge, *sponsorSle); ctx_.view().insert(sleBridge); ctx_.view().update(sleAcct); @@ -1991,9 +1995,11 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) return terNO_ACCOUNT; auto const balance = (*sleAcc)[sfBalance]; - auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); - if (auto const ret = - checkInsufficientReserve(ctx.view, ctx.tx, sleAcc, balance, sponsor, 1, 0, ctx.j); + auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + if (auto const ret = checkInsufficientReserve( + ctx.view, ctx.tx, sleAcc, balance, *sponsorSle, 1, 0, ctx.j); !isTesSuccess(ret)) return ret; } @@ -2051,9 +2057,11 @@ XChainCreateClaimID::doApply() (*sleClaimID)[sfOwnerNode] = *page; } - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal); - addSponsorToLedgerEntry(sleClaimID, sponsor); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + adjustOwnerCount(ctx_.view(), sleAcct, *sponsorSle, 1, ctx_.journal); + addSponsorToLedgerEntry(sleClaimID, *sponsorSle); ctx_.view().insert(sleClaimID); ctx_.view().update(sleBridge); diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index 20392c3e73e..6e1f6a60ade 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -385,10 +385,9 @@ CheckCash::doApply() STAmount const flowDeliver{ optDeliverMin ? maxDeliverMin() : ctx_.tx.getFieldAmount(sfAmount)}; - auto const sponsorAccountID = getTxReserveSponsorAccountID(ctx_.tx); - std::shared_ptr sponsorSle = {}; - if (sponsorAccountID) - sponsorSle = psb.peek(keylet::account(*sponsorAccountID)); + auto const sponsorSle = getTxReserveSponsor(psb, ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE // Check reserve. Return destination account SLE if enough reserve, // otherwise return nullptr. @@ -397,7 +396,7 @@ CheckCash::doApply() // Can the account cover the trust line's or MPT reserve? if (auto const ret = checkInsufficientReserve( - psb, ctx_.tx, sleDst, preFeeBalance_, sponsorSle, 1, 0, j_); + psb, ctx_.tx, sleDst, preFeeBalance_, *sponsorSle, 1, 0, j_); !isTesSuccess(ret)) { JLOG(j_.trace()) << "Trust line does not exist. " @@ -455,7 +454,7 @@ CheckCash::doApply() Issue(currency, account_), // limit of zero 0, // quality in 0, // quality out - sponsorAccountID, // sponsor + *sponsorSle, // sponsor viewJ); // journal !isTesSuccess(ter)) { @@ -503,7 +502,7 @@ CheckCash::doApply() return tecINSUFFICIENT_RESERVE; if (auto const err = - checkCreateMPT(psb, mptID, account_, sponsorAccountID, j_); + checkCreateMPT(psb, mptID, account_, *sponsorSle, j_); !isTesSuccess(err)) { return err; diff --git a/src/libxrpl/tx/transactors/check/CheckCreate.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp index d5a01383399..033f6b5220f 100644 --- a/src/libxrpl/tx/transactors/check/CheckCreate.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -185,9 +185,11 @@ CheckCreate::doApply() // A check counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sle, preFeeBalance_, sponsor, 1, 0, ctx_.journal); + view(), ctx_.tx, sle, preFeeBalance_, *sponsorSle, 1, 0, ctx_.journal); !isTesSuccess(ret)) return ret; // Note that we use the value from the sequence or ticket as the @@ -243,8 +245,8 @@ CheckCreate::doApply() } // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sle, sponsor, 1, viewJ); - addSponsorToLedgerEntry(sleCheck, sponsor); + adjustOwnerCount(view(), sle, *sponsorSle, 1, viewJ); + addSponsorToLedgerEntry(sleCheck, *sponsorSle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index 3f7da85897e..efb500cf3c7 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -96,9 +96,11 @@ CredentialAccept::doApply() if (!sleSubject || !sleIssuer) return tefINTERNAL; // LCOV_EXCL_LINE - auto const newSponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleSubject, preFeeBalance_, newSponsor, 1, 0, ctx_.journal); + view(), ctx_.tx, sleSubject, preFeeBalance_, *sponsorSle, 1, 0, ctx_.journal); !isTesSuccess(ret)) return ret; @@ -119,8 +121,8 @@ CredentialAccept::doApply() adjustOwnerCountObj(view(), sleIssuer, sleCred, -1, j_); removeSponsorFromLedgerEntry(sleCred); - adjustOwnerCount(view(), sleSubject, newSponsor, 1, j_); - addSponsorToLedgerEntry(sleCred, newSponsor); + adjustOwnerCount(view(), sleSubject, *sponsorSle, 1, j_); + addSponsorToLedgerEntry(sleCred, *sponsorSle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index 71bde394389..01c36bdd2ed 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -131,9 +131,11 @@ CredentialCreate::doApply() if (!sleIssuer) return tefINTERNAL; // LCOV_EXCL_LINE - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleIssuer, preFeeBalance_, sponsor, 1, 0, ctx_.journal); + view(), ctx_.tx, sleIssuer, preFeeBalance_, *sponsorSle, 1, 0, ctx_.journal); !isTesSuccess(ret)) return ret; @@ -153,8 +155,8 @@ CredentialCreate::doApply() return tecDIR_FULL; sleCred->setFieldU64(sfIssuerNode, *page); - adjustOwnerCount(view(), sleIssuer, sponsor, 1, j_); - addSponsorToLedgerEntry(sleCred, sponsor); + adjustOwnerCount(view(), sleIssuer, *sponsorSle, 1, j_); + addSponsorToLedgerEntry(sleCred, *sponsorSle); } if (subject == account_) diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 982e4489e26..2d69a1eb80e 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -95,9 +95,11 @@ DelegateSet::doApply() if (permissions.empty()) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleOwner, preFeeBalance_, sponsor, 1, 0, ctx_.journal); + view(), ctx_.tx, sleOwner, preFeeBalance_, *sponsorSle, 1, 0, ctx_.journal); !isTesSuccess(ret)) return ret; @@ -127,8 +129,8 @@ DelegateSet::doApply() (*sle)[sfDestinationNode] = *destPage; ctx_.view().insert(sle); - adjustOwnerCount(ctx_.view(), sleOwner, sponsor, 1, ctx_.journal); - addSponsorToLedgerEntry(sle, sponsor); + adjustOwnerCount(ctx_.view(), sleOwner, *sponsorSle, 1, ctx_.journal); + addSponsorToLedgerEntry(sle, *sponsorSle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index c02c510d779..cd0e080917f 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -147,6 +147,9 @@ AMMCreate::preclaim(PreclaimContext const& ctx) if (ctx.view.rules().enabled(featureSponsor)) { auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + if(!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + // Check the reserve for LPToken trustline // Insufficient reserve auto const accountSle = ctx.view.read(keylet::account(accountID)); @@ -155,7 +158,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx) ctx.tx, accountSle, accountSle->getFieldAmount(sfBalance), - sponsorSle, + *sponsorSle, 1, 0, ctx.j); @@ -316,8 +319,11 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou sb.insert(ammSle); // Send LPT to LP. - auto const sponsor = getTxReserveSponsorAccountID(ctx.tx); - auto res = accountSend(sb, accountId, account, lpTokens, ctx.journal, sponsor); + auto const sponsorSle = getTxReserveSponsor(sb, ctx.tx); + if (!sponsorSle) + return {sponsorSle.error(), false}; // LCOV_EXCL_LINE + + auto res = accountSend(sb, accountId, account, lpTokens, ctx.journal, *sponsorSle); if (!isTesSuccess(res)) { JLOG(j.debug()) << "AMM Instance: failed to send LPT " << lpTokens; @@ -346,7 +352,7 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou } } - if (auto const err = createMPToken(sb, mptID, accountId, std::nullopt, flags); + if (auto const err = createMPToken(sb, mptID, accountId, {}, flags); !isTesSuccess(err)) return err; // Don't adjust AMM owner count. @@ -357,7 +363,7 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou accountId, amount, ctx.journal, - std::nullopt, // don't sponsor for AMM Trustline + {}, // don't sponsor for AMM Trustline WaiveTransferFee::Yes); }, // Set AMM flag on AMM trustline @@ -368,7 +374,7 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou accountId, amount, ctx.journal, - std::nullopt, // don't sponsor for AMM Trustline + {}, // don't sponsor for AMM Trustline WaiveTransferFee::Yes)) return res; // Set AMM flag on AMM trustline diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index 2771a9ea5a0..5d75ff71380 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -239,8 +239,10 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) ctx.view.read(keylet::line(accountID, lpIssue.account, lpIssue.currency)); auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE auto const accountSle = ctx.view.read(keylet::account(accountID)); - auto const reserveAdj = (sponsorSle || sle) ? 0 : 1; + auto const reserveAdj = (*sponsorSle || sle) ? 0 : 1; if (xrpLiquid(ctx.view, accountID, reserveAdj, ctx.j) < deposit) { @@ -254,11 +256,11 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) ctx.tx, accountSle, accountSle->getFieldAmount(sfBalance) - deposit, - sponsorSle, + *sponsorSle, 1, !sle, ctx.j); - sponsorSle && !isTesSuccess(ret)) + *sponsorSle && !isTesSuccess(ret)) return tecINSUF_RESERVE_LINE; return tesSUCCESS; @@ -383,14 +385,16 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) if (ctx.view.rules().enabled(featureSponsor)) { auto const accountSle = ctx.view.read(keylet::account(accountID)); - auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); + auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE // Insufficient reserve if (auto const ret = checkInsufficientReserve( ctx.view, ctx.tx, accountSle, accountSle->getFieldAmount(sfBalance), - sponsor, + *sponsorSle, 1, 0, ctx.j); @@ -554,7 +558,9 @@ AMMDeposit::deposit( std::optional const& lpTokensDepositMin, std::uint16_t tfee) { - auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view, ctx_.tx); + if (!sponsorSle) + return {sponsorSle.error(), STAmount{}}; // LCOV_EXCL_LINE // Check account has sufficient funds. // Return true if it does, false otherwise. @@ -567,7 +573,7 @@ AMMDeposit::deposit( // Adjust the reserve if LP doesn't have LPToken trustline auto const trustlineExists = view.exists(keylet::line(account_, lpIssue.account, lpIssue.currency)); - auto const reserveAdj = (sponsor || trustlineExists) ? 0 : 1; + auto const reserveAdj = (*sponsorSle || trustlineExists) ? 0 : 1; if (xrpLiquid(view, account_, reserveAdj, j_) >= depositAmount) return tesSUCCESS; } @@ -627,7 +633,7 @@ AMMDeposit::deposit( ammAccount, amountDepositActual, ctx_.journal, - std::nullopt, // don't sponsor for AMM Trustline + {}, // don't sponsor for AMM Trustline WaiveTransferFee::Yes); if (!isTesSuccess(res)) { @@ -652,7 +658,7 @@ AMMDeposit::deposit( ammAccount, *amount2DepositActual, ctx_.journal, - std::nullopt, // don't sponsor for AMM Trustline + {}, // don't sponsor for AMM Trustline WaiveTransferFee::Yes); if (!isTesSuccess(res)) { @@ -663,7 +669,7 @@ AMMDeposit::deposit( } // Deposit LP tokens - res = accountSend(view, ammAccount, account_, lpTokensDepositActual, ctx_.journal, sponsor); + res = accountSend(view, ammAccount, account_, lpTokensDepositActual, ctx_.journal, *sponsorSle); if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens"; diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index cb496dd85ab..e842a588887 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -616,7 +616,14 @@ AMMWithdraw::withdraw( // this is also called from AMMClawback, but only AMMWithdraw does sponsor // the new trustline - auto const sponsor = tx[sfAccount] == account ? getTxReserveSponsorAccountID(tx) : std::nullopt; + SLE::pointer sponsorSle; + if (tx[sfAccount] == account) + { + auto sle = getTxReserveSponsor(view, tx); + if (!sle) + return {sle.error(), STAmount{}, STAmount{}, STAmount{}}; // LCOV_EXCL_LINE + sponsorSle = std::move(*sle); + } // Check the reserve in case a trustline or MPT has to be created bool const enabledFixAmMv12 = view.rules().enabled(fixAMMv1_2); @@ -644,8 +651,6 @@ AMMWithdraw::withdraw( if (!sleAccount) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sponsorSle = getTxReserveSponsor(view, tx); - auto const balance = (*sleAccount)[sfBalance]->xrp(); std::uint32_t const count = ownerCount(view, sponsorSle ? sponsorSle : sleAccount, journal); @@ -657,8 +662,7 @@ AMMWithdraw::withdraw( tx, sleAccount, std::max(priorBalance, balance), - sponsor ? view.read(keylet::account(*sponsor)) - : std::shared_ptr(), + sponsorSle, 1, 0, journal); @@ -682,7 +686,7 @@ AMMWithdraw::withdraw( !isTesSuccess(err)) return err; - if (auto const err = checkCreateMPT(view, mptIssue, account, sponsor, journal); + if (auto const err = checkCreateMPT(view, mptIssue, account, sponsorSle, journal); !isTesSuccess(err)) { return err; @@ -699,7 +703,7 @@ AMMWithdraw::withdraw( // Withdraw amountWithdraw auto res = accountSend( - view, ammAccount, account, amountWithdrawActual, journal, sponsor, WaiveTransferFee::Yes); + view, ammAccount, account, amountWithdrawActual, journal, sponsorSle, WaiveTransferFee::Yes); if (!isTesSuccess(res)) { // LCOV_EXCL_START @@ -723,7 +727,7 @@ AMMWithdraw::withdraw( account, *amount2WithdrawActual, journal, - sponsor, + sponsorSle, WaiveTransferFee::Yes); if (!isTesSuccess(res)) { diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index 6c92d28de56..669b6593334 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -823,25 +823,26 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) if (!sleCreator) return {tefINTERNAL, false}; - { - auto const sponsor = getTxReserveSponsor(sb, ctx_.tx); - if (auto const ret = checkInsufficientReserve( - sb, ctx_.tx, sleCreator, preFeeBalance_, sponsor, 1, 0, j_); - !isTesSuccess(ret)) - { - // If we are here, the signing account had an insufficient reserve - // *prior* to our processing. If something actually crossed, then - // we allow this; otherwise, we just claim a fee. - if (!crossed) - result = tecINSUF_RESERVE_OFFER; + auto const sponsorSle = getTxReserveSponsor(sb, ctx_.tx); + if (!sponsorSle) + return {sponsorSle.error(), false}; // LCOV_EXCL_LINE - if (!isTesSuccess(result)) - { - JLOG(j_.debug()) << "final result: " << transToken(result); - } + if (auto const ret = checkInsufficientReserve( + sb, ctx_.tx, sleCreator, preFeeBalance_, *sponsorSle, 1, 0, j_); + !isTesSuccess(ret)) + { + // If we are here, the signing account had an insufficient reserve + // *prior* to our processing. If something actually crossed, then + // we allow this; otherwise, we just claim a fee. + if (!crossed) + result = tecINSUF_RESERVE_OFFER; - return {result, true}; + if (!isTesSuccess(result)) + { + JLOG(j_.debug()) << "final result: " << transToken(result); } + + return {result, true}; } // We need to place the remainder of the offer into its order book. @@ -860,8 +861,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) } // Update owner count. - auto const sponsor = getTxReserveSponsor(sb, ctx_.tx); - adjustOwnerCount(sb, sleCreator, sponsor, 1, viewJ); + adjustOwnerCount(sb, sleCreator, *sponsorSle, 1, viewJ); JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.asset()) << " : " << to_string(saTakerGets.asset()) @@ -930,7 +930,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) sleOffer->setFlag(lsfSell); if (domainID) sleOffer->setFieldH256(sfDomainID, *domainID); - addSponsorToLedgerEntry(sleOffer, sponsor); + addSponsorToLedgerEntry(sleOffer, *sponsorSle); // if it's a hybrid offer, set hybrid flag, and create an open dir if (bHybrid) diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index 59b7aece227..842583e657f 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -72,10 +72,12 @@ addSLE(ApplyContext& ctx, std::shared_ptr const& sle, AccountID const& owne return tefINTERNAL; // LCOV_EXCL_LINE // Check reserve availability for new object creation - auto const sponsor = getTxReserveSponsor(ctx.view(), ctx.tx); + auto const sponsorSle = getTxReserveSponsor(ctx.view(), ctx.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE auto const balance = STAmount((*sleAccount)[sfBalance]).xrp(); if (auto const ret = checkInsufficientReserve( - ctx.view(), ctx.tx, sleAccount, balance, sponsor, 1, 0, ctx.journal); + ctx.view(), ctx.tx, sleAccount, balance, *sponsorSle, 1, 0, ctx.journal); !isTesSuccess(ret)) return ret; @@ -90,8 +92,8 @@ addSLE(ApplyContext& ctx, std::shared_ptr const& sle, AccountID const& owne return tecDIR_FULL; // LCOV_EXCL_LINE (*sle)[sfOwnerNode] = *page; } - adjustOwnerCount(ctx.view(), sleAccount, sponsor, 1, ctx.journal); - addSponsorToLedgerEntry(sle, sponsor); + adjustOwnerCount(ctx.view(), sleAccount, *sponsorSle, 1, ctx.journal); + addSponsorToLedgerEntry(sle, *sponsorSle); ctx.view().update(sleAccount); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 5079448ec8e..df52e47921a 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -426,9 +426,11 @@ EscrowCreate::doApply() STAmount const amount{ctx_.tx[sfAmount]}; auto const balance = sle->getFieldAmount(sfBalance).xrp(); - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = - checkInsufficientReserve(ctx_.view(), ctx_.tx, sle, balance, sponsor, 1, 0, j_); + checkInsufficientReserve(ctx_.view(), ctx_.tx, sle, balance, *sponsorSle, 1, 0, j_); !isTesSuccess(ret)) return ret; @@ -529,8 +531,8 @@ EscrowCreate::doApply() } // increment owner count - adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal); - addSponsorToLedgerEntry(slep, sponsor); + adjustOwnerCount(ctx_.view(), sle, *sponsorSle, 1, ctx_.journal); + addSponsorToLedgerEntry(slep, *sponsorSle); ctx_.view().update(sle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index b071c41dfc3..742a3f4ec06 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -223,6 +223,11 @@ EscrowFinish::preclaim(PreclaimContext const& ctx) return ret; } } + + auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + if (!sponsorSle) + return sponsorSle.error(); + return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index 18232ce0253..88a959617a6 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -237,17 +237,19 @@ LoanBrokerSet::doApply() if (auto const ter = dirLink(view, vaultPseudoID, broker, sfVaultNode)) return ter; // LCOV_EXCL_LINE - auto const sponsor = getTxReserveSponsor(view, tx); + auto const sponsorSle = getTxReserveSponsor(view, tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - view, tx, owner, preFeeBalance_, {}, sponsor ? 1 : 2, 0, j_); + view, tx, owner, preFeeBalance_, {}, *sponsorSle ? 1 : 2, 0, j_); !isTesSuccess(ret)) return ret; - if (sponsor) + if (*sponsorSle) { - if (auto const ret = - checkInsufficientReserve(view, tx, owner, preFeeBalance_, sponsor, 1, 0, j_); + if (auto const ret = checkInsufficientReserve( + view, tx, owner, preFeeBalance_, *sponsorSle, 1, 0, j_); !isTesSuccess(ret)) return ret; } @@ -257,7 +259,7 @@ LoanBrokerSet::doApply() // Pseudo-account cannot be sponsored adjustOwnerCount(view, owner, {}, 1, j_); // LoanBroker object can be sponsored - adjustOwnerCount(view, owner, sponsor, 1, j_); + adjustOwnerCount(view, owner, *sponsorSle, 1, j_); auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID); if (!maybePseudo) @@ -287,7 +289,7 @@ LoanBrokerSet::doApply() if (auto const coverLiq = tx[~sfCoverRateLiquidation]) broker->at(sfCoverRateLiquidation) = *coverLiq; - addSponsorToLedgerEntry(broker, sponsor); + addSponsorToLedgerEntry(broker, *sponsorSle); view.insert(broker); diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index 0cfc6bf2b3f..7d4f94c1e6b 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -505,15 +505,17 @@ LoanSet::doApply() } auto const sponsorSle = getTxReserveSponsor(view, tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE { auto const balance = account_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp(); if (auto const ret = - checkInsufficientReserve(view, tx, borrowerSle, balance, sponsorSle, 1, 0, j_); + checkInsufficientReserve(view, tx, borrowerSle, balance, *sponsorSle, 1, 0, j_); !isTesSuccess(ret)) return ret; } - adjustOwnerCount(view, borrowerSle, sponsorSle, 1, j_); + adjustOwnerCount(view, borrowerSle, *sponsorSle, 1, j_); // Account for the origination fee using two payments // @@ -613,7 +615,7 @@ LoanSet::doApply() loan->at(sfPreviousPaymentDueDate) = 0; loan->at(sfNextPaymentDueDate) = startDate + paymentInterval; loan->at(sfPaymentRemaining) = paymentTotal; - addSponsorToLedgerEntry(loan, sponsorSle); + addSponsorToLedgerEntry(loan, *sponsorSle); view.insert(loan); // Update the balances in the vault diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index 77f8286fbff..c1be373b580 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -375,9 +375,12 @@ NFTokenAcceptOffer::transferNFToken( std::uint32_t const buyerOwnerCountBefore = sleBuyer->getFieldU32(sfOwnerCount); - auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + auto const insertRet = - nft::insertToken(view(), ctx_.tx, buyer, sponsor, std::move(tokenAndPage->token)); + nft::insertToken(view(), ctx_.tx, buyer, *sponsorSle, std::move(tokenAndPage->token)); // if fixNFTokenReserve is enabled, check if the buyer has sufficient // reserve to own a new object, if their OwnerCount changed. @@ -397,10 +400,11 @@ NFTokenAcceptOffer::transferNFToken( auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount); if (buyerOwnerCountAfter > buyerOwnerCountBefore) { - auto const sponsorSle = account_ == buyer ? getTxReserveSponsor(ctx_.view(), ctx_.tx) - : std::shared_ptr(); + SLE::const_pointer buyerSponsorSle; + if (account_ == buyer) + buyerSponsorSle = *sponsorSle; if (auto const ret = checkInsufficientReserve( - ctx_.view(), ctx_.tx, sleBuyer, buyerBalance, sponsorSle, 0, 0, j_); + ctx_.view(), ctx_.tx, sleBuyer, buyerBalance, buyerSponsorSle, 0, 0, j_); !isTesSuccess(ret)) return ret; } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index f486824aecd..231f7dacb11 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -307,9 +307,12 @@ NFTokenMint::doApply() object.setFieldVL(sfURI, *uri); }); - auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + if (TER const ret = - nft::insertToken(ctx_.view(), ctx_.tx, account_, sponsor, std::move(newToken)); + nft::insertToken(ctx_.view(), ctx_.tx, account_, *sponsorSle, std::move(newToken)); !isTesSuccess(ret)) return ret; @@ -340,14 +343,13 @@ NFTokenMint::doApply() if (auto const ownerCountAfter = view().read(keylet::account(account_))->getFieldU32(sfOwnerCount); ownerCountAfter > ownerCountBefore) - { - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + { if (auto const ret = checkInsufficientReserve( ctx_.view(), ctx_.tx, view().read(keylet::account(account_)), preFeeBalance_, - sponsor, + *sponsorSle, 0, 0, j_); diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index a3bc4817d48..291006d2103 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -183,9 +183,11 @@ OracleSet::preclaim(PreclaimContext const& ctx) return tecARRAY_TOO_LARGE; auto const& balance = sleSetter->getFieldAmount(sfBalance); - auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); + auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - ctx.view, ctx.tx, sleSetter, balance, sponsor, adjustReserve, 0, ctx.j); + ctx.view, ctx.tx, sleSetter, balance, *sponsorSle, adjustReserve, 0, ctx.j); !isTesSuccess(ret)) return ret; @@ -283,13 +285,15 @@ OracleSet::doApply() // Otherwise, the sponsorship will be deleted. auto const newSponsorSle = getTxReserveSponsor(ctx_.view(), ctx_.tx); + if (!newSponsorSle) + return newSponsorSle.error(); // LCOV_EXCL_LINE // decrease current sponsored owner count adjustOwnerCountObj(ctx_.view(), accountSle, sle, -oldCount, ctx_.journal); removeSponsorFromLedgerEntry(sle); // increase new owner count - adjustOwnerCount(ctx_.view(), accountSle, newSponsorSle, newCount, ctx_.journal); - addSponsorToLedgerEntry(sle, newSponsorSle); + adjustOwnerCount(ctx_.view(), accountSle, *newSponsorSle, newCount, ctx_.journal); + addSponsorToLedgerEntry(sle, *newSponsorSle); } else if (adjust < 0) { @@ -344,13 +348,15 @@ OracleSet::doApply() (*sle)[sfOwnerNode] = *page; auto const count = calculateOracleReserve(series.size()); - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE auto const accountSle = ctx_.view().peek(keylet::account(ctx_.tx[sfAccount])); if (!accountSle) return tefINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(ctx_.view(), accountSle, sponsor, count, ctx_.journal); - addSponsorToLedgerEntry(sle, sponsor); + adjustOwnerCount(ctx_.view(), accountSle, *sponsorSle, count, ctx_.journal); + addSponsorToLedgerEntry(sle, *sponsorSle); ctx_.view().insert(sle); } diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 5cb428d6b7a..e61c3f0971c 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -161,9 +161,11 @@ DepositPreauth::doApply() // A preauth counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleOwner, preFeeBalance_, sponsor, 1, 0, j_); + view(), ctx_.tx, sleOwner, preFeeBalance_, *sponsorSle, 1, 0, j_); !isTesSuccess(ret)) return ret; @@ -189,8 +191,8 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); - addSponsorToLedgerEntry(slePreauth, sponsor); + adjustOwnerCount(view(), sleOwner, *sponsorSle, 1, j_); + addSponsorToLedgerEntry(slePreauth, *sponsorSle); } else if (ctx_.tx.isFieldPresent(sfUnauthorize)) { @@ -207,9 +209,11 @@ DepositPreauth::doApply() // A preauth counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleOwner, preFeeBalance_, sponsor, 1, 0, j_); + view(), ctx_.tx, sleOwner, preFeeBalance_, *sponsorSle, 1, 0, j_); !isTesSuccess(ret)) return ret; @@ -249,8 +253,8 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sleOwner, sponsor, 1, j_); - addSponsorToLedgerEntry(slePreauth, sponsor); + adjustOwnerCount(view(), sleOwner, *sponsorSle, 1, j_); + addSponsorToLedgerEntry(slePreauth, *sponsorSle); } else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials)) { diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp index d4300758f7a..be0e4d68bd8 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -80,14 +80,16 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx) // Check reserve and funds availability { auto const balance = (*sle)[sfBalance]; - auto const sponsor = getTxReserveSponsor(ctx.view, ctx.tx); + auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = - checkInsufficientReserve(ctx.view, ctx.tx, sle, balance, sponsor, 1, 0, ctx.j); + checkInsufficientReserve(ctx.view, ctx.tx, sle, balance, *sponsorSle, 1, 0, ctx.j); !isTesSuccess(ret)) return ret; if (auto const ret = checkInsufficientReserve( - ctx.view, ctx.tx, sle, balance - ctx.tx[sfAmount], sponsor, 1, 0, ctx.j); + ctx.view, ctx.tx, sle, balance - ctx.tx[sfAmount], *sponsorSle, 1, 0, ctx.j); !isTesSuccess(ret)) return tecUNFUNDED; } @@ -184,9 +186,11 @@ PaymentChannelCreate::doApply() // Deduct owner's balance, increment owner count (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); - adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal); - addSponsorToLedgerEntry(slep, sponsor); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + adjustOwnerCount(ctx_.view(), sle, *sponsorSle, 1, ctx_.journal); + addSponsorToLedgerEntry(slep, *sponsorSle); ctx_.view().update(sle); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp index 2fcac033a61..f7b40308176 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -84,9 +84,11 @@ PaymentChannelFund::doApply() { // Check reserve and funds availability auto const balance = (*sle)[sfBalance]; - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = - checkInsufficientReserve(ctx_.view(), ctx_.tx, sle, balance, sponsor, 0, 0, j_); + checkInsufficientReserve(ctx_.view(), ctx_.tx, sle, balance, *sponsorSle, 0, 0, j_); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index 30c05a22ca0..43ae9c420cf 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -107,9 +107,11 @@ PermissionedDomainSet::doApply() // Create new permissioned domain. // Check reserve availability for new object creation auto const balance = STAmount((*ownerSle)[sfBalance]).xrp(); - auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - ctx_.view(), ctx_.tx, ownerSle, balance, sponsor, 1, 0, j_); + ctx_.view(), ctx_.tx, ownerSle, balance, *sponsorSle, 1, 0, j_); !isTesSuccess(ret)) return ret; @@ -127,8 +129,8 @@ PermissionedDomainSet::doApply() slePd->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), ownerSle, sponsor, 1, ctx_.journal); - addSponsorToLedgerEntry(slePd, sponsor); + adjustOwnerCount(view(), ownerSle, *sponsorSle, 1, ctx_.journal); + addSponsorToLedgerEntry(slePd, *sponsorSle); view().insert(slePd); } diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp index c70328aded8..4e74c96fc9a 100644 --- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -76,9 +76,11 @@ TicketCreate::doApply() // check the starting balance because we want to allow dipping into the // reserve to pay fees. std::uint32_t const ticketCount = ctx_.tx[sfTicketCount]; - auto const sponsor = getTxReserveSponsor(view(), ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleAccountRoot, preFeeBalance_, sponsor, ticketCount, 0, j_); + view(), ctx_.tx, sleAccountRoot, preFeeBalance_, *sponsorSle, ticketCount, 0, j_); !isTesSuccess(ret)) return ret; @@ -117,7 +119,7 @@ TicketCreate::doApply() return tecDIR_FULL; // LCOV_EXCL_LINE sleTicket->setFieldU64(sfOwnerNode, *page); - addSponsorToLedgerEntry(sleTicket, sponsor); + addSponsorToLedgerEntry(sleTicket, *sponsorSle); } // Update the record of the number of Tickets this account owns. @@ -126,7 +128,7 @@ TicketCreate::doApply() sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount); // Every added Ticket counts against the creator's reserve. - adjustOwnerCount(view(), sleAccountRoot, sponsor, ticketCount, viewJ); + adjustOwnerCount(view(), sleAccountRoot, *sponsorSle, ticketCount, viewJ); // TicketCreate is the only transaction that can cause an account root's // Sequence field to increase by more than one. October 2018. diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 4a69f3ffcbc..917dc51ab5e 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -106,12 +106,19 @@ MPTokenIssuanceCreate::create( if (!acct) return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE - auto const sponsor = - !isPseudoAccount((acct)) ? getTxReserveSponsor(view, tx) : std::shared_ptr(); + SLE::pointer sponsorSle; + if (!isPseudoAccount(acct)) + { + auto sle = getTxReserveSponsor(view, tx); + if (!sle) + return Unexpected(sle.error()); + sponsorSle = std::move(*sle); + } + if (args.priorBalance) { if (auto const ret = checkInsufficientReserve( - view, tx, acct, *(args.priorBalance), sponsor, 1, 0, journal); + view, tx, acct, *(args.priorBalance), sponsorSle, 1, 0, journal); !isTesSuccess(ret)) return Unexpected(ret); // tecINSUFFICIENT_RESERVE } @@ -152,13 +159,13 @@ MPTokenIssuanceCreate::create( if (args.mutableFlags) (*mptIssuance)[sfMutableFlags] = *args.mutableFlags; - addSponsorToLedgerEntry(mptIssuance, sponsor); + addSponsorToLedgerEntry(mptIssuance, sponsorSle); view.insert(mptIssuance); } // Update owner count. - adjustOwnerCount(view, acct, sponsor, 1, journal); + adjustOwnerCount(view, acct, sponsorSle, 1, journal); return mptId; } diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index b7cbb22b247..7add2b6a963 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -366,18 +366,17 @@ TrustSet::doApply() // but the incremental reserve for the trust line as // well. A person with no intention of using the gateway // could use the extra XRP for their own purposes. - auto const txSponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); - std::shared_ptr txSponsorSle; - if (txSponsorAcc) - txSponsorSle = view().peek(keylet::account(*txSponsorAcc)); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE - std::uint32_t const uOwnerCount = ownerCount(view(), txSponsorSle ? txSponsorSle : sle, j_); + std::uint32_t const uOwnerCount = ownerCount(view(), *sponsorSle ? *sponsorSle : sle, j_); - bool const isSponsoredAndPreFunded = txSponsorSle && !isSponsorReserveCoSigning(ctx_.tx); + bool const isSponsoredAndPreFunded = *sponsorSle && !isSponsorReserveCoSigning(ctx_.tx); // If PreFunded Sponsor, it must be checked whether sufficient // ReserveCount exists. - bool const freeTrustLine = uOwnerCount < 2 && !txSponsorSle; + bool const freeTrustLine = uOwnerCount < 2 && !*sponsorSle; std::uint32_t const uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0); std::uint32_t uQualityOut(bQualityOut ? ctx_.tx.getFieldU32(sfQualityOut) : 0); @@ -581,15 +580,15 @@ TrustSet::doApply() // For PreFunded sponsors, we need to check if there are sufficient reserves before // calling adjustOwnerCount(). if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleLowAccount, preFeeBalance_, txSponsorSle, 1, 0, j_); + view(), ctx_.tx, sleLowAccount, preFeeBalance_, *sponsorSle, 1, 0, j_); isSponsoredAndPreFunded && !isTesSuccess(ret)) return tecINSUF_RESERVE_LINE; // Set reserve for low account. - adjustOwnerCount(view(), sleLowAccount, txSponsorSle, 1, viewJ); + adjustOwnerCount(view(), sleLowAccount, *sponsorSle, 1, viewJ); uFlagsOut |= lsfLowReserve; - addSponsorToLedgerEntry(sleRippleState, txSponsorSle, sfLowSponsor); + addSponsorToLedgerEntry(sleRippleState, *sponsorSle, sfLowSponsor); if (!bHigh) bReserveIncrease = true; @@ -610,15 +609,15 @@ TrustSet::doApply() // For PreFunded sponsors, we need to check if there are sufficient reserves before // calling adjustOwnerCount(). if (auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sleHighAccount, preFeeBalance_, txSponsorSle, 1, 0, j_); + view(), ctx_.tx, sleHighAccount, preFeeBalance_, *sponsorSle, 1, 0, j_); isSponsoredAndPreFunded && !isTesSuccess(ret)) return tecINSUF_RESERVE_LINE; // Set reserve for high account. - adjustOwnerCount(view(), sleHighAccount, txSponsorSle, 1, viewJ); + adjustOwnerCount(view(), sleHighAccount, *sponsorSle, 1, viewJ); uFlagsOut |= lsfHighReserve; - addSponsorToLedgerEntry(sleRippleState, txSponsorSle, sfHighSponsor); + addSponsorToLedgerEntry(sleRippleState, *sponsorSle, sfHighSponsor); if (bHigh) bReserveIncrease = true; @@ -645,7 +644,7 @@ TrustSet::doApply() // Reserve is not scaled by load. else if ( auto const ret = checkInsufficientReserve( - view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 0, 0, j_); + view(), ctx_.tx, sle, preFeeBalance_, *sponsorSle, 0, 0, j_); !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " @@ -676,7 +675,7 @@ TrustSet::doApply() } else if ( auto const ret = checkInsufficientReserve( - ctx_.view(), ctx_.tx, sle, preFeeBalance_, txSponsorSle, 1, 0, j_); + ctx_.view(), ctx_.tx, sle, preFeeBalance_, *sponsorSle, 1, 0, j_); !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " @@ -711,7 +710,7 @@ TrustSet::doApply() saLimitAllow, // Limit for who is being charged. uQualityIn, uQualityOut, - txSponsorAcc, + *sponsorSle, viewJ); } diff --git a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp index 7d80d245aa7..64c044344d3 100644 --- a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp @@ -395,7 +395,7 @@ VaultClawback::doApply() auto const& vaultAccount = vault->at(sfAccount); // Transfer shares from holder to vault. if (auto const ter = accountSend( - view(), holder, vaultAccount, sharesDestroyed, j_, std::nullopt, WaiveTransferFee::Yes); + view(), holder, vaultAccount, sharesDestroyed, j_, {}, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -430,13 +430,7 @@ VaultClawback::doApply() { // Transfer assets from vault to issuer. if (auto const ter = accountSend( - view(), - vaultAccount, - account_, - assetsRecovered, - j_, - std::nullopt, - WaiveTransferFee::Yes); + view(), vaultAccount, account_, assetsRecovered, j_, {}, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index 8fc4d0dfb92..466c901d992 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -155,13 +155,15 @@ VaultCreate::doApply() if (auto ter = dirLink(view(), account_, vault)) return ter; // We will create Vault and PseudoAccount, hence increase OwnerCount by 2 - auto const sponsor = getTxReserveSponsor(view(), tx); + auto const sponsorSle = getTxReserveSponsor(view(), tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE if (!ctx_.view().rules().enabled(featureSponsor)) { - adjustOwnerCount(view(), owner, sponsor, 2, j_); - addSponsorToLedgerEntry(vault, sponsor); + adjustOwnerCount(view(), owner, *sponsorSle, 2, j_); + addSponsorToLedgerEntry(vault, *sponsorSle); if (auto const ret = - checkInsufficientReserve(view(), tx, owner, preFeeBalance_, sponsor, 0, 0, j_); + checkInsufficientReserve(view(), tx, owner, preFeeBalance_, *sponsorSle, 0, 0, j_); !isTesSuccess(ret)) return ret; } @@ -169,11 +171,11 @@ VaultCreate::doApply() { // after Sponsor Amendment, check insufficient reserve first if (auto const ret = - checkInsufficientReserve(view(), tx, owner, preFeeBalance_, sponsor, 2, 0, j_); + checkInsufficientReserve(view(), tx, owner, preFeeBalance_, *sponsorSle, 2, 0, j_); !isTesSuccess(ret)) return ret; - adjustOwnerCount(view(), owner, sponsor, 2, j_); - addSponsorToLedgerEntry(vault, sponsor); + adjustOwnerCount(view(), owner, *sponsorSle, 2, j_); + addSponsorToLedgerEntry(vault, *sponsorSle); } auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID); diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index f5c20301dd4..cddd8380ab2 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -261,7 +261,7 @@ VaultDeposit::doApply() vaultAccount, assetsDeposited, j_, - std::nullopt, + {}, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -281,11 +281,13 @@ VaultDeposit::doApply() // LCOV_EXCL_STOP } - auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE // Transfer shares from vault to depositor. if (auto const ter = accountSend( - view(), vaultAccount, account_, sharesCreated, j_, sponsor, WaiveTransferFee::Yes); + view(), vaultAccount, account_, sharesCreated, j_, *sponsorSle, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index 5baebbfa243..747196cafda 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -251,11 +251,13 @@ VaultWithdraw::doApply() view().update(vault); auto const& vaultAccount = vault->at(sfAccount); - auto const sponsor = getTxReserveSponsorAccountID(ctx_.tx); + auto const sponsorSle = getTxReserveSponsor(view(), ctx_.tx); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE // Transfer shares from depositor to vault. if (auto const ter = accountSend( - view(), account_, vaultAccount, sharesRedeemed, j_, sponsor, WaiveTransferFee::Yes); + view(), account_, vaultAccount, sharesRedeemed, j_, *sponsorSle, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 3cf4f055dbe..ef808948bde 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -419,13 +419,14 @@ class Sponsor_test : public beast::unit_test::Suite using namespace test::jtx; Env env{*this, testableAmendments()}; Account const alice("alice"); + Account const bob("bob"); Account const sponsor("sponsor"); Account const invalid("invalid"); Account const signer1("signer1"); Account const signer2("signer2"); - env.fund(XRP(10000), alice, sponsor); + env.fund(XRP(10000), alice, bob, sponsor); env.close(); env(signers(sponsor, 1, {{signer1, 1}, {signer2, 1}})); @@ -439,10 +440,10 @@ class Sponsor_test : public beast::unit_test::Suite signers1[sfTxnSignature.jsonName] = "DEADBEEF"; env(tx, Fee(XRP(1)), sponsor::As(sponsor, spfSponsorReserve), Ter(telENV_RPC_FAILED)); - // Signer account doesn't exist + // bob is not a multi-signing account. env(noop(alice), Fee(XRP(1)), - sponsor::As(invalid, spfSponsorReserve), + sponsor::As(bob, spfSponsorReserve), Msig(sfSponsorSignature, {signer1}), Ter(tefNOT_MULTI_SIGNING)); @@ -491,7 +492,7 @@ class Sponsor_test : public beast::unit_test::Suite env(noop(alice), Sig(sfSponsorSignature, sponsor), Ter(temMALFORMED)); // Invalid Sponsor Account (Sponsor.Account doesn't exist) - env(noop(alice), sponsor::As(noFunded, spfSponsorReserve), Ter(terNO_SPONSORSHIP)); + env(noop(alice), sponsor::As(noFunded, spfSponsorReserve), Ter(terNO_ACCOUNT)); env(noop(alice), sponsor::As(noFunded, spfSponsorReserve), Sig(sfSponsorSignature, noFunded), From 0bfdfa73ff50fe79e33543c7fcdd0c5ab3e27b43 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Thu, 14 May 2026 01:24:56 -0400 Subject: [PATCH 239/249] Merge fixes --- .../tx/transactors/sponsor/SponsorshipSet.h | 2 +- .../transactors/sponsor/SponsorshipTransfer.h | 2 +- .../ledger/helpers/RippleStateHelpers.cpp | 1 + src/libxrpl/tx/Transactor.cpp | 1 + .../tx/invariants/SponsorshipInvariant.cpp | 1 + .../tx/transactors/escrow/EscrowFinish.cpp | 1 + .../token/MPTokenIssuanceCreate.cpp | 1 + src/test/app/MPToken_test.cpp | 2 +- src/test/app/Sponsor_test.cpp | 26 +++++++++---------- src/test/jtx/impl/sponsor.cpp | 12 ++++----- src/test/rpc/AccountTx_test.cpp | 3 ++- src/test/rpc/Simulate_test.cpp | 2 +- 12 files changed, 30 insertions(+), 24 deletions(-) diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h index 5ca90f7963e..b0579428b60 100644 --- a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h @@ -7,7 +7,7 @@ namespace xrpl { class SponsorshipSet : public Transactor { public: - static constexpr ConsequencesFactoryType kCONSEQUENCES_FACTORY{Normal}; + static constexpr auto kCONSEQUENCES_FACTORY = ConsequencesFactoryType::Normal; explicit SponsorshipSet(ApplyContext& ctx) : Transactor(ctx) { diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h b/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h index 78888f022df..93a0cd68833 100644 --- a/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h @@ -7,7 +7,7 @@ namespace xrpl { class SponsorshipTransfer : public Transactor { public: - static constexpr ConsequencesFactoryType kCONSEQUENCES_FACTORY{Normal}; + static constexpr auto kCONSEQUENCES_FACTORY = ConsequencesFactoryType::Normal; explicit SponsorshipTransfer(ApplyContext& ctx) : Transactor(ctx) { diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index d39c8134cd9..fe23456c79d 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -31,6 +31,7 @@ #include #include #include +#include namespace xrpl { diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index ef8b7541a91..f17cb2fa5e6 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp index 1dee7f2dac5..41d55e90195 100644 --- a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp +++ b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp @@ -13,6 +13,7 @@ #include #include +#include namespace xrpl { diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index b3cc24ccb08..c23731ec350 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 065905f65de..17e7b7ff6d6 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -23,6 +23,7 @@ #include #include +#include namespace xrpl { diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 0f26a4d2b0e..2cee6637a57 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -2099,7 +2099,7 @@ class MPToken_test : public beast::unit_test::Suite jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = alice.human(); jv[sfSponsee.fieldName] = carol.human(); - jv[sfFeeAmount.fieldName] = mpt.getJson(JsonOptions::KNone); + jv[sfFeeAmount.fieldName] = mpt.getJson(JsonOptions::Values::None); test(jv, sfFeeAmount.fieldName.c_str()); } } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index ef808948bde..272cafca8ba 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2178,8 +2178,8 @@ class Sponsor_test : public beast::unit_test::Suite json::Value jv; jv[jss::TransactionType] = jss::AMMCreate; jv[jss::Account] = account.human(); - jv[jss::Amount] = amount1.getJson(JsonOptions::KNone); - jv[jss::Amount2] = amount2.getJson(JsonOptions::KNone); + jv[jss::Amount] = amount1.getJson(JsonOptions::Values::None); + jv[jss::Amount2] = amount2.getJson(JsonOptions::Values::None); jv[jss::TradingFee] = 0; jv[jss::Fee] = std::to_string(env.current()->fees().increment.drops()); return jv; @@ -2192,10 +2192,10 @@ class Sponsor_test : public beast::unit_test::Suite json::Value jv; jv[jss::TransactionType] = jss::AMMDeposit; jv[jss::Account] = account.human(); - jv[jss::Asset] = STIssue(sfAsset, amount1.asset()).getJson(JsonOptions::KNone); - jv[jss::Asset2] = STIssue(sfAsset, amount2.asset()).getJson(JsonOptions::KNone); - jv[jss::Amount] = amount1.value().getJson(JsonOptions::KNone); - jv[jss::Amount2] = amount2.value().getJson(JsonOptions::KNone); + jv[jss::Asset] = STIssue(sfAsset, amount1.asset()).getJson(JsonOptions::Values::None); + jv[jss::Asset2] = STIssue(sfAsset, amount2.asset()).getJson(JsonOptions::Values::None); + jv[jss::Amount] = amount1.value().getJson(JsonOptions::Values::None); + jv[jss::Amount2] = amount2.value().getJson(JsonOptions::Values::None); jv[jss::Flags] = tfTwoAsset; return jv; }; @@ -2368,9 +2368,9 @@ class Sponsor_test : public beast::unit_test::Suite json::Value jv; jv[jss::TransactionType] = jss::AMMWithdraw; jv[jss::Account] = alice.human(); - jv[jss::Asset] = STIssue(sfAsset, usd.issue()).getJson(JsonOptions::KNone); - jv[jss::Asset2] = STIssue(sfAsset, eur.issue()).getJson(JsonOptions::KNone); - jv[jss::Amount] = usd(100).value().getJson(JsonOptions::KNone); + jv[jss::Asset] = STIssue(sfAsset, usd.issue()).getJson(JsonOptions::Values::None); + jv[jss::Asset2] = STIssue(sfAsset, eur.issue()).getJson(JsonOptions::Values::None); + jv[jss::Amount] = usd(100).value().getJson(JsonOptions::Values::None); jv[jss::Flags] = tfSingleAsset; env(ticket::create(sponsor, 1)); // adjust for free @@ -2418,8 +2418,8 @@ class Sponsor_test : public beast::unit_test::Suite json::Value jv; jv[jss::TransactionType] = jss::AMMWithdraw; jv[jss::Account] = alice.human(); - jv[jss::Asset] = STIssue(sfAsset, usd.issue()).getJson(JsonOptions::KNone); - jv[jss::Asset2] = STIssue(sfAsset, eur.issue()).getJson(JsonOptions::KNone); + jv[jss::Asset] = STIssue(sfAsset, usd.issue()).getJson(JsonOptions::Values::None); + jv[jss::Asset2] = STIssue(sfAsset, eur.issue()).getJson(JsonOptions::Values::None); jv[jss::Flags] = tfWithdrawAll; env(ticket::create(sponsor, 1)); // adjust for free trustline @@ -4217,7 +4217,7 @@ class Sponsor_test : public beast::unit_test::Suite duration_cast(env.current()->header().closeTime.time_since_epoch()) .count() + kEPOCH_OFFSET.count() + 100); - jv[jss::PriceDataSeries] = json::ArrayValue; + jv[jss::PriceDataSeries] = json::ValueType::Array; jv[jss::Provider] = strHex(std::string{"provider"}); jv[jss::AssetClass] = strHex(std::string{"currency"}); @@ -4236,7 +4236,7 @@ class Sponsor_test : public beast::unit_test::Suite DataSeries const actualSeries(series.begin(), series.begin() + dataSeriesSize); - json::Value dataSeries(json::ArrayValue); + json::Value dataSeries(json::ValueType::Array); for (auto const& data : actualSeries) { json::Value priceData; diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp index 0901a0aec3a..5593e1b78cb 100644 --- a/src/test/jtx/impl/sponsor.cpp +++ b/src/test/jtx/impl/sponsor.cpp @@ -6,11 +6,11 @@ #include #include +#include #include #include #include #include -#include #include #include @@ -31,9 +31,9 @@ set(jtx::Account const& account, if (reserveCount) jv[sfReserveCount.jsonName] = *reserveCount; if (feeAmount) - jv[sfFeeAmount.jsonName] = feeAmount->getJson(JsonOptions::KNone); + jv[sfFeeAmount.jsonName] = feeAmount->getJson(JsonOptions::Values::None); if (maxFee) - jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::KNone); + jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::Values::None); return jv; } @@ -48,9 +48,9 @@ set_fee( jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); jv[sfFlags.jsonName] = flags; - jv[sfFeeAmount.jsonName] = feeAmount.getJson(JsonOptions::KNone); + jv[sfFeeAmount.jsonName] = feeAmount.getJson(JsonOptions::Values::None); if (maxFee) - jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::KNone); + jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::Values::None); return jv; } @@ -72,7 +72,7 @@ set_max_fee(jtx::Account const& account, uint32_t flags, STAmount maxFee) jv[jss::TransactionType] = jss::SponsorshipSet; jv[jss::Account] = account.human(); jv[sfFlags.jsonName] = flags; - jv[sfMaxFee.jsonName] = maxFee.getJson(JsonOptions::KNone); + jv[sfMaxFee.jsonName] = maxFee.getJson(JsonOptions::Values::None); return jv; } diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index f118a3a832b..e2660b30c0e 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -916,7 +916,8 @@ class AccountTx_test : public beast::unit_test::Suite auto const& tx0(jv[jss::transactions][0u][jss::tx]); BEAST_EXPECT(tx0[jss::TransactionType] == txType); - std::string const txHash{env.tx()->getJson(JsonOptions::Values::None)[jss::hash].asString()}; + std::string const txHash{ + env.tx()->getJson(JsonOptions::Values::None)[jss::hash].asString()}; BEAST_EXPECT(tx0[jss::hash] == txHash); }; diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 96dbe85596f..2306b543520 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -603,7 +603,7 @@ class Simulate_test : public beast::unit_test::Suite tx[sfDomain.jsonName] = kNEW_DOMAIN; tx[sfSponsor.jsonName] = sponsor.human(); tx[sfSponsorFlags.jsonName] = spfSponsorFee; - tx[sfSponsorSignature.jsonName] = json::ObjectValue; + tx[sfSponsorSignature.jsonName] = json::ValueType::Object; // test with autofill testTx(env, tx, validateOutput); From 0997b92de3751d7c1b1aae2ad87702009ab8beca Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Tue, 19 May 2026 11:47:54 -0400 Subject: [PATCH 240/249] Merge fixes --- .../tx/transactors/sponsor/SponsorshipSet.h | 2 +- .../transactors/sponsor/SponsorshipTransfer.h | 2 +- src/libxrpl/ledger/View.cpp | 3 +- src/libxrpl/tx/Transactor.cpp | 2 +- .../tx/transactors/Sponsor/SponsorshipSet.cpp | 35 +++----- .../Sponsor/SponsorshipTransfer.cpp | 53 ++++------- src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 4 +- .../tx/transactors/dex/AMMWithdraw.cpp | 8 +- .../tx/transactors/nft/NFTokenAcceptOffer.cpp | 2 +- .../tx/transactors/nft/NFTokenMint.cpp | 2 +- .../tx/transactors/payment/Payment.cpp | 8 +- .../tx/transactors/vault/VaultDeposit.cpp | 16 ++-- .../tx/transactors/vault/VaultWithdraw.cpp | 8 +- src/test/app/Credentials_test.cpp | 4 +- src/test/app/DepositAuth_test.cpp | 87 ++++++++++++------- src/test/app/Invariants_test.cpp | 8 +- src/test/app/Loan_test.cpp | 4 +- src/test/app/OfferMPT_test.cpp | 16 ++-- src/test/app/Sponsor_test.cpp | 54 ++++++------ src/test/rpc/Simulate_test.cpp | 2 +- 20 files changed, 165 insertions(+), 155 deletions(-) diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h index b0579428b60..d7e78da2ca7 100644 --- a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h @@ -7,7 +7,7 @@ namespace xrpl { class SponsorshipSet : public Transactor { public: - static constexpr auto kCONSEQUENCES_FACTORY = ConsequencesFactoryType::Normal; + static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal; explicit SponsorshipSet(ApplyContext& ctx) : Transactor(ctx) { diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h b/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h index 93a0cd68833..a473e5008df 100644 --- a/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h @@ -7,7 +7,7 @@ namespace xrpl { class SponsorshipTransfer : public Transactor { public: - static constexpr auto kCONSEQUENCES_FACTORY = ConsequencesFactoryType::Normal; + static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal; explicit SponsorshipTransfer(ApplyContext& ctx) : Transactor(ctx) { diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 1799845a5b1..935c0aa6978 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -454,8 +454,7 @@ doWithdraw( // Move the funds directly from the broker's pseudo-account to the // dstAcct - return accountSend( - view, sourceAcct, dstAcct, amount, j, *sponsorSle, WaiveTransferFee::Yes); + return accountSend(view, sourceAcct, dstAcct, amount, j, *sponsorSle, WaiveTransferFee::Yes); } TER diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 9c22db838d0..af003e6e6dc 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -1281,7 +1281,7 @@ Transactor::reset(XRPAmount fee) // then the ledger is corrupted. Rather than make things worse we // reject the transaction. auto const feeAmountAfter = balance - fee; - if (feeAmountAfter == beast::kZERO && feePayer.balanceField == sfFeeAmount) + if (feeAmountAfter == beast::kZero && feePayer.balanceField == sfFeeAmount) { // Because ltSponsorship.sfFeeAmount is soeOptional payerSle->makeFieldAbsent(feePayer.balanceField); diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 0c329f52eeb..07dd8628f39 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -32,13 +32,11 @@ SponsorshipSet::getFlagsMask(PreflightContext const& ctx) NotTEC SponsorshipSet::preflight(PreflightContext const& ctx) { - auto const flags = ctx.tx.getFlags(); - - if (((flags & tfSponsorshipSetRequireSignForFee) != 0u) && - ((flags & tfSponsorshipClearRequireSignForFee) != 0u)) + if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForFee) && + ctx.tx.isFlag(tfSponsorshipClearRequireSignForFee)) return temINVALID_FLAG; - if (((flags & tfSponsorshipSetRequireSignForReserve) != 0u) && - ((flags & tfSponsorshipClearRequireSignForReserve) != 0u)) + if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForReserve) && + ctx.tx.isFlag(tfSponsorshipClearRequireSignForReserve)) return temINVALID_FLAG; auto const account = ctx.tx.getAccountID(sfAccount); @@ -55,14 +53,14 @@ SponsorshipSet::preflight(PreflightContext const& ctx) if (sponsorAccountID == sponseeAccountID) return temMALFORMED; - if ((flags & tfDeleteObject) != 0u) + if (ctx.tx.isFlag(tfDeleteObject)) { // can not combine with any modification flags when deleting - constexpr std::uint32_t kMODIFY_FLAGS = tfSponsorshipSetRequireSignForFee | + constexpr std::uint32_t kModifyFlags = tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForFee | tfSponsorshipClearRequireSignForReserve; - if ((flags & kMODIFY_FLAGS) != 0u) + if ((ctx.tx.getFlags() & kModifyFlags) != 0u) return temINVALID_FLAG; // can not include these fields when deleting @@ -180,8 +178,8 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) TER SponsorshipSet::doApply() { - auto const sponsorAccountID = ctx_.tx[~sfCounterpartySponsor].value_or(account_); - auto const sponseeAccountID = ctx_.tx[~sfSponsee].value_or(account_); + auto const sponsorAccountID = ctx_.tx[~sfCounterpartySponsor].value_or(accountID_); + auto const sponseeAccountID = ctx_.tx[~sfSponsee].value_or(accountID_); if (sponseeAccountID == sponsorAccountID) return tecINTERNAL; // LCOV_EXCL_LINE @@ -303,11 +301,11 @@ SponsorshipSet::doApply() auto const currentFeeAmount = (*sponsorObjSle)[~sfFeeAmount].valueOr(XRPAmount(0)); auto feeAmountDelta = XRPAmount(*feeAmount - currentFeeAmount); - if (feeAmountDelta > beast::kZERO && feeAmountDelta > (*sponsorAccSle)[sfBalance]) + if (feeAmountDelta > beast::kZero && feeAmountDelta > (*sponsorAccSle)[sfBalance]) return tecUNFUNDED; // transfer feeAmount to ledger entry - if (feeAmountDelta != beast::kZERO) + if (feeAmountDelta != beast::kZero) { (*sponsorAccSle)[sfBalance] -= feeAmountDelta; @@ -347,16 +345,7 @@ SponsorshipSet::doApply() } if (reserveCount) - { - if (*reserveCount == 0) - { - (*sponsorObjSle).makeFieldAbsent(sfReserveCount); - } - else - { - (*sponsorObjSle)[sfReserveCount] = *reserveCount; - } - } + sponsorObjSle->at(sfReserveCount) = *reserveCount; // update Flags auto flags = sponsorObjSle->getFieldU32(sfFlags); diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 69b93b6255e..cd5dc055607 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -42,7 +42,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } - if ((flags & tfSponsorshipCreate) != 0u) + if (ctx.tx.isFlag(tfSponsorshipCreate)) { if (!isReserveSponsored(ctx.tx)) { @@ -57,7 +57,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) return temMALFORMED; } } - if ((flags & tfSponsorshipReassign) != 0u) + if (ctx.tx.isFlag(tfSponsorshipReassign)) { if (!isReserveSponsored(ctx.tx)) { @@ -72,7 +72,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx) return temMALFORMED; } } - if ((flags & tfSponsorshipEnd) != 0u) + if (ctx.tx.isFlag(tfSponsorshipEnd)) { if (isReserveSponsored(ctx.tx)) { @@ -232,7 +232,6 @@ TER SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { auto const index = ctx.tx[~sfObjectID]; - auto const flags = ctx.tx.getFlags(); auto const newSponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); if (!newSponsorSle) return newSponsorSle.error(); // LCOV_EXCL_LINE @@ -260,7 +259,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) auto const& sponsorField = getLedgerEntrySponsorField(sle, *owner); - if ((flags & tfSponsorshipCreate) != 0u) + if (ctx.tx.isFlag(tfSponsorshipCreate)) { if (!*newSponsorSle) return tecNO_PERMISSION; @@ -269,7 +268,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (sle->isFieldPresent(sponsorField)) return tecNO_PERMISSION; } - else if ((flags & tfSponsorshipReassign) != 0u) + else if (ctx.tx.isFlag(tfSponsorshipReassign)) { if (!*newSponsorSle) return tecNO_PERMISSION; @@ -278,7 +277,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (!sle->isFieldPresent(sponsorField)) return tecNO_PERMISSION; } - else if ((flags & tfSponsorshipEnd) != 0u) + else if (ctx.tx.isFlag(tfSponsorshipEnd)) { if (*newSponsorSle) return tecNO_PERMISSION; @@ -309,7 +308,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) } else { - if ((flags & tfSponsorshipCreate) != 0u) + if (ctx.tx.isFlag(tfSponsorshipCreate)) { if (!*newSponsorSle) return tecNO_PERMISSION; @@ -318,7 +317,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (sponseeSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; } - else if ((flags & tfSponsorshipReassign) != 0u) + else if (ctx.tx.isFlag(tfSponsorshipReassign)) { if (!*newSponsorSle) return tecNO_PERMISSION; @@ -327,7 +326,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (!sponseeSle->isFieldPresent(sfSponsor)) return tecNO_PERMISSION; } - else if ((flags & tfSponsorshipEnd) != 0u) + else if (ctx.tx.isFlag(tfSponsorshipEnd)) { if (*newSponsorSle) return tecNO_PERMISSION; @@ -386,14 +385,7 @@ adjustReserveCount( return tefINTERNAL; // LCOV_EXCL_LINE } - if (afterReserveCount == 0) - { - sponsorSle->makeFieldAbsent(sfReserveCount); - } - else - { - sponsorSle->setFieldU32(sfReserveCount, afterReserveCount); - } + sponsorSle->at(sfReserveCount) = static_cast(afterReserveCount); view.update(sponsorSle); return tesSUCCESS; } @@ -404,10 +396,9 @@ SponsorshipTransfer::doApply() auto const& tx = ctx_.tx; auto const index = tx[~sfObjectID]; - auto const flags = tx.getFlags(); bool const isObjectSponsor = index != std::nullopt; - auto const sponseeAccountID = tx[~sfSponsee].value_or(account_); + auto const sponseeAccountID = tx[~sfSponsee].value_or(accountID_); auto const sponseeSle = view().peek(keylet::account(sponseeAccountID)); if (!sponseeSle) return tefINTERNAL; // LCOV_EXCL_LINE @@ -420,14 +411,8 @@ SponsorshipTransfer::doApply() UNREACHABLE("xrpl::SponsorshipTransfer::doApply : Invalid sponsor field value"); return; } - if (newValue == 0) - { - sle->makeFieldAbsent(field); - } - else - { - sle->setFieldU32(field, static_cast(newValue)); - } + + sle->at(field) = static_cast(newValue); }; if (isObjectSponsor) @@ -451,7 +436,7 @@ SponsorshipTransfer::doApply() auto const& sponsorField = getLedgerEntrySponsorField(objSle, *ownerAccountID); - if ((flags & tfSponsorshipCreate) != 0u) + if (ctx_.tx.isFlag(tfSponsorshipCreate)) { auto const newSponsorAccountID = tx.getAccountID(sfSponsor); XRPL_ASSERT(!!newSponsorAccountID, "New sponsor is required when creating sponsorship"); @@ -480,7 +465,7 @@ SponsorshipTransfer::doApply() return ter; } } - else if ((flags & tfSponsorshipReassign) != 0u) + else if (ctx_.tx.isFlag(tfSponsorshipReassign)) { auto const newSponsorAccountID = tx.getAccountID(sfSponsor); XRPL_ASSERT( @@ -528,7 +513,7 @@ SponsorshipTransfer::doApply() return ter; } } - else if ((flags & tfSponsorshipEnd) != 0u) + else if (ctx_.tx.isFlag(tfSponsorshipEnd)) { auto const oldSponsorAccountID = objSle->getAccountID(sponsorField); XRPL_ASSERT(!!oldSponsorAccountID, "Old sponsor is required when ending sponsorship"); @@ -563,7 +548,7 @@ SponsorshipTransfer::doApply() } else { - if ((flags & tfSponsorshipCreate) != 0u) + if (ctx_.tx.isFlag(tfSponsorshipCreate)) { // create account sponsor // increment new sponsoring count @@ -578,7 +563,7 @@ SponsorshipTransfer::doApply() sponseeSle->setAccountID(sfSponsor, newSponsorAccountID); view().update(sponseeSle); } - else if ((flags & tfSponsorshipReassign) != 0u) + else if (ctx_.tx.isFlag(tfSponsorshipReassign)) { // reassign account sponsor // increment new sponsoring count @@ -601,7 +586,7 @@ SponsorshipTransfer::doApply() sponseeSle->setAccountID(sfSponsor, newSponsorAccountID); view().update(sponseeSle); } - else if ((flags & tfSponsorshipEnd) != 0u) + else if (ctx_.tx.isFlag(tfSponsorshipEnd)) { // dissolve account sponsor auto const oldSponsorAccountID = sponseeSle->getAccountID(sfSponsor); diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index cd1abcf5ad3..d9aaaaf45e1 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -147,8 +147,8 @@ AMMCreate::preclaim(PreclaimContext const& ctx) if (ctx.view.rules().enabled(featureSponsor)) { auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); - if(!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE // Check the reserve for LPToken trustline // Insufficient reserve diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index e50afbe6176..94f10c7ba4f 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -703,7 +703,13 @@ AMMWithdraw::withdraw( // Withdraw amountWithdraw auto res = accountSend( - view, ammAccount, account, amountWithdrawActual, journal, sponsorSle, WaiveTransferFee::Yes); + view, + ammAccount, + account, + amountWithdrawActual, + journal, + sponsorSle, + WaiveTransferFee::Yes); if (!isTesSuccess(res)) { // LCOV_EXCL_START diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index a3ab73eca92..4288c593d70 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -401,7 +401,7 @@ NFTokenAcceptOffer::transferNFToken( if (buyerOwnerCountAfter > buyerOwnerCountBefore) { SLE::const_pointer buyerSponsorSle; - if (account_ == buyer) + if (accountID_ == buyer) buyerSponsorSle = *sponsorSle; if (auto const ret = checkInsufficientReserve( ctx_.view(), ctx_.tx, sleBuyer, buyerBalance, buyerSponsorSle, 0, 0, j_); diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index 67cea4a2a6d..53d29b38013 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -343,7 +343,7 @@ NFTokenMint::doApply() if (auto const ownerCountAfter = view().read(keylet::account(accountID_))->getFieldU32(sfOwnerCount); ownerCountAfter > ownerCountBefore) - { + { if (auto const ret = checkInsufficientReserve( ctx_.view(), ctx_.tx, diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 50cd9666388..c08351d7cb2 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -355,7 +355,7 @@ Payment::preclaim(PreclaimContext const& ctx) // transaction would succeed. return telNO_DST_PARTIAL; } - if (tx.isFlag(tfSponsorCreatedAccount)) + if (ctx.tx.isFlag(tfSponsorCreatedAccount)) { // The minimum amount when creating a Sponsored Account is 1 drop. // Since the reserve is covered by the sponsor, you don't need to hold the 1-increment @@ -379,7 +379,7 @@ Payment::preclaim(PreclaimContext const& ctx) // The tfSponsorCreatedAccount flag is specific to account creation via // sponsorship. If the destination account already exists, applying this // flag is invalid. - if (tx.isFlag(tfSponsorCreatedAccount)) + if (ctx.tx.isFlag(tfSponsorCreatedAccount)) return tecNO_SPONSOR_PERMISSION; if (sleDst->isFlag(lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag)) @@ -458,7 +458,7 @@ Payment::doApply() if (ctx_.tx.isFlag(tfSponsorCreatedAccount)) { - auto const sponsor = view().peek(keylet::account(account_)); + auto const sponsor = view().peek(keylet::account(accountID_)); if (!sponsor) return tefINTERNAL; // LCOV_EXCL_LINE auto const currentSponsoringAccountCount = @@ -466,7 +466,7 @@ Payment::doApply() if (currentSponsoringAccountCount == std::numeric_limits::max()) { JLOG(j_.fatal()) << "Sponsoring account count overflow for account " - << to_string(account_); + << to_string(accountID_); return tecINTERNAL; // LCOV_EXCL_LINE } sponsor->setFieldU32(sfSponsoringAccountCount, currentSponsoringAccountCount + 1); diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index 42a3d5ddeeb..dcfe2b681b5 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -256,13 +256,7 @@ VaultDeposit::doApply() // Transfer assets from depositor to vault. if (auto const ter = accountSend( - view(), - accountID_, - vaultAccount, - assetsDeposited, - j_, - {}, - WaiveTransferFee::Yes); + view(), accountID_, vaultAccount, assetsDeposited, j_, {}, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -287,7 +281,13 @@ VaultDeposit::doApply() // Transfer shares from vault to depositor. if (auto const ter = accountSend( - view(), vaultAccount, accountID_, sharesCreated, j_, *sponsorSle, WaiveTransferFee::Yes); + view(), + vaultAccount, + accountID_, + sharesCreated, + j_, + *sponsorSle, + WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index 3ff2ca522a1..283201a3ae0 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -257,7 +257,13 @@ VaultWithdraw::doApply() // Transfer shares from depositor to vault. if (auto const ter = accountSend( - view(), accountID_, vaultAccount, sharesRedeemed, j_, *sponsorSle, WaiveTransferFee::Yes); + view(), + accountID_, + vaultAccount, + sharesRedeemed, + j_, + *sponsorSle, + WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/test/app/Credentials_test.cpp b/src/test/app/Credentials_test.cpp index 94dd937e15c..9e7575cd504 100644 --- a/src/test/app/Credentials_test.cpp +++ b/src/test/app/Credentials_test.cpp @@ -1079,7 +1079,7 @@ struct Credentials_test : public beast::unit_test::Suite } // Create DepositPreauth - env(deposit::authCredentials(becky, {{subject, credType}})); + env(deposit::authCredentials(becky, {{.issuer = subject, .credType = credType}})); env.close(); // env(); auto jtx = env.jt(pay(subject, becky, XRP(100)), credentials::Ids({credIdx})); @@ -1088,7 +1088,7 @@ struct Credentials_test : public beast::unit_test::Suite auto const stx = std::make_shared(*jtx.stx); // Create PermissionedDomain - env(pdomain::setTx(becky, {{issuer, credType}})); + env(pdomain::setTx(becky, {{.issuer = issuer, .credType = credType}})); env.close(); auto const objects = pdomain::getObjects(becky, env); if (!BEAST_EXPECT(!objects.empty())) diff --git a/src/test/app/DepositAuth_test.cpp b/src/test/app/DepositAuth_test.cpp index c82152c3283..c615bded164 100644 --- a/src/test/app/DepositAuth_test.cpp +++ b/src/test/app/DepositAuth_test.cpp @@ -615,7 +615,8 @@ struct DepositPreauth_test : public beast::unit_test::Suite TER const expectTer(!supportsCredentials ? TER(temDISABLED) : TER(tesSUCCESS)); - env(deposit::authCredentials(becky, {{.issuer=carol, .credType=credType}}), Ter(expectTer)); + env(deposit::authCredentials(becky, {{.issuer = carol, .credType = credType}}), + Ter(expectTer)); env.close(); // gw accept credentials @@ -745,7 +746,8 @@ struct DepositPreauth_test : public beast::unit_test::Suite env.close(); // Setup DepositPreauth object failed - amendent is not supported - env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}), Ter(temDISABLED)); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}), + Ter(temDISABLED)); env.close(); // But can create old DepositPreauth @@ -783,10 +785,11 @@ struct DepositPreauth_test : public beast::unit_test::Suite // Bob will accept payments from accounts with credentials signed // by 'issuer' - env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); - auto const jDP = ledgerEntryDepositPreauth(env, bob, {{.issuer=issuer, .credType=credType}}); + auto const jDP = + ledgerEntryDepositPreauth(env, bob, {{.issuer = issuer, .credType = credType}}); BEAST_EXPECT( jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) && jDP[jss::result].isMember(jss::node) && @@ -859,11 +862,14 @@ struct DepositPreauth_test : public beast::unit_test::Suite } // Bob setup DepositPreauth object, duplicates is not allowed - env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}, {.issuer=issuer, .credType=credType}}), + env(deposit::authCredentials( + bob, + {{.issuer = issuer, .credType = credType}, + {.issuer = issuer, .credType = credType}}), Ter(temMALFORMED)); // Bob setup DepositPreauth object - env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); { @@ -929,35 +935,37 @@ struct DepositPreauth_test : public beast::unit_test::Suite { // both included [AuthorizeCredentials UnauthorizeCredentials] - auto jv = deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfUnauthorizeCredentials.jsonName] = json::ValueType::Array; env(jv, Ter(temMALFORMED)); } { // both included [Unauthorize, AuthorizeCredentials] - auto jv = deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfUnauthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } { // both included [Authorize, AuthorizeCredentials] - auto jv = deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfAuthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } { // both included [Unauthorize, UnauthorizeCredentials] - auto jv = deposit::unauthCredentials(bob, {{.issuer=issuer, .credType=credType}}); + auto jv = + deposit::unauthCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfUnauthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } { // both included [Authorize, UnauthorizeCredentials] - auto jv = deposit::unauthCredentials(bob, {{.issuer=issuer, .credType=credType}}); + auto jv = + deposit::unauthCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfAuthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } @@ -984,7 +992,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite { // empty credential type - auto jv = deposit::authCredentials(bob, {{.issuer=issuer, .credType={}}}); + auto jv = deposit::authCredentials(bob, {{.issuer = issuer, .credType = {}}}); env(jv, Ter(temMALFORMED)); } @@ -994,14 +1002,23 @@ struct DepositPreauth_test : public beast::unit_test::Suite i("i"); auto const& z = credType; auto jv = deposit::authCredentials( - bob, {{.issuer=a, .credType=z}, {.issuer=b, .credType=z}, {.issuer=c, .credType=z}, {.issuer=d, .credType=z}, {.issuer=e, .credType=z}, {.issuer=f, .credType=z}, {.issuer=g, .credType=z}, {.issuer=h, .credType=z}, {.issuer=i, .credType=z}}); + bob, + {{.issuer = a, .credType = z}, + {.issuer = b, .credType = z}, + {.issuer = c, .credType = z}, + {.issuer = d, .credType = z}, + {.issuer = e, .credType = z}, + {.issuer = f, .credType = z}, + {.issuer = g, .credType = z}, + {.issuer = h, .credType = z}, + {.issuer = i, .credType = z}}); env(jv, Ter(temARRAY_TOO_LARGE)); } { // Can't create with non-existing issuer Account const rick{"rick"}; - auto jv = deposit::authCredentials(bob, {{.issuer=rick, .credType=credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer = rick, .credType = credType}}); env(jv, Ter(tecNO_ISSUER)); env.close(); } @@ -1011,21 +1028,24 @@ struct DepositPreauth_test : public beast::unit_test::Suite Account const john{"john"}; env.fund(baseAccountReserve(*env.current(), 0), john); env.close(); - auto jv = deposit::authCredentials(john, {{.issuer=issuer, .credType=credType}}); + auto jv = + deposit::authCredentials(john, {{.issuer = issuer, .credType = credType}}); env(jv, Ter(tecINSUFFICIENT_RESERVE)); } { // NO deposit object exists - env(deposit::unauthCredentials(bob, {{.issuer=issuer, .credType=credType}}), Ter(tecNO_ENTRY)); + env(deposit::unauthCredentials(bob, {{.issuer = issuer, .credType = credType}}), + Ter(tecNO_ENTRY)); } // Create DepositPreauth object { - env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); - auto const jDP = ledgerEntryDepositPreauth(env, bob, {{.issuer=issuer, .credType=credType}}); + auto const jDP = + ledgerEntryDepositPreauth(env, bob, {{.issuer = issuer, .credType = credType}}); BEAST_EXPECT( jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) && @@ -1046,14 +1066,16 @@ struct DepositPreauth_test : public beast::unit_test::Suite } // can't create duplicate - env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}}), Ter(tecDUPLICATE)); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}), + Ter(tecDUPLICATE)); } // Delete DepositPreauth object { - env(deposit::unauthCredentials(bob, {{.issuer=issuer, .credType=credType}})); + env(deposit::unauthCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); - auto const jDP = ledgerEntryDepositPreauth(env, bob, {{.issuer=issuer, .credType=credType}}); + auto const jDP = + ledgerEntryDepositPreauth(env, bob, {{.issuer = issuer, .credType = credType}}); BEAST_EXPECT( jDP.isObject() && jDP.isMember(jss::result) && jDP[jss::result].isMember(jss::error) && @@ -1120,7 +1142,10 @@ struct DepositPreauth_test : public beast::unit_test::Suite env(fset(bob, asfDepositAuth)); env.close(); // Bob setup DepositPreauth object - env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}, {.issuer=issuer, .credType=credType2}})); + env(deposit::authCredentials( + bob, + {{.issuer = issuer, .credType = credType}, + {.issuer = issuer, .credType = credType2}})); env.close(); { @@ -1229,7 +1254,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite env(fset(bob, asfDepositAuth)); env.close(); // Bob setup DepositPreauth object - env(deposit::authCredentials(bob, {{.issuer=issuer, .credType=credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); auto const seq = env.seq(alice); @@ -1287,14 +1312,14 @@ struct DepositPreauth_test : public beast::unit_test::Suite env.fund(XRP(5000), stock, alice, bob); std::vector credentials = { - {.issuer="a", .credType="a"}, - {.issuer="b", .credType="b"}, - {.issuer="c", .credType="c"}, - {.issuer="d", .credType="d"}, - {.issuer="e", .credType="e"}, - {.issuer="f", .credType="f"}, - {.issuer="g", .credType="g"}, - {.issuer="h", .credType="h"}}; + {.issuer = "a", .credType = "a"}, + {.issuer = "b", .credType = "b"}, + {.issuer = "c", .credType = "c"}, + {.issuer = "d", .credType = "d"}, + {.issuer = "e", .credType = "e"}, + {.issuer = "f", .credType = "f"}, + {.issuer = "g", .credType = "g"}, + {.issuer = "h", .credType = "h"}}; for (auto const& c : credentials) env.fund(XRP(5000), c.issuer); diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 30f432b6c96..d1cb8dc77eb 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -348,7 +348,7 @@ class Invariants_test : public beast::unit_test::Suite auto const sleA1 = ac.view().peek(keylet::account(a1.id())); if (!sleA1) return false; - sleA1->at(sfBalance) = beast::kZERO; + sleA1->at(sfBalance) = beast::kZero; sleA1->setFieldU32(sfSponsoredOwnerCount, 1); ac.view().erase(sleA1); @@ -364,7 +364,7 @@ class Invariants_test : public beast::unit_test::Suite auto const sleA1 = ac.view().peek(keylet::account(a1.id())); if (!sleA1) return false; - sleA1->at(sfBalance) = beast::kZERO; + sleA1->at(sfBalance) = beast::kZero; sleA1->setFieldU32(sfSponsoringOwnerCount, 1); ac.view().erase(sleA1); @@ -381,7 +381,7 @@ class Invariants_test : public beast::unit_test::Suite auto const sleA1 = ac.view().peek(keylet::account(a1Id)); if (!sleA1) return false; - sleA1->at(sfBalance) = beast::kZERO; + sleA1->at(sfBalance) = beast::kZero; sleA1->setFieldU32(sfSponsoringAccountCount, 1); ac.view().erase(sleA1); @@ -397,7 +397,7 @@ class Invariants_test : public beast::unit_test::Suite auto const sleA1 = ac.view().peek(keylet::account(a1.id())); if (!sleA1) return false; - sleA1->at(sfBalance) = beast::kZERO; + sleA1->at(sfBalance) = beast::kZero; sleA1->setAccountID(sfSponsor, a2.id()); ac.view().erase(sleA1); diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 3f6146a0cd5..df80596133d 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -6882,7 +6882,7 @@ class Loan_test : public beast::unit_test::Suite auto credType = "credential1"; - pdomain::Credentials const credentials1{{.issuer=issuer, .credType=credType}}; + pdomain::Credentials const credentials1{{.issuer = issuer, .credType = credType}}; env(pdomain::setTx(issuer, credentials1)); env.close(); @@ -6985,7 +6985,7 @@ class Loan_test : public beast::unit_test::Suite auto credType = "credential1"; - pdomain::Credentials const credentials1{{.issuer=issuer, .credType=credType}}; + pdomain::Credentials const credentials1{{.issuer = issuer, .credType = credType}}; env(pdomain::setTx(issuer, credentials1)); env.close(); diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index 9c0ed2ce0e5..c30fceb6fc9 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -4606,14 +4606,14 @@ class OfferMPT_test : public beast::unit_test::Suite // IOU/IOU, XRP/IOU, IOU/XRP offers have TickSize logic unchanged // IOU/MPT, MPT/IOU have TickSize logic applied to adjust IOU only std::vector const tests = { - {.toAsset1=getIOU, .toAsset2=getIOU, .val1=10, .val2=30}, - {.toAsset1=getIOU, .toAsset2=getXRP, .val1=10, .val2=30'000'000}, - {.toAsset1=getXRP, .toAsset2=getIOU, .val1=10'000'000, .val2=30}, - {.toAsset1=getMPT, .toAsset2=getXRP, .val1=10'000'000, .val2=30'000'000}, - {.toAsset1=getXRP, .toAsset2=getMPT, .val1=10'000'000, .val2=30'000'000}, - {.toAsset1=getIOU, .toAsset2=getMPT, .val1=10, .val2=30'000'000}, - {.toAsset1=getMPT, .toAsset2=getIOU, .val1=10'000'000, .val2=30}, - {.toAsset1=getMPT, .toAsset2=getMPT, .val1=10'000'000, .val2=30'000'000}}; + {.toAsset1 = getIOU, .toAsset2 = getIOU, .val1 = 10, .val2 = 30}, + {.toAsset1 = getIOU, .toAsset2 = getXRP, .val1 = 10, .val2 = 30'000'000}, + {.toAsset1 = getXRP, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30}, + {.toAsset1 = getMPT, .toAsset2 = getXRP, .val1 = 10'000'000, .val2 = 30'000'000}, + {.toAsset1 = getXRP, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000}, + {.toAsset1 = getIOU, .toAsset2 = getMPT, .val1 = 10, .val2 = 30'000'000}, + {.toAsset1 = getMPT, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30}, + {.toAsset1 = getMPT, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000}}; for (TestInfo const& t : tests) { Env env{*this, features}; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 272cafca8ba..511aa550063 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -22,16 +22,21 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include +#include + #include +#include #include #include #include @@ -39,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -55,13 +62,6 @@ #include #include -#include "test/jtx/seq.h" -#include "test/jtx/txflags.h" -#include "xrpl/basics/Slice.h" -#include "xrpl/ledger/ApplyView.h" -#include "xrpl/protocol/Protocol.h" -#include "xrpld/core/Config.h" - #include #include #include @@ -2515,7 +2515,7 @@ class Sponsor_test : public beast::unit_test::Suite env.close(); AMM amm(env, gw, XRP(10'000), usd(10'000)); - for (auto i = 0; i < (kMAX_DELETABLE_AMM_TRUST_LINES * 2) + 10; ++i) + for (auto i = 0; i < (kMaxDeletableAmmTrustLines * 2) + 10; ++i) { Account const a{std::to_string(i)}; env.fund(XRP(1'000), a); @@ -2537,7 +2537,7 @@ class Sponsor_test : public beast::unit_test::Suite } BEAST_EXPECT( - sponsoringOwnerCount(env, sponsor) == ((kMAX_DELETABLE_AMM_TRUST_LINES * 2) + 10)); + sponsoringOwnerCount(env, sponsor) == ((kMaxDeletableAmmTrustLines * 2) + 10)); // The trustlines are partially deleted. amm.withdrawAll(gw); @@ -3356,8 +3356,8 @@ class Sponsor_test : public beast::unit_test::Suite seq = env.seq(alice); submit( escrow::create(alice, bob, XRP(100)), - escrow::kCONDITION(escrow::kCB1), - escrow::kCANCEL_TIME(env.now() + 100s)); + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s)); }); BEAST_EXPECT( env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); @@ -3390,8 +3390,8 @@ class Sponsor_test : public beast::unit_test::Suite // EscrowFinish env(escrow::finish(bob, alice, seq), - escrow::kCONDITION(escrow::kCB1), - escrow::kFULFILLMENT(escrow::kFB1), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), Fee(baseFee * 150)); env.close(); @@ -3435,8 +3435,8 @@ class Sponsor_test : public beast::unit_test::Suite seq = env.seq(alice); submit( escrow::create(alice, bob, usd(100)), - escrow::kCONDITION(escrow::kCB1), - escrow::kCANCEL_TIME(env.now() + 100s)); + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s)); }); BEAST_EXPECT( @@ -3454,8 +3454,8 @@ class Sponsor_test : public beast::unit_test::Suite [&](Env& env, auto const& submit) { submit( escrow::finish(bob, alice, seq), - escrow::kCONDITION(escrow::kCB1), - escrow::kFULFILLMENT(escrow::kFB1), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), Fee(baseFee * 150)); }); @@ -3484,8 +3484,8 @@ class Sponsor_test : public beast::unit_test::Suite // create Escrow from alice to bob auto const seq = env.seq(alice); env(escrow::create(alice, bob, mpt(100)), - escrow::kCONDITION(escrow::kCB1), - escrow::kCANCEL_TIME(env.now() + 100s)); + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s)); env.close(); BEAST_EXPECT(ownerCount(env, alice) == 2); @@ -3494,8 +3494,8 @@ class Sponsor_test : public beast::unit_test::Suite // finish Escrow env(escrow::finish(bob, alice, seq), - escrow::kCONDITION(escrow::kCB1), - escrow::kFULFILLMENT(escrow::kFB1), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), sponsor::As(sponsor, spfSponsorReserve), Sig(sfSponsorSignature, sponsor), Fee(XRP(1))); @@ -4208,7 +4208,7 @@ class Sponsor_test : public beast::unit_test::Suite auto const oracleSet = [](Env& env, Account const& account, uint8_t dataSeriesSize) { auto const now = env.timeKeeper().now(); - env.close(now + oracle::kTEST_START_TIME - kEPOCH_OFFSET); + env.close(now + oracle::kTestStartTime - kEpochOffset); json::Value jv; jv[jss::TransactionType] = jss::OracleSet; jv[jss::Account] = to_string(account); @@ -4216,7 +4216,7 @@ class Sponsor_test : public beast::unit_test::Suite jv[jss::LastUpdateTime] = to_string( duration_cast(env.current()->header().closeTime.time_since_epoch()) .count() + - kEPOCH_OFFSET.count() + 100); + kEpochOffset.count() + 100); jv[jss::PriceDataSeries] = json::ValueType::Array; jv[jss::Provider] = strHex(std::string{"provider"}); jv[jss::AssetClass] = strHex(std::string{"currency"}); @@ -5237,7 +5237,7 @@ class Sponsor_test : public beast::unit_test::Suite env.fund(XRP(1000), alice, bob, issuer, sponsor); env.close(); - MPTTester mptt{env, issuer, kMPT_INIT_NO_FUND}; + MPTTester mptt{env, issuer, kMptInitNoFund}; mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); env.close(); PrettyAsset const asset = mptt["MPT"]; @@ -5299,8 +5299,8 @@ class Sponsor_test : public beast::unit_test::Suite // LoanBrokerCoverClawback // doesn't sponsor anything env(loanBroker::coverClawback(issuer), - loanBroker::kLOAN_BROKER_ID(brokerKeylet.key), - kAMOUNT(asset(1)), + loanBroker::kLoanBrokerId(brokerKeylet.key), + kAmount(asset(1)), sponsor::As(sponsor, spfSponsorReserve), Sig(sfSponsorSignature, sponsor)); env.close(); @@ -5315,7 +5315,7 @@ class Sponsor_test : public beast::unit_test::Suite env.fund(XRP(1000000), alice, bob, issuer, sponsor, sponsor2); env.close(); - MPTTester mptt{env, issuer, kMPT_INIT_NO_FUND}; + MPTTester mptt{env, issuer, kMptInitNoFund}; mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); env.close(); PrettyAsset const asset = mptt["MPT"]; diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index d9d7129785b..f2e0e81c500 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -600,7 +600,7 @@ class Simulate_test : public beast::unit_test::Suite tx[jss::Account] = env.master.human(); tx[jss::TransactionType] = jss::AccountSet; - tx[sfDomain.jsonName] = kNEW_DOMAIN; + tx[sfDomain.jsonName] = kNewDomain; tx[sfSponsor.jsonName] = sponsor.human(); tx[sfSponsorFlags.jsonName] = spfSponsorFee; tx[sfSponsorSignature.jsonName] = json::ValueType::Object; From 3859fcad73452924f1963147af50c3ecc7ddfaa1 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Wed, 20 May 2026 18:18:02 -0400 Subject: [PATCH 241/249] clang-tidy fixes --- include/xrpl/nodestore/detail/varint.h | 2 +- include/xrpl/server/detail/Door.h | 8 +- src/libxrpl/basics/Log.cpp | 3 +- src/libxrpl/basics/Number.cpp | 7 +- src/libxrpl/beast/core/CurrentThreadName.cpp | 1 - src/libxrpl/server/Port.cpp | 4 +- src/test/app/AccountDelete_test.cpp | 2 +- src/test/app/Batch_test.cpp | 972 +++++++++++++++--- src/test/app/CrossingLimitsMPT_test.cpp | 4 +- src/test/app/CrossingLimits_test.cpp | 4 +- src/test/app/Escrow_test.cpp | 4 +- src/test/app/LedgerReplay_test.cpp | 2 +- src/test/app/LendingHelpers_test.cpp | 16 +- src/test/app/PayChan_test.cpp | 2 +- src/test/app/PayStrand_test.cpp | 28 +- src/test/app/PermissionedDEX_test.cpp | 6 +- src/test/app/PermissionedDomains_test.cpp | 98 +- src/test/app/TxQ_test.cpp | 2 +- src/test/app/ValidatorList_test.cpp | 87 +- src/test/app/ValidatorSite_test.cpp | 411 +++++--- .../beast/aged_associative_container_test.cpp | 6 +- src/test/core/Config_test.cpp | 16 +- src/test/jtx/impl/WSClient.cpp | 2 +- src/test/jtx/impl/permissioned_dex.cpp | 2 +- src/test/jtx/impl/permissioned_domains.cpp | 4 +- src/test/nodestore/import_test.cpp | 6 +- src/test/rpc/DepositAuthorized_test.cpp | 4 +- src/test/rpc/LedgerEntry_test.cpp | 49 +- src/tests/libxrpl/tx/AccountSet.cpp | 14 +- src/xrpld/app/misc/detail/TxQ.cpp | 4 +- src/xrpld/app/misc/detail/ValidatorList.cpp | 2 +- src/xrpld/consensus/DisputedTx.h | 2 +- .../peerfinder/detail/PeerfinderConfig.cpp | 3 +- src/xrpld/rpc/detail/Pathfinder.cpp | 84 +- src/xrpld/rpc/detail/ServerHandler.cpp | 18 +- 35 files changed, 1357 insertions(+), 522 deletions(-) diff --git a/include/xrpl/nodestore/detail/varint.h b/include/xrpl/nodestore/detail/varint.h index e6b78fcf08c..0c49274d70b 100644 --- a/include/xrpl/nodestore/detail/varint.h +++ b/include/xrpl/nodestore/detail/varint.h @@ -25,7 +25,7 @@ struct varint_traits { explicit varint_traits() = default; - static constexpr std::size_t kMax = (8 * sizeof(T) + 6) / 7; + static constexpr std::size_t kMax = ((8 * sizeof(T)) + 6) / 7; }; // Returns: Number of bytes consumed or 0 on error, diff --git a/include/xrpl/server/detail/Door.h b/include/xrpl/server/detail/Door.h index d3fff8b4763..8f171b00ae0 100644 --- a/include/xrpl/server/detail/Door.h +++ b/include/xrpl/server/detail/Door.h @@ -90,11 +90,11 @@ class Door : public IOList::Work, public std::enable_shared_from_this strand_; bool ssl_{ - port_.protocol.count("https") > 0 || port_.protocol.count("wss") > 0 || - port_.protocol.count("wss2") > 0 || port_.protocol.count("peer") > 0}; + port_.protocol.contains("https") || port_.protocol.contains("wss") || + port_.protocol.contains("wss2") || port_.protocol.contains("peer")}; bool plain_{ - port_.protocol.count("http") > 0 || port_.protocol.count("ws") > 0 || - (port_.protocol.count("ws2") != 0u)}; + port_.protocol.contains("http") || port_.protocol.contains("ws") || + (port_.protocol.contains("ws2"))}; static constexpr std::chrono::milliseconds kInitialAcceptDelay{50}; static constexpr std::chrono::milliseconds kMaxAcceptDelay{2000}; std::chrono::milliseconds accept_delay_{kInitialAcceptDelay}; diff --git a/src/libxrpl/basics/Log.cpp b/src/libxrpl/basics/Log.cpp index 1079f91280e..d1e54a515fe 100644 --- a/src/libxrpl/basics/Log.cpp +++ b/src/libxrpl/basics/Log.cpp @@ -61,7 +61,8 @@ Logs::File::open(boost::filesystem::path const& path) bool wasOpened = false; // VFALCO TODO Make this work with Unicode file paths - std::unique_ptr stream(new std::ofstream(path.c_str(), std::fstream::app)); + std::unique_ptr stream = + std::make_unique(path.c_str(), std::fstream::app); if (stream->good()) { diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 06bd78d8b02..96ebf042c1e 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -1000,9 +1000,10 @@ root(Number f, unsigned d) } // Quadratic least squares curve fit of f^(1/d) in the range [0, 1] - auto const D = (((6 * di + 11) * di + 6) * di) + 1; // NOLINT(readability-identifier-naming) - auto const a0 = 3 * di * ((2 * di - 3) * di + 1); - auto const a1 = 24 * di * (2 * di - 1); + auto const D = + (((((6 * di) + 11) * di) + 6) * di) + 1; // NOLINT(readability-identifier-naming) + auto const a0 = 3 * di * ((((2 * di) - 3) * di) + 1); + auto const a1 = 24 * di * ((2 * di) - 1); auto const a2 = -30 * (di - 1) * di; Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D}; if (neg) diff --git a/src/libxrpl/beast/core/CurrentThreadName.cpp b/src/libxrpl/beast/core/CurrentThreadName.cpp index 52d9063179e..628fec5b7ab 100644 --- a/src/libxrpl/beast/core/CurrentThreadName.cpp +++ b/src/libxrpl/beast/core/CurrentThreadName.cpp @@ -71,7 +71,6 @@ setCurrentThreadNameImpl(std::string_view name) #if BOOST_OS_LINUX #include -#include #include // IWYU pragma: keep namespace beast::detail { diff --git a/src/libxrpl/server/Port.cpp b/src/libxrpl/server/Port.cpp index 9a6b6dce357..fb9f31cfe18 100644 --- a/src/libxrpl/server/Port.cpp +++ b/src/libxrpl/server/Port.cpp @@ -26,8 +26,8 @@ namespace xrpl { bool Port::secure() const { - return protocol.count("peer") > 0 || protocol.count("https") > 0 || protocol.count("wss") > 0 || - protocol.count("wss2") > 0; + return protocol.contains("peer") || protocol.contains("https") || protocol.contains("wss") || + protocol.contains("wss2"); } std::string diff --git a/src/test/app/AccountDelete_test.cpp b/src/test/app/AccountDelete_test.cpp index 951f99919b3..65ff9ed8395 100644 --- a/src/test/app/AccountDelete_test.cpp +++ b/src/test/app/AccountDelete_test.cpp @@ -813,7 +813,7 @@ class AccountDelete_test : public beast::unit_test::Suite env.close(); // alice create DepositPreauth Object - env(deposit::authCredentials(alice, {{carol, credType}})); + env(deposit::authCredentials(alice, {{.issuer = carol, .credType = credType}})); env.close(); // becky attempts to delete her account, but alice won't take her diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index 8755fe9f9c4..791bb5a4d67 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -1017,7 +1017,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1059,7 +1063,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1101,7 +1109,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1143,7 +1155,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1185,7 +1201,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1520,9 +1540,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1552,7 +1584,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1581,7 +1617,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1610,7 +1650,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1662,10 +1706,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tecUNFUNDED_PAYMENT", txIDs[0], batchID}, - {2, "Payment", "tecUNFUNDED_PAYMENT", txIDs[1], batchID}, - {3, "Payment", "tecUNFUNDED_PAYMENT", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1695,9 +1755,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tecUNFUNDED_PAYMENT", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1727,8 +1799,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1758,8 +1838,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1789,8 +1877,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1826,11 +1922,31 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "OfferCreate", "tecKILLED", txIDs[0], batchID}, - {2, "OfferCreate", "tecKILLED", txIDs[1], batchID}, - {3, "OfferCreate", "tecKILLED", txIDs[2], batchID}, - {4, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[2], + .batchID = batchID}, + {.index = 4, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1878,8 +1994,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tecUNFUNDED_PAYMENT", txIDs[0], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[0], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1909,11 +2033,31 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[2], batchID}, - {4, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[2], + .batchID = batchID}, + {.index = 4, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1944,10 +2088,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tecUNFUNDED_PAYMENT", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1978,9 +2138,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2011,9 +2183,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2044,10 +2228,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "OfferCreate", "tecKILLED", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2095,11 +2295,31 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tecUNFUNDED_PAYMENT", txIDs[1], batchID}, - {3, "Payment", "tecUNFUNDED_PAYMENT", txIDs[2], batchID}, - {4, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[2], + .batchID = batchID}, + {.index = 4, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2130,11 +2350,31 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tecUNFUNDED_PAYMENT", txIDs[2], batchID}, - {4, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[2], + .batchID = batchID}, + {.index = 4, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2165,10 +2405,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2199,10 +2455,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2232,10 +2504,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "OfferCreate", "tecKILLED", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2454,9 +2742,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "AccountSet", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2503,9 +2803,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "AccountSet", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2558,9 +2870,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "AccountDelete", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "AccountDelete", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2601,10 +2925,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "AccountDelete", "tecHAS_OBLIGATIONS", txIDs[1], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "AccountDelete", + .result = "tecHAS_OBLIGATIONS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2642,7 +2982,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); @@ -2876,9 +3220,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "CheckCash", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2922,9 +3278,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tecDST_TAG_NEEDED", txIDs[0], batchID}, - {2, "CheckCash", "tecNO_ENTRY", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tecDST_TAG_NEEDED", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "CheckCash", + .result = "tecNO_ENTRY", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2987,10 +3355,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "TicketCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "CheckCreate", "tesSUCCESS", txIDs[1], batchID}, - {3, "CheckCash", "tesSUCCESS", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "TicketCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3047,9 +3431,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "CheckCash", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3099,9 +3495,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3147,9 +3555,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3196,9 +3616,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3257,9 +3689,21 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3268,7 +3712,11 @@ class Batch_test : public beast::unit_test::Suite { // next ledger contains noop txn std::vector const testCases = { - {0, "AccountSet", "tesSUCCESS", noopTxnID, std::nullopt}, + {.index = 0, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = noopTxnID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -3301,9 +3749,21 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3340,9 +3800,21 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3382,10 +3854,26 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "AccountSet", "tesSUCCESS", noopTxnID, std::nullopt}, - {1, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {2, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = noopTxnID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3442,9 +3930,21 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3489,9 +3989,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3552,10 +4064,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "CheckCash", "tesSUCCESS", objTxnID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = objTxnID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -3601,10 +4129,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "CheckCreate", "tesSUCCESS", objTxnID, std::nullopt}, - {1, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {2, "CheckCash", "tesSUCCESS", txIDs[0], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = objTxnID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 2, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3646,10 +4190,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "CheckCash", "tesSUCCESS", objTxnID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = objTxnID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -3742,10 +4302,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Payment", "tesSUCCESS", payTxn1ID, std::nullopt}, - {1, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {2, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = payTxn1ID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3753,7 +4329,11 @@ class Batch_test : public beast::unit_test::Suite { // next ledger includes the payment txn std::vector const testCases = { - {0, "Payment", "tesSUCCESS", payTxn2ID, std::nullopt}, + {.index = 0, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = payTxn2ID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -3965,9 +4545,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -4014,9 +4606,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -4064,9 +4668,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "AccountSet", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -4126,9 +4742,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "MPTokenIssuanceSet", "tesSUCCESS", txIDs[0], batchID}, - {2, "MPTokenIssuanceSet", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "MPTokenIssuanceSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "MPTokenIssuanceSet", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -4167,9 +4795,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "TrustSet", "tesSUCCESS", txIDs[0], batchID}, - {2, "TrustSet", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "TrustSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "TrustSet", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -4207,8 +4847,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "TrustSet", "tesSUCCESS", txIDs[0], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "TrustSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, // jv2 fails with terNO_DELEGATE_PERMISSION. }; validateClosedLedger(env, testCases); diff --git a/src/test/app/CrossingLimitsMPT_test.cpp b/src/test/app/CrossingLimitsMPT_test.cpp index 4a016f31dc0..8bd0c767f7d 100644 --- a/src/test/app/CrossingLimitsMPT_test.cpp +++ b/src/test/app/CrossingLimitsMPT_test.cpp @@ -270,7 +270,7 @@ class CrossingLimitsMPT_test : public beast::unit_test::Suite env.require(Balance(alice, usd(2'503))); env.require(Balance(alice, eur(1'100))); - auto const numAOffers = 2'000 + 100 + 1'000 + 1 - (2 * 100 + 2 * 199 + 1 + 1); + auto const numAOffers = 2'000 + 100 + 1'000 + 1 - ((2 * 100) + (2 * 199) + 1 + 1); env.require(offers(alice, numAOffers)); env.require(Owners(alice, numAOffers + 2)); @@ -358,7 +358,7 @@ class CrossingLimitsMPT_test : public beast::unit_test::Suite env.require(Balance(alice, usd(2'494))); env.require(Balance(alice, eur(1'100))); auto const numAOffers = - 1 + 2'000 + 100 + 1'000 + 1 - (1 + 2 * 100 + 2 * 199 + 1 + 1); + 1 + 2'000 + 100 + 1'000 + 1 - (1 + (2 * 100) + (2 * 199) + 1 + 1); env.require(offers(alice, numAOffers)); env.require(Owners(alice, numAOffers + 2)); diff --git a/src/test/app/CrossingLimits_test.cpp b/src/test/app/CrossingLimits_test.cpp index 3cf8f50990f..c48892f04ea 100644 --- a/src/test/app/CrossingLimits_test.cpp +++ b/src/test/app/CrossingLimits_test.cpp @@ -258,7 +258,7 @@ class CrossingLimits_test : public beast::unit_test::Suite env.require(Balance(alice, usd(2503))); env.require(Balance(alice, eur(1100))); - auto const numAOffers = 2000 + 100 + 1000 + 1 - (2 * 100 + 2 * 199 + 1 + 1); + auto const numAOffers = 2000 + 100 + 1000 + 1 - ((2 * 100) + (2 * 199) + 1 + 1); env.require(offers(alice, numAOffers)); env.require(Owners(alice, numAOffers + 2)); @@ -329,7 +329,7 @@ class CrossingLimits_test : public beast::unit_test::Suite env.require(Balance(alice, usd(2494))); env.require(Balance(alice, eur(1100))); - auto const numAOffers = 1 + 2000 + 100 + 1000 + 1 - (1 + 2 * 100 + 2 * 199 + 1 + 1); + auto const numAOffers = 1 + 2000 + 100 + 1000 + 1 - (1 + (2 * 100) + (2 * 199) + 1 + 1); env.require(offers(alice, numAOffers)); env.require(Owners(alice, numAOffers + 2)); diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 3e76524cf1e..5623bc44431 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -1544,7 +1544,7 @@ struct Escrow_test : public beast::unit_test::Suite credentials::Ids({credIdx}), Ter(tecNO_PERMISSION)); - env(deposit::authCredentials(bob, {{zelda, credType}})); + env(deposit::authCredentials(bob, {{.issuer = zelda, .credType = credType}})); env.close(); // Success @@ -1601,7 +1601,7 @@ struct Escrow_test : public beast::unit_test::Suite // Bob require pre-authorization env(fset(bob, asfDepositAuth)); env.close(); - env(deposit::authCredentials(bob, {{zelda, credType}})); + env(deposit::authCredentials(bob, {{.issuer = zelda, .credType = credType}})); env.close(); // Use any valid credentials if account == dst diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index 4b1e4510d63..e1a1a76f922 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -549,7 +549,7 @@ struct LedgerServer while (senders.contains(fromIdx)) fromIdx = (fromIdx + 1) % fundedAccounts; senders.insert(fromIdx); - toIdx = (toIdx + r * 2) % fundedAccounts; + toIdx = (toIdx + (r * 2)) % fundedAccounts; if (toIdx == fromIdx) toIdx = (toIdx + 1) % fundedAccounts; }; diff --git a/src/test/app/LendingHelpers_test.cpp b/src/test/app/LendingHelpers_test.cpp index 96d0722732e..be73a939dc9 100644 --- a/src/test/app/LendingHelpers_test.cpp +++ b/src/test/app/LendingHelpers_test.cpp @@ -287,9 +287,9 @@ class LendingHelpers_test : public beast::unit_test::Suite std::uint32_t n; }; auto const cases = std::vector{ - {"r=5%, n=3", Number{5, -2}, 3}, - {"r=0.1%, n=1000", Number{1, -3}, 1'000}, - {"r=1e-7, n=100 (above threshold by 10x)", Number{1, -7}, 100}, + {.name = "r=5%, n=3", .r = Number{5, -2}, .n = 3}, + {.name = "r=0.1%, n=1000", .r = Number{1, -3}, .n = 1'000}, + {.name = "r=1e-7, n=100 (above threshold by 10x)", .r = Number{1, -7}, .n = 100}, }; for (auto const& tc : cases) { @@ -318,8 +318,10 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const cases = std::vector{ // bug regime: r = 1 TenthBips32 over 600s payment interval // → r ≈ 1.9e-10, r*n ≈ 3.8e-10 < 1e-9. - {"bug regime: r~1.9e-10, n=2", loanPeriodicRate(TenthBips32{1}, 600), 2}, - {"r=1e-12, n=100", Number{1, -12}, 100}, + {.name = "bug regime: r~1.9e-10, n=2", + .r = loanPeriodicRate(TenthBips32{1}, 600), + .n = 2}, + {.name = "r=1e-12, n=100", .r = Number{1, -12}, .n = 100}, }; for (auto const& tc : cases) { @@ -356,8 +358,8 @@ class LendingHelpers_test : public beast::unit_test::Suite std::uint32_t n; }; auto const cases = std::vector{ - {"r=1e-9, n=1", Number{1, -9}, 1}, - {"r=1e-12, n=1000", Number{1, -12}, 1'000}, + {.name = "r=1e-9, n=1", .r = Number{1, -9}, .n = 1}, + {.name = "r=1e-12, n=1000", .r = Number{1, -12}, .n = 1'000}, }; for (auto const& tc : cases) diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index b81afa830e9..870366df78a 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -869,7 +869,7 @@ struct PayChan_test : public beast::unit_test::Suite env.close(); // Setup deposit authorization - env(deposit::authCredentials(bob, {{carol, credType}})); + env(deposit::authCredentials(bob, {{.issuer = carol, .credType = credType}})); env.close(); // Fail, credentials doesn’t belong to root account diff --git a/src/test/app/PayStrand_test.cpp b/src/test/app/PayStrand_test.cpp index 67a37833b20..471c641f36c 100644 --- a/src/test/app/PayStrand_test.cpp +++ b/src/test/app/PayStrand_test.cpp @@ -632,7 +632,13 @@ struct PayStrand_test : public beast::unit_test::Suite // Insert implied account test( - env, usd, std::nullopt, STPath(), tesSUCCESS, D{alice, gw, usdC}, D{gw, bob, usdC}); + env, + usd, + std::nullopt, + STPath(), + tesSUCCESS, + D{.src = alice, .dst = gw, .currency = usdC}, + D{.src = gw, .dst = bob, .currency = usdC}); env.trust(eur(1000), alice, bob); // Insert implied offer @@ -642,9 +648,9 @@ struct PayStrand_test : public beast::unit_test::Suite usd, STPath(), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, eur, std::nullopt}, - D{gw, bob, eurC}); + D{.src = gw, .dst = bob, .currency = eurC}); // Path with explicit offer test( @@ -653,9 +659,9 @@ struct PayStrand_test : public beast::unit_test::Suite usd, STPath({ipe(eur)}), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, eur, std::nullopt}, - D{gw, bob, eurC}); + D{.src = gw, .dst = bob, .currency = eurC}); // Path with offer that changes issuer only env.trust(carol["USD"](1000), bob); @@ -665,9 +671,9 @@ struct PayStrand_test : public beast::unit_test::Suite usd, STPath({iape(carol)}), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, carol["USD"], std::nullopt}, - D{carol, bob, usdC}); + D{.src = carol, .dst = bob, .currency = usdC}); // Path with XRP src currency test( @@ -678,7 +684,7 @@ struct PayStrand_test : public beast::unit_test::Suite tesSUCCESS, XRPS{alice}, B{XRP, usd, std::nullopt}, - D{gw, bob, usdC}); + D{.src = gw, .dst = bob, .currency = usdC}); // Path with XRP dst currency. test( @@ -688,7 +694,7 @@ struct PayStrand_test : public beast::unit_test::Suite STPath({STPathElement{ STPathElement::TypeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()}}), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, XRP, std::nullopt}, XRPS{bob}); @@ -699,10 +705,10 @@ struct PayStrand_test : public beast::unit_test::Suite usd, STPath({cpe(xrpCurrency())}), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, XRP, std::nullopt}, B{XRP, eur, std::nullopt}, - D{gw, bob, eurC}); + D{.src = gw, .dst = bob, .currency = eurC}); // XRP -> XRP transaction can't include a path test(env, XRP, std::nullopt, STPath({ape(carol)}), temBAD_PATH); diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index be377c0c1d0..55e18e1dfe6 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -668,7 +668,8 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); auto const badCredType = "badCred"; - pdomain::Credentials const credentials{{badDomainOwner, badCredType}}; + pdomain::Credentials const credentials{ + {.issuer = badDomainOwner, .credType = badCredType}}; env(pdomain::setTx(badDomainOwner, credentials)); auto objects = pdomain::getObjects(badDomainOwner, env); @@ -1186,7 +1187,8 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); auto const badCredType = "badCred"; - pdomain::Credentials const credentials{{badDomainOwner, badCredType}}; + pdomain::Credentials const credentials{ + {.issuer = badDomainOwner, .credType = badCredType}}; env(pdomain::setTx(badDomainOwner, credentials)); auto objects = pdomain::getObjects(badDomainOwner, env); diff --git a/src/test/app/PermissionedDomains_test.cpp b/src/test/app/PermissionedDomains_test.cpp index 0857a4bdef9..f2d7bce152e 100644 --- a/src/test/app/PermissionedDomains_test.cpp +++ b/src/test/app/PermissionedDomains_test.cpp @@ -62,7 +62,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite Account const alice("alice"); Env env(*this, features); env.fund(XRP(1000), alice); - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials)); BEAST_EXPECT(env.ownerCount(alice) == 1); auto objects = pdomain::getObjects(alice, env); @@ -84,7 +84,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite Account const alice("alice"); Env env(*this, amendments); env.fund(XRP(1000), alice); - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials), Ter(temDISABLED)); } @@ -96,7 +96,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite Account const alice("alice"); Env env(*this, testableAmendments() - featurePermissionedDomains); env.fund(XRP(1000), alice); - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials), Ter(temDISABLED)); env(pdomain::deleteTx(alice, uint256(75)), Ter(temDISABLED)); } @@ -124,40 +124,40 @@ class PermissionedDomains_test : public beast::unit_test::Suite // Test 11 credentials. pdomain::Credentials const credentials11{ - {alice2, "credential1"}, - {alice3, "credential2"}, - {alice4, "credential3"}, - {alice5, "credential4"}, - {alice6, "credential5"}, - {alice7, "credential6"}, - {alice8, "credential7"}, - {alice9, "credential8"}, - {alice10, "credential9"}, - {alice11, "credential10"}, - {alice12, "credential11"}}; + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice4, .credType = "credential3"}, + {.issuer = alice5, .credType = "credential4"}, + {.issuer = alice6, .credType = "credential5"}, + {.issuer = alice7, .credType = "credential6"}, + {.issuer = alice8, .credType = "credential7"}, + {.issuer = alice9, .credType = "credential8"}, + {.issuer = alice10, .credType = "credential9"}, + {.issuer = alice11, .credType = "credential10"}, + {.issuer = alice12, .credType = "credential11"}}; BEAST_EXPECT(credentials11.size() == kMaxPermissionedDomainCredentialsArraySize + 1); env(pdomain::setTx(account, credentials11, domain), Ter(temARRAY_TOO_LARGE)); // Test credentials including non-existent issuer. Account const nobody("nobody"); pdomain::Credentials const credentialsNon{ - {alice2, "credential1"}, - {alice3, "credential2"}, - {alice4, "credential3"}, - {nobody, "credential4"}, - {alice5, "credential5"}, - {alice6, "credential6"}, - {alice7, "credential7"}}; + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice4, .credType = "credential3"}, + {.issuer = nobody, .credType = "credential4"}, + {.issuer = alice5, .credType = "credential5"}, + {.issuer = alice6, .credType = "credential6"}, + {.issuer = alice7, .credType = "credential7"}}; env(pdomain::setTx(account, credentialsNon, domain), Ter(tecNO_ISSUER)); // Test bad fee env(pdomain::setTx(account, credentials11, domain), Fee(1, true), Ter(temBAD_FEE)); pdomain::Credentials const credentials4{ - {alice2, "credential1"}, - {alice3, "credential2"}, - {alice4, "credential3"}, - {alice5, "credential4"}, + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice4, .credType = "credential3"}, + {.issuer = alice5, .credType = "credential4"}, }; auto txJsonMutable = pdomain::setTx(account, credentials4, domain); auto const credentialOrig = txJsonMutable["AcceptedCredentials"][2u]; @@ -192,11 +192,11 @@ class PermissionedDomains_test : public beast::unit_test::Suite // permissioned domains, so transactions should return errors { pdomain::Credentials const credentialsDup{ - {alice7, "credential6"}, - {alice2, "credential1"}, - {alice3, "credential2"}, - {alice2, "credential1"}, - {alice5, "credential4"}, + {.issuer = alice7, .credType = "credential6"}, + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice5, .credType = "credential4"}, }; std::unordered_map human2Acc; @@ -230,11 +230,11 @@ class PermissionedDomains_test : public beast::unit_test::Suite // sort correctly. { pdomain::Credentials const credentialsSame{ - {alice2, "credential3"}, - {alice3, "credential2"}, - {alice2, "credential9"}, - {alice5, "credential4"}, - {alice2, "credential6"}, + {.issuer = alice2, .credType = "credential3"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice2, .credType = "credential9"}, + {.issuer = alice5, .credType = "credential4"}, + {.issuer = alice2, .credType = "credential6"}, }; std::unordered_map human2Acc; for (auto const& c : credentialsSame) @@ -290,7 +290,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite env.fund(XRP(1000), alice[i]); // Create new from existing account with a single credential. - pdomain::Credentials const credentials1{{alice[2], "credential1"}}; + pdomain::Credentials const credentials1{{.issuer = alice[2], .credType = "credential1"}}; { env(pdomain::setTx(alice[0], credentials1)); BEAST_EXPECT(env.ownerCount(alice[0]) == 1); @@ -314,7 +314,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite "89"; static_assert(kLongCredentialType.size() == kMaxCredentialTypeLength); pdomain::Credentials const longCredentials{ - {alice[1], std::string(kLongCredentialType)}}; + {.issuer = alice[1], .credType = std::string(kLongCredentialType)}}; env(pdomain::setTx(alice[0], longCredentials)); @@ -345,16 +345,16 @@ class PermissionedDomains_test : public beast::unit_test::Suite // Create new from existing account with 10 credentials. // Last credential describe domain owner itself pdomain::Credentials const credentials10{ - {alice[2], "credential1"}, - {alice[3], "credential2"}, - {alice[4], "credential3"}, - {alice[5], "credential4"}, - {alice[6], "credential5"}, - {alice[7], "credential6"}, - {alice[8], "credential7"}, - {alice[9], "credential8"}, - {alice[10], "credential9"}, - {alice[0], "credential10"}, + {.issuer = alice[2], .credType = "credential1"}, + {.issuer = alice[3], .credType = "credential2"}, + {.issuer = alice[4], .credType = "credential3"}, + {.issuer = alice[5], .credType = "credential4"}, + {.issuer = alice[6], .credType = "credential5"}, + {.issuer = alice[7], .credType = "credential6"}, + {.issuer = alice[8], .credType = "credential7"}, + {.issuer = alice[9], .credType = "credential8"}, + {.issuer = alice[10], .credType = "credential9"}, + {.issuer = alice[0], .credType = "credential10"}, }; uint256 domain2; { @@ -434,7 +434,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite env.fund(XRP(1000), alice); auto const setFee(drops(env.current()->fees().increment)); - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials)); env.close(); @@ -498,7 +498,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite BEAST_EXPECT(env.ownerCount(alice) == 0); // alice does not have enough XRP to cover the reserve. - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials), Ter(tecINSUFFICIENT_RESERVE)); BEAST_EXPECT(env.ownerCount(alice) == 0); BEAST_EXPECT(pdomain::getObjects(alice, env).empty()); diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index d8882cf3462..938ec0b7c6d 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -1228,7 +1228,7 @@ class TxQPosNegFlows_test : public beast::unit_test::Suite // Try to replace a middle item in the queue // with enough fee to bankrupt bob and make the // later transactions unable to pay their fees - std::int64_t bobFee = env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10 - 1); + std::int64_t bobFee = env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - ((9 * 10) - 1); env(noop(bob), Seq(bobSeq + 5), Fee(bobFee), Ter(telCAN_NOT_QUEUE_BALANCE)); checkMetrics(*this, env, 10, 12, 7, 6); diff --git a/src/test/app/ValidatorList_test.cpp b/src/test/app/ValidatorList_test.cpp index 15bd1bc5860..184b2b98757 100644 --- a/src/test/app/ValidatorList_test.cpp +++ b/src/test/app/ValidatorList_test.cpp @@ -632,7 +632,11 @@ class ValidatorList_test : public beast::unit_test::Suite checkResult( trustedKeys->applyLists( - manifest1, version, {{expiredblob, expiredSig, {}}, {blob2, sig2, {}}}, siteUri), + manifest1, + version, + {{.blob = expiredblob, .signature = expiredSig, .manifest = {}}, + {.blob = blob2, .signature = sig2, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Expired, ListDisposition::Accepted); @@ -665,7 +669,11 @@ class ValidatorList_test : public beast::unit_test::Suite checkResult( trustedKeys->applyLists( - manifest1, version2, {{blob7, sig7, {}}, {blob8, sig8, {}}}, siteUri), + manifest1, + version2, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob8, .signature = sig8, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Pending, ListDisposition::Pending); @@ -697,7 +705,11 @@ class ValidatorList_test : public beast::unit_test::Suite checkResult( trustedKeys->applyLists( - manifest1, version, {{blob6a, sig6a, {}}, {blob6, sig6, {}}}, siteUri), + manifest1, + version, + {{.blob = blob6a, .signature = sig6a, .manifest = {}}, + {.blob = blob6, .signature = sig6, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Pending, ListDisposition::Pending); @@ -709,7 +721,11 @@ class ValidatorList_test : public beast::unit_test::Suite checkResult( trustedKeys->applyLists( - manifest1, version, {{blob7, sig7, {}}, {blob6, sig6, {}}}, siteUri), + manifest1, + version, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob6, .signature = sig6, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::KnownSequence, ListDisposition::KnownSequence); @@ -720,7 +736,12 @@ class ValidatorList_test : public beast::unit_test::Suite // try empty or mangled manifest checkResult( - trustedKeys->applyLists("", version, {{blob7, sig7, {}}, {blob6, sig6, {}}}, siteUri), + trustedKeys->applyLists( + "", + version, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob6, .signature = sig6, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Invalid, ListDisposition::Invalid); @@ -729,7 +750,8 @@ class ValidatorList_test : public beast::unit_test::Suite trustedKeys->applyLists( base64Encode("not a manifest"), version, - {{blob7, sig7, {}}, {blob6, sig6, {}}}, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob6, .signature = sig6, .manifest = {}}}, siteUri), publisherPublic, ListDisposition::Invalid, @@ -740,7 +762,11 @@ class ValidatorList_test : public beast::unit_test::Suite randomMasterKey(), publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); checkResult( - trustedKeys->applyLists(untrustedManifest, version, {{blob2, sig2, {}}}, siteUri), + trustedKeys->applyLists( + untrustedManifest, + version, + {{.blob = blob2, .signature = sig2, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Untrusted, ListDisposition::Untrusted); @@ -748,7 +774,11 @@ class ValidatorList_test : public beast::unit_test::Suite // do not use list with unhandled version auto const badVersion = 666; checkResult( - trustedKeys->applyLists(manifest1, badVersion, {{blob2, sig2, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, + badVersion, + {{.blob = blob2, .signature = sig2, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::UnsupportedVersion, ListDisposition::UnsupportedVersion); @@ -759,7 +789,8 @@ class ValidatorList_test : public beast::unit_test::Suite auto const sig3 = signList(blob3, pubSigningKeys1); checkResult( - trustedKeys->applyLists(manifest1, version, {{blob3, sig3, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, version, {{.blob = blob3, .signature = sig3, .manifest = {}}}, siteUri), publisherPublic, ListDisposition::Accepted, ListDisposition::Accepted); @@ -780,7 +811,11 @@ class ValidatorList_test : public beast::unit_test::Suite // do not re-apply lists with past or current sequence numbers checkResult( trustedKeys->applyLists( - manifest1, version, {{blob2, sig2, {}}, {blob3, sig3, {}}}, siteUri), + manifest1, + version, + {{.blob = blob2, .signature = sig2, .manifest = {}}, + {.blob = blob3, .signature = sig3, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Stale, ListDisposition::SameSequence); @@ -799,7 +834,9 @@ class ValidatorList_test : public beast::unit_test::Suite trustedKeys->applyLists( manifest2, version, - {{blob2, sig2, manifest1}, {blob3, sig3, manifest1}, {blob4, sig4, {}}}, + {{.blob = blob2, .signature = sig2, .manifest = manifest1}, + {.blob = blob3, .signature = sig3, .manifest = manifest1}, + {.blob = blob4, .signature = sig4, .manifest = {}}}, siteUri), publisherPublic, ListDisposition::Stale, @@ -820,7 +857,11 @@ class ValidatorList_test : public beast::unit_test::Suite auto const blob5 = makeList(lists.at(5), sequence5, validUntil.time_since_epoch().count()); auto const badSig = signList(blob5, pubSigningKeys1); checkResult( - trustedKeys->applyLists(manifest1, version, {{blob5, badSig, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, + version, + {{.blob = blob5, .signature = badSig, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Invalid, ListDisposition::Invalid); @@ -833,7 +874,11 @@ class ValidatorList_test : public beast::unit_test::Suite // Reprocess the pending list, but the signature is no longer valid checkResult( trustedKeys->applyLists( - manifest1, version, {{blob7, sig7, {}}, {blob8, sig8, {}}}, siteUri), + manifest1, + version, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob8, .signature = sig8, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Invalid, ListDisposition::Invalid); @@ -884,7 +929,11 @@ class ValidatorList_test : public beast::unit_test::Suite checkResult( trustedKeys->applyLists( - manifest2, version, {{blob8, sig8, manifest1}, {blob8, sig82, {}}}, siteUri), + manifest2, + version, + {{.blob = blob8, .signature = sig8, .manifest = manifest1}, + {.blob = blob8, .signature = sig82, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Invalid, ListDisposition::SameSequence); @@ -903,7 +952,11 @@ class ValidatorList_test : public beast::unit_test::Suite auto const sig9 = signList(blob9, signingKeysMax); checkResult( - trustedKeys->applyLists(maxManifest, version, {{blob9, sig9, {}}}, siteUri), + trustedKeys->applyLists( + maxManifest, + version, + {{.blob = blob9, .signature = sig9, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Untrusted, ListDisposition::Untrusted); @@ -1900,7 +1953,9 @@ class ValidatorList_test : public beast::unit_test::Suite return PreparedList{ .publisherPublic = publisherPublic, .manifest = manifest, - .blobs = {{blob1, sig1, {}}, {blob2, sig2, {}}}, + .blobs = + {{.blob = blob1, .signature = sig1, .manifest = {}}, + {.blob = blob2, .signature = sig2, .manifest = {}}}, .version = version, .expirations = {expiration1, expiration2}}; }; diff --git a/src/test/app/ValidatorSite_test.cpp b/src/test/app/ValidatorSite_test.cpp index f7f805faa25..e3e3ec27df7 100644 --- a/src/test/app/ValidatorSite_test.cpp +++ b/src/test/app/ValidatorSite_test.cpp @@ -380,226 +380,333 @@ class ValidatorSite_test : public beast::unit_test::Suite for (auto ssl : {true, false}) { // fetch single site - testFetchList(good, {{"/validators", "", ssl}}); - testFetchList(good, {{"/validators2", "", ssl}}); + testFetchList(good, {{.path = "/validators", .msg = "", .ssl = ssl}}); + testFetchList(good, {{.path = "/validators2", .msg = "", .ssl = ssl}}); // fetch multiple sites - testFetchList(good, {{"/validators", "", ssl}, {"/validators", "", ssl}}); - testFetchList(good, {{"/validators", "", ssl}, {"/validators2", "", ssl}}); - testFetchList(good, {{"/validators2", "", ssl}, {"/validators", "", ssl}}); - testFetchList(good, {{"/validators2", "", ssl}, {"/validators2", "", ssl}}); + testFetchList( + good, + {{.path = "/validators", .msg = "", .ssl = ssl}, + {.path = "/validators", .msg = "", .ssl = ssl}}); + testFetchList( + good, + {{.path = "/validators", .msg = "", .ssl = ssl}, + {.path = "/validators2", .msg = "", .ssl = ssl}}); + testFetchList( + good, + {{.path = "/validators2", .msg = "", .ssl = ssl}, + {.path = "/validators", .msg = "", .ssl = ssl}}); + testFetchList( + good, + {{.path = "/validators2", .msg = "", .ssl = ssl}, + {.path = "/validators2", .msg = "", .ssl = ssl}}); // fetch single site with single redirects - testFetchList(good, {{"/redirect_once/301", "", ssl}}); - testFetchList(good, {{"/redirect_once/302", "", ssl}}); - testFetchList(good, {{"/redirect_once/307", "", ssl}}); - testFetchList(good, {{"/redirect_once/308", "", ssl}}); + testFetchList(good, {{.path = "/redirect_once/301", .msg = "", .ssl = ssl}}); + testFetchList(good, {{.path = "/redirect_once/302", .msg = "", .ssl = ssl}}); + testFetchList(good, {{.path = "/redirect_once/307", .msg = "", .ssl = ssl}}); + testFetchList(good, {{.path = "/redirect_once/308", .msg = "", .ssl = ssl}}); // one redirect, one not - testFetchList(good, {{"/validators", "", ssl}, {"/redirect_once/302", "", ssl}}); - testFetchList(good, {{"/validators2", "", ssl}, {"/redirect_once/302", "", ssl}}); + testFetchList( + good, + {{.path = "/validators", .msg = "", .ssl = ssl}, + {.path = "/redirect_once/302", .msg = "", .ssl = ssl}}); + testFetchList( + good, + {{.path = "/validators2", .msg = "", .ssl = ssl}, + {.path = "/redirect_once/302", .msg = "", .ssl = ssl}}); // UNLs with a "gap" between validUntil of one and validFrom of the // next testFetchList( good, - {{"/validators2", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - std::chrono::seconds{-90}}}); + {{.path = "/validators2", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = std::chrono::seconds{-90}}}); // fetch single site with unending redirect (fails to load) testFetchList( - good, {{"/redirect_forever/301", "Exceeded max redirects", ssl, true, true}}); + good, + {{.path = "/redirect_forever/301", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // two that redirect forever testFetchList( good, - {{"/redirect_forever/307", "Exceeded max redirects", ssl, true, true}, - {"/redirect_forever/308", "Exceeded max redirects", ssl, true, true}}); + {{.path = "/redirect_forever/307", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}, + {.path = "/redirect_forever/308", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // one unending redirect, one not testFetchList( good, - {{"/validators", "", ssl}, - {"/redirect_forever/302", "Exceeded max redirects", ssl, true, true}}); + {{.path = "/validators", .msg = "", .ssl = ssl}, + {.path = "/redirect_forever/302", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // one unending redirect, one not testFetchList( good, - {{"/validators2", "", ssl}, - {"/redirect_forever/302", "Exceeded max redirects", ssl, true, true}}); + {{.path = "/validators2", .msg = "", .ssl = ssl}, + {.path = "/redirect_forever/302", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // invalid redir Location testFetchList( good, - {{"/redirect_to/ftp://invalid-url/302", - "Invalid redirect location", - ssl, - true, - true}}); + {{.path = "/redirect_to/ftp://invalid-url/302", + .msg = "Invalid redirect location", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); testFetchList( good, - {{"/redirect_to/file://invalid-url/302", - "Invalid redirect location", - ssl, - true, - true}}); + {{.path = "/redirect_to/file://invalid-url/302", + .msg = "Invalid redirect location", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // invalid json testFetchList( - good, {{"/validators/bad", "Unable to parse JSON response", ssl, true, true}}); + good, + {{.path = "/validators/bad", + .msg = "Unable to parse JSON response", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); testFetchList( - good, {{"/validators2/bad", "Unable to parse JSON response", ssl, true, true}}); + good, + {{.path = "/validators2/bad", + .msg = "Unable to parse JSON response", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // error status returned - testFetchList(good, {{"/bad-resource", "returned bad status", ssl, true, true}}); + testFetchList( + good, + {{.path = "/bad-resource", + .msg = "returned bad status", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // location field missing testFetchList( good, - {{"/redirect_nolo/308", "returned a redirect with no Location", ssl, true, true}}); + {{.path = "/redirect_nolo/308", + .msg = "returned a redirect with no Location", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // json fields missing testFetchList( good, - {{"/validators/missing", "Missing fields in JSON response", ssl, true, true}}); + {{.path = "/validators/missing", + .msg = "Missing fields in JSON response", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); testFetchList( good, - {{"/validators2/missing", "Missing fields in JSON response", ssl, true, true}}); + {{.path = "/validators2/missing", + .msg = "Missing fields in JSON response", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // timeout - testFetchList(good, {{"/sleep/13", "took too long", ssl, true, true}}); + testFetchList( + good, + {{.path = "/sleep/13", + .msg = "took too long", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // bad manifest format using known versions // * Retrieves a v1 formatted list claiming version 2 - testFetchList(good, {{"/validators", "Missing fields", ssl, true, true, 2}}); + testFetchList( + good, + {{.path = "/validators", + .msg = "Missing fields", + .ssl = ssl, + .failFetch = true, + .failApply = true, + .serverVersion = 2}}); // * Retrieves a v2 formatted list claiming version 1 - testFetchList(good, {{"/validators2", "Missing fields", ssl, true, true, 0}}); + testFetchList( + good, + {{.path = "/validators2", + .msg = "Missing fields", + .ssl = ssl, + .failFetch = true, + .failApply = true, + .serverVersion = 0}}); // bad manifest version // Because versions other than 1 are treated as v2, the v1 // list won't have the blobs_v2 fields, and thus will claim to have // missing fields - testFetchList(good, {{"/validators", "Missing fields", ssl, true, true, 4}}); - testFetchList(good, {{"/validators2", "1 unsupported version", ssl, false, true, 4}}); + testFetchList( + good, + {{.path = "/validators", + .msg = "Missing fields", + .ssl = ssl, + .failFetch = true, + .failApply = true, + .serverVersion = 4}}); + testFetchList( + good, + {{.path = "/validators2", + .msg = "1 unsupported version", + .ssl = ssl, + .failFetch = false, + .failApply = true, + .serverVersion = 4}}); using namespace std::chrono_literals; // get expired validator list testFetchList( good, - {{"/validators", "Applied 1 expired validator list(s)", ssl, false, false, 1, 0s}}); + {{.path = "/validators", + .msg = "Applied 1 expired validator list(s)", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = 0s}}); testFetchList( good, - {{"/validators2", - "Applied 1 expired validator list(s)", - ssl, - false, - false, - 1, - 0s, - -1s}}); + {{.path = "/validators2", + .msg = "Applied 1 expired validator list(s)", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = 0s, + .effectiveOverlap = -1s}}); // force an out-of-range validUntil value testFetchList( good, - {{"/validators", - "1 invalid validator list(s)", - ssl, - false, - true, - 1, - std::chrono::seconds{json::Value::kMinInt}}}); + {{.path = "/validators", + .msg = "1 invalid validator list(s)", + .ssl = ssl, + .failFetch = false, + .failApply = true, + .serverVersion = 1, + .expiresFromNow = std::chrono::seconds{json::Value::kMinInt}}}); // force an out-of-range validUntil value on the future list // The first list is accepted. The second fails. The parser // returns the "best" result, so this looks like a success. testFetchList( good, - {{"/validators2", - "", - ssl, - false, - false, - 1, - std::chrono::seconds{json::Value::kMaxInt - 300}, - 299s}}); + {{.path = "/validators2", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = std::chrono::seconds{json::Value::kMaxInt - 300}, + .effectiveOverlap = 299s}}); // force an out-of-range validFrom value // The first list is accepted. The second fails. The parser // returns the "best" result, so this looks like a success. testFetchList( good, - {{"/validators2", - "", - ssl, - false, - false, - 1, - std::chrono::seconds{json::Value::kMaxInt - 300}, - 301s}}); + {{.path = "/validators2", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = std::chrono::seconds{json::Value::kMaxInt - 300}, + .effectiveOverlap = 301s}}); // force an out-of-range validUntil value on _both_ lists testFetchList( good, - {{"/validators2", - "2 invalid validator list(s)", - ssl, - false, - true, - 1, - std::chrono::seconds{json::Value::kMinInt}, - std::chrono::seconds{json::Value::kMaxInt - 6000}}}); + {{.path = "/validators2", + .msg = "2 invalid validator list(s)", + .ssl = ssl, + .failFetch = false, + .failApply = true, + .serverVersion = 1, + .expiresFromNow = std::chrono::seconds{json::Value::kMinInt}, + .effectiveOverlap = std::chrono::seconds{json::Value::kMaxInt - 6000}}}); // verify refresh intervals are properly clamped testFetchList( good, - {{"/validators/refresh/0", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 1}}); // minimum of 1 minute - testFetchList( - good, - {{"/validators2/refresh/0", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 1}}); // minimum of 1 minute - testFetchList( - good, - {{"/validators/refresh/10", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 10}}); // 10 minutes is fine - testFetchList( - good, - {{"/validators2/refresh/10", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 10}}); // 10 minutes is fine - testFetchList( - good, - {{"/validators/refresh/2000", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 60 * 24}}); // max of 24 hours - testFetchList( - good, - {{"/validators2/refresh/2000", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 60 * 24}}); // max of 24 hours + {{.path = "/validators/refresh/0", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 1}}); // minimum of 1 minute + testFetchList( + good, + {{.path = "/validators2/refresh/0", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 1}}); // minimum of 1 minute + testFetchList( + good, + {{.path = "/validators/refresh/10", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 10}}); // 10 minutes is fine + testFetchList( + good, + {{.path = "/validators2/refresh/10", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 10}}); // 10 minutes is fine + testFetchList( + good, + {{.path = "/validators/refresh/2000", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 60 * 24}}); // max of 24 hours + testFetchList( + good, + {{.path = "/validators2/refresh/2000", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 60 * 24}}); // max of 24 hours } using namespace boost::filesystem; for (auto const& file : directory_iterator(good.subdir())) diff --git a/src/test/beast/aged_associative_container_test.cpp b/src/test/beast/aged_associative_container_test.cpp index f2ce72b5843..d7f74aaa7d7 100644 --- a/src/test/beast/aged_associative_container_test.cpp +++ b/src/test/beast/aged_associative_container_test.cpp @@ -414,11 +414,11 @@ class AgedAssociativeContainerTestBase : public unit_test::Suite // unordered template - std::enable_if_t::type::is_unordered::value> + std::enable_if_t::is_unordered::value> checkUnorderedContentsRefRef(C&& c, Values const& v); template - std::enable_if_t::type::is_unordered::value> + std::enable_if_t::is_unordered::value> checkUnorderedContentsRefRef(C&&, Values const&) { } @@ -641,7 +641,7 @@ AgedAssociativeContainerTestBase::checkMapContents(Container& c, Values const& v // unordered template -std::enable_if_t::type::is_unordered::value> +std::enable_if_t::is_unordered::value> AgedAssociativeContainerTestBase::checkUnorderedContentsRefRef(C&& c, Values const& v) { using Cont = std::remove_reference_t; diff --git a/src/test/core/Config_test.cpp b/src/test/core/Config_test.cpp index 3e6495bc2fc..3b50253cb31 100644 --- a/src/test/core/Config_test.cpp +++ b/src/test/core/Config_test.cpp @@ -1457,14 +1457,14 @@ r.ripple.com:51235 }; std::vector const units = { - {"seconds", 1, 15 * 60, false}, - {"minutes", 60, 14, false}, - {"minutes", 60, 15, true}, - {"hours", 3600, 10, true}, - {"days", 86400, 10, true}, - {"weeks", 604800, 2, true}, - {"months", 2592000, 1, false}, - {"years", 31536000, 1, false}}; + {.unit = "seconds", .numSeconds = 1, .configVal = 15 * 60, .shouldPass = false}, + {.unit = "minutes", .numSeconds = 60, .configVal = 14, .shouldPass = false}, + {.unit = "minutes", .numSeconds = 60, .configVal = 15, .shouldPass = true}, + {.unit = "hours", .numSeconds = 3600, .configVal = 10, .shouldPass = true}, + {.unit = "days", .numSeconds = 86400, .configVal = 10, .shouldPass = true}, + {.unit = "weeks", .numSeconds = 604800, .configVal = 2, .shouldPass = true}, + {.unit = "months", .numSeconds = 2592000, .configVal = 1, .shouldPass = false}, + {.unit = "years", .numSeconds = 31536000, .configVal = 1, .shouldPass = false}}; std::string space; for (auto& [unit, sec, val, shouldPass] : units) diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index ba09647ce72..674eac1bddc 100644 --- a/src/test/jtx/impl/WSClient.cpp +++ b/src/test/jtx/impl/WSClient.cpp @@ -70,7 +70,7 @@ class WSClientImpl : public WSClient continue; ParsedPort pp; parsePort(pp, cfg[name], log); - if (pp.protocol.count(ps) == 0) + if (!pp.protocol.contains(ps)) continue; using namespace boost::asio::ip; if (pp.ip && pp.ip->is_unspecified()) diff --git a/src/test/jtx/impl/permissioned_dex.cpp b/src/test/jtx/impl/permissioned_dex.cpp index 012932fed5a..2f12749f654 100644 --- a/src/test/jtx/impl/permissioned_dex.cpp +++ b/src/test/jtx/impl/permissioned_dex.cpp @@ -26,7 +26,7 @@ setupDomain( env.fund(XRP(100000), domainOwner); env.close(); - pdomain::Credentials const credentials{{domainOwner, credType}}; + pdomain::Credentials const credentials{{.issuer = domainOwner, .credType = credType}}; env(pdomain::setTx(domainOwner, credentials)); auto const objects = pdomain::getObjects(domainOwner, env); diff --git a/src/test/jtx/impl/permissioned_domains.cpp b/src/test/jtx/impl/permissioned_domains.cpp index 690451c7d86..385008be431 100644 --- a/src/test/jtx/impl/permissioned_domains.cpp +++ b/src/test/jtx/impl/permissioned_domains.cpp @@ -130,7 +130,9 @@ credentialsFromJson( auto const& credentialType = obj["CredentialType"]; // NOLINTNEXTLINE(bugprone-unchecked-optional-access): used only in tests auto blob = strUnHex(credentialType.asString()).value(); - ret.push_back({human2Acc.at(issuer.asString()), std::string(blob.begin(), blob.end())}); + ret.push_back( + {.issuer = human2Acc.at(issuer.asString()), + .credType = std::string(blob.begin(), blob.end())}); } return ret; } diff --git a/src/test/nodestore/import_test.cpp b/src/test/nodestore/import_test.cpp index a80b5ccc93d..de99edd6552 100644 --- a/src/test/nodestore/import_test.cpp +++ b/src/test/nodestore/import_test.cpp @@ -297,17 +297,17 @@ class import_test : public beast::unit_test::Suite auto const args = parseArgs(arg()); bool usage = args.empty(); - if (!usage && args.find("from") == args.end()) + if (!usage && !args.contains("from")) { log << "Missing parameter: from"; usage = true; } - if (!usage && args.find("to") == args.end()) + if (!usage && !args.contains("to")) { log << "Missing parameter: to"; usage = true; } - if (!usage && args.find("buffer") == args.end()) + if (!usage && !args.contains("buffer")) { log << "Missing parameter: buffer"; usage = true; diff --git a/src/test/rpc/DepositAuthorized_test.cpp b/src/test/rpc/DepositAuthorized_test.cpp index 89053557a71..e6720602c92 100644 --- a/src/test/rpc/DepositAuthorized_test.cpp +++ b/src/test/rpc/DepositAuthorized_test.cpp @@ -324,7 +324,7 @@ class DepositAuthorized_test : public beast::unit_test::Suite env.close(); // becky authorize any account recognized by carol to make a payment - env(deposit::authCredentials(becky, {{carol, credType}})); + env(deposit::authCredentials(becky, {{.issuer = carol, .credType = credType}})); env.close(); { @@ -507,7 +507,7 @@ class DepositAuthorized_test : public beast::unit_test::Suite env.close(); // becky authorize any account recognized by carol to make a payment - env(deposit::authCredentials(becky, {{carol, credType2}})); + env(deposit::authCredentials(becky, {{.issuer = carol, .credType = credType2}})); env.close(); { diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index 0f9421beb28..cf616b976bf 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -784,8 +784,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::amm, { - {jss::asset, "malformedRequest"}, - {jss::asset2, "malformedRequest"}, + {.fieldName = jss::asset, .malformedErrorMsg = "malformedRequest"}, + {.fieldName = jss::asset2, .malformedErrorMsg = "malformedRequest"}, }); }; auto getIOU = [&](Env& env) -> PrettyAsset { return alice["USD"]; }; @@ -900,9 +900,9 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::credential, { - {jss::subject, "malformedRequest"}, - {jss::issuer, "malformedRequest"}, - {jss::credential_type, "malformedRequest"}, + {.fieldName = jss::subject, .malformedErrorMsg = "malformedRequest"}, + {.fieldName = jss::issuer, .malformedErrorMsg = "malformedRequest"}, + {.fieldName = jss::credential_type, .malformedErrorMsg = "malformedRequest"}, }); } } @@ -954,8 +954,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::delegate, { - {jss::account, "malformedAddress"}, - {jss::authorize, "malformedAddress"}, + {.fieldName = jss::account, .malformedErrorMsg = "malformedAddress"}, + {.fieldName = jss::authorize, .malformedErrorMsg = "malformedAddress"}, }); } } @@ -1011,8 +1011,10 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::deposit_preauth, { - {jss::owner, "malformedOwner"}, - {jss::authorized, "malformedAuthorized", false}, + {.fieldName = jss::owner, .malformedErrorMsg = "malformedOwner"}, + {.fieldName = jss::authorized, + .malformedErrorMsg = "malformedAuthorized", + .required = false}, }); } } @@ -1037,7 +1039,7 @@ class LedgerEntry_test : public beast::unit_test::Suite // Setup Bob with DepositAuth env(fset(bob, asfDepositAuth)); env.close(); - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); } @@ -1458,7 +1460,10 @@ class LedgerEntry_test : public beast::unit_test::Suite { // Malformed escrow fields runLedgerEntryTest( - env, jss::escrow, {{jss::owner, "malformedOwner"}, {jss::seq, "malformedSeq"}}); + env, + jss::escrow, + {{.fieldName = jss::owner, .malformedErrorMsg = "malformedOwner"}, + {.fieldName = jss::seq, .malformedErrorMsg = "malformedSeq"}}); } } @@ -1667,7 +1672,8 @@ class LedgerEntry_test : public beast::unit_test::Suite runLedgerEntryTest( env, jss::offer, - {{jss::account, "malformedAddress"}, {jss::seq, "malformedRequest"}}); + {{.fieldName = jss::account, .malformedErrorMsg = "malformedAddress"}, + {.fieldName = jss::seq, .malformedErrorMsg = "malformedRequest"}}); } } @@ -1774,8 +1780,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, fieldName, { - {jss::accounts, "malformedRequest"}, - {jss::currency, "malformedCurrency"}, + {.fieldName = jss::accounts, .malformedErrorMsg = "malformedRequest"}, + {.fieldName = jss::currency, .malformedErrorMsg = "malformedCurrency"}, }); } { @@ -1955,8 +1961,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::ticket, { - {jss::account, "malformedAddress"}, - {jss::ticket_seq, "malformedRequest"}, + {.fieldName = jss::account, .malformedErrorMsg = "malformedAddress"}, + {.fieldName = jss::ticket_seq, .malformedErrorMsg = "malformedRequest"}, }); } } @@ -2034,8 +2040,9 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::oracle, { - {jss::account, "malformedAccount"}, - {jss::oracle_document_id, "malformedDocumentID"}, + {.fieldName = jss::account, .malformedErrorMsg = "malformedAccount"}, + {.fieldName = jss::oracle_document_id, + .malformedErrorMsg = "malformedDocumentID"}, }); } } @@ -2172,7 +2179,7 @@ class LedgerEntry_test : public beast::unit_test::Suite env.close(); auto const seq = env.seq(alice); - env(pdomain::setTx(alice, {{alice, "first credential"}})); + env(pdomain::setTx(alice, {{.issuer = alice, .credType = "first credential"}})); env.close(); auto const objects = pdomain::getObjects(alice, env); if (!BEAST_EXPECT(objects.size() == 1)) @@ -2221,8 +2228,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::permissioned_domain, { - {jss::account, "malformedAddress"}, - {jss::seq, "malformedRequest"}, + {.fieldName = jss::account, .malformedErrorMsg = "malformedAddress"}, + {.fieldName = jss::seq, .malformedErrorMsg = "malformedRequest"}, }); } } diff --git a/src/tests/libxrpl/tx/AccountSet.cpp b/src/tests/libxrpl/tx/AccountSet.cpp index d00df152ae3..a7e41213a90 100644 --- a/src/tests/libxrpl/tx/AccountSet.cpp +++ b/src/tests/libxrpl/tx/AccountSet.cpp @@ -432,13 +432,13 @@ TEST(AccountSet, TransferRate) // Test data: {rate to set, expected TER, expected stored rate} std::vector const testData = { - {1.0, tesSUCCESS, 1.0}, - {1.1, tesSUCCESS, 1.1}, - {2.0, tesSUCCESS, 2.0}, - {2.1, temBAD_TRANSFER_RATE, 2.0}, // > 2.0 is invalid - {0.0, tesSUCCESS, 1.0}, // 0 clears the rate (default = 1.0) - {2.0, tesSUCCESS, 2.0}, - {0.9, temBAD_TRANSFER_RATE, 2.0}, // < 1.0 is invalid + {.set = 1.0, .code = tesSUCCESS, .get = 1.0}, + {.set = 1.1, .code = tesSUCCESS, .get = 1.1}, + {.set = 2.0, .code = tesSUCCESS, .get = 2.0}, + {.set = 2.1, .code = temBAD_TRANSFER_RATE, .get = 2.0}, // > 2.0 is invalid + {.set = 0.0, .code = tesSUCCESS, .get = 1.0}, // 0 clears the rate (default = 1.0) + {.set = 2.0, .code = tesSUCCESS, .get = 2.0}, + {.set = 0.9, .code = temBAD_TRANSFER_RATE, .get = 2.0}, // < 1.0 is invalid }; TxTest env; diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 0f70f170465..670cf0a68fe 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -150,7 +150,7 @@ TxQ::FeeMetrics::update( // current size limit, use a limit that is // 90% of the way from max_element to the // current size limit. - return (txnsExpected_ * 9 + *iter) / 10; + return ((txnsExpected_ * 9) + *iter) / 10; }(); // Ledgers are processing in a timely manner, // so keep the limit high, but don't let it @@ -218,7 +218,7 @@ sumOfFirstSquares(std::size_t xIn) // in a ledger, this is the least of our problems. if (x >= (1 << 21)) return {false, std::numeric_limits::max()}; - return {true, (x * (x + 1) * (2 * x + 1)) / 6}; + return {true, (x * (x + 1) * ((2 * x) + 1)) / 6}; } // Unit tests for sumOfSquares() diff --git a/src/xrpld/app/misc/detail/ValidatorList.cpp b/src/xrpld/app/misc/detail/ValidatorList.cpp index 57b65814e1d..0981c32050a 100644 --- a/src/xrpld/app/misc/detail/ValidatorList.cpp +++ b/src/xrpld/app/misc/detail/ValidatorList.cpp @@ -455,7 +455,7 @@ ValidatorList::parseBlobs(std::uint32_t version, json::Value const& body) std::vector ValidatorList::parseBlobs(protocol::TMValidatorList const& body) { - return {{body.blob(), body.signature(), {}}}; + return {{.blob = body.blob(), .signature = body.signature(), .manifest = {}}}; } // static diff --git a/src/xrpld/consensus/DisputedTx.h b/src/xrpld/consensus/DisputedTx.h index e888b95ed63..980090af235 100644 --- a/src/xrpld/consensus/DisputedTx.h +++ b/src/xrpld/consensus/DisputedTx.h @@ -286,7 +286,7 @@ DisputedTx::updateVote(int percentTime, bool proposing, ConsensusPar if (proposing) // give ourselves full weight { // This is basically the percentage of nodes voting 'yes' (including us) - weight = (yays_ * 100 + (ourVote_ ? 100 : 0)) / (nays_ + yays_ + 1); + weight = ((yays_ * 100) + (ourVote_ ? 100 : 0)) / (nays_ + yays_ + 1); newPosition = weight > requiredPct; } diff --git a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp index bbaeeff8e6a..0d5521b4ab7 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp @@ -18,7 +18,8 @@ Config::Config() : outPeers(calcOutPeers()) std::size_t Config::calcOutPeers() const { - return std::max((maxPeers * Tuning::kOutPercent + 50) / 100, std::size_t(Tuning::kMinOutCount)); + return std::max( + ((maxPeers * Tuning::kOutPercent) + 50) / 100, std::size_t(Tuning::kMinOutCount)); } void diff --git a/src/xrpld/rpc/detail/Pathfinder.cpp b/src/xrpld/rpc/detail/Pathfinder.cpp index e949c9b6ff3..8728d15518c 100644 --- a/src/xrpld/rpc/detail/Pathfinder.cpp +++ b/src/xrpld/rpc/detail/Pathfinder.cpp @@ -568,7 +568,11 @@ Pathfinder::rankPaths( JLOG(j_.debug()) << "findPaths: quality: " << uQuality << ": " << currentPath.getJson(JsonOptions::Values::None); - rankedPaths.push_back({uQuality, currentPath.size(), liquidity, i}); + rankedPaths.push_back( + {.quality = uQuality, + .length = currentPath.size(), + .liquidity = liquidity, + .index = i}); } } } @@ -1373,7 +1377,7 @@ fillPaths(Pathfinder::PaymentType type, PathCostList const& costs) auto& list = gPathTable[type]; XRPL_ASSERT(list.empty(), "xrpl::fillPaths : empty paths"); for (auto& cost : costs) - list.push_back({cost.cost, makePath(cost.path)}); + list.push_back({.searchLevel = cost.cost, .type = makePath(cost.path)}); } } // namespace @@ -1396,58 +1400,58 @@ Pathfinder::initPathTable() fillPaths( PaymentType::XrpToNonXrp, - {{1, "sfd"}, // source -> book -> gateway - {3, "sfad"}, // source -> book -> account -> destination - {5, "sfaad"}, // source -> book -> account -> account -> destination - {6, "sbfd"}, // source -> book -> book -> destination - {8, "sbafd"}, // source -> book -> account -> book -> destination - {9, "sbfad"}, // source -> book -> book -> account -> destination - {10, "sbafad"}}); + {{.cost = 1, .path = "sfd"}, // source -> book -> gateway + {.cost = 3, .path = "sfad"}, // source -> book -> account -> destination + {.cost = 5, .path = "sfaad"}, // source -> book -> account -> account -> destination + {.cost = 6, .path = "sbfd"}, // source -> book -> book -> destination + {.cost = 8, .path = "sbafd"}, // source -> book -> account -> book -> destination + {.cost = 9, .path = "sbfad"}, // source -> book -> book -> account -> destination + {.cost = 10, .path = "sbafad"}}); fillPaths( PaymentType::NonXrpToXrp, - {{1, "sxd"}, // gateway buys XRP - {2, "saxd"}, // source -> gateway -> book(XRP) -> dest - {6, "saaxd"}, - {7, "sbxd"}, - {8, "sabxd"}, - {9, "sabaxd"}}); + {{.cost = 1, .path = "sxd"}, // gateway buys XRP + {.cost = 2, .path = "saxd"}, // source -> gateway -> book(XRP) -> dest + {.cost = 6, .path = "saaxd"}, + {.cost = 7, .path = "sbxd"}, + {.cost = 8, .path = "sabxd"}, + {.cost = 9, .path = "sabaxd"}}); // non-XRP to non-XRP (same currency) fillPaths( PaymentType::NonXrpToSame, { - {1, "sad"}, // source -> gateway -> destination - {1, "sfd"}, // source -> book -> destination - {4, "safd"}, // source -> gateway -> book -> destination - {4, "sfad"}, - {5, "saad"}, - {5, "sbfd"}, - {6, "sxfad"}, - {6, "safad"}, - {6, "saxfd"}, // source -> gateway -> book to XRP -> book -> - // destination - {6, "saxfad"}, - {6, "sabfd"}, // source -> gateway -> book -> book -> destination - {7, "saaad"}, + {.cost = 1, .path = "sad"}, // source -> gateway -> destination + {.cost = 1, .path = "sfd"}, // source -> book -> destination + {.cost = 4, .path = "safd"}, // source -> gateway -> book -> destination + {.cost = 4, .path = "sfad"}, + {.cost = 5, .path = "saad"}, + {.cost = 5, .path = "sbfd"}, + {.cost = 6, .path = "sxfad"}, + {.cost = 6, .path = "safad"}, + {.cost = 6, .path = "saxfd"}, // source -> gateway -> book to XRP -> book -> + // destination + {.cost = 6, .path = "saxfad"}, + {.cost = 6, .path = "sabfd"}, // source -> gateway -> book -> book -> destination + {.cost = 7, .path = "saaad"}, }); // non-XRP to non-XRP (different currency) fillPaths( PaymentType::NonXrpToNonXrp, { - {1, "sfad"}, - {1, "safd"}, - {3, "safad"}, - {4, "sxfd"}, - {5, "saxfd"}, - {5, "sxfad"}, - {5, "sbfd"}, - {6, "saxfad"}, - {6, "sabfd"}, - {7, "saafd"}, - {8, "saafad"}, - {9, "safaad"}, + {.cost = 1, .path = "sfad"}, + {.cost = 1, .path = "safd"}, + {.cost = 3, .path = "safad"}, + {.cost = 4, .path = "sxfd"}, + {.cost = 5, .path = "saxfd"}, + {.cost = 5, .path = "sxfad"}, + {.cost = 5, .path = "sbfd"}, + {.cost = 6, .path = "saxfad"}, + {.cost = 6, .path = "sabfd"}, + {.cost = 7, .path = "saafd"}, + {.cost = 8, .path = "saafad"}, + {.cost = 9, .path = "safaad"}, }); /* cspell: enable */ } diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index 2ed6630af22..ea9a574e345 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -165,10 +165,10 @@ ServerHandler::setup(Setup const& setup, beast::Journal journal) port.port = endpointPort; if ((setup_.client.port == 0u) && - (port.protocol.count("http") > 0 || port.protocol.count("https") > 0)) + (port.protocol.contains("http") || port.protocol.contains("https"))) setup_.client.port = endpointPort; - if ((setup_.overlay.port() == 0u) && (port.protocol.count("peer") > 0)) + if ((setup_.overlay.port() == 0u) && (port.protocol.contains("peer"))) setup_.overlay.port(endpointPort); } } @@ -217,7 +217,7 @@ ServerHandler::onHandoff( using namespace boost::beast; auto const& p{session.port().protocol}; bool const isWs{ - p.count("ws") > 0 || p.count("ws2") > 0 || p.count("wss") > 0 || p.count("wss2") > 0}; + p.contains("ws") || p.contains("ws2") || p.contains("wss") || p.contains("wss2")}; if (websocket::is_upgrade(request)) { @@ -251,7 +251,7 @@ ServerHandler::onHandoff( return handoff; } - if (bundle && p.count("peer") > 0) + if (bundle && p.contains("peer")) return app_.getOverlay().onHandoff(std::move(bundle), std::move(request), remoteAddress); if (isWs && isStatusRequest(request)) @@ -301,7 +301,7 @@ void ServerHandler::onRequest(Session& session) { // Make sure RPC is enabled on the port - if (session.port().protocol.count("http") == 0 && session.port().protocol.count("https") == 0) + if (!session.port().protocol.contains("http") && !session.port().protocol.contains("https")) { httpReply(403, "Forbidden", makeOutput(session), app_.getJournal("RPC")); session.close(true); @@ -1180,7 +1180,7 @@ parsePorts(Config const& config, std::ostream& log) else { auto const count = std::count_if(result.cbegin(), result.cend(), [](Port const& p) { - return p.protocol.count("peer") != 0; + return p.protocol.contains("peer"); }); if (count > 1) @@ -1203,12 +1203,12 @@ setupClient(ServerHandler::Setup& setup) decltype(setup.ports)::const_iterator iter; for (iter = setup.ports.cbegin(); iter != setup.ports.cend(); ++iter) { - if (iter->protocol.count("http") > 0 || iter->protocol.count("https") > 0) + if (iter->protocol.contains("http") || iter->protocol.contains("https")) break; } if (iter == setup.ports.cend()) return; - setup.client.secure = iter->protocol.count("https") > 0; + setup.client.secure = iter->protocol.contains("https"); if (beast::IP::isUnspecified(iter->ip)) { // VFALCO HACK! to make localhost work @@ -1230,7 +1230,7 @@ static void setupOverlay(ServerHandler::Setup& setup) { auto const iter = std::ranges::find_if( - setup.ports, [](Port const& port) { return port.protocol.count("peer") != 0; }); + setup.ports, [](Port const& port) { return port.protocol.contains("peer"); }); if (iter == setup.ports.cend()) { setup.overlay = {}; From 595b612589a2a967017996a2923667a438979c73 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Tue, 26 May 2026 17:17:40 -0400 Subject: [PATCH 242/249] Merge fixes --- cspell.config.yaml | 2 +- src/libxrpl/tx/Transactor.cpp | 6 ++++-- src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 2 +- src/libxrpl/tx/transactors/token/TrustSet.cpp | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cspell.config.yaml b/cspell.config.yaml index c75f901c018..bf21bc2a4ce 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -261,9 +261,9 @@ words: - sles - soci - socidb - - SRPMS - sponsee - sponsees + - SRPMS - sslws - statsd - STATSDCOLLECTOR diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index af003e6e6dc..d69f001cf28 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -386,10 +386,12 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx) auto const sponsorFlags = tx.getFieldU32(sfSponsorFlags); - if (tx.isFlag(spfSponsorFee) && sponsorshipSle->isFlag(lsfSponsorshipRequireSignForFee)) + if (((sponsorFlags & spfSponsorFee) != 0u) && + sponsorshipSle->isFlag(lsfSponsorshipRequireSignForFee)) return terNO_SPONSORSHIP; - if (tx.isFlag(spfSponsorReserve) && sponsorshipSle->isFlag(lsfSponsorshipRequireSignForReserve)) + if (((sponsorFlags & spfSponsorReserve) != 0u) && + sponsorshipSle->isFlag(lsfSponsorshipRequireSignForReserve)) return terNO_SPONSORSHIP; return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index d9aaaaf45e1..8ae68182e0a 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -172,7 +172,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx) { STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j); // Insufficient reserve - if (xrpBalance <= beast::kZERO) + if (xrpBalance <= beast::kZero) { JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves"; return tecINSUF_RESERVE_LINE; diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index 6d75eeb2f4a..d609033cb96 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -144,7 +144,7 @@ TrustSet::checkPermission(ReadView const& view, STTx const& tx) // Currently we only support TrustlineAuthorize, TrustlineFreeze and // TrustlineUnfreeze granular permission. Setting other flags returns // error. - if (tx.isFlag(tfTrustSetPermissionMask)) + if ((tx.getFlags() & tfTrustSetPermissionMask) != 0u) return terNO_DELEGATE_PERMISSION; if (tx.isFieldPresent(sfQualityIn) || tx.isFieldPresent(sfQualityOut)) From 03fb6d1e0c0ac1943cd1cbb605278991f3237adf Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:29:55 -0400 Subject: [PATCH 243/249] Codegen update --- .../ledger_entries/AccountRoot.h | 12 ++--- .../ledger_entries/RippleState.h | 8 ++-- .../ledger_entries/Sponsorship.h | 38 +++++++-------- .../transactions/SponsorshipSet.h | 26 +++++------ .../transactions/SponsorshipTransfer.h | 14 +++--- .../transactions/SponsorshipSetTests.cpp | 46 +++++++++++++------ .../transactions/SponsorshipTransferTests.cpp | 40 +++++++++++----- 7 files changed, 109 insertions(+), 75 deletions(-) diff --git a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h index 01b96331fb3..099fa5dc360 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h +++ b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h @@ -520,7 +520,7 @@ class AccountRoot : public LedgerEntryBase } /** - * @brief Get sfSponsoredOwnerCount (soeDEFAULT) + * @brief Get sfSponsoredOwnerCount (SoeDefault) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -544,7 +544,7 @@ class AccountRoot : public LedgerEntryBase } /** - * @brief Get sfSponsoringOwnerCount (soeDEFAULT) + * @brief Get sfSponsoringOwnerCount (SoeDefault) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -568,7 +568,7 @@ class AccountRoot : public LedgerEntryBase } /** - * @brief Get sfSponsoringAccountCount (soeDEFAULT) + * @brief Get sfSponsoringAccountCount (SoeDefault) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -892,7 +892,7 @@ class AccountRootBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfSponsoredOwnerCount (soeDEFAULT) + * @brief Set sfSponsoredOwnerCount (SoeDefault) * @return Reference to this builder for method chaining. */ AccountRootBuilder& @@ -903,7 +903,7 @@ class AccountRootBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfSponsoringOwnerCount (soeDEFAULT) + * @brief Set sfSponsoringOwnerCount (SoeDefault) * @return Reference to this builder for method chaining. */ AccountRootBuilder& @@ -914,7 +914,7 @@ class AccountRootBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfSponsoringAccountCount (soeDEFAULT) + * @brief Set sfSponsoringAccountCount (SoeDefault) * @return Reference to this builder for method chaining. */ AccountRootBuilder& diff --git a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h index a19c30ed230..73ea53f3d62 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h +++ b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h @@ -245,7 +245,7 @@ class RippleState : public LedgerEntryBase } /** - * @brief Get sfHighSponsor (soeOPTIONAL) + * @brief Get sfHighSponsor (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -269,7 +269,7 @@ class RippleState : public LedgerEntryBase } /** - * @brief Get sfLowSponsor (soeOPTIONAL) + * @brief Get sfLowSponsor (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -459,7 +459,7 @@ class RippleStateBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfHighSponsor (soeOPTIONAL) + * @brief Set sfHighSponsor (SoeOptional) * @return Reference to this builder for method chaining. */ RippleStateBuilder& @@ -470,7 +470,7 @@ class RippleStateBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfLowSponsor (soeOPTIONAL) + * @brief Set sfLowSponsor (SoeOptional) * @return Reference to this builder for method chaining. */ RippleStateBuilder& diff --git a/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h b/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h index fdc13e89a1c..81b75380123 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h @@ -46,7 +46,7 @@ class Sponsorship : public LedgerEntryBase // Ledger entry-specific field getters /** - * @brief Get sfPreviousTxnID (soeREQUIRED) + * @brief Get sfPreviousTxnID (SoeRequired) * @return The field value. */ [[nodiscard]] @@ -57,7 +57,7 @@ class Sponsorship : public LedgerEntryBase } /** - * @brief Get sfPreviousTxnLgrSeq (soeREQUIRED) + * @brief Get sfPreviousTxnLgrSeq (SoeRequired) * @return The field value. */ [[nodiscard]] @@ -68,7 +68,7 @@ class Sponsorship : public LedgerEntryBase } /** - * @brief Get sfOwner (soeREQUIRED) + * @brief Get sfOwner (SoeRequired) * @return The field value. */ [[nodiscard]] @@ -79,7 +79,7 @@ class Sponsorship : public LedgerEntryBase } /** - * @brief Get sfSponsee (soeREQUIRED) + * @brief Get sfSponsee (SoeRequired) * @return The field value. */ [[nodiscard]] @@ -90,7 +90,7 @@ class Sponsorship : public LedgerEntryBase } /** - * @brief Get sfFeeAmount (soeOPTIONAL) + * @brief Get sfFeeAmount (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -114,7 +114,7 @@ class Sponsorship : public LedgerEntryBase } /** - * @brief Get sfMaxFee (soeOPTIONAL) + * @brief Get sfMaxFee (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -138,7 +138,7 @@ class Sponsorship : public LedgerEntryBase } /** - * @brief Get sfReserveCount (soeDEFAULT) + * @brief Get sfReserveCount (SoeDefault) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -162,7 +162,7 @@ class Sponsorship : public LedgerEntryBase } /** - * @brief Get sfOwnerNode (soeREQUIRED) + * @brief Get sfOwnerNode (SoeRequired) * @return The field value. */ [[nodiscard]] @@ -173,7 +173,7 @@ class Sponsorship : public LedgerEntryBase } /** - * @brief Get sfSponseeNode (soeREQUIRED) + * @brief Get sfSponseeNode (SoeRequired) * @return The field value. */ [[nodiscard]] @@ -188,7 +188,7 @@ class Sponsorship : public LedgerEntryBase * @brief Builder for Sponsorship ledger entries. * * Provides a fluent interface for constructing ledger entries with method chaining. - * Uses Json::Value internally for flexible ledger entry construction. + * Uses STObject internally for flexible ledger entry construction. * Inherits common field setters from LedgerEntryBuilderBase. */ class SponsorshipBuilder : public LedgerEntryBuilderBase @@ -231,7 +231,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase /** @brief Ledger entry-specific field setters */ /** - * @brief Set sfPreviousTxnID (soeREQUIRED) + * @brief Set sfPreviousTxnID (SoeRequired) * @return Reference to this builder for method chaining. */ SponsorshipBuilder& @@ -242,7 +242,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfPreviousTxnLgrSeq (soeREQUIRED) + * @brief Set sfPreviousTxnLgrSeq (SoeRequired) * @return Reference to this builder for method chaining. */ SponsorshipBuilder& @@ -253,7 +253,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfOwner (soeREQUIRED) + * @brief Set sfOwner (SoeRequired) * @return Reference to this builder for method chaining. */ SponsorshipBuilder& @@ -264,7 +264,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfSponsee (soeREQUIRED) + * @brief Set sfSponsee (SoeRequired) * @return Reference to this builder for method chaining. */ SponsorshipBuilder& @@ -275,7 +275,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfFeeAmount (soeOPTIONAL) + * @brief Set sfFeeAmount (SoeOptional) * @return Reference to this builder for method chaining. */ SponsorshipBuilder& @@ -286,7 +286,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfMaxFee (soeOPTIONAL) + * @brief Set sfMaxFee (SoeOptional) * @return Reference to this builder for method chaining. */ SponsorshipBuilder& @@ -297,7 +297,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfReserveCount (soeDEFAULT) + * @brief Set sfReserveCount (SoeDefault) * @return Reference to this builder for method chaining. */ SponsorshipBuilder& @@ -308,7 +308,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfOwnerNode (soeREQUIRED) + * @brief Set sfOwnerNode (SoeRequired) * @return Reference to this builder for method chaining. */ SponsorshipBuilder& @@ -319,7 +319,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase } /** - * @brief Set sfSponseeNode (soeREQUIRED) + * @brief Set sfSponseeNode (SoeRequired) * @return Reference to this builder for method chaining. */ SponsorshipBuilder& diff --git a/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h b/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h index c89392a54f9..0b64104e618 100644 --- a/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h +++ b/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h @@ -19,9 +19,9 @@ class SponsorshipSetBuilder; * @brief Transaction: SponsorshipSet * * Type: ttSPONSORSHIP_SET (86) - * Delegable: Delegation::delegable + * Delegable: Delegation::Delegable * Amendment: featureSponsor - * Privileges: noPriv + * Privileges: NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use SponsorshipSetBuilder to construct new transactions. @@ -48,7 +48,7 @@ class SponsorshipSet : public TransactionBase // Transaction-specific field getters /** - * @brief Get sfCounterpartySponsor (soeOPTIONAL) + * @brief Get sfCounterpartySponsor (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -74,7 +74,7 @@ class SponsorshipSet : public TransactionBase } /** - * @brief Get sfSponsee (soeOPTIONAL) + * @brief Get sfSponsee (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -100,7 +100,7 @@ class SponsorshipSet : public TransactionBase } /** - * @brief Get sfFeeAmount (soeOPTIONAL) + * @brief Get sfFeeAmount (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -126,7 +126,7 @@ class SponsorshipSet : public TransactionBase } /** - * @brief Get sfMaxFee (soeOPTIONAL) + * @brief Get sfMaxFee (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -152,7 +152,7 @@ class SponsorshipSet : public TransactionBase } /** - * @brief Get sfReserveCount (soeOPTIONAL) + * @brief Get sfReserveCount (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] @@ -182,7 +182,7 @@ class SponsorshipSet : public TransactionBase * @brief Builder for SponsorshipSet transactions. * * Provides a fluent interface for constructing transactions with method chaining. - * Uses Json::Value internally for flexible transaction construction. + * Uses STObject internally for flexible transaction construction. * Inherits common field setters from TransactionBuilderBase. */ class SponsorshipSetBuilder : public TransactionBuilderBase @@ -219,7 +219,7 @@ class SponsorshipSetBuilder : public TransactionBuilderBase @@ -141,7 +141,7 @@ class SponsorshipTransferBuilder : public TransactionBuilderBase -#include -#include -#include -#include #include + #include +#include +#include +#include +#include +#include + #include namespace xrpl::transactions { @@ -33,7 +35,11 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip) auto const maxFeeValue = canonical_AMOUNT(); auto const reserveCountValue = canonical_UINT32(); - SponsorshipSetBuilder builder{accountValue, sequenceValue, feeValue}; + SponsorshipSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; // Set optional fields builder.setCounterpartySponsor(counterpartySponsorValue); @@ -61,8 +67,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip) { auto const& expected = counterpartySponsorValue; auto const actualOpt = tx.getCounterpartySponsor(); - ASSERT_TRUE(actualOpt.has_value()) - << "Optional field sfCounterpartySponsor should be present"; + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySponsor should be present"; expectEqualField(expected, *actualOpt, "sfCounterpartySponsor"); EXPECT_TRUE(tx.hasCounterpartySponsor()); } @@ -98,6 +103,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip) expectEqualField(expected, *actualOpt, "sfReserveCount"); EXPECT_TRUE(tx.hasReserveCount()); } + } // 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, @@ -121,7 +127,11 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip) auto const reserveCountValue = canonical_UINT32(); // Build an initial transaction - SponsorshipSetBuilder initialBuilder{accountValue, sequenceValue, feeValue}; + SponsorshipSetBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; initialBuilder.setCounterpartySponsor(counterpartySponsorValue); initialBuilder.setSponsee(sponseeValue); @@ -149,8 +159,7 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip) { auto const& expected = counterpartySponsorValue; auto const actualOpt = rebuiltTx.getCounterpartySponsor(); - ASSERT_TRUE(actualOpt.has_value()) - << "Optional field sfCounterpartySponsor should be present"; + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySponsor should be present"; expectEqualField(expected, *actualOpt, "sfCounterpartySponsor"); } @@ -181,13 +190,15 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip) ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfReserveCount should be present"; expectEqualField(expected, *actualOpt, "sfReserveCount"); } + } // 3) Verify wrapper throws when constructed from wrong transaction type. TEST(TransactionsSponsorshipSetTests, WrapperThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongType")); + auto const [pk, sk] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongType")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -200,7 +211,8 @@ TEST(TransactionsSponsorshipSetTests, WrapperThrowsOnWrongTxType) TEST(TransactionsSponsorshipSetTests, BuilderThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongTypeBuilder")); + auto const [pk, sk] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongTypeBuilder")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -223,7 +235,11 @@ TEST(TransactionsSponsorshipSetTests, OptionalFieldsReturnNullopt) // Transaction-specific required field values - SponsorshipSetBuilder builder{accountValue, sequenceValue, feeValue}; + SponsorshipSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; // Do NOT set optional fields @@ -242,4 +258,4 @@ TEST(TransactionsSponsorshipSetTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(tx.getReserveCount().has_value()); } -} // namespace xrpl::transactions +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp index 88f3ab3a606..c8521b7b20c 100644 --- a/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp @@ -1,14 +1,16 @@ // Auto-generated unit tests for transaction SponsorshipTransfer -#include -#include -#include -#include -#include #include + #include +#include +#include +#include +#include +#include + #include namespace xrpl::transactions { @@ -30,7 +32,11 @@ TEST(TransactionsSponsorshipTransferTests, BuilderSettersRoundTrip) auto const objectIDValue = canonical_UINT256(); auto const sponseeValue = canonical_ACCOUNT(); - SponsorshipTransferBuilder builder{accountValue, sequenceValue, feeValue}; + SponsorshipTransferBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; // Set optional fields builder.setObjectID(objectIDValue); @@ -67,6 +73,7 @@ TEST(TransactionsSponsorshipTransferTests, BuilderSettersRoundTrip) expectEqualField(expected, *actualOpt, "sfSponsee"); EXPECT_TRUE(tx.hasSponsee()); } + } // 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, @@ -87,7 +94,11 @@ TEST(TransactionsSponsorshipTransferTests, BuilderFromStTxRoundTrip) auto const sponseeValue = canonical_ACCOUNT(); // Build an initial transaction - SponsorshipTransferBuilder initialBuilder{accountValue, sequenceValue, feeValue}; + SponsorshipTransferBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; initialBuilder.setObjectID(objectIDValue); initialBuilder.setSponsee(sponseeValue); @@ -122,13 +133,15 @@ TEST(TransactionsSponsorshipTransferTests, BuilderFromStTxRoundTrip) ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSponsee should be present"; expectEqualField(expected, *actualOpt, "sfSponsee"); } + } // 3) Verify wrapper throws when constructed from wrong transaction type. TEST(TransactionsSponsorshipTransferTests, WrapperThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongType")); + auto const [pk, sk] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongType")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -141,7 +154,8 @@ TEST(TransactionsSponsorshipTransferTests, WrapperThrowsOnWrongTxType) TEST(TransactionsSponsorshipTransferTests, BuilderThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongTypeBuilder")); + auto const [pk, sk] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongTypeBuilder")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -164,7 +178,11 @@ TEST(TransactionsSponsorshipTransferTests, OptionalFieldsReturnNullopt) // Transaction-specific required field values - SponsorshipTransferBuilder builder{accountValue, sequenceValue, feeValue}; + SponsorshipTransferBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; // Do NOT set optional fields @@ -177,4 +195,4 @@ TEST(TransactionsSponsorshipTransferTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(tx.getSponsee().has_value()); } -} // namespace xrpl::transactions +} From 942a950e9392f1de1a31cd4862578be95f141d16 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:48:09 -0400 Subject: [PATCH 244/249] Codegen update --- include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h b/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h index 81b75380123..96b46a6aab3 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h @@ -33,7 +33,7 @@ class Sponsorship : public LedgerEntryBase * @brief Construct a Sponsorship ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Sponsorship(std::shared_ptr sle) + explicit Sponsorship(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -219,7 +219,7 @@ class SponsorshipBuilder : public LedgerEntryBuilderBase * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - SponsorshipBuilder(std::shared_ptr sle) + SponsorshipBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltSPONSORSHIP) { From 64159eb04046a70f354ba73f4f43869d1007424f Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:40:10 -0400 Subject: [PATCH 245/249] Revert clang-tidy --- src/libxrpl/basics/Number.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index c8290fa7366..275d82d8c9d 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -1241,10 +1241,9 @@ root(Number f, unsigned d) } // Quadratic least squares curve fit of f^(1/d) in the range [0, 1] - auto const D = - (((((6 * di) + 11) * di) + 6) * di) + 1; // NOLINT(readability-identifier-naming) - auto const a0 = 3 * di * ((((2 * di) - 3) * di) + 1); - auto const a1 = 24 * di * ((2 * di) - 1); + auto const D = (((6 * di + 11) * di + 6) * di) + 1; // NOLINT(readability-identifier-naming) + auto const a0 = 3 * di * ((2 * di - 3) * di + 1); + auto const a1 = 24 * di * (2 * di - 1); auto const a2 = -30 * (di - 1) * di; Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D}; if (neg) From e52113956e5775176272a3c25e6c704ecc1f8e63 Mon Sep 17 00:00:00 2001 From: Oleksandr <115580134+oleks-rip@users.noreply.github.com> Date: Mon, 15 Jun 2026 18:21:33 -0400 Subject: [PATCH 246/249] clang-tidy --- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index d807518fad5..c77f49c8649 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include From 14986cc043469ae7c596eab263cb9b97cb7b5001 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Wed, 17 Jun 2026 14:08:06 -0400 Subject: [PATCH 247/249] Sponsorship should be non-obligated for sponsee (#7552) --- .../tx/transactors/sponsor/SponsorshipSet.h | 3 + .../tx/transactors/Sponsor/SponsorshipSet.cpp | 68 ++++++++++------ .../tx/transactors/account/AccountDelete.cpp | 31 +++++-- src/test/app/Sponsor_test.cpp | 80 +++++++++++++++++-- 4 files changed, 145 insertions(+), 37 deletions(-) diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h index d7e78da2ca7..3eaeba6392a 100644 --- a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h @@ -25,6 +25,9 @@ class SponsorshipSet : public Transactor static TER preclaim(PreclaimContext const& ctx); + static TER + deleteSponsorship(ApplyView& view, SLE::ref sle, beast::Journal j); + TER doApply() override; diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index 07dd8628f39..8529ebd59bc 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -1,7 +1,10 @@ #include +#include +#include #include #include +#include #include #include #include @@ -175,6 +178,47 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } +TER +SponsorshipSet::deleteSponsorship(ApplyView& view, SLE::ref sle, beast::Journal j) +{ + if (!sle) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const sponsorAccountID = (*sle)[sfOwner]; + auto const sponseeAccountID = (*sle)[sfSponsee]; + + // The reserve for the Sponsorship object is held by the sponsor (Owner). + auto sponsorAccSle = view.peek(keylet::account(sponsorAccountID)); + if (!sponsorAccSle) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (!view.dirRemove(keylet::ownerDir(sponsorAccountID), (*sle)[sfOwnerNode], sle->key(), false)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Unable to delete Sponsorship from sponsor."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + if (!view.dirRemove( + keylet::ownerDir(sponseeAccountID), (*sle)[sfSponseeNode], sle->key(), false)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Unable to delete Sponsorship from sponsee."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + + adjustOwnerCountObj(view, sponsorAccSle, sle, -1, j); + + // transfer feeAmount back to the sponsor + if (sle->isFieldPresent(sfFeeAmount)) + (*sponsorAccSle)[sfBalance] += sle->getFieldAmount(sfFeeAmount); + + view.erase(sle); + + return tesSUCCESS; +} + TER SponsorshipSet::doApply() { @@ -200,29 +244,7 @@ SponsorshipSet::doApply() if (!sponsorObjSle) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCountObj(ctx_.view(), sponsorAccSle, sponsorObjSle, -1, ctx_.journal); - - ctx_.view().dirRemove( - keylet::ownerDir(sponsorAccountID), - (*sponsorObjSle)[sfOwnerNode], - sponsorObjSle->key(), - false); - ctx_.view().dirRemove( - keylet::ownerDir(sponseeAccountID), - (*sponsorObjSle)[sfSponseeNode], - sponsorObjSle->key(), - false); - - // transfer feeAmount from ledger entry - if (sponsorObjSle->isFieldPresent(sfFeeAmount)) - { - auto const feeAmount = sponsorObjSle->getFieldAmount(sfFeeAmount); - (*sponsorAccSle)[sfBalance] += feeAmount; - } - - ctx_.view().erase(sponsorObjSle); - - return tesSUCCESS; + return deleteSponsorship(ctx_.view(), sponsorObjSle, ctx_.journal); } auto const feeAmount = ctx_.tx[~sfFeeAmount]; diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index 231783b2cc0..f3d8b4a5c6c 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -185,11 +186,23 @@ removeDelegateFromLedger( return DelegateSet::deleteDelegate(view, sleDel, j); } -// Return nullptr if the LedgerEntryType represents an obligation that can't -// be deleted. Otherwise return the pointer to the function that can delete -// the non-obligation +TER +removeSponsorshipFromLedger( + ServiceRegistry&, + ApplyView& view, + AccountID const&, + uint256 const&, + SLE::ref sleDel, + beast::Journal j) +{ + return SponsorshipSet::deleteSponsorship(view, sleDel, j); +} + +// Return nullptr if the object represents an obligation that can't be deleted +// during deletion of account. Otherwise return the pointer to the function +// that can delete the non-obligation. DeleterFuncPtr -nonObligationDeleter(LedgerEntryType t) +nonObligationDeleter(LedgerEntryType t, SLE::const_ref sleItem, AccountID const& account) { switch (t) { @@ -211,6 +224,12 @@ nonObligationDeleter(LedgerEntryType t) return removeCredentialFromLedger; case ltDELEGATE: return removeDelegateFromLedger; + case ltSPONSORSHIP: + // A Sponsorship lives in both the sponsor's (Owner) and the + // sponsee's owner directories, but it is an obligation only for the + // sponsor, who holds its reserve. The sponsee must remain free to + // delete its account. + return (*sleItem)[sfOwner] == account ? nullptr : removeSponsorshipFromLedger; default: return nullptr; } @@ -335,7 +354,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx) LedgerEntryType const nodeType{safeCast((*sleItem)[sfLedgerEntryType])}; - if (nonObligationDeleter(nodeType) == nullptr) + if (nonObligationDeleter(nodeType, sleItem, account) == nullptr) return tecHAS_OBLIGATIONS; // We found a deletable directory entry. Count it. If we find too @@ -376,7 +395,7 @@ AccountDelete::doApply() [&](LedgerEntryType nodeType, uint256 const& dirEntry, SLE::pointer& sleItem) -> std::pair { - if (auto deleter = nonObligationDeleter(nodeType)) + if (auto deleter = nonObligationDeleter(nodeType, sleItem, accountID_)) { TER const result{deleter(ctx_.registry, view(), accountID_, dirEntry, sleItem, j_)}; diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index c5399c71cae..d3f94c2ccb6 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -5456,7 +5456,8 @@ class Sponsor_test : public beast::unit_test::Suite Account const sponsor("sponsor"); { - // Delete Sponsor/Sponsee Account with ltSponsorship (tecHAS_OBLIGATIONS) + // A Sponsorship object blocks deletion of the sponsor, + // but NOT of the sponsee. Env env{*this, testableAmendments()}; env.fund(XRP(1000000), alice, bob, sponsor); env.close(); @@ -5467,20 +5468,83 @@ class Sponsor_test : public beast::unit_test::Suite Ter(tesSUCCESS)); env.close(); - incLgrSeqForAccDel(env, sponsor); - auto const keylet = keylet::sponsor(sponsor, alice); - auto const sponsorObj = env.le(keylet); - BEAST_EXPECT(sponsorObj); + BEAST_EXPECT(env.le(keylet)); + // sponsor pays its own reserve here, so there is no SponsoredOwnerCount. + BEAST_EXPECT(ownerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - // AccountDelete + // sponsor's sequence is the higher one, so a single call readies + // both accounts for deletion + incLgrSeqForAccDel(env, sponsor); auto const requiredFee = drops(env.current()->fees().increment); - env(acctdelete(alice, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); + + // sponsor cannot be deleted while the Sponsorship exists env(acctdelete(sponsor, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); + env.close(); + + // sponsee can be deleted + auto const sponsorBalBefore = env.balance(sponsor); + env(acctdelete(alice, bob), Fee(requiredFee), Ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(!env.le(keylet)); + BEAST_EXPECT(!env.le(keylet::account(alice))); + // deleting sponsee makes those counts zero. + BEAST_EXPECT(ownerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // FeeAmount is returned to the sponsor + BEAST_EXPECT(env.balance(sponsor) == sponsorBalBefore + XRP(100)); + } + + { + // The SponsorshipSet transaction itself + // is sponsored by a 3rd counterparty. Test deleting sponsee. + Env env{*this, testableAmendments()}; + Account const counterparty("counterparty"); + env.fund(XRP(1000000), alice, bob, sponsor, counterparty); + env.close(); + + // sponsor creates a Sponsorship for alice; the SponsorshipSet tx is + // itself reserve-sponsored by counterparty, so the object's reserve is counterparty's. + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::SponseeAcc(alice), + sponsor::As(counterparty, spfSponsorReserve), + Sig(sfSponsorSignature, counterparty), + Ter(tesSUCCESS)); + env.close(); + + auto const keylet = keylet::sponsor(sponsor, alice); + auto const obj = env.le(keylet); + BEAST_EXPECT(obj); + BEAST_EXPECT( + obj->isFieldPresent(sfSponsor) && + obj->getAccountID(sfSponsor) == counterparty.id()); + // sponsor owns the object; its reserve is sponsored by counterparty. + BEAST_EXPECT(ownerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, counterparty) == 1); + + auto const sponsorBalBefore = env.balance(sponsor); + incLgrSeqForAccDel(env, alice); + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(alice, bob), Fee(requiredFee), Ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(!env.le(keylet)); + // deleting sponsee makes those counts zero. + BEAST_EXPECT(ownerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, counterparty) == 0); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalBefore + XRP(100)); } { - // Delete SponsoredAccount + // Deleting SponsoredAccount, whose account reserve is paid by a sponsor. Env env{*this, testableAmendments()}; env.memoize(alice); env.fund(XRP(1000000), bob, sponsor); From aac6be218ac760bb437927c2b7e19a3777b5084b Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Wed, 17 Jun 2026 17:39:12 -0400 Subject: [PATCH 248/249] fix new CI rule switch fallthrough (#7568) --- src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 79995c07a99..1d352d3edc6 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -221,6 +221,7 @@ getLedgerEntrySponsorField(T const& sle, AccountID const& owner) } // LCOV_EXCL_START UNREACHABLE("Should not happen. Owner should be checked before calling this function."); + return sfSponsor; // LCOV_EXCL_STOP } default: From c2ab83c96079713c93f73998ca95d691520bc73c Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 17 Jun 2026 17:50:17 -0400 Subject: [PATCH 249/249] fix: Fix UBSan issue (#7554) --- .../Sponsor/SponsorshipTransfer.cpp | 84 +++++++++++++------ 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index 1d352d3edc6..24eb54e632a 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -20,11 +20,21 @@ #include #include +#include #include #include namespace xrpl { +static std::optional +applyCountDelta(std::uint32_t current, std::int64_t delta) +{ + std::int64_t const next = static_cast(current) + delta; + if (next < 0 || next > std::numeric_limits::max()) + return std::nullopt; + return static_cast(next); +} + std::uint32_t SponsorshipTransfer::getFlagsMask(PreflightContext const& ctx) { @@ -363,12 +373,12 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } -TER +static TER reduceReserveCount( ApplyView& view, AccountID const& account, AccountID const& sponsor, - int32_t delta) + int64_t delta) { if (delta == 0) return tesSUCCESS; @@ -380,16 +390,15 @@ reduceReserveCount( if (!sponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE - auto const reserveCount = sponsorSle->getFieldU32(sfReserveCount); - int32_t const afterReserveCount = reserveCount + delta; - - if (afterReserveCount < 0) + auto const afterReserveCount = applyCountDelta(sponsorSle->getFieldU32(sfReserveCount), delta); + if (!afterReserveCount) { // already checked in preclaim() + UNREACHABLE("xrpl::reduceReserveCount : invalid reserve count"); return tefINTERNAL; // LCOV_EXCL_LINE } - sponsorSle->at(sfReserveCount) = static_cast(afterReserveCount); + sponsorSle->at(sfReserveCount) = *afterReserveCount; view.update(sponsorSle); return tesSUCCESS; } @@ -407,16 +416,17 @@ SponsorshipTransfer::doApply() if (!sponseeSle) return tefINTERNAL; // LCOV_EXCL_LINE - auto const setSponsorFieldU32 = [](auto const& sle, auto const& field, auto const& delta) { - int32_t const newValue = static_cast(sle->getFieldU32(field)) + delta; - - if (newValue < 0) + auto const setSponsorFieldU32 = + [] [[nodiscard]] (auto const& sle, auto const& field, auto const& delta) -> TER { + auto const newValue = applyCountDelta(sle->getFieldU32(field), delta); + if (!newValue) { UNREACHABLE("xrpl::SponsorshipTransfer::doApply : Invalid sponsor field value"); - return; + return tecINTERNAL; // LCOV_EXCL_LINE } - sle->at(field) = static_cast(newValue); + sle->at(field) = *newValue; + return tesSUCCESS; }; if (isObjectSponsor) @@ -436,7 +446,7 @@ SponsorshipTransfer::doApply() if (!ownerSle) return tefINTERNAL; // LCOV_EXCL_LINE - auto const ownerCountDelta = getLedgerEntryOwnerCount(objSle); + std::int64_t const ownerCountDelta = getLedgerEntryOwnerCount(objSle); auto const& sponsorField = getLedgerEntrySponsorField(objSle, *ownerAccountID); @@ -446,14 +456,20 @@ SponsorshipTransfer::doApply() XRPL_ASSERT(!!newSponsorAccountID, "New sponsor is required when creating sponsorship"); // update owner's sponsored count - setSponsorFieldU32(ownerSle, sfSponsoredOwnerCount, ownerCountDelta); + if (auto const ter = + setSponsorFieldU32(ownerSle, sfSponsoredOwnerCount, ownerCountDelta); + !isTesSuccess(ter)) + return ter; view().update(ownerSle); // increment new sponsor's sponsoring count auto const newSponsorSle = view().peek(keylet::account(newSponsorAccountID)); if (!newSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE - setSponsorFieldU32(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); + if (auto const ter = + setSponsorFieldU32(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); + !isTesSuccess(ter)) + return ter; view().update(newSponsorSle); // set new sponsor to object @@ -483,14 +499,20 @@ SponsorshipTransfer::doApply() auto const oldSponsorSle = view().peek(keylet::account(oldSponsorAccountID)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE - setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); + if (auto const ter = + setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); + !isTesSuccess(ter)) + return ter; view().update(oldSponsorSle); // increment new sponsor's sponsoring count auto const newSponsorSle = view().peek(keylet::account(newSponsorAccountID)); if (!newSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE - setSponsorFieldU32(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); + if (auto const ter = + setSponsorFieldU32(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); + !isTesSuccess(ter)) + return ter; view().update(newSponsorSle); // set new sponsor to object @@ -516,11 +538,17 @@ SponsorshipTransfer::doApply() return tefINTERNAL; // LCOV_EXCL_LINE // decrement sponsored count - setSponsorFieldU32(sponseeSle, sfSponsoredOwnerCount, -ownerCountDelta); + if (auto const ter = + setSponsorFieldU32(sponseeSle, sfSponsoredOwnerCount, -ownerCountDelta); + !isTesSuccess(ter)) + return ter; view().update(sponseeSle); // decrement old sponsoring count - setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); + if (auto const ter = + setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); + !isTesSuccess(ter)) + return ter; view().update(oldSponsorSle); // remove sponsor from object @@ -538,7 +566,9 @@ SponsorshipTransfer::doApply() auto const newSponsorSle = view().peek(keylet::account(newSponsorAccountID)); if (!newSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE - setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); + if (auto const ter = setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); + !isTesSuccess(ter)) + return ter; view().update(newSponsorSle); // set new sponsor to account @@ -553,7 +583,9 @@ SponsorshipTransfer::doApply() auto const newSponsorSle = view().peek(keylet::account(newSponsorAccountID)); if (!newSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE - setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); + if (auto const ter = setSponsorFieldU32(newSponsorSle, sfSponsoringAccountCount, 1); + !isTesSuccess(ter)) + return ter; view().update(newSponsorSle); // decrement old sponsoring count @@ -561,7 +593,9 @@ SponsorshipTransfer::doApply() auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE - setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); + if (auto const ter = setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); + !isTesSuccess(ter)) + return ter; view().update(oldSponsorSle); // set new sponsor to account @@ -579,7 +613,9 @@ SponsorshipTransfer::doApply() auto const oldSponsorSle = view().peek(keylet::account(oldSponsorAccountID)); if (!oldSponsorSle) return tefINTERNAL; // LCOV_EXCL_LINE - setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); + if (auto const ter = setSponsorFieldU32(oldSponsorSle, sfSponsoringAccountCount, -1); + !isTesSuccess(ter)) + return ter; view().update(oldSponsorSle); } }