21#include <test/jtx/AMM.h>
22#include <test/jtx/Env.h>
24#include <xrpld/app/tx/apply.h>
25#include <xrpld/app/tx/detail/ApplyContext.h>
27#include <xrpl/beast/utility/Journal.h>
28#include <xrpl/protocol/InnerObjectFormats.h>
29#include <xrpl/protocol/STLedgerEntry.h>
31#include <boost/algorithm/string/predicate.hpp>
80 using namespace test::jtx;
82 featureInvariantsV1_1 | featureSingleAssetVault;
85 Account
const A1{
"A1"};
86 Account
const A2{
"A2"};
87 env.fund(
XRP(1000), A1, A2);
89 BEAST_EXPECT(preclose(A1, A2, env));
92 OpenView ov{*env.current()};
100 env.current()->fees().base,
104 BEAST_EXPECT(precheck(A1, A2, ac));
107 if (!BEAST_EXPECT(ters.
size() == 2))
111 for (
TER const& terExpect : ters)
113 terActual = ac.checkInvariants(terActual, fee);
114 BEAST_EXPECT(terExpect == terActual);
116 sink.messages().str().starts_with(
"Invariant failed:") ||
117 sink.messages().str().starts_with(
118 "Transaction caused an exception"));
119 for (
auto const& m : expect_logs)
121 if (sink.messages().str().find(m) == std::string::npos)
134 using namespace test::jtx;
137 {{
"XRP net change was positive: 500"}},
138 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
143 auto amt = sle->getFieldAmount(sfBalance);
144 sle->setFieldAmount(sfBalance, amt +
STAmount{500});
145 ac.view().update(sle);
153 using namespace test::jtx;
158 {{
"an account root was deleted"}},
159 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
164 ac.view().erase(sle);
174 {{
"account deletion succeeded without deleting an account"}},
184 {{
"account deletion succeeded but deleted multiple accounts"}},
185 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
189 if (!sleA1 || !sleA2)
191 ac.view().erase(sleA1);
192 ac.view().erase(sleA2);
202 using namespace test::jtx;
203 testcase <<
"account root deletion left artifact";
211 if (!keyletInfo.includeInTests)
213 auto const& keyletfunc = keyletInfo.function;
214 auto const& type = keyletInfo.expectedLEName;
216 using namespace std::string_literals;
219 {{
"account deletion left behind a "s + type.c_str() +
221 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
224 auto const a1 = A1.id();
231 ac.view().insert(newSLE);
232 ac.view().erase(sleA1);
242 {{
"account deletion left behind a NFTokenPage object"}},
243 [&](Account
const& A1, Account
const&,
ApplyContext& ac) {
248 ac.view().erase(sle);
254 [&](Account
const& A1, Account
const&, Env& env) {
257 env(token::mint(A1));
267 {{
"account deletion left behind a DirectoryNode object"}},
268 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
275 BEAST_EXPECT(sle->at(~sfAMMID));
276 BEAST_EXPECT(sle->at(~sfAMMID) == ammKey);
278 ac.view().erase(sle);
285 [&](Account
const& A1, Account
const& A2, Env& env) {
288 AMM const amm(env, A1, XRP(100), A1[
"USD"](50));
289 ammAcctID = amm.ammAccount();
290 ammKey = amm.ammID();
291 ammIssue = amm.lptIssue();
295 {{
"account deletion left behind a AMM object"}},
296 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
304 BEAST_EXPECT(sle->at(~sfAMMID));
305 BEAST_EXPECT(sle->at(~sfAMMID) == ammKey);
307 for (
auto const& trustKeylet :
311 if (
auto const line = ac.view().peek(trustKeylet); !line)
317 STAmount const lowLimit = line->at(sfLowLimit);
318 STAmount const highLimit = line->at(sfHighLimit);
329 auto const ammSle = ac.view().peek(
keylet::amm(ammKey));
330 if (!BEAST_EXPECT(ammSle))
334 BEAST_EXPECT(ac.view().dirRemove(
335 ownerDirKeylet, ammSle->at(sfOwnerNode), ammKey,
false));
337 !ac.view().exists(ownerDirKeylet) ||
338 ac.view().emptyDirDelete(ownerDirKeylet));
340 ac.view().erase(sle);
347 [&](Account
const& A1, Account
const& A2, Env& env) {
350 AMM const amm(env, A1, XRP(100), A1[
"USD"](50));
351 ammAcctID = amm.ammAccount();
352 ammKey = amm.ammID();
353 ammIssue = amm.lptIssue();
361 using namespace test::jtx;
362 testcase <<
"ledger entry types don't match";
364 {{
"ledger entry type mismatch"},
365 {
"XRP net change of -1000000000 doesn't match fee 0"}},
366 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
372 ac.rawView().rawReplace(sleNew);
377 {{
"invalid ledger entry type added"}},
378 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
393 ac.view().insert(sleNew);
401 using namespace test::jtx;
402 testcase <<
"trust lines with XRP not allowed";
404 {{
"an XRP trust line was created"}},
405 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
409 ac.view().insert(sleNew);
417 using namespace test::jtx;
418 testcase <<
"trust lines with deep freeze flag without freeze "
421 {{
"a trust line with deep freeze flag without normal freeze was "
423 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
426 sleNew->setFieldAmount(sfLowLimit, A1[
"USD"](0));
427 sleNew->setFieldAmount(sfHighLimit, A1[
"USD"](0));
431 sleNew->setFieldU32(sfFlags, uFlags);
432 ac.view().insert(sleNew);
437 {{
"a trust line with deep freeze flag without normal freeze was "
439 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
442 sleNew->setFieldAmount(sfLowLimit, A1[
"USD"](0));
443 sleNew->setFieldAmount(sfHighLimit, A1[
"USD"](0));
446 sleNew->setFieldU32(sfFlags, uFlags);
447 ac.view().insert(sleNew);
452 {{
"a trust line with deep freeze flag without normal freeze was "
454 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
457 sleNew->setFieldAmount(sfLowLimit, A1[
"USD"](0));
458 sleNew->setFieldAmount(sfHighLimit, A1[
"USD"](0));
461 sleNew->setFieldU32(sfFlags, uFlags);
462 ac.view().insert(sleNew);
467 {{
"a trust line with deep freeze flag without normal freeze was "
469 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
472 sleNew->setFieldAmount(sfLowLimit, A1[
"USD"](0));
473 sleNew->setFieldAmount(sfHighLimit, A1[
"USD"](0));
476 sleNew->setFieldU32(sfFlags, uFlags);
477 ac.view().insert(sleNew);
482 {{
"a trust line with deep freeze flag without normal freeze was "
484 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
487 sleNew->setFieldAmount(sfLowLimit, A1[
"USD"](0));
488 sleNew->setFieldAmount(sfHighLimit, A1[
"USD"](0));
491 sleNew->setFieldU32(sfFlags, uFlags);
492 ac.view().insert(sleNew);
500 using namespace test::jtx;
501 testcase <<
"transfers when frozen";
505 auto const createTrustlines =
506 [&](Account
const& A1, Account
const& A2, Env& env) {
508 env.fund(XRP(1000), G1);
510 env.trust(G1[
"USD"](10000), A1);
511 env.trust(G1[
"USD"](10000), A2);
514 env(pay(G1, A1, G1[
"USD"](1000)));
515 env(pay(G1, A2, G1[
"USD"](1000)));
521 auto const A1FrozenByIssuer =
522 [&](Account
const& A1, Account
const& A2, Env& env) {
523 createTrustlines(A1, A2, env);
530 auto const A1DeepFrozenByIssuer =
531 [&](Account
const& A1, Account
const& A2, Env& env) {
532 A1FrozenByIssuer(A1, A2, env);
539 auto const changeBalances = [&](Account
const& A1,
544 auto const sleA1 = ac.view().peek(
keylet::line(A1, G1[
"USD"]));
545 auto const sleA2 = ac.view().peek(
keylet::line(A2, G1[
"USD"]));
547 sleA1->setFieldAmount(sfBalance, G1[
"USD"](A1Balance));
548 sleA2->setFieldAmount(sfBalance, G1[
"USD"](A2Balance));
550 ac.view().update(sleA1);
551 ac.view().update(sleA2);
556 {{
"Attempting to move frozen funds"}},
557 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
558 changeBalances(A1, A2, ac, -900, -1100);
568 {{
"Attempting to move frozen funds"}},
569 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
570 changeBalances(A1, A2, ac, -900, -1100);
576 A1DeepFrozenByIssuer);
580 {{
"Attempting to move frozen funds"}},
581 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
582 changeBalances(A1, A2, ac, -1100, -900);
588 A1DeepFrozenByIssuer);
594 using namespace test::jtx;
598 {{
"Cannot return non-native STAmount as XRPAmount"}},
599 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
604 STAmount const nonNative(A2[
"USD"](51));
605 sle->setFieldAmount(sfBalance, nonNative);
606 ac.view().update(sle);
611 {{
"incorrect account XRP balance"},
612 {
"XRP net change was positive: 99999999000000001"}},
613 [
this](Account
const& A1, Account
const&,
ApplyContext& ac) {
620 sle->setFieldAmount(sfBalance,
INITIAL_XRP + drops(1));
621 BEAST_EXPECT(!sle->getFieldAmount(sfBalance).negative());
622 ac.view().update(sle);
627 {{
"incorrect account XRP balance"},
628 {
"XRP net change of -1000000001 doesn't match fee 0"}},
629 [
this](Account
const& A1, Account
const&,
ApplyContext& ac) {
634 sle->setFieldAmount(sfBalance,
STAmount{1,
true});
635 BEAST_EXPECT(sle->getFieldAmount(sfBalance).negative());
636 ac.view().update(sle);
644 using namespace test::jtx;
645 using namespace std::string_literals;
646 testcase <<
"Transaction fee checks";
649 {{
"fee paid was negative: -1"},
650 {
"XRP net change of 0 doesn't match fee -1"}},
651 [](Account
const&, Account
const&,
ApplyContext&) {
return true; },
656 {
"XRP net change of 0 doesn't match fee "s +
658 [](Account
const&, Account
const&,
ApplyContext&) {
return true; },
662 {{
"fee paid is 20 exceeds fee specified in transaction."},
663 {
"XRP net change of 0 doesn't match fee 20"}},
664 [](Account
const&, Account
const&,
ApplyContext&) {
return true; },
674 using namespace test::jtx;
678 {{
"offer with a bad amount"}},
679 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
686 sleNew->setAccountID(sfAccount, A1.id());
687 sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]);
688 sleNew->setFieldAmount(sfTakerPays, XRP(-1));
689 ac.view().insert(sleNew);
694 {{
"offer with a bad amount"}},
695 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
702 sleNew->setAccountID(sfAccount, A1.id());
703 sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]);
704 sleNew->setFieldAmount(sfTakerPays, A1[
"USD"](10));
705 sleNew->setFieldAmount(sfTakerGets, XRP(-1));
706 ac.view().insert(sleNew);
711 {{
"offer with a bad amount"}},
712 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
719 sleNew->setAccountID(sfAccount, A1.id());
720 sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]);
721 sleNew->setFieldAmount(sfTakerPays, XRP(10));
722 sleNew->setFieldAmount(sfTakerGets, XRP(11));
723 ac.view().insert(sleNew);
731 using namespace test::jtx;
735 {{
"XRP net change of -1000000 doesn't match fee 0"},
736 {
"escrow specifies invalid amount"}},
737 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
744 sleNew->setFieldAmount(sfAmount, XRP(-1));
745 ac.view().insert(sleNew);
750 {{
"XRP net change was positive: 100000000000000001"},
751 {
"escrow specifies invalid amount"}},
752 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
761 sleNew->setFieldAmount(sfAmount,
INITIAL_XRP + drops(1));
762 ac.view().insert(sleNew);
768 {{
"escrow specifies invalid amount"}},
769 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
780 sleNew->setFieldAmount(sfAmount, amt);
781 ac.view().insert(sleNew);
787 {{
"escrow specifies invalid amount"}},
788 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
798 sleNew->setFieldAmount(sfAmount, amt);
799 ac.view().insert(sleNew);
805 {{
"escrow specifies invalid amount"}},
806 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
817 sleNew->setFieldAmount(sfAmount, amt);
818 ac.view().insert(sleNew);
824 {{
"escrow specifies invalid amount"}},
825 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
835 sleNew->setFieldU64(sfOutstandingAmount, -1);
836 ac.view().insert(sleNew);
842 {{
"escrow specifies invalid amount"}},
843 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
853 sleNew->setFieldU64(sfLockedAmount, -1);
854 ac.view().insert(sleNew);
860 {{
"escrow specifies invalid amount"}},
861 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
871 sleNew->setFieldU64(sfOutstandingAmount, 1);
872 sleNew->setFieldU64(sfLockedAmount, 10);
873 ac.view().insert(sleNew);
879 {{
"escrow specifies invalid amount"}},
880 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
890 sleNew->setFieldU64(sfMPTAmount, -1);
891 ac.view().insert(sleNew);
897 {{
"escrow specifies invalid amount"}},
898 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
908 sleNew->setFieldU64(sfLockedAmount, -1);
909 ac.view().insert(sleNew);
917 using namespace test::jtx;
918 testcase <<
"valid new account root";
921 {{
"account root created illegally"}},
925 Account
const A3{
"A3"};
928 ac.view().insert(sleNew);
933 {{
"multiple accounts created in a single transaction"}},
937 Account
const A3{
"A3"};
940 ac.view().insert(sleA3);
943 Account
const A4{
"A4"};
946 ac.view().insert(sleA4);
952 {{
"account created with wrong starting sequence number"}},
955 Account
const A3{
"A3"};
958 sleNew->setFieldU32(sfSequence, ac.view().seq() + 1);
959 ac.view().insert(sleNew);
966 {{
"pseudo-account created by a wrong transaction type"}},
968 Account
const A3{
"A3"};
971 sleNew->setFieldU32(sfSequence, 0);
972 sleNew->setFieldH256(sfAMMID,
uint256(1));
976 ac.view().insert(sleNew);
983 {{
"account created with wrong starting sequence number"}},
985 Account
const A3{
"A3"};
988 sleNew->setFieldU32(sfSequence, ac.view().seq());
989 sleNew->setFieldH256(sfAMMID,
uint256(1));
993 ac.view().insert(sleNew);
1000 {{
"pseudo-account created with wrong flags"}},
1002 Account
const A3{
"A3"};
1005 sleNew->setFieldU32(sfSequence, 0);
1006 sleNew->setFieldH256(sfAMMID,
uint256(1));
1007 sleNew->setFieldU32(
1009 ac.view().insert(sleNew);
1016 {{
"pseudo-account created with wrong flags"}},
1018 Account
const A3{
"A3"};
1021 sleNew->setFieldU32(sfSequence, 0);
1022 sleNew->setFieldH256(sfAMMID,
uint256(1));
1023 sleNew->setFieldU32(
1027 ac.view().insert(sleNew);
1037 using namespace test::jtx;
1042 "0000000000000000000000000000000000000001FFFFFFFFFFFFFFFF00000000");
1043 auto makeNFTokenIDs = [&firstNFTID](
unsigned int nftCount) {
1050 for (
int i = 0; i < nftCount; ++i)
1053 *nfTokenTemplate, sfNFToken, [&nftID](
STObject&
object) {
1054 object.setFieldH256(sfNFTokenID, nftID);
1063 {{
"NFT page has invalid size"}},
1067 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(0));
1069 ac.view().insert(nftPage);
1074 {{
"NFT page has invalid size"}},
1078 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(33));
1080 ac.view().insert(nftPage);
1085 {{
"NFTs on page are not sorted"}},
1088 STArray nfTokens = makeNFTokenIDs(2);
1092 nftPage->setFieldArray(sfNFTokens, nfTokens);
1094 ac.view().insert(nftPage);
1099 {{
"NFT contains empty URI"}},
1102 STArray nfTokens = makeNFTokenIDs(1);
1103 nfTokens[0].setFieldVL(sfURI,
Blob{});
1106 nftPage->setFieldArray(sfNFTokens, nfTokens);
1108 ac.view().insert(nftPage);
1113 {{
"NFT page is improperly linked"}},
1117 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(1));
1118 nftPage->setFieldH256(
1121 ac.view().insert(nftPage);
1126 {{
"NFT page is improperly linked"}},
1128 Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1130 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(1));
1131 nftPage->setFieldH256(
1134 ac.view().insert(nftPage);
1139 {{
"NFT page is improperly linked"}},
1143 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(1));
1144 nftPage->setFieldH256(sfNextPageMin, nftPage->key());
1146 ac.view().insert(nftPage);
1151 {{
"NFT page is improperly linked"}},
1153 Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1154 STArray nfTokens = makeNFTokenIDs(1);
1157 ++(nfTokens[0].getFieldH256(sfNFTokenID))));
1158 nftPage->setFieldArray(sfNFTokens, std::move(nfTokens));
1159 nftPage->setFieldH256(
1162 ac.view().insert(nftPage);
1167 {{
"NFT found in incorrect page"}},
1170 STArray nfTokens = makeNFTokenIDs(2);
1173 (nfTokens[1].getFieldH256(sfNFTokenID))));
1174 nftPage->setFieldArray(sfNFTokens, std::move(nfTokens));
1176 ac.view().insert(nftPage);
1188 sle->setAccountID(sfOwner, A1);
1189 sle->setFieldU32(sfSequence, 10);
1191 STArray credentials(sfAcceptedCredentials, 2);
1195 cred.setAccountID(sfIssuer, A2);
1198 sfCredentialType,
Slice(credType.c_str(), credType.size()));
1201 sle->setFieldArray(sfAcceptedCredentials, credentials);
1208 using namespace test::jtx;
1212 {{
"permissioned domain with no rules."}},
1213 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
1216 slePd->setAccountID(sfOwner, A1);
1217 slePd->setFieldU32(sfSequence, 10);
1219 ac.view().insert(slePd);
1226 testcase <<
"PermissionedDomain 2";
1230 {{
"permissioned domain bad credentials size " +
1232 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1235 slePd->setAccountID(sfOwner, A1);
1236 slePd->setFieldU32(sfSequence, 10);
1238 STArray credentials(sfAcceptedCredentials, tooBig);
1242 cred.setAccountID(sfIssuer, A2);
1247 Slice(credType.c_str(), credType.size()));
1250 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1251 ac.view().insert(slePd);
1258 testcase <<
"PermissionedDomain 3";
1260 {{
"permissioned domain credentials aren't sorted"}},
1261 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1264 slePd->setAccountID(sfOwner, A1);
1265 slePd->setFieldU32(sfSequence, 10);
1267 STArray credentials(sfAcceptedCredentials, 2);
1271 cred.setAccountID(sfIssuer, A2);
1276 Slice(credType.c_str(), credType.size()));
1279 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1280 ac.view().insert(slePd);
1287 testcase <<
"PermissionedDomain 4";
1289 {{
"permissioned domain credentials aren't unique"}},
1290 [](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1293 slePd->setAccountID(sfOwner, A1);
1294 slePd->setFieldU32(sfSequence, 10);
1296 STArray credentials(sfAcceptedCredentials, 2);
1300 cred.setAccountID(sfIssuer, A2);
1301 cred.setFieldVL(sfCredentialType,
Slice(
"cred_type", 9));
1304 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1305 ac.view().insert(slePd);
1312 testcase <<
"PermissionedDomain Set 1";
1314 {{
"permissioned domain with no rules."}},
1315 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1324 STArray credentials(sfAcceptedCredentials, 2);
1325 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1326 ac.view().update(slePd);
1335 testcase <<
"PermissionedDomain Set 2";
1337 {{
"permissioned domain bad credentials size " +
1339 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1348 STArray credentials(sfAcceptedCredentials, tooBig);
1353 cred.setAccountID(sfIssuer, A2);
1357 Slice(credType.c_str(), credType.size()));
1361 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1362 ac.view().update(slePd);
1371 testcase <<
"PermissionedDomain Set 3";
1373 {{
"permissioned domain credentials aren't sorted"}},
1374 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1383 STArray credentials(sfAcceptedCredentials, 2);
1387 cred.setAccountID(sfIssuer, A2);
1392 Slice(credType.c_str(), credType.size()));
1396 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1397 ac.view().update(slePd);
1406 testcase <<
"PermissionedDomain Set 4";
1408 {{
"permissioned domain credentials aren't unique"}},
1409 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1418 STArray credentials(sfAcceptedCredentials, 2);
1422 cred.setAccountID(sfIssuer, A2);
1424 sfCredentialType,
Slice(
"cred_type", 9));
1427 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1428 ac.view().update(slePd);
1441 using namespace test::jtx;
1445 {{
"domain doesn't exist"}},
1446 [](Account
const& A1, Account
const&,
ApplyContext& ac) {
1449 sleOffer->setAccountID(sfAccount, A1);
1450 sleOffer->setFieldAmount(sfTakerPays, A1[
"USD"](10));
1451 sleOffer->setFieldAmount(sfTakerGets, XRP(1));
1452 ac.view().insert(sleOffer);
1462 "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E33"
1464 Account
const A1{
"A1"};
1465 tx.setFieldAmount(sfTakerPays, A1[
"USD"](10));
1466 tx.setFieldAmount(sfTakerGets, XRP(1));
1472 {{
"hybrid offer is malformed"}},
1473 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1480 sleOffer->setAccountID(sfAccount, A2);
1481 sleOffer->setFieldAmount(sfTakerPays, A1[
"USD"](10));
1482 sleOffer->setFieldAmount(sfTakerGets, XRP(1));
1487 sleOffer->setFieldArray(sfAdditionalBooks, bookArr);
1488 ac.view().insert(sleOffer);
1497 {{
"hybrid offer is malformed"}},
1498 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1505 sleOffer->setAccountID(sfAccount, A2);
1506 sleOffer->setFieldAmount(sfTakerPays, A1[
"USD"](10));
1507 sleOffer->setFieldAmount(sfTakerGets, XRP(1));
1509 sleOffer->setFieldH256(sfDomainID, pdKeylet.
key);
1514 sleOffer->setFieldArray(sfAdditionalBooks, bookArr);
1515 ac.view().insert(sleOffer);
1524 {{
"hybrid offer is malformed"}},
1525 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1532 sleOffer->setAccountID(sfAccount, A2);
1533 sleOffer->setFieldAmount(sfTakerPays, A1[
"USD"](10));
1534 sleOffer->setFieldAmount(sfTakerGets, XRP(1));
1536 sleOffer->setFieldH256(sfDomainID, pdKeylet.
key);
1537 ac.view().insert(sleOffer);
1545 {{
"transaction consumed wrong domains"}},
1546 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1551 Keylet const badDomainKeylet =
1558 sleOffer->setAccountID(sfAccount, A2);
1559 sleOffer->setFieldAmount(sfTakerPays, A1[
"USD"](10));
1560 sleOffer->setFieldAmount(sfTakerGets, XRP(1));
1561 sleOffer->setFieldH256(sfDomainID, pdKeylet.
key);
1562 ac.view().insert(sleOffer);
1569 Account
const A1{
"A1"};
1570 Keylet const badDomainKey =
1572 tx.setFieldH256(sfDomainID, badDomainKey.
key);
1573 tx.setFieldAmount(sfTakerPays, A1[
"USD"](10));
1574 tx.setFieldAmount(sfTakerGets, XRP(1));
1579 {{
"domain transaction affected regular offers"}},
1580 [&](Account
const& A1, Account
const& A2,
ApplyContext& ac) {
1587 sleOffer->setAccountID(sfAccount, A2);
1588 sleOffer->setFieldAmount(sfTakerPays, A1[
"USD"](10));
1589 sleOffer->setFieldAmount(sfTakerGets, XRP(1));
1590 ac.view().insert(sleOffer);
1597 Account
const A1{
"A1"};
1600 tx.setFieldH256(sfDomainID, domainKey.
key);
1601 tx.setFieldAmount(sfTakerPays, A1[
"USD"](10));
1602 tx.setFieldAmount(sfTakerGets, XRP(1));
1629BEAST_DEFINE_TESTSUITE(Invariants, app,
ripple);
A generic endpoint for log messages.
testcase_t testcase
Memberspace for declaring test cases.
void fail(String const &reason, char const *file, int line)
Record a failure.
State information when applying a tx.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
void testAccountRootsDeletedClean()
void testTransfersNotFrozen()
void testAccountRootsNotRemoved()
void testTransactionFeeCheck()
void doInvariantCheck(std::vector< std::string > const &expect_logs, Precheck const &precheck, XRPAmount fee=XRPAmount{}, STTx tx=STTx{ttACCOUNT_SET, [](STObject &) {}}, std::initializer_list< TER > ters={tecINVARIANT_FAILED, tefINVARIANT_FAILED}, Preclose const &preclose={})
Run a specific test case to put the ledger into a state that will be detected by an invariant.
void testValidNewAccountRoot()
void testXRPBalanceCheck()
std::function< bool(test::jtx::Account const &a, test::jtx::Account const &b, test::jtx::Env &env)> Preclose
void createPermissionedDomain(ApplyContext &ac, std::shared_ptr< SLE > &sle, test::jtx::Account const &A1, test::jtx::Account const &A2)
void run() override
Runs the suite.
void testNFTokenPageInvariants()
void testNoDeepFreezeTrustLinesWithoutFreeze()
void testPermissionedDEX()
void testNoXRPTrustLine()
void testPermissionedDomainInvariants()
A currency issued by an account.
Defines the fields and their attributes within a STObject.
AccountID const & getIssuer() const
void push_back(STObject const &object)
void setFieldH256(SField const &field, uint256 const &)
void setFieldAmount(SField const &field, STAmount const &)
static STObject makeInnerObject(SField const &name)
An immutable linear range of bytes.
Immutable cryptographic account descriptor.
A transaction testing environment.
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Keylet const & amendments() noexcept
The index of the amendment table.
Keylet nftpage(Keylet const &k, uint256 const &token)
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
FeatureBitset testable_amendments()
XRP_t const XRP
Converts to XRP Issue or STAmount.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
constexpr std::uint32_t tfSetDeepFreeze
std::size_t constexpr maxPermissionedDomainCredentialsArraySize
The maximum number of credentials can be passed in array for permissioned domain.
constexpr XRPAmount INITIAL_XRP
Configure the native currency.
std::array< keyletDesc< AccountID const & >, 6 > const directAccountKeylets
base_uint< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
std::string to_string(base_uint< Bits, Tag > const &a)
LedgerEntryType
Identifiers for on-ledger objects.
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
TERSubset< CanCvtToTER > TER
constexpr std::uint32_t tfSetFreeze
A pair of SHAMap key and LedgerEntryType.