mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-23 07:10:53 +00:00
feat: Add checks to block reserve sponsorship on OOS txs (#7605)
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include <test/jtx/mpt.h>
|
||||
#include <test/jtx/multisign.h>
|
||||
#include <test/jtx/noop.h>
|
||||
#include <test/jtx/offer.h>
|
||||
#include <test/jtx/paths.h>
|
||||
#include <test/jtx/pay.h>
|
||||
#include <test/jtx/sendmax.h>
|
||||
@@ -22,10 +23,12 @@
|
||||
#include <test/jtx/sponsor.h>
|
||||
#include <test/jtx/ter.h>
|
||||
#include <test/jtx/ticket.h>
|
||||
#include <test/jtx/token.h>
|
||||
#include <test/jtx/trust.h>
|
||||
#include <test/jtx/txflags.h>
|
||||
#include <test/jtx/vault.h>
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
@@ -677,6 +680,7 @@ public:
|
||||
using namespace test::jtx;
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const charlie("charlie");
|
||||
Account const sponsor("sponsor");
|
||||
|
||||
{
|
||||
@@ -715,10 +719,10 @@ public:
|
||||
{
|
||||
// if pre-funded value is not enough, error
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(10000), alice, bob, sponsor);
|
||||
env.fund(XRP(10000), alice, bob, charlie, sponsor);
|
||||
env.close();
|
||||
|
||||
env(sponsor::set(sponsor, 0, 10, XRP(10), XRP(100)),
|
||||
env(sponsor::set(sponsor, 0, 1, XRP(10), XRP(100)),
|
||||
sponsor::SponseeAcc(alice),
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
@@ -847,19 +851,21 @@ public:
|
||||
Env env{*this, testableAmendments()};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const charlie("charlie");
|
||||
Account const sponsor("sponsor");
|
||||
env.fund(XRP(10000), alice, bob, sponsor);
|
||||
env.close();
|
||||
|
||||
{
|
||||
// sponsor object
|
||||
env(did::set(alice),
|
||||
did::Uri("uri"),
|
||||
env.fund(XRP(1000), charlie);
|
||||
env.close();
|
||||
env(deposit::auth(alice, charlie),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
|
||||
auto const keylet = keylet::did(alice);
|
||||
auto const keylet = keylet::depositPreauth(alice, charlie);
|
||||
env(sponsor::transfer(bob, tfSponsorshipEnd, keylet.key),
|
||||
sponsor::SponseeAcc(alice),
|
||||
Ter(tecNO_PERMISSION));
|
||||
@@ -1451,6 +1457,73 @@ public:
|
||||
Ter(tecNO_PERMISSION));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// existing owner objects that are outside the v1 SponsorshipTransfer
|
||||
// object allow-list
|
||||
Env env{*this, testableAmendments()};
|
||||
Account const alice("alice");
|
||||
Account const sponsor("sponsor");
|
||||
env.fund(XRP(10000), alice, sponsor);
|
||||
env.close();
|
||||
|
||||
auto const checkBlocked = [&](Account const& account, uint256 const& objectID) {
|
||||
env(sponsor::transfer(account, tfSponsorshipCreate, objectID),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor),
|
||||
Ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
};
|
||||
|
||||
auto const ticketSeq = env.seq(alice);
|
||||
env(ticket::create(alice, 1));
|
||||
env.close();
|
||||
auto const ticketID = keylet::TicketT()(alice, ticketSeq + 1).key;
|
||||
BEAST_EXPECT(env.le(keylet::unchecked(ticketID)));
|
||||
checkBlocked(alice, ticketID);
|
||||
|
||||
env(did::setValid(alice));
|
||||
env.close();
|
||||
auto const didKeylet = keylet::did(alice.id());
|
||||
BEAST_EXPECT(env.le(didKeylet));
|
||||
checkBlocked(alice, didKeylet.key);
|
||||
|
||||
env(token::mint(alice, 0u));
|
||||
env.close();
|
||||
auto const nftPageKeylet = keylet::nftpageMax(alice);
|
||||
BEAST_EXPECT(env.le(nftPageKeylet));
|
||||
checkBlocked(alice, nftPageKeylet.key);
|
||||
|
||||
Account const borrower("borrower");
|
||||
env.fund(XRP(1000000), borrower);
|
||||
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();
|
||||
|
||||
env(vault.deposit(
|
||||
{.depositor = alice, .id = vaultKeylet.key, .amount = xrpAsset(1000)}));
|
||||
env.close();
|
||||
|
||||
auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice));
|
||||
env(loanBroker::set(alice, vaultKeylet.key),
|
||||
loanBroker::kDebtMaximum(xrpAsset(1000).value()),
|
||||
loanBroker::kManagementFeeRate(TenthBips16{0}),
|
||||
loanBroker::kCoverRateMinimum(TenthBips32{0}),
|
||||
loanBroker::kCoverRateLiquidation(TenthBips32{0}));
|
||||
env.close();
|
||||
|
||||
auto const loanKeylet = keylet::loan(brokerKeylet.key, 1);
|
||||
env(loan::set(borrower, brokerKeylet.key, xrpAsset(100).value()),
|
||||
Sig(sfCounterpartySignature, alice),
|
||||
Fee(env.current()->fees().base * 2));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.le(loanKeylet));
|
||||
checkBlocked(borrower, loanKeylet.key);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2041,7 +2114,6 @@ public:
|
||||
env.fund(XRP(10000), alice, bob, sponsor);
|
||||
env.close();
|
||||
|
||||
// test Sufficient sponsor balance
|
||||
if (cosigning)
|
||||
{
|
||||
adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1));
|
||||
@@ -3728,6 +3800,40 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the central allow-list in preflight1Sponsor rejects
|
||||
// spfSponsorReserve for transaction types that v1 does not permit.
|
||||
void
|
||||
testReserveSponsorGate()
|
||||
{
|
||||
testcase("Reserve sponsor allow-list gate");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this, testableAmendments()};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor("sponsor");
|
||||
env.fund(XRP(10000), alice, bob, sponsor);
|
||||
env.close();
|
||||
|
||||
env(sponsor::set(sponsor, 0, 10, XRP(10)), sponsor::SponseeAcc(alice));
|
||||
env.close();
|
||||
|
||||
auto checkBlocked = [&](json::Value const& jv) {
|
||||
env(jv,
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor),
|
||||
Ter(temINVALID_FLAG));
|
||||
};
|
||||
|
||||
checkBlocked(ticket::create(alice, 1));
|
||||
checkBlocked(offer(alice, XRP(100), bob["USD"](100)));
|
||||
checkBlocked(did::setValid(alice));
|
||||
checkBlocked(token::mint(alice, 0u));
|
||||
checkBlocked(sponsor::set(alice, 0, 10, XRP(10)));
|
||||
checkBlocked(acctdelete(alice, bob));
|
||||
checkBlocked(loan::set(alice, uint256(1), Number{1}));
|
||||
}
|
||||
|
||||
void
|
||||
testSponsorReserve(bool cosigning)
|
||||
{
|
||||
@@ -3769,6 +3875,8 @@ protected:
|
||||
|
||||
testDelegatePermission();
|
||||
testBatch();
|
||||
|
||||
testReserveSponsorGate();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1535,86 +1535,43 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// A Sponsorship object is visible to both sides, but its reserve side
|
||||
// belongs only to sfOwner.
|
||||
// A Sponsorship object is visible to both sides.
|
||||
{
|
||||
Env env(*this, testableAmendments());
|
||||
Account const owner("owner");
|
||||
Account const sponsee("sponsee");
|
||||
Account const sponsor("sponsor");
|
||||
|
||||
env.fund(XRP(10000), owner, sponsee, sponsor);
|
||||
env.fund(XRP(10000), owner, sponsee);
|
||||
env.close();
|
||||
|
||||
env(sponsor::set_reserve(sponsor, 0, 100), sponsor::SponseeAcc(owner));
|
||||
env(sponsor::set(owner, 0, 100, XRP(100)), sponsor::SponseeAcc(sponsee));
|
||||
env.close();
|
||||
|
||||
env(sponsor::set(owner, 0, 100, XRP(100)),
|
||||
sponsor::SponseeAcc(sponsee),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor));
|
||||
env.close();
|
||||
|
||||
auto const sponsorship = env.le(keylet::sponsorship(owner, sponsee));
|
||||
if (!BEAST_EXPECT(sponsorship))
|
||||
auto const sponsorshipKeylet = keylet::sponsorship(owner, sponsee);
|
||||
if (!BEAST_EXPECT(env.le(sponsorshipKeylet)))
|
||||
return;
|
||||
BEAST_EXPECT(sponsorship->isFieldPresent(sfSponsor));
|
||||
|
||||
{
|
||||
auto const resp = acctObjsSponsored(env, owner.id(), true, jss::sponsorship);
|
||||
auto const resp = acctObjsSponsored(env, owner.id(), false, jss::sponsorship);
|
||||
auto const& objs = resp[jss::result][jss::account_objects];
|
||||
if (BEAST_EXPECT(objs.size() == 1))
|
||||
BEAST_EXPECT(objs[0u][sfLedgerEntryType.jsonName] == jss::Sponsorship);
|
||||
}
|
||||
{
|
||||
auto const resp = acctObjsSponsored(env, sponsee.id(), true, jss::sponsorship);
|
||||
auto const& objs = resp[jss::result][jss::account_objects];
|
||||
BEAST_EXPECT(objs.size() == 0);
|
||||
}
|
||||
{
|
||||
auto const resp = acctObjsSponsored(env, sponsee.id(), false, jss::sponsorship);
|
||||
auto const& objs = resp[jss::result][jss::account_objects];
|
||||
if (BEAST_EXPECT(objs.size() == 1))
|
||||
BEAST_EXPECT(objs[0u][sfLedgerEntryType.jsonName] == jss::Sponsorship);
|
||||
}
|
||||
}
|
||||
|
||||
// NFT page sponsored filter
|
||||
{
|
||||
// Mint an NFT for bob (creates NFT page)
|
||||
env(token::mint(bob, 0));
|
||||
env.close();
|
||||
|
||||
auto const nftPageKeylet = keylet::nftpageMax(bob);
|
||||
if (!BEAST_EXPECT(env.le(nftPageKeylet)))
|
||||
return;
|
||||
|
||||
// Sponsor the NFT page
|
||||
env(sponsor::transfer(bob, tfSponsorshipCreate, nftPageKeylet.key),
|
||||
sponsor::As(sponsor1, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor1));
|
||||
env.close();
|
||||
|
||||
// Verify NFT page has sponsor field
|
||||
auto const nftPage = env.le(nftPageKeylet);
|
||||
if (!BEAST_EXPECT(nftPage))
|
||||
return;
|
||||
BEAST_EXPECT(nftPage->isFieldPresent(sfSponsor));
|
||||
|
||||
// sponsored=true should include the sponsored NFT page
|
||||
// sponsored=false should NOT include the sponsored NFT page
|
||||
for (auto const sponsored : {true, false})
|
||||
{
|
||||
auto const resp = acctObjsSponsored(env, bob.id(), sponsored);
|
||||
auto const resp = acctObjsSponsored(env, owner.id(), true, jss::sponsorship);
|
||||
auto const& objs = resp[jss::result][jss::account_objects];
|
||||
bool foundNFTPage = false;
|
||||
for (auto const& obj : objs)
|
||||
{
|
||||
if (obj[sfLedgerEntryType.jsonName] == jss::NFTokenPage &&
|
||||
obj.isMember(sfSponsor.jsonName))
|
||||
foundNFTPage = true;
|
||||
}
|
||||
BEAST_EXPECT(foundNFTPage == sponsored);
|
||||
BEAST_EXPECT(objs.size() == 0);
|
||||
}
|
||||
{
|
||||
auto const resp = acctObjsSponsored(env, sponsee.id(), true, jss::sponsorship);
|
||||
auto const& objs = resp[jss::result][jss::account_objects];
|
||||
BEAST_EXPECT(objs.size() == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user