mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-23 07:00:21 +00:00
Merge branch 'xrplf/sponsor' of https://github.com/XRPLF/rippled into mvadari/sponsor/apply-view-context
This commit is contained in:
@@ -24,14 +24,6 @@ isReserveSponsored(STTx const& tx)
|
||||
return (tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve) != 0u;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isSponsorReserveCoSigning(STTx const& tx)
|
||||
{
|
||||
if (!tx.isFieldPresent(sfSponsorSignature))
|
||||
return false;
|
||||
return isReserveSponsored(tx);
|
||||
}
|
||||
|
||||
inline std::optional<AccountID>
|
||||
getTxReserveSponsorAccountID(STTx const& tx)
|
||||
{
|
||||
|
||||
@@ -343,14 +343,14 @@ checkInsufficientReserve(
|
||||
{
|
||||
if (sponsorSle)
|
||||
{
|
||||
auto const isCoSigning = isSponsorReserveCoSigning(tx);
|
||||
|
||||
auto const sle = view.read(
|
||||
keylet::sponsorship(
|
||||
sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount)));
|
||||
|
||||
// prefunded sponsor should have a sponsorship entry
|
||||
if (!isCoSigning && !sle)
|
||||
// A reserve-sponsored tx must carry a sponsor signature
|
||||
// (cosigning path) and/or have a pre-existing sponsorship SLE
|
||||
// (prefunded path). Absence of both is an internal invariant break.
|
||||
if (isReserveSponsored(tx) && !sle && !tx.isFieldPresent(sfSponsorSignature))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (sle)
|
||||
|
||||
@@ -196,17 +196,15 @@ authorizeMPToken(
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
auto const isSponsoredAndPreFunded = *sponsorSle && !isSponsorReserveCoSigning(ctx.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 PreFunded Sponsor, it must be checked whether sufficient
|
||||
// ReserveCount exists.
|
||||
if (ownerCount(view, *sponsorSle ? *sponsorSle : sleAcct, journal) >= 2 ||
|
||||
isSponsoredAndPreFunded)
|
||||
// The "free-tier" shortcut (ownerCount < 2) does not apply once a sponsor is on
|
||||
// the tx — the sponsor must always cover the reserve (via balance or prefunded
|
||||
// budget), so this check always runs for sponsored transactions.
|
||||
if (*sponsorSle || ownerCount(view, sleAcct, journal) >= 2)
|
||||
{
|
||||
if (auto const ret = checkInsufficientReserve(
|
||||
view, ctx.tx, sleAcct, priorBalance, *sponsorSle, 1, 0, journal);
|
||||
|
||||
@@ -334,9 +334,9 @@ TrustSet::doApply()
|
||||
|
||||
std::uint32_t const uOwnerCount = ownerCount(view(), *sponsorSle ? *sponsorSle : sle, j_);
|
||||
|
||||
bool const isSponsoredAndPreFunded = *sponsorSle && !isSponsorReserveCoSigning(ctx_.tx);
|
||||
// If PreFunded Sponsor, it must be checked whether sufficient
|
||||
// ReserveCount exists.
|
||||
// The "free-tier" shortcut (ownerCount < 2) only applies when there is no sponsor.
|
||||
// With any sponsor on the tx, the sponsor must cover the reserve (via balance or
|
||||
// prefunded budget), so the reserve check always runs.
|
||||
bool const freeTrustLine = uOwnerCount < 2 && !*sponsorSle;
|
||||
|
||||
std::uint32_t const uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0);
|
||||
@@ -540,7 +540,7 @@ TrustSet::doApply()
|
||||
// calling adjustOwnerCount().
|
||||
if (auto const ret = checkInsufficientReserve(
|
||||
view(), ctx_.tx, sleLowAccount, preFeeBalance_, *sponsorSle, 1, 0, j_);
|
||||
isSponsoredAndPreFunded && !isTesSuccess(ret))
|
||||
*sponsorSle && !isTesSuccess(ret))
|
||||
return tecINSUF_RESERVE_LINE;
|
||||
|
||||
// Set reserve for low account.
|
||||
@@ -569,7 +569,7 @@ TrustSet::doApply()
|
||||
// calling adjustOwnerCount().
|
||||
if (auto const ret = checkInsufficientReserve(
|
||||
view(), ctx_.tx, sleHighAccount, preFeeBalance_, *sponsorSle, 1, 0, j_);
|
||||
isSponsoredAndPreFunded && !isTesSuccess(ret))
|
||||
*sponsorSle && !isTesSuccess(ret))
|
||||
return tecINSUF_RESERVE_LINE;
|
||||
|
||||
// Set reserve for high account.
|
||||
|
||||
@@ -746,6 +746,47 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testSponsoredFreeTierReserve()
|
||||
{
|
||||
testcase("Sponsored Free-Tier Reserve");
|
||||
using namespace test::jtx;
|
||||
Account const alice("alice");
|
||||
Account const issuer("issuer");
|
||||
Account const sponsor("sponsor");
|
||||
|
||||
// Trust lines and MPTokens normally skip the reserve check when the
|
||||
// holder's ownerCount < 2 (the "free-tier" / first-two-items shortcut). When the
|
||||
// tx is sponsored, that shortcut must not apply — the sponsor must
|
||||
// still cover the reserve.
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(10000), alice, issuer);
|
||||
// Sponsor is funded just below the reserve required to cover a single
|
||||
// sponsored item.
|
||||
env.fund(reserve(env, 1) - drops(1), sponsor);
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
|
||||
MPTTester mptt(env, issuer, {.fund = false});
|
||||
mptt.create();
|
||||
|
||||
// Free-tier trust line cosigned by an undercapitalized sponsor must
|
||||
// fail — the holder's free-first-two-items shortcut does not let the
|
||||
// sponsor skip the reserve check.
|
||||
env(trust(alice, issuer["USD"](100)),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor),
|
||||
Ter(tecNO_LINE_INSUF_RESERVE));
|
||||
env.close();
|
||||
|
||||
// Free-tier MPTokenAuthorize must also fail for the same reason.
|
||||
env(MPTTester::authorizeJV({.account = alice, .id = mptt.issuanceID()}),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor),
|
||||
Ter(tecINSUFFICIENT_RESERVE));
|
||||
env.close();
|
||||
}
|
||||
|
||||
void
|
||||
testTransferSponsor()
|
||||
{
|
||||
@@ -2821,10 +2862,11 @@ public:
|
||||
env(noop(sponsor), ticket::Use(ticketSeq));
|
||||
env.close();
|
||||
|
||||
// pass (free mptoken)
|
||||
// pass (free-tier mptoken for the holder, but the sponsor is still
|
||||
// charged a reserve increment regardless of the ownerCount < 2 shortcut).
|
||||
if (cosigning)
|
||||
{
|
||||
adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1));
|
||||
adjustAccountXRPBalance(env, sponsor, reserve(env, 2));
|
||||
env(jv,
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor),
|
||||
@@ -3723,6 +3765,7 @@ protected:
|
||||
testSimpleSponsorshipSet();
|
||||
|
||||
testPreFundAndCosign();
|
||||
testSponsoredFreeTierReserve();
|
||||
|
||||
testTransferSponsor();
|
||||
testSponsorFee();
|
||||
|
||||
Reference in New Issue
Block a user