From 152130082d34568944ab66e43ae15cbe78eae31a Mon Sep 17 00:00:00 2001 From: Kassaking7 <96991820+Kassaking7@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:35:55 -0400 Subject: [PATCH] Merge pull request #7765 from Kassaking7/codecov-test fix: sponsor test coverage --- .../ledger/helpers/AccountRootHelpers.cpp | 8 ++++++ src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 2 ++ src/libxrpl/ledger/helpers/SponsorHelpers.cpp | 2 ++ .../tx/transactors/escrow/EscrowFinish.cpp | 2 +- .../tx/transactors/payment/Payment.cpp | 4 ++- .../sponsor/SponsorshipTransfer.cpp | 18 ++++++------- .../token/MPTokenIssuanceCreate.cpp | 2 +- src/test/app/Invariants_test.cpp | 25 ++++++++++++++++++ src/test/app/Sponsor_test.cpp | 26 +++++++++++++++++++ src/test/rpc/AccountObjects_test.cpp | 9 +++++++ 10 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 2ef95ac609..ac4eb1a62e 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -64,11 +64,13 @@ confineOwnerCount( // Overflow is well defined on unsigned if (totalOwnerCount < currentOwnerCount) { + // LCOV_EXCL_START if (id) { JLOG(j.fatal()) << "Account " << *id << " owner count exceeds max!"; } totalOwnerCount = std::numeric_limits::max(); + // LCOV_EXCL_STOP } } else @@ -99,8 +101,10 @@ accountCountImpl(SLE::const_ref sle, std::int32_t accountCountAdj, beast::Journa std::int64_t totalAccountCount{currentAccountCount + accountCountAdj}; if (totalAccountCount > std::numeric_limits::max()) { + // LCOV_EXCL_START JLOG(j.fatal()) << "Reserve count exceeds max!"; totalAccountCount = std::numeric_limits::max(); + // LCOV_EXCL_STOP } else if (totalAccountCount < 0) { @@ -215,19 +219,23 @@ ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj) if (deltaCount > std::numeric_limits::max()) { + // LCOV_EXCL_START deltaCount = std::numeric_limits::max(); JLOG(j.fatal()) << "Account " << id << " delta count exceeds max, " << "adjustment: " << ownerCountAdj << ", sponsoredCount: " << sponsoredOwnerCount << ", sponsoringOwnerCount: " << sponsoringOwnerCount; + // LCOV_EXCL_STOP } else if (deltaCount < std::numeric_limits::min()) { + // LCOV_EXCL_START deltaCount = std::numeric_limits::min(); JLOG(j.fatal()) << "Account " << id << " delta count is below min, " << "adjustment: " << ownerCountAdj << ", sponsoredCount: " << sponsoredOwnerCount << ", sponsoringCount: " << sponsoringOwnerCount; + // LCOV_EXCL_STOP } return confineOwnerCount(currentOwnerCount, deltaCount); diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index fd3c5483c5..c67ab6775d 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -726,9 +726,11 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) auto const newPrev = view.peek(Keylet(ltNFTOKEN_PAGE, *prevLink)); if (!newPrev) { + // LCOV_EXCL_START Throw( "NFTokenPage directory for " + to_string(owner) + " cannot be repaired. Unexpected link problem."); + // LCOV_EXCL_STOP } newPrev->at(sfNextPageMin) = nextPage->key(); view.update(newPrev); diff --git a/src/libxrpl/ledger/helpers/SponsorHelpers.cpp b/src/libxrpl/ledger/helpers/SponsorHelpers.cpp index 9004654a3c..c57b415f02 100644 --- a/src/libxrpl/ledger/helpers/SponsorHelpers.cpp +++ b/src/libxrpl/ledger/helpers/SponsorHelpers.cpp @@ -174,8 +174,10 @@ getLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& accou return std::nullopt; } default: + // LCOV_EXCL_START UNREACHABLE("xrpl::getLedgerEntryOwner : object is not supported by sponsorship."); return std::nullopt; + // LCOV_EXCL_STOP }; } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index 5d73427236..598b6a1e0b 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -226,7 +226,7 @@ EscrowFinish::preclaim(PreclaimContext const& ctx) auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); if (!sponsorSle) - return sponsorSle.error(); + return sponsorSle.error(); // LCOV_EXCL_LINE return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 40b6a95c36..93a6db3f63 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -509,9 +509,11 @@ Payment::doApply() sponsor->getFieldU32(sfSponsoringAccountCount); if (currentSponsoringAccountCount == std::numeric_limits::max()) { + // LCOV_EXCL_START JLOG(j_.fatal()) << "Sponsoring account count overflow for account " << to_string(accountID_); - return tecINTERNAL; // LCOV_EXCL_LINE + return tecINTERNAL; + // LCOV_EXCL_STOP } sponsor->setFieldU32(sfSponsoringAccountCount, currentSponsoringAccountCount + 1); diff --git a/src/libxrpl/tx/transactors/sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/sponsor/SponsorshipTransfer.cpp index 03bf00f9d0..37504fa0a9 100644 --- a/src/libxrpl/tx/transactors/sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/sponsor/SponsorshipTransfer.cpp @@ -359,7 +359,7 @@ SponsorshipTransfer::doApply() if (auto const ter = incrementSponsorCount( view(), ownerSle, sfSponsoredOwnerCount, ownerCountDelta); !isTesSuccess(ter)) - return ter; + return ter; // LCOV_EXCL_LINE } else if (isReassign) { @@ -374,14 +374,14 @@ SponsorshipTransfer::doApply() if (auto const ter = decrementSponsorCount( view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); !isTesSuccess(ter)) - return ter; + return ter; // LCOV_EXCL_LINE } // Increment new sponsor's sponsoring count if (auto const ter = incrementSponsorCount( view(), newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); !isTesSuccess(ter)) - return ter; + return ter; // LCOV_EXCL_LINE // Object is now sponsored by new sponsor objectSle->setAccountID(sponsorField, newSponsorID); @@ -394,7 +394,7 @@ SponsorshipTransfer::doApply() if (auto const ter = decrementPrefundedReserveCount(view(), sponsorshipSle, ownerCountDelta); !isTesSuccess(ter)) - return ter; + return ter; // LCOV_EXCL_LINE } } else if (ctx_.tx.isFlag(tfSponsorshipEnd)) @@ -423,13 +423,13 @@ SponsorshipTransfer::doApply() if (auto const ter = decrementSponsorCount( view(), sponseeSle, sfSponsoredOwnerCount, ownerCountDelta); !isTesSuccess(ter)) - return ter; + return ter; // LCOV_EXCL_LINE // Decrement old sponsoring count if (auto const ter = decrementSponsorCount( view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); !isTesSuccess(ter)) - return ter; + return ter; // LCOV_EXCL_LINE // Remove sponsor from object objectSle->makeFieldAbsent(sponsorField); @@ -477,14 +477,14 @@ SponsorshipTransfer::doApply() if (auto const ter = decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1); !isTesSuccess(ter)) - return ter; + return ter; // LCOV_EXCL_LINE } // Increment new sponsoring count if (auto const ter = incrementSponsorCount(view(), newSponsorSle, sfSponsoringAccountCount, 1); !isTesSuccess(ter)) - return ter; + return ter; // LCOV_EXCL_LINE // Account is now sponsored by new sponsor sponseeSle->setAccountID(sfSponsor, newSponsorID); @@ -519,7 +519,7 @@ SponsorshipTransfer::doApply() if (auto const ter = decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1); !isTesSuccess(ter)) - return ter; + return ter; // LCOV_EXCL_LINE } } diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 369795de4e..7d44d516c7 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -126,7 +126,7 @@ MPTokenIssuanceCreate::create( // A reserve sponsor only covers tx.Account's own objects. auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, acct); if (!sponsorExp) - return std::unexpected(sponsorExp.error()); + return std::unexpected(sponsorExp.error()); // LCOV_EXCL_LINE auto const sponsorSle = *sponsorExp; if (args.priorBalance) diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index bca698ce3a..533722e30e 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -5201,6 +5201,31 @@ class Invariants_test : public beast::unit_test::Suite }); } + { + auto const expectMessage = + "SponsoredObjectOwnerCount does not equal SponsoredOwnerCount delta."; + uint256 checkID; + + doInvariantCheck( + {{expectMessage}}, + [&](Account const&, Account const& a2, ApplyContext& ac) { + auto const check = ac.view().peek(keylet::check(checkID)); + if (!check) + return false; + check->setAccountID(sfSponsor, a2.id()); + ac.view().update(check); + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_SET, [](STObject&) {}}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + [&checkID](Account const& a1, Account const& a2, Env& env) { + checkID = keylet::check(a1.id(), env.seq(a1)).key; + env(check::create(a1, a2, XRP(1))); + return true; + }); + } + { auto const expectMessage = "Invariant failed: Net delta of SponsoringAccountCount does " diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index ff49213ecc..f447b9aaee 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -618,6 +618,32 @@ public: BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100)); BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1)); + // update sponsorship flags + auto testFlagUpdate = [&](auto setFlag, auto clearFlag, auto ledgerFlag) { + env(sponsor::set(sponsor, setFlag), sponsor::SponseeAcc(alice), Fee(XRP(1))); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->isFlag(ledgerFlag)); + + env(sponsor::set(sponsor, clearFlag), sponsor::SponseeAcc(alice), Fee(XRP(1))); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(!sle->isFlag(ledgerFlag)); + }; + + testFlagUpdate( + tfSponsorshipSetRequireSignForFee, + tfSponsorshipClearRequireSignForFee, + lsfSponsorshipRequireSignForFee); + testFlagUpdate( + tfSponsorshipSetRequireSignForReserve, + tfSponsorshipClearRequireSignForReserve, + lsfSponsorshipRequireSignForReserve); + // update sponsorship with zero value env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), sponsor::SponseeAcc(alice), diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 775a9aa8bf..23a0536374 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -205,6 +205,15 @@ public: resp[jss::result][jss::error_message] == "Invalid field 'limit', not unsigned integer."); } + // test error on sponsored param not a boolean + { + json::Value params; + params[jss::account] = bob.human(); + params[jss::sponsored] = "true"; + auto resp = env.rpc("json", "account_objects", to_string(params)); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == "Invalid field 'sponsored', not boolean."); + } // test errors on marker { Account const gw{"G"};