mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-27 09:00:32 +00:00
Merge branch 'xrplf/sponsor' into mvadari/sponsor/accdelete
This commit is contained in:
@@ -1170,7 +1170,7 @@ TRANSACTION(ttCONFIDENTIAL_MPT_CLAWBACK, 89, ConfidentialMPTClawback,
|
||||
TRANSACTION(ttSPONSORSHIP_TRANSFER, 90, SponsorshipTransfer,
|
||||
Delegation::NotDelegable,
|
||||
featureSponsor,
|
||||
MayModifyVault,
|
||||
NoPriv,
|
||||
({
|
||||
{sfObjectID, SoeOptional},
|
||||
{sfSponsee, SoeOptional},
|
||||
|
||||
@@ -21,7 +21,7 @@ class SponsorshipTransferBuilder;
|
||||
* Type: ttSPONSORSHIP_TRANSFER (90)
|
||||
* Delegable: Delegation::NotDelegable
|
||||
* Amendment: featureSponsor
|
||||
* Privileges: MayModifyVault
|
||||
* Privileges: NoPriv
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use SponsorshipTransferBuilder to construct new transactions.
|
||||
|
||||
@@ -151,18 +151,6 @@ ValidPermissionedDomain::finalize(
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case ttSPONSORSHIP_TRANSFER: {
|
||||
if (sleStatus_.empty())
|
||||
return true;
|
||||
|
||||
if (sleStatus_[0].isDelete)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: domain object "
|
||||
"deleted by SponsorshipTransfer";
|
||||
return false;
|
||||
}
|
||||
return check(sleStatus_[0], j);
|
||||
}
|
||||
default: {
|
||||
if (!sleStatus_.empty())
|
||||
{
|
||||
|
||||
@@ -222,7 +222,10 @@ ValidVault::deltaAssetsTxAccount(STTx const& tx, XRPAmount fee) const
|
||||
if (!ret.has_value() || !vaultAsset.native())
|
||||
return ret;
|
||||
|
||||
if (auto const delegate = tx[~sfDelegate]; delegate.has_value() && *delegate != tx[sfAccount])
|
||||
// Only add the fee back if tx[sfAccount] actually paid it. When the fee is
|
||||
// paid by someone else (a delegate or a fee sponsor), the
|
||||
// account's XRP balance moved only by the vault amount.
|
||||
if (tx.getFeePayerID() != tx[sfAccount])
|
||||
return ret;
|
||||
|
||||
ret->delta += fee.drops();
|
||||
@@ -1045,8 +1048,6 @@ ValidVault::finalize(
|
||||
case ttLOAN_SET:
|
||||
case ttLOAN_MANAGE:
|
||||
case ttLOAN_PAY:
|
||||
case ttSPONSORSHIP_TRANSFER:
|
||||
// TBD for loans; SponsorshipTransfer may update a vault's sfSponsor
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
||||
@@ -4583,6 +4583,56 @@ public:
|
||||
BEAST_EXPECT(issuerSponsoredAfter == issuerSponsoredBefore);
|
||||
}
|
||||
|
||||
void
|
||||
testFeeSponsoredVaultInvariant()
|
||||
{
|
||||
// The ValidVault invariant checks that the vault's balance and the
|
||||
// depositor's balance change by equal amounts. For XRP vaults it adds the
|
||||
// fee back into the depositor's balance change: normally the depositor
|
||||
// pays the fee, so their balance drops by (deposit amount + fee), and
|
||||
// adding the fee back leaves just the deposit amount to compare against
|
||||
// the vault. But when a fee sponsor pays, the depositor's balance drops
|
||||
// by only the deposit amount, so the fee must NOT be added back or the
|
||||
// equal-amount check fails.
|
||||
testcase("Fee-sponsored VaultDeposit/VaultWithdraw pass ValidVault invariant");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this, testableAmendments()};
|
||||
Account const alice("alice");
|
||||
Account const sponsor("sponsor");
|
||||
env.fund(XRP(10000), alice, sponsor);
|
||||
env.close();
|
||||
|
||||
PrettyAsset const xrpAsset{xrpIssue(), 1'000'000};
|
||||
Vault const vault{env};
|
||||
auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = xrpAsset});
|
||||
env(vaultTx);
|
||||
env.close();
|
||||
|
||||
// Control: the same deposit shape, unsponsored, succeeds.
|
||||
env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = xrpAsset(100)}),
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
// Fee-sponsored (co-signed) deposit succeeds.
|
||||
env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = xrpAsset(100)}),
|
||||
Fee(XRP(1)),
|
||||
sponsor::As(sponsor, spfSponsorFee),
|
||||
Sig(sfSponsorSignature, sponsor),
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
// The same helper (deltaAssetsTxAccount) drives the withdraw path, so a
|
||||
// fee-sponsored withdrawal back to the depositor's own account also
|
||||
// passes on the destination side.
|
||||
env(vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = xrpAsset(50)}),
|
||||
Fee(XRP(1)),
|
||||
sponsor::As(sponsor, spfSponsorFee),
|
||||
Sig(sfSponsorSignature, sponsor),
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
testSponsor()
|
||||
@@ -4620,6 +4670,8 @@ protected:
|
||||
|
||||
testZeroBalanceSponsoredPaymentFeePayerCheck();
|
||||
testTrustSetCounterpartySponsorMisroute();
|
||||
|
||||
testFeeSponsoredVaultInvariant();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user