mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-23 15:10:34 +00:00
fix: SponsorshipSet cannot create empty ltSPONSORSHIP objects (#7788)
This commit is contained in:
@@ -19,9 +19,29 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
static bool
|
||||
hasSponsorshipBudget(
|
||||
SLE::const_ref sponsorshipSle,
|
||||
std::optional<STAmount> const& feeAmount,
|
||||
std::optional<std::uint32_t> const& remainingOwnerCount)
|
||||
{
|
||||
// A field the transaction omits keeps whatever the existing object holds,
|
||||
// so fall back to the current SLE value when the tx does not set it.
|
||||
bool const hasFeeAmount = feeAmount
|
||||
? *feeAmount > beast::kZero
|
||||
: sponsorshipSle && (*sponsorshipSle)[~sfFeeAmount].value_or(STAmount{0}) > beast::kZero;
|
||||
|
||||
bool const hasRemainingOwnerCount = remainingOwnerCount
|
||||
? *remainingOwnerCount > 0
|
||||
: sponsorshipSle && (*sponsorshipSle)[~sfRemainingOwnerCount].value_or(0) > 0;
|
||||
|
||||
return hasFeeAmount || hasRemainingOwnerCount;
|
||||
}
|
||||
|
||||
TxConsequences
|
||||
SponsorshipSet::makeTxConsequences(PreflightContext const& ctx)
|
||||
{
|
||||
@@ -134,6 +154,13 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx)
|
||||
if (ctx.tx.isFlag(tfDeleteObject) && !sponsorshipSle)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
// Reject creating or updating a Sponsorship that would be left with no
|
||||
// budget (neither a positive FeeAmount nor a positive RemainingOwnerCount).
|
||||
// Such an object is unusable yet still consumes the sponsor's reserve.
|
||||
if (!ctx.tx.isFlag(tfDeleteObject) &&
|
||||
!hasSponsorshipBudget(sponsorshipSle, ctx.tx[~sfFeeAmount], ctx.tx[~sfRemainingOwnerCount]))
|
||||
return temMALFORMED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -591,20 +591,28 @@ public:
|
||||
env.close();
|
||||
BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice)));
|
||||
|
||||
// create sponsorship with zero value
|
||||
// Cannot create sponsorship with no fee or reserve budget. MaxFee
|
||||
// and flags do not make a sponsorship object useful by themselves.
|
||||
env(sponsor::set(sponsor, 0),
|
||||
sponsor::SponseeAcc(alice),
|
||||
Fee(XRP(1)),
|
||||
Ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice)));
|
||||
|
||||
env(sponsor::set_max_fee(sponsor, 0, XRP(1)),
|
||||
sponsor::SponseeAcc(alice),
|
||||
Fee(XRP(1)),
|
||||
Ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice)));
|
||||
|
||||
env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)),
|
||||
sponsor::SponseeAcc(alice),
|
||||
Fee(XRP(1)));
|
||||
Fee(XRP(1)),
|
||||
Ter(temMALFORMED));
|
||||
env.close();
|
||||
|
||||
sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfRemainingOwnerCount));
|
||||
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));
|
||||
BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice)));
|
||||
|
||||
// update sponsorship with non-zero value
|
||||
env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)),
|
||||
@@ -644,17 +652,55 @@ public:
|
||||
tfSponsorshipClearRequireSignForReserve,
|
||||
lsfSponsorshipRequireSignForReserve);
|
||||
|
||||
// update sponsorship with zero value
|
||||
// Cannot update sponsorship so both fee and reserve budgets are absent.
|
||||
env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)),
|
||||
sponsor::SponseeAcc(alice),
|
||||
Fee(XRP(1)));
|
||||
Fee(XRP(1)),
|
||||
Ter(temMALFORMED));
|
||||
env.close();
|
||||
|
||||
sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 100);
|
||||
BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100));
|
||||
BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1));
|
||||
}
|
||||
|
||||
{
|
||||
// Removing one budget field while the other remains keeps the
|
||||
// Sponsorship valid. Starting state (from above):
|
||||
// RemainingOwnerCount = 100, FeeAmount = XRP(100).
|
||||
|
||||
// Remove only FeeAmount (set to 0); RemainingOwnerCount remains.
|
||||
env(sponsor::set_fee(sponsor, 0, XRP(0)),
|
||||
sponsor::SponseeAcc(alice),
|
||||
Fee(XRP(1)),
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
auto sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 100);
|
||||
|
||||
// Re-add FeeAmount, then remove only RemainingOwnerCount;
|
||||
// FeeAmount remains.
|
||||
env(sponsor::set_fee(sponsor, 0, XRP(100)),
|
||||
sponsor::SponseeAcc(alice),
|
||||
Fee(XRP(1)),
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
env(sponsor::set_reserve(sponsor, 0, 0),
|
||||
sponsor::SponseeAcc(alice),
|
||||
Fee(XRP(1)),
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
sle = env.le(keylet::sponsorship(sponsor, alice));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfRemainingOwnerCount));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfMaxFee));
|
||||
BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100));
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -1899,7 +1899,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
Account const bob{"bob"};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env.close();
|
||||
env(sponsor::set(alice, 0), sponsor::SponseeAcc(bob));
|
||||
env(sponsor::set(alice, 0, 100), sponsor::SponseeAcc(bob));
|
||||
env.close();
|
||||
std::string const ledgerHash{to_string(env.closed()->header().hash)};
|
||||
auto const sponsorshipIndex = to_string(keylet::sponsorship(alice.id(), bob.id()).key);
|
||||
|
||||
Reference in New Issue
Block a user