add reserve checks for SponsorTransfer

This commit is contained in:
tequ
2025-08-01 18:21:44 +09:00
parent abd8620c97
commit 5e65813a52
5 changed files with 137 additions and 25 deletions

View File

@@ -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) +

View File

@@ -63,7 +63,17 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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();

View File

@@ -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));

View File

@@ -458,7 +458,8 @@ checkInsufficientReserve(
std::shared_ptr<SLE const> accSle,
STAmount const& accBalance,
std::optional<std::shared_ptr<SLE const>> const& _sponsorSle,
std::int32_t ownerCountDelta);
std::int32_t ownerCountDelta,
std::int32_t accountCountDelta = 0);
std::optional<std::shared_ptr<SLE>>
getTxReserveSponsor(ApplyView& view, STTx const& tx);

View File

@@ -1027,7 +1027,8 @@ checkInsufficientReserve(
std::shared_ptr<SLE const> accSle,
STAmount const& accBalance,
std::optional<std::shared_ptr<SLE const>> 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;