Fix the behavior of co-sign + pre-fund

This commit is contained in:
tequ
2025-10-28 17:18:38 +09:00
parent 4fe7e87b4f
commit d0bcca6bf1
25 changed files with 143 additions and 79 deletions

View File

@@ -520,7 +520,6 @@ adjustOwnerCount(
std::shared_ptr<SLE> const& accountSle,
std::optional<std::shared_ptr<SLE>> 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<SLE> const& accountSle,
std::optional<std::shared_ptr<SLE>> 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

View File

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

View File

@@ -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<SLE> const& accountSle,
std::optional<std::shared_ptr<SLE>> 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<SLE> 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);

View File

@@ -26,6 +26,8 @@
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/Feature.h>
#include "test/jtx/txflags.h"
namespace ripple {
namespace test {
@@ -426,6 +428,67 @@ public:
}
}
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 @@ public:
testSimpleSponsorshipSet();
testPreFundAndCosign();
testTransferSponsor();
testSponsorFee();
testSponsorAccount();

View File

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

View File

@@ -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.

View File

@@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<SLE> const& offer)
return false;
auto const sponsor = getLedgerEntryReserveSponsor(view, offer);
reduceOwnerCount(
adjustOwnerCount(
view,
view.peek(keylet::account(owner)),
sponsor,

View File

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

View File

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

View File

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

View File

@@ -233,7 +233,7 @@ removeSignersFromLedger(
}
auto const sponsor = getLedgerEntryReserveSponsor(view, signers);
reduceOwnerCount(
adjustOwnerCount(
view,
view.peek(accountKeylet),
sponsor,

View File

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

View File

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

View File

@@ -444,7 +444,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
std::optional<XRPAmount> 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))

View File

@@ -337,7 +337,7 @@ private:
reset(XRPAmount fee);
static FeePayer
getFeePayer(STTx const& tx);
getFeePayer(ReadView const& view, STTx const& tx);
TER
consumeSeqProxy(SLE::pointer const& sleAccount);

View File

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

View File

@@ -769,7 +769,7 @@ finalizeClaimHelper(
auto const sponsor =
getLedgerEntryReserveSponsor(outerSb, sleClaimID);
reduceOwnerCount(outerSb, sleOwner, sponsor, -1, j);
adjustOwnerCount(outerSb, sleOwner, sponsor, -1, j);
}
}