Skip to content

Commit 5dbe37d

Browse files
committed
add a boatload of tests (AI)
1 parent ae84349 commit 5dbe37d

8 files changed

Lines changed: 2021 additions & 25 deletions

File tree

src/test/app/Invariants_test.cpp

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <test/jtx/mpt.h>
88
#include <test/jtx/pay.h>
99
#include <test/jtx/permissioned_domains.h>
10+
#include <test/jtx/sig.h>
11+
#include <test/jtx/sponsor.h>
1012
#include <test/jtx/tags.h>
1113
#include <test/jtx/token.h>
1214
#include <test/jtx/trust.h>
@@ -5250,6 +5252,139 @@ class Invariants_test : public beast::unit_test::Suite
52505252
return true;
52515253
});
52525254
}
5255+
5256+
{
5257+
// XRPNotCreated: the sfFeeAmount escrowed in a Sponsorship entry
5258+
// counts toward the XRP total. Creating a Sponsorship holding a
5259+
// FeeAmount without debiting any account manufactures XRP.
5260+
doInvariantCheck(
5261+
{{"XRP net change was positive: 100000000"}},
5262+
[](Account const& a1, Account const& a2, ApplyContext& ac) {
5263+
auto const sleNew =
5264+
std::make_shared<SLE>(keylet::sponsorship(a1.id(), a2.id()));
5265+
sleNew->setAccountID(sfOwner, a1.id());
5266+
sleNew->setAccountID(sfSponsee, a2.id());
5267+
sleNew->setFieldAmount(sfFeeAmount, XRP(100));
5268+
ac.view().insert(sleNew);
5269+
return true;
5270+
});
5271+
5272+
// ... and deleting a Sponsorship entry without refunding its
5273+
// escrowed FeeAmount destroys XRP beyond the transaction fee.
5274+
doInvariantCheck(
5275+
{{"XRP net change of -100000000 doesn't match fee 0"}},
5276+
[](Account const& a1, Account const& a2, ApplyContext& ac) {
5277+
auto const sle = ac.view().peek(keylet::sponsorship(a2.id(), a1.id()));
5278+
if (!sle)
5279+
return false;
5280+
ac.view().erase(sle);
5281+
return true;
5282+
},
5283+
XRPAmount{},
5284+
STTx{ttACCOUNT_SET, [](STObject&) {}},
5285+
{tecINVARIANT_FAILED, tefINVARIANT_FAILED},
5286+
[](Account const& a1, Account const& a2, Env& env) {
5287+
// a2 sponsors a1's fees with a pre-funded XRP(100)
5288+
env(sponsor::set_fee(a2, 0, XRP(100)), sponsor::SponseeAcc(a1));
5289+
env.close();
5290+
return env.le(keylet::sponsorship(a2.id(), a1.id())) != nullptr;
5291+
});
5292+
}
5293+
5294+
{
5295+
// Deleting a sponsored object without adjusting the accounts'
5296+
// Sponsored/SponsoringOwnerCount fields must be caught (the
5297+
// isDelete path of SponsorshipOwnerCountsMatch::visitEntry: the
5298+
// sponsored-object delta is -1 while the account deltas are 0).
5299+
auto const expectMessage =
5300+
"SponsoredObjectOwnerCount does not equal SponsoredOwnerCount delta.";
5301+
uint256 checkID;
5302+
5303+
doInvariantCheck(
5304+
{{expectMessage}},
5305+
[&](Account const&, Account const&, ApplyContext& ac) {
5306+
auto const check = ac.view().peek(keylet::check(checkID));
5307+
if (!check)
5308+
return false;
5309+
ac.view().erase(check);
5310+
return true;
5311+
},
5312+
XRPAmount{},
5313+
STTx{ttACCOUNT_SET, [](STObject&) {}},
5314+
{tecINVARIANT_FAILED, tefINVARIANT_FAILED},
5315+
[&checkID](Account const& a1, Account const& a2, Env& env) {
5316+
// a2 reserve-sponsors a1's check by co-signing its creation
5317+
checkID = keylet::check(a1.id(), env.seq(a1)).key;
5318+
env(check::create(a1, a2, XRP(1)),
5319+
sponsor::As(a2, spfSponsorReserve),
5320+
Sig(sfSponsorSignature, a2),
5321+
Fee(XRP(1)));
5322+
env.close();
5323+
auto const sle = env.le(keylet::check(checkID));
5324+
return sle && sle->isFieldPresent(sfSponsor);
5325+
});
5326+
}
5327+
5328+
{
5329+
// Trust lines use sfLowSponsor/sfHighSponsor instead of sfSponsor
5330+
// and can be reserve-sponsored independently on each side.
5331+
auto const expectMessage =
5332+
"SponsoredObjectOwnerCount does not equal SponsoredOwnerCount delta.";
5333+
5334+
auto const preclose = [](Account const& a1, Account const& a2, Env& env) {
5335+
env(trust(a1, a2["USD"](100)));
5336+
return true;
5337+
};
5338+
5339+
// Marking one side of an existing trust line as sponsored without
5340+
// touching any account counts (object delta 1, account deltas 0).
5341+
doInvariantCheck(
5342+
{{expectMessage}},
5343+
[](Account const& a1, Account const& a2, ApplyContext& ac) {
5344+
auto const line = ac.view().peek(keylet::trustLine(a1, a2, a2["USD"].currency));
5345+
if (!line)
5346+
return false;
5347+
line->setAccountID(sfLowSponsor, a2.id());
5348+
ac.view().update(line);
5349+
return true;
5350+
},
5351+
XRPAmount{},
5352+
STTx{ttACCOUNT_SET, [](STObject&) {}},
5353+
{tecINVARIANT_FAILED, tefINVARIANT_FAILED},
5354+
preclose);
5355+
5356+
// A trust line sponsored on both sides contributes two sponsored
5357+
// owner counts, so bumping the account fields by only one each is
5358+
// caught (object delta 2, account deltas 1).
5359+
doInvariantCheck(
5360+
{{expectMessage}},
5361+
[](Account const& a1, Account const& a2, ApplyContext& ac) {
5362+
auto const line = ac.view().peek(keylet::trustLine(a1, a2, a2["USD"].currency));
5363+
if (!line)
5364+
return false;
5365+
line->setAccountID(sfLowSponsor, a2.id());
5366+
line->setAccountID(sfHighSponsor, a2.id());
5367+
ac.view().update(line);
5368+
5369+
// a1 owns the trust line, so its OwnerCount is already 1.
5370+
auto const sponsee = ac.view().peek(keylet::account(a1.id()));
5371+
if (!sponsee)
5372+
return false;
5373+
sponsee->setFieldU32(sfSponsoredOwnerCount, 1);
5374+
ac.view().update(sponsee);
5375+
5376+
auto const sponsoring = ac.view().peek(keylet::account(a2.id()));
5377+
if (!sponsoring)
5378+
return false;
5379+
sponsoring->setFieldU32(sfSponsoringOwnerCount, 1);
5380+
ac.view().update(sponsoring);
5381+
return true;
5382+
},
5383+
XRPAmount{},
5384+
STTx{ttACCOUNT_SET, [](STObject&) {}},
5385+
{tecINVARIANT_FAILED, tefINVARIANT_FAILED},
5386+
preclose);
5387+
}
52535388
}
52545389

52555390
void

0 commit comments

Comments
 (0)