This commit is contained in:
tequ
2025-09-15 11:04:47 +09:00
parent 6aa0331ffe
commit 4abd94e7ac
7 changed files with 121 additions and 30 deletions

View File

@@ -513,10 +513,11 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
LEDGER_ENTRY(ltSPONSORSHIP, 0x0085, Sponsorship, sponsorship, ({
{sfOwner, soeREQUIRED},
{sfSponsee, soeREQUIRED},
{sfFeeAmount, soeOPTIONAL},
{sfMaxFee, soeOPTIONAL},
{sfReserveCount, soeOPTIONAL},
{sfOwnerNode, soeREQUIRED},
{sfSponseeNode, soeREQUIRED},
{sfFeeAmount, soeOPTIONAL},
{sfReserveCount, soeOPTIONAL},
}))
#undef EXPAND

View File

@@ -248,6 +248,7 @@ TYPED_SFIELD(sfSignatureReward, AMOUNT, 29)
TYPED_SFIELD(sfMinAccountCreateAmount, AMOUNT, 30)
TYPED_SFIELD(sfLPTokenBalance, AMOUNT, 31)
TYPED_SFIELD(sfFeeAmount, AMOUNT, 32)
TYPED_SFIELD(sfMaxFee, AMOUNT, 33)
// variable length (common)
TYPED_SFIELD(sfPublicKey, VL, 1)

View File

@@ -536,6 +536,7 @@ TRANSACTION(ttSPONSORSHIP_SET, 73, SponsorshipSet, Delegation::notDelegatable, (
{sfSponsorAccount, soeOPTIONAL},
{sfSponsee, soeREQUIRED},
{sfFeeAmount, soeOPTIONAL},
{sfMaxFee, soeOPTIONAL},
{sfReserveCount, soeOPTIONAL},
}))

View File

@@ -137,6 +137,7 @@ public:
env(sponsor::set(sponsor, alice, tfDeleteObject, 1), ter(temMALFORMED));
env(sponsor::set(sponsor, alice, tfDeleteObject, std::nullopt, XRP(1)),
ter(temMALFORMED));
// TODO: test MaxFee with tfDeleteObject
//
// preclaim
@@ -492,6 +493,13 @@ public:
env.fund(XRP(10000), alice, sponsor);
env.close();
// not yet funded
env(noop(alice),
fee(drops(500)),
sponsor::as(sponsor, tfSponsorFee),
ter(tecNO_SPONSOR_PERMISSION));
// set sponsorship
env(sponsor::set(sponsor, alice, 0, std::nullopt, XRP(1)),
ter(tesSUCCESS));
env.close();

View File

@@ -94,6 +94,18 @@ SponsorshipSet::preflight(PreflightContext const& ctx)
return temBAD_AMOUNT;
}
if (ctx.tx.isFieldPresent(sfMaxFee))
{
auto const maxFee = ctx.tx.getFieldAmount(sfMaxFee);
if (!isXRP(maxFee))
return temBAD_AMOUNT;
if (maxFee.xrp().drops() <= 0)
return temBAD_AMOUNT;
// TODO: check maxFee > basefee
}
if (ctx.tx.isFieldPresent(sfReserveCount))
{
if (ctx.tx.getFlags() & tfSponsorshipClearRequireSignForReserve)
@@ -108,7 +120,8 @@ SponsorshipSet::preflight(PreflightContext const& ctx)
if (ctx.tx.isFlag(tfDeleteObject))
{
if (ctx.tx.isFieldPresent(sfFeeAmount) ||
ctx.tx.isFieldPresent(sfReserveCount))
ctx.tx.isFieldPresent(sfReserveCount) ||
ctx.tx.isFieldPresent(sfMaxFee))
return temMALFORMED;
}
@@ -188,6 +201,7 @@ SponsorshipSet::doApply()
}
auto const feeAmount = ctx_.tx[~sfFeeAmount];
auto const maxFee = ctx_.tx[~sfMaxFee];
auto const reserveCount = ctx_.tx[~sfReserveCount];
auto reserveSponsorAccSle = getTxReserveSponsor(view(), ctx_.tx);
@@ -214,6 +228,10 @@ SponsorshipSet::doApply()
(*sponsorAccSle)[sfBalance] -= *feeAmount;
(*newSle)[sfFeeAmount] = *feeAmount;
}
if (maxFee)
{
(*newSle)[sfMaxFee] = *maxFee;
}
if (reserveCount)
{
(*newSle)[sfReserveCount] = *reserveCount;
@@ -244,11 +262,16 @@ SponsorshipSet::doApply()
(*sponsorObjSle)[sfFeeAmount] += *feeAmount;
}
if (maxFee)
{
(*sponsorObjSle)[sfMaxFee] = *maxFee;
}
if (reserveCount)
(*sponsorObjSle)[sfReserveCount] =
(*sponsorObjSle)[sfReserveCount] + *reserveCount;
// TODO: update Flags?
// update Flags
auto flags = sponsorObjSle->getFieldU32(sfFlags);
if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForFee))
flags |= lsfSponsorshipRequireSignForFee;

View File

@@ -259,25 +259,35 @@ Transactor::checkSponsor(ReadView const& view, STTx const& tx)
auto const sponsorAcc = txSponsor.getAccountID(sfAccount);
auto const sponseeAcc = tx.getAccountID(sfAccount);
auto const sponsorSle = view.read(keylet::sponsor(sponsorAcc, sponseeAcc));
if (!sponsorSle)
return tesSUCCESS;
auto const hasSignature = txSponsor.isFieldPresent(sfTxnSignature) ||
!txSponsor.getFieldVL(sfSigningPubKey).empty() ||
txSponsor.isFieldPresent(sfSigners);
if (txSponsor.isFlag(tfSponsorFee) &&
sponsorSle->isFlag(lsfSponsorshipRequireSignForFee))
auto const sponsorSle = view.read(keylet::sponsor(sponsorAcc, sponseeAcc));
if (!hasSignature)
{
if (!hasSignature)
// pre funded
if (!sponsorSle)
return tecNO_SPONSOR_PERMISSION;
}
if (txSponsor.isFlag(tfSponsorReserve) &&
sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve))
else
{
if (!hasSignature)
return tecNO_SPONSOR_PERMISSION;
// co-signed
if (!sponsorSle)
return tesSUCCESS;
if (txSponsor.isFlag(tfSponsorFee) &&
sponsorSle->isFlag(lsfSponsorshipRequireSignForFee))
{
if (!hasSignature)
return tecNO_SPONSOR_PERMISSION;
}
if (txSponsor.isFlag(tfSponsorReserve) &&
sponsorSle->isFlag(lsfSponsorshipRequireSignForReserve))
{
if (!hasSignature)
return tecNO_SPONSOR_PERMISSION;
}
}
return tesSUCCESS;
@@ -359,21 +369,68 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
if (feePaid == beast::zero)
return tesSUCCESS;
auto const id = ctx.tx.getFeePayer();
JLOG(ctx.j.trace()) << "Fee payer: " + to_string(id);
auto const sle = ctx.view.read(keylet::account(id));
if (!sle)
return terNO_ACCOUNT;
std::optional<XRPAmount> availableBalance;
auto const balance = (*sle)[sfBalance].xrp();
if (ctx.tx.isFieldPresent(sfSponsor))
{
auto const txSponsor = ctx.tx.getFieldObject(sfSponsor);
if (txSponsor.isFlag(tfSponsorFee) &&
(!txSponsor.isFieldPresent(sfTxnSignature) &&
!txSponsor.isFieldPresent(sfSigners)))
{
// use prefunded fee sponsor
auto const keylet = keylet::sponsor(
txSponsor.getAccountID(sfAccount), ctx.tx[sfAccount]);
auto const sponsorSle = ctx.view.read(keylet);
if (!sponsorSle)
return tecNO_SPONSOR_PERMISSION;
if (balance < feePaid)
XRPAmount const maxFee = sponsorSle->isFieldPresent(sfMaxFee)
? sponsorSle->getFieldAmount(sfMaxFee).xrp()
: INITIAL_XRP;
XRPAmount const feeAmount = sponsorSle->isFieldPresent(sfFeeAmount)
? sponsorSle->getFieldAmount(sfFeeAmount).xrp()
: XRPAmount(0);
// feePaid should <= maxFee
if (feePaid > maxFee)
return tecNO_SPONSOR_PERMISSION;
// feePaid should <= feeAmount
if (feePaid > feeAmount)
return tecNO_SPONSOR_PERMISSION;
availableBalance = feeAmount;
}
else
{
// proceed to use fee payer
}
}
if (!availableBalance)
{
auto const id = ctx.tx.getFeePayer();
JLOG(ctx.j.trace()) << "Fee payer: " + to_string(id);
auto const sle = ctx.view.read(keylet::account(id));
if (!sle)
return terNO_ACCOUNT;
availableBalance = (*sle)[sfBalance].xrp();
}
XRPL_ASSERT(
availableBalance,
"ripple::Transactor::checkFee : could not get balance for fee");
if (*availableBalance < feePaid)
{
JLOG(ctx.j.trace())
<< "Insufficient balance:" << " balance=" << to_string(balance)
<< " paid=" << to_string(feePaid);
<< "Insufficient balance:" << " balance="
<< to_string(*availableBalance) << " paid=" << to_string(feePaid);
if ((balance > beast::zero) && !ctx.view.open())
if ((*availableBalance > beast::zero) && !ctx.view.open())
{
// Closed ledger, non-zero balance, less than fee
return tecINSUFF_FEE;

View File

@@ -197,6 +197,11 @@ invoke_preclaim(PreclaimContext const& ctx)
result = T::checkPriorTxAndLastLedger(ctx);
if (result != tesSUCCESS)
return result;
result = T::checkSponsor(ctx.view, ctx.tx);
if (result != tesSUCCESS)
return result;
@@ -207,11 +212,6 @@ invoke_preclaim(PreclaimContext const& ctx)
result = T::checkPermission(ctx.view, ctx.tx);
if (result != tesSUCCESS)
return result;
result = T::checkSponsor(ctx.view, ctx.tx);
if (result != tesSUCCESS)
return result;