rippled
Loading...
Searching...
No Matches
Invariants_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2017 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx.h>
21#include <test/jtx/AMM.h>
22#include <test/jtx/Env.h>
23
24#include <xrpld/app/tx/apply.h>
25#include <xrpld/app/tx/detail/ApplyContext.h>
26
27#include <xrpl/beast/utility/Journal.h>
28#include <xrpl/protocol/InnerObjectFormats.h>
29#include <xrpl/protocol/STLedgerEntry.h>
30
31#include <boost/algorithm/string/predicate.hpp>
32
33namespace ripple {
34
36{
37 // The optional Preclose function is used to process additional transactions
38 // on the ledger after creating two accounts, but before closing it, and
39 // before the Precheck function. These should only be valid functions, and
40 // not direct manipulations. Preclose is not commonly used.
41 using Preclose = std::function<bool(
42 test::jtx::Account const& a,
43 test::jtx::Account const& b,
44 test::jtx::Env& env)>;
45
46 // this is common setup/method for running a failing invariant check. The
47 // precheck function is used to manipulate the ApplyContext with view
48 // changes that will cause the check to fail.
49 using Precheck = std::function<bool(
50 test::jtx::Account const& a,
51 test::jtx::Account const& b,
52 ApplyContext& ac)>;
53
70 void
72 std::vector<std::string> const& expect_logs,
73 Precheck const& precheck,
75 STTx tx = STTx{ttACCOUNT_SET, [](STObject&) {}},
78 Preclose const& preclose = {})
79 {
80 using namespace test::jtx;
81 FeatureBitset amendments = supported_amendments() |
82 featureInvariantsV1_1 | featureSingleAssetVault;
83 Env env{*this, amendments};
84
85 Account const A1{"A1"};
86 Account const A2{"A2"};
87 env.fund(XRP(1000), A1, A2);
88 if (preclose)
89 BEAST_EXPECT(preclose(A1, A2, env));
90 env.close();
91
92 OpenView ov{*env.current()};
93 test::StreamSink sink{beast::severities::kWarning};
94 beast::Journal jlog{sink};
95 ApplyContext ac{
96 env.app(),
97 ov,
98 tx,
100 env.current()->fees().base,
101 tapNONE,
102 jlog};
103
104 BEAST_EXPECT(precheck(A1, A2, ac));
105
106 // invoke check twice to cover tec and tef cases
107 if (!BEAST_EXPECT(ters.size() == 2))
108 return;
109
110 TER terActual = tesSUCCESS;
111 for (TER const& terExpect : ters)
112 {
113 terActual = ac.checkInvariants(terActual, fee);
114 BEAST_EXPECT(terExpect == terActual);
115 BEAST_EXPECT(
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)
120 {
121 if (sink.messages().str().find(m) == std::string::npos)
122 {
123 // uncomment if you want to log the invariant failure
124 // message log << " --> " << m << std::endl;
125 fail();
126 }
127 }
128 }
129 }
130
131 void
133 {
134 using namespace test::jtx;
135 testcase << "XRP created";
137 {{"XRP net change was positive: 500"}},
138 [](Account const& A1, Account const&, ApplyContext& ac) {
139 // put a single account in the view and "manufacture" some XRP
140 auto const sle = ac.view().peek(keylet::account(A1.id()));
141 if (!sle)
142 return false;
143 auto amt = sle->getFieldAmount(sfBalance);
144 sle->setFieldAmount(sfBalance, amt + STAmount{500});
145 ac.view().update(sle);
146 return true;
147 });
148 }
149
150 void
152 {
153 using namespace test::jtx;
154 testcase << "account root removed";
155
156 // An account was deleted, but not by an AccountDelete transaction.
158 {{"an account root was deleted"}},
159 [](Account const& A1, Account const&, ApplyContext& ac) {
160 // remove an account from the view
161 auto const sle = ac.view().peek(keylet::account(A1.id()));
162 if (!sle)
163 return false;
164 ac.view().erase(sle);
165 return true;
166 });
167
168 // Successful AccountDelete transaction that didn't delete an account.
169 //
170 // Note that this is a case where a second invocation of the invariant
171 // checker returns a tecINVARIANT_FAILED, not a tefINVARIANT_FAILED.
172 // After a discussion with the team, we believe that's okay.
174 {{"account deletion succeeded without deleting an account"}},
175 [](Account const&, Account const&, ApplyContext& ac) {
176 return true;
177 },
178 XRPAmount{},
179 STTx{ttACCOUNT_DELETE, [](STObject& tx) {}},
181
182 // Successful AccountDelete that deleted more than one account.
184 {{"account deletion succeeded but deleted multiple accounts"}},
185 [](Account const& A1, Account const& A2, ApplyContext& ac) {
186 // remove two accounts from the view
187 auto const sleA1 = ac.view().peek(keylet::account(A1.id()));
188 auto const sleA2 = ac.view().peek(keylet::account(A2.id()));
189 if (!sleA1 || !sleA2)
190 return false;
191 ac.view().erase(sleA1);
192 ac.view().erase(sleA2);
193 return true;
194 },
195 XRPAmount{},
196 STTx{ttACCOUNT_DELETE, [](STObject& tx) {}});
197 }
198
199 void
201 {
202 using namespace test::jtx;
203 testcase << "account root deletion left artifact";
204
205 for (auto const& keyletInfo : directAccountKeylets)
206 {
207 // TODO: Use structured binding once LLVM 16 is the minimum
208 // supported version. See also:
209 // https://github.com/llvm/llvm-project/issues/48582
210 // https://github.com/llvm/llvm-project/commit/127bf44385424891eb04cff8e52d3f157fc2cb7c
211 if (!keyletInfo.includeInTests)
212 continue;
213 auto const& keyletfunc = keyletInfo.function;
214 auto const& type = keyletInfo.expectedLEName;
215
216 using namespace std::string_literals;
217
219 {{"account deletion left behind a "s + type.c_str() +
220 " object"}},
221 [&](Account const& A1, Account const& A2, ApplyContext& ac) {
222 // Add an object to the ledger for account A1, then delete
223 // A1
224 auto const a1 = A1.id();
225 auto const sleA1 = ac.view().peek(keylet::account(a1));
226 if (!sleA1)
227 return false;
228
229 auto const key = std::invoke(keyletfunc, a1);
230 auto const newSLE = std::make_shared<SLE>(key);
231 ac.view().insert(newSLE);
232 ac.view().erase(sleA1);
233
234 return true;
235 },
236 XRPAmount{},
237 STTx{ttACCOUNT_DELETE, [](STObject& tx) {}});
238 };
239
240 // NFT special case
242 {{"account deletion left behind a NFTokenPage object"}},
243 [&](Account const& A1, Account const&, ApplyContext& ac) {
244 // remove an account from the view
245 auto const sle = ac.view().peek(keylet::account(A1.id()));
246 if (!sle)
247 return false;
248 ac.view().erase(sle);
249 return true;
250 },
251 XRPAmount{},
252 STTx{ttACCOUNT_DELETE, [](STObject& tx) {}},
254 [&](Account const& A1, Account const&, Env& env) {
255 // Preclose callback to mint the NFT which will be deleted in
256 // the Precheck callback above.
257 env(token::mint(A1));
258
259 return true;
260 });
261
262 // AMM special cases
263 AccountID ammAcctID;
264 uint256 ammKey;
265 Issue ammIssue;
267 {{"account deletion left behind a DirectoryNode object"}},
268 [&](Account const& A1, Account const& A2, ApplyContext& ac) {
269 // Delete the AMM account without cleaning up the directory or
270 // deleting the AMM object
271 auto const sle = ac.view().peek(keylet::account(ammAcctID));
272 if (!sle)
273 return false;
274
275 BEAST_EXPECT(sle->at(~sfAMMID));
276 BEAST_EXPECT(sle->at(~sfAMMID) == ammKey);
277
278 ac.view().erase(sle);
279
280 return true;
281 },
282 XRPAmount{},
283 STTx{ttAMM_WITHDRAW, [](STObject& tx) {}},
285 [&](Account const& A1, Account const& A2, Env& env) {
286 // Preclose callback to create the AMM which will be partially
287 // deleted in the Precheck callback above.
288 AMM const amm(env, A1, XRP(100), A1["USD"](50));
289 ammAcctID = amm.ammAccount();
290 ammKey = amm.ammID();
291 ammIssue = amm.lptIssue();
292 return true;
293 });
295 {{"account deletion left behind a AMM object"}},
296 [&](Account const& A1, Account const& A2, ApplyContext& ac) {
297 // Delete all the AMM's trust lines, remove the AMM from the AMM
298 // account's directory (this deletes the directory), and delete
299 // the AMM account. Do not delete the AMM object.
300 auto const sle = ac.view().peek(keylet::account(ammAcctID));
301 if (!sle)
302 return false;
303
304 BEAST_EXPECT(sle->at(~sfAMMID));
305 BEAST_EXPECT(sle->at(~sfAMMID) == ammKey);
306
307 for (auto const& trustKeylet :
308 {keylet::line(ammAcctID, A1["USD"]),
309 keylet::line(A1, ammIssue)})
310 {
311 if (auto const line = ac.view().peek(trustKeylet); !line)
312 {
313 return false;
314 }
315 else
316 {
317 STAmount const lowLimit = line->at(sfLowLimit);
318 STAmount const highLimit = line->at(sfHighLimit);
319 BEAST_EXPECT(
321 ac.view(),
322 line,
323 lowLimit.getIssuer(),
324 highLimit.getIssuer(),
325 ac.journal) == tesSUCCESS);
326 }
327 }
328
329 auto const ammSle = ac.view().peek(keylet::amm(ammKey));
330 if (!BEAST_EXPECT(ammSle))
331 return false;
332 auto const ownerDirKeylet = keylet::ownerDir(ammAcctID);
333
334 BEAST_EXPECT(ac.view().dirRemove(
335 ownerDirKeylet, ammSle->at(sfOwnerNode), ammKey, false));
336 BEAST_EXPECT(
337 !ac.view().exists(ownerDirKeylet) ||
338 ac.view().emptyDirDelete(ownerDirKeylet));
339
340 ac.view().erase(sle);
341
342 return true;
343 },
344 XRPAmount{},
345 STTx{ttAMM_WITHDRAW, [](STObject& tx) {}},
347 [&](Account const& A1, Account const& A2, Env& env) {
348 // Preclose callback to create the AMM which will be partially
349 // deleted in the Precheck callback above.
350 AMM const amm(env, A1, XRP(100), A1["USD"](50));
351 ammAcctID = amm.ammAccount();
352 ammKey = amm.ammID();
353 ammIssue = amm.lptIssue();
354 return true;
355 });
356 }
357
358 void
360 {
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) {
367 // replace an entry in the table with an SLE of a different type
368 auto const sle = ac.view().peek(keylet::account(A1.id()));
369 if (!sle)
370 return false;
371 auto const sleNew = std::make_shared<SLE>(ltTICKET, sle->key());
372 ac.rawView().rawReplace(sleNew);
373 return true;
374 });
375
377 {{"invalid ledger entry type added"}},
378 [](Account const& A1, Account const&, ApplyContext& ac) {
379 // add an entry in the table with an SLE of an invalid type
380 auto const sle = ac.view().peek(keylet::account(A1.id()));
381 if (!sle)
382 return false;
383
384 // make a dummy escrow ledger entry, then change the type to an
385 // unsupported value so that the valid type invariant check
386 // will fail.
387 auto const sleNew = std::make_shared<SLE>(
388 keylet::escrow(A1, (*sle)[sfSequence] + 2));
389
390 // We don't use ltNICKNAME directly since it's marked deprecated
391 // to prevent accidental use elsewhere.
392 sleNew->type_ = static_cast<LedgerEntryType>('n');
393 ac.view().insert(sleNew);
394 return true;
395 });
396 }
397
398 void
400 {
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) {
406 // create simple trust SLE with xrp currency
407 auto const sleNew = std::make_shared<SLE>(
408 keylet::line(A1, A2, xrpIssue().currency));
409 ac.view().insert(sleNew);
410 return true;
411 });
412 }
413
414 void
416 {
417 using namespace test::jtx;
418 testcase << "trust lines with deep freeze flag without freeze "
419 "not allowed";
421 {{"a trust line with deep freeze flag without normal freeze was "
422 "created"}},
423 [](Account const& A1, Account const& A2, ApplyContext& ac) {
424 auto const sleNew = std::make_shared<SLE>(
425 keylet::line(A1, A2, A1["USD"].currency));
426 sleNew->setFieldAmount(sfLowLimit, A1["USD"](0));
427 sleNew->setFieldAmount(sfHighLimit, A1["USD"](0));
428
429 std::uint32_t uFlags = 0u;
430 uFlags |= lsfLowDeepFreeze;
431 sleNew->setFieldU32(sfFlags, uFlags);
432 ac.view().insert(sleNew);
433 return true;
434 });
435
437 {{"a trust line with deep freeze flag without normal freeze was "
438 "created"}},
439 [](Account const& A1, Account const& A2, ApplyContext& ac) {
440 auto const sleNew = std::make_shared<SLE>(
441 keylet::line(A1, A2, A1["USD"].currency));
442 sleNew->setFieldAmount(sfLowLimit, A1["USD"](0));
443 sleNew->setFieldAmount(sfHighLimit, A1["USD"](0));
444 std::uint32_t uFlags = 0u;
445 uFlags |= lsfHighDeepFreeze;
446 sleNew->setFieldU32(sfFlags, uFlags);
447 ac.view().insert(sleNew);
448 return true;
449 });
450
452 {{"a trust line with deep freeze flag without normal freeze was "
453 "created"}},
454 [](Account const& A1, Account const& A2, ApplyContext& ac) {
455 auto const sleNew = std::make_shared<SLE>(
456 keylet::line(A1, A2, A1["USD"].currency));
457 sleNew->setFieldAmount(sfLowLimit, A1["USD"](0));
458 sleNew->setFieldAmount(sfHighLimit, A1["USD"](0));
459 std::uint32_t uFlags = 0u;
461 sleNew->setFieldU32(sfFlags, uFlags);
462 ac.view().insert(sleNew);
463 return true;
464 });
465
467 {{"a trust line with deep freeze flag without normal freeze was "
468 "created"}},
469 [](Account const& A1, Account const& A2, ApplyContext& ac) {
470 auto const sleNew = std::make_shared<SLE>(
471 keylet::line(A1, A2, A1["USD"].currency));
472 sleNew->setFieldAmount(sfLowLimit, A1["USD"](0));
473 sleNew->setFieldAmount(sfHighLimit, A1["USD"](0));
474 std::uint32_t uFlags = 0u;
476 sleNew->setFieldU32(sfFlags, uFlags);
477 ac.view().insert(sleNew);
478 return true;
479 });
480
482 {{"a trust line with deep freeze flag without normal freeze was "
483 "created"}},
484 [](Account const& A1, Account const& A2, ApplyContext& ac) {
485 auto const sleNew = std::make_shared<SLE>(
486 keylet::line(A1, A2, A1["USD"].currency));
487 sleNew->setFieldAmount(sfLowLimit, A1["USD"](0));
488 sleNew->setFieldAmount(sfHighLimit, A1["USD"](0));
489 std::uint32_t uFlags = 0u;
491 sleNew->setFieldU32(sfFlags, uFlags);
492 ac.view().insert(sleNew);
493 return true;
494 });
495 }
496
497 void
499 {
500 using namespace test::jtx;
501 testcase << "transfers when frozen";
502
503 Account G1{"G1"};
504 // Helper function to establish the trustlines
505 auto const createTrustlines =
506 [&](Account const& A1, Account const& A2, Env& env) {
507 // Preclose callback to establish trust lines with gateway
508 env.fund(XRP(1000), G1);
509
510 env.trust(G1["USD"](10000), A1);
511 env.trust(G1["USD"](10000), A2);
512 env.close();
513
514 env(pay(G1, A1, G1["USD"](1000)));
515 env(pay(G1, A2, G1["USD"](1000)));
516 env.close();
517
518 return true;
519 };
520
521 auto const A1FrozenByIssuer =
522 [&](Account const& A1, Account const& A2, Env& env) {
523 createTrustlines(A1, A2, env);
524 env(trust(G1, A1["USD"](10000), tfSetFreeze));
525 env.close();
526
527 return true;
528 };
529
530 auto const A1DeepFrozenByIssuer =
531 [&](Account const& A1, Account const& A2, Env& env) {
532 A1FrozenByIssuer(A1, A2, env);
533 env(trust(G1, A1["USD"](10000), tfSetDeepFreeze));
534 env.close();
535
536 return true;
537 };
538
539 auto const changeBalances = [&](Account const& A1,
540 Account const& A2,
541 ApplyContext& ac,
542 int A1Balance,
543 int A2Balance) {
544 auto const sleA1 = ac.view().peek(keylet::line(A1, G1["USD"]));
545 auto const sleA2 = ac.view().peek(keylet::line(A2, G1["USD"]));
546
547 sleA1->setFieldAmount(sfBalance, G1["USD"](A1Balance));
548 sleA2->setFieldAmount(sfBalance, G1["USD"](A2Balance));
549
550 ac.view().update(sleA1);
551 ac.view().update(sleA2);
552 };
553
554 // test: imitating frozen A1 making a payment to A2.
556 {{"Attempting to move frozen funds"}},
557 [&](Account const& A1, Account const& A2, ApplyContext& ac) {
558 changeBalances(A1, A2, ac, -900, -1100);
559 return true;
560 },
561 XRPAmount{},
562 STTx{ttPAYMENT, [](STObject& tx) {}},
564 A1FrozenByIssuer);
565
566 // test: imitating deep frozen A1 making a payment to A2.
568 {{"Attempting to move frozen funds"}},
569 [&](Account const& A1, Account const& A2, ApplyContext& ac) {
570 changeBalances(A1, A2, ac, -900, -1100);
571 return true;
572 },
573 XRPAmount{},
574 STTx{ttPAYMENT, [](STObject& tx) {}},
576 A1DeepFrozenByIssuer);
577
578 // test: imitating A2 making a payment to deep frozen A1.
580 {{"Attempting to move frozen funds"}},
581 [&](Account const& A1, Account const& A2, ApplyContext& ac) {
582 changeBalances(A1, A2, ac, -1100, -900);
583 return true;
584 },
585 XRPAmount{},
586 STTx{ttPAYMENT, [](STObject& tx) {}},
588 A1DeepFrozenByIssuer);
589 }
590
591 void
593 {
594 using namespace test::jtx;
595 testcase << "XRP balance checks";
596
598 {{"Cannot return non-native STAmount as XRPAmount"}},
599 [](Account const& A1, Account const& A2, ApplyContext& ac) {
600 // non-native balance
601 auto const sle = ac.view().peek(keylet::account(A1.id()));
602 if (!sle)
603 return false;
604 STAmount const nonNative(A2["USD"](51));
605 sle->setFieldAmount(sfBalance, nonNative);
606 ac.view().update(sle);
607 return true;
608 });
609
611 {{"incorrect account XRP balance"},
612 {"XRP net change was positive: 99999999000000001"}},
613 [this](Account const& A1, Account const&, ApplyContext& ac) {
614 // balance exceeds genesis amount
615 auto const sle = ac.view().peek(keylet::account(A1.id()));
616 if (!sle)
617 return false;
618 // Use `drops(1)` to bypass a call to STAmount::canonicalize
619 // with an invalid value
620 sle->setFieldAmount(sfBalance, INITIAL_XRP + drops(1));
621 BEAST_EXPECT(!sle->getFieldAmount(sfBalance).negative());
622 ac.view().update(sle);
623 return true;
624 });
625
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) {
630 // balance is negative
631 auto const sle = ac.view().peek(keylet::account(A1.id()));
632 if (!sle)
633 return false;
634 sle->setFieldAmount(sfBalance, STAmount{1, true});
635 BEAST_EXPECT(sle->getFieldAmount(sfBalance).negative());
636 ac.view().update(sle);
637 return true;
638 });
639 }
640
641 void
643 {
644 using namespace test::jtx;
645 using namespace std::string_literals;
646 testcase << "Transaction fee checks";
647
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; },
652 XRPAmount{-1});
653
655 {{"fee paid exceeds system limit: "s + to_string(INITIAL_XRP)},
656 {"XRP net change of 0 doesn't match fee "s +
658 [](Account const&, Account const&, ApplyContext&) { return true; },
660
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; },
665 XRPAmount{20},
666 STTx{ttACCOUNT_SET, [](STObject& tx) {
667 tx.setFieldAmount(sfFee, XRPAmount{10});
668 }});
669 }
670
671 void
673 {
674 using namespace test::jtx;
675 testcase << "no bad offers";
676
678 {{"offer with a bad amount"}},
679 [](Account const& A1, Account const&, ApplyContext& ac) {
680 // offer with negative takerpays
681 auto const sle = ac.view().peek(keylet::account(A1.id()));
682 if (!sle)
683 return false;
684 auto sleNew = std::make_shared<SLE>(
685 keylet::offer(A1.id(), (*sle)[sfSequence]));
686 sleNew->setAccountID(sfAccount, A1.id());
687 sleNew->setFieldU32(sfSequence, (*sle)[sfSequence]);
688 sleNew->setFieldAmount(sfTakerPays, XRP(-1));
689 ac.view().insert(sleNew);
690 return true;
691 });
692
694 {{"offer with a bad amount"}},
695 [](Account const& A1, Account const&, ApplyContext& ac) {
696 // offer with negative takergets
697 auto const sle = ac.view().peek(keylet::account(A1.id()));
698 if (!sle)
699 return false;
700 auto sleNew = std::make_shared<SLE>(
701 keylet::offer(A1.id(), (*sle)[sfSequence]));
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);
707 return true;
708 });
709
711 {{"offer with a bad amount"}},
712 [](Account const& A1, Account const&, ApplyContext& ac) {
713 // offer XRP to XRP
714 auto const sle = ac.view().peek(keylet::account(A1.id()));
715 if (!sle)
716 return false;
717 auto sleNew = std::make_shared<SLE>(
718 keylet::offer(A1.id(), (*sle)[sfSequence]));
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);
724 return true;
725 });
726 }
727
728 void
730 {
731 using namespace test::jtx;
732 testcase << "no zero escrow";
733
735 {{"Cannot return non-native STAmount as XRPAmount"}},
736 [](Account const& A1, Account const& A2, ApplyContext& ac) {
737 // escrow with nonnative amount
738 auto const sle = ac.view().peek(keylet::account(A1.id()));
739 if (!sle)
740 return false;
741 auto sleNew = std::make_shared<SLE>(
742 keylet::escrow(A1, (*sle)[sfSequence] + 2));
743 STAmount nonNative(A2["USD"](51));
744 sleNew->setFieldAmount(sfAmount, nonNative);
745 ac.view().insert(sleNew);
746 return true;
747 });
748
750 {{"XRP net change of -1000000 doesn't match fee 0"},
751 {"escrow specifies invalid amount"}},
752 [](Account const& A1, Account const&, ApplyContext& ac) {
753 // escrow with negative amount
754 auto const sle = ac.view().peek(keylet::account(A1.id()));
755 if (!sle)
756 return false;
757 auto sleNew = std::make_shared<SLE>(
758 keylet::escrow(A1, (*sle)[sfSequence] + 2));
759 sleNew->setFieldAmount(sfAmount, XRP(-1));
760 ac.view().insert(sleNew);
761 return true;
762 });
763
765 {{"XRP net change was positive: 100000000000000001"},
766 {"escrow specifies invalid amount"}},
767 [](Account const& A1, Account const&, ApplyContext& ac) {
768 // escrow with too-large amount
769 auto const sle = ac.view().peek(keylet::account(A1.id()));
770 if (!sle)
771 return false;
772 auto sleNew = std::make_shared<SLE>(
773 keylet::escrow(A1, (*sle)[sfSequence] + 2));
774 // Use `drops(1)` to bypass a call to STAmount::canonicalize
775 // with an invalid value
776 sleNew->setFieldAmount(sfAmount, INITIAL_XRP + drops(1));
777 ac.view().insert(sleNew);
778 return true;
779 });
780 }
781
782 void
784 {
785 using namespace test::jtx;
786 testcase << "valid new account root";
787
789 {{"account root created illegally"}},
790 [](Account const&, Account const&, ApplyContext& ac) {
791 // Insert a new account root created by a non-payment into
792 // the view.
793 Account const A3{"A3"};
794 Keylet const acctKeylet = keylet::account(A3);
795 auto const sleNew = std::make_shared<SLE>(acctKeylet);
796 ac.view().insert(sleNew);
797 return true;
798 });
799
801 {{"multiple accounts created in a single transaction"}},
802 [](Account const&, Account const&, ApplyContext& ac) {
803 // Insert two new account roots into the view.
804 {
805 Account const A3{"A3"};
806 Keylet const acctKeylet = keylet::account(A3);
807 auto const sleA3 = std::make_shared<SLE>(acctKeylet);
808 ac.view().insert(sleA3);
809 }
810 {
811 Account const A4{"A4"};
812 Keylet const acctKeylet = keylet::account(A4);
813 auto const sleA4 = std::make_shared<SLE>(acctKeylet);
814 ac.view().insert(sleA4);
815 }
816 return true;
817 });
818
820 {{"account created with wrong starting sequence number"}},
821 [](Account const&, Account const&, ApplyContext& ac) {
822 // Insert a new account root with the wrong starting sequence.
823 Account const A3{"A3"};
824 Keylet const acctKeylet = keylet::account(A3);
825 auto const sleNew = std::make_shared<SLE>(acctKeylet);
826 sleNew->setFieldU32(sfSequence, ac.view().seq() + 1);
827 ac.view().insert(sleNew);
828 return true;
829 },
830 XRPAmount{},
831 STTx{ttPAYMENT, [](STObject& tx) {}});
832
834 {{"pseudo-account created by a wrong transaction type"}},
835 [](Account const&, Account const&, ApplyContext& ac) {
836 Account const A3{"A3"};
837 Keylet const acctKeylet = keylet::account(A3);
838 auto const sleNew = std::make_shared<SLE>(acctKeylet);
839 sleNew->setFieldU32(sfSequence, 0);
840 sleNew->setFieldH256(sfAMMID, uint256(1));
841 sleNew->setFieldU32(
842 sfFlags,
844 ac.view().insert(sleNew);
845 return true;
846 },
847 XRPAmount{},
848 STTx{ttPAYMENT, [](STObject& tx) {}});
849
851 {{"account created with wrong starting sequence number"}},
852 [](Account const&, Account const&, ApplyContext& ac) {
853 Account const A3{"A3"};
854 Keylet const acctKeylet = keylet::account(A3);
855 auto const sleNew = std::make_shared<SLE>(acctKeylet);
856 sleNew->setFieldU32(sfSequence, ac.view().seq());
857 sleNew->setFieldH256(sfAMMID, uint256(1));
858 sleNew->setFieldU32(
859 sfFlags,
861 ac.view().insert(sleNew);
862 return true;
863 },
864 XRPAmount{},
865 STTx{ttAMM_CREATE, [](STObject& tx) {}});
866
868 {{"pseudo-account created with wrong flags"}},
869 [](Account const&, Account const&, ApplyContext& ac) {
870 Account const A3{"A3"};
871 Keylet const acctKeylet = keylet::account(A3);
872 auto const sleNew = std::make_shared<SLE>(acctKeylet);
873 sleNew->setFieldU32(sfSequence, 0);
874 sleNew->setFieldH256(sfAMMID, uint256(1));
875 sleNew->setFieldU32(
877 ac.view().insert(sleNew);
878 return true;
879 },
880 XRPAmount{},
881 STTx{ttVAULT_CREATE, [](STObject& tx) {}});
882
884 {{"pseudo-account created with wrong flags"}},
885 [](Account const&, Account const&, ApplyContext& ac) {
886 Account const A3{"A3"};
887 Keylet const acctKeylet = keylet::account(A3);
888 auto const sleNew = std::make_shared<SLE>(acctKeylet);
889 sleNew->setFieldU32(sfSequence, 0);
890 sleNew->setFieldH256(sfAMMID, uint256(1));
891 sleNew->setFieldU32(
892 sfFlags,
895 ac.view().insert(sleNew);
896 return true;
897 },
898 XRPAmount{},
899 STTx{ttAMM_CREATE, [](STObject& tx) {}});
900 }
901
902 void
904 {
905 using namespace test::jtx;
906 testcase << "NFTokenPage";
907
908 // lambda that returns an STArray of NFTokenIDs.
909 uint256 const firstNFTID(
910 "0000000000000000000000000000000000000001FFFFFFFFFFFFFFFF00000000");
911 auto makeNFTokenIDs = [&firstNFTID](unsigned int nftCount) {
912 SOTemplate const* nfTokenTemplate =
914 sfNFToken);
915
916 uint256 nftID(firstNFTID);
917 STArray ret;
918 for (int i = 0; i < nftCount; ++i)
919 {
920 STObject newNFToken(
921 *nfTokenTemplate, sfNFToken, [&nftID](STObject& object) {
922 object.setFieldH256(sfNFTokenID, nftID);
923 });
924 ret.push_back(std::move(newNFToken));
925 ++nftID;
926 }
927 return ret;
928 };
929
931 {{"NFT page has invalid size"}},
932 [&makeNFTokenIDs](
933 Account const& A1, Account const&, ApplyContext& ac) {
934 auto nftPage = std::make_shared<SLE>(keylet::nftpage_max(A1));
935 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(0));
936
937 ac.view().insert(nftPage);
938 return true;
939 });
940
942 {{"NFT page has invalid size"}},
943 [&makeNFTokenIDs](
944 Account const& A1, Account const&, ApplyContext& ac) {
945 auto nftPage = std::make_shared<SLE>(keylet::nftpage_max(A1));
946 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(33));
947
948 ac.view().insert(nftPage);
949 return true;
950 });
951
953 {{"NFTs on page are not sorted"}},
954 [&makeNFTokenIDs](
955 Account const& A1, Account const&, ApplyContext& ac) {
956 STArray nfTokens = makeNFTokenIDs(2);
957 std::iter_swap(nfTokens.begin(), nfTokens.begin() + 1);
958
959 auto nftPage = std::make_shared<SLE>(keylet::nftpage_max(A1));
960 nftPage->setFieldArray(sfNFTokens, nfTokens);
961
962 ac.view().insert(nftPage);
963 return true;
964 });
965
967 {{"NFT contains empty URI"}},
968 [&makeNFTokenIDs](
969 Account const& A1, Account const&, ApplyContext& ac) {
970 STArray nfTokens = makeNFTokenIDs(1);
971 nfTokens[0].setFieldVL(sfURI, Blob{});
972
973 auto nftPage = std::make_shared<SLE>(keylet::nftpage_max(A1));
974 nftPage->setFieldArray(sfNFTokens, nfTokens);
975
976 ac.view().insert(nftPage);
977 return true;
978 });
979
981 {{"NFT page is improperly linked"}},
982 [&makeNFTokenIDs](
983 Account const& A1, Account const&, ApplyContext& ac) {
984 auto nftPage = std::make_shared<SLE>(keylet::nftpage_max(A1));
985 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(1));
986 nftPage->setFieldH256(
987 sfPreviousPageMin, keylet::nftpage_max(A1).key);
988
989 ac.view().insert(nftPage);
990 return true;
991 });
992
994 {{"NFT page is improperly linked"}},
995 [&makeNFTokenIDs](
996 Account const& A1, Account const& A2, ApplyContext& ac) {
997 auto nftPage = std::make_shared<SLE>(keylet::nftpage_max(A1));
998 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(1));
999 nftPage->setFieldH256(
1000 sfPreviousPageMin, keylet::nftpage_min(A2).key);
1001
1002 ac.view().insert(nftPage);
1003 return true;
1004 });
1005
1007 {{"NFT page is improperly linked"}},
1008 [&makeNFTokenIDs](
1009 Account const& A1, Account const&, ApplyContext& ac) {
1010 auto nftPage = std::make_shared<SLE>(keylet::nftpage_max(A1));
1011 nftPage->setFieldArray(sfNFTokens, makeNFTokenIDs(1));
1012 nftPage->setFieldH256(sfNextPageMin, nftPage->key());
1013
1014 ac.view().insert(nftPage);
1015 return true;
1016 });
1017
1019 {{"NFT page is improperly linked"}},
1020 [&makeNFTokenIDs](
1021 Account const& A1, Account const& A2, ApplyContext& ac) {
1022 STArray nfTokens = makeNFTokenIDs(1);
1023 auto nftPage = std::make_shared<SLE>(keylet::nftpage(
1025 ++(nfTokens[0].getFieldH256(sfNFTokenID))));
1026 nftPage->setFieldArray(sfNFTokens, std::move(nfTokens));
1027 nftPage->setFieldH256(
1028 sfNextPageMin, keylet::nftpage_max(A2).key);
1029
1030 ac.view().insert(nftPage);
1031 return true;
1032 });
1033
1035 {{"NFT found in incorrect page"}},
1036 [&makeNFTokenIDs](
1037 Account const& A1, Account const&, ApplyContext& ac) {
1038 STArray nfTokens = makeNFTokenIDs(2);
1039 auto nftPage = std::make_shared<SLE>(keylet::nftpage(
1041 (nfTokens[1].getFieldH256(sfNFTokenID))));
1042 nftPage->setFieldArray(sfNFTokens, std::move(nfTokens));
1043
1044 ac.view().insert(nftPage);
1045 return true;
1046 });
1047 }
1048
1049 void
1051 {
1052 using namespace test::jtx;
1053
1054 testcase << "PermissionedDomain";
1056 {{"permissioned domain with no rules."}},
1057 [](Account const& A1, Account const&, ApplyContext& ac) {
1058 Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
1059 auto slePd = std::make_shared<SLE>(pdKeylet);
1060 slePd->setAccountID(sfOwner, A1);
1061 slePd->setFieldU32(sfSequence, 10);
1062
1063 ac.view().insert(slePd);
1064 return true;
1065 },
1066 XRPAmount{},
1067 STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
1069
1070 testcase << "PermissionedDomain 2";
1071
1072 auto constexpr tooBig = maxPermissionedDomainCredentialsArraySize + 1;
1074 {{"permissioned domain bad credentials size " +
1075 std::to_string(tooBig)}},
1076 [](Account const& A1, Account const& A2, ApplyContext& ac) {
1077 Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
1078 auto slePd = std::make_shared<SLE>(pdKeylet);
1079 slePd->setAccountID(sfOwner, A1);
1080 slePd->setFieldU32(sfSequence, 10);
1081
1082 STArray credentials(sfAcceptedCredentials, tooBig);
1083 for (std::size_t n = 0; n < tooBig; ++n)
1084 {
1085 auto cred = STObject::makeInnerObject(sfCredential);
1086 cred.setAccountID(sfIssuer, A2);
1087 auto credType =
1088 std::string("cred_type") + std::to_string(n);
1089 cred.setFieldVL(
1090 sfCredentialType,
1091 Slice(credType.c_str(), credType.size()));
1092 credentials.push_back(std::move(cred));
1093 }
1094 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1095 ac.view().insert(slePd);
1096 return true;
1097 },
1098 XRPAmount{},
1099 STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
1101
1102 testcase << "PermissionedDomain 3";
1104 {{"permissioned domain credentials aren't sorted"}},
1105 [](Account const& A1, Account const& A2, ApplyContext& ac) {
1106 Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
1107 auto slePd = std::make_shared<SLE>(pdKeylet);
1108 slePd->setAccountID(sfOwner, A1);
1109 slePd->setFieldU32(sfSequence, 10);
1110
1111 STArray credentials(sfAcceptedCredentials, 2);
1112 for (std::size_t n = 0; n < 2; ++n)
1113 {
1114 auto cred = STObject::makeInnerObject(sfCredential);
1115 cred.setAccountID(sfIssuer, A2);
1116 auto credType =
1117 std::string("cred_type") + std::to_string(9 - n);
1118 cred.setFieldVL(
1119 sfCredentialType,
1120 Slice(credType.c_str(), credType.size()));
1121 credentials.push_back(std::move(cred));
1122 }
1123 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1124 ac.view().insert(slePd);
1125 return true;
1126 },
1127 XRPAmount{},
1128 STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
1130
1131 testcase << "PermissionedDomain 4";
1133 {{"permissioned domain credentials aren't unique"}},
1134 [](Account const& A1, Account const& A2, ApplyContext& ac) {
1135 Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
1136 auto slePd = std::make_shared<SLE>(pdKeylet);
1137 slePd->setAccountID(sfOwner, A1);
1138 slePd->setFieldU32(sfSequence, 10);
1139
1140 STArray credentials(sfAcceptedCredentials, 2);
1141 for (std::size_t n = 0; n < 2; ++n)
1142 {
1143 auto cred = STObject::makeInnerObject(sfCredential);
1144 cred.setAccountID(sfIssuer, A2);
1145 cred.setFieldVL(sfCredentialType, Slice("cred_type", 9));
1146 credentials.push_back(std::move(cred));
1147 }
1148 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1149 ac.view().insert(slePd);
1150 return true;
1151 },
1152 XRPAmount{},
1153 STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
1155
1156 auto const createPD = [](ApplyContext& ac,
1158 Account const& A1,
1159 Account const& A2) {
1160 sle->setAccountID(sfOwner, A1);
1161 sle->setFieldU32(sfSequence, 10);
1162
1163 STArray credentials(sfAcceptedCredentials, 2);
1164 for (std::size_t n = 0; n < 2; ++n)
1165 {
1166 auto cred = STObject::makeInnerObject(sfCredential);
1167 cred.setAccountID(sfIssuer, A2);
1168 auto credType = "cred_type" + std::to_string(n);
1169 cred.setFieldVL(
1170 sfCredentialType, Slice(credType.c_str(), credType.size()));
1171 credentials.push_back(std::move(cred));
1172 }
1173 sle->setFieldArray(sfAcceptedCredentials, credentials);
1174 ac.view().insert(sle);
1175 };
1176
1177 testcase << "PermissionedDomain Set 1";
1179 {{"permissioned domain with no rules."}},
1180 [createPD](Account const& A1, Account const& A2, ApplyContext& ac) {
1181 Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
1182 auto slePd = std::make_shared<SLE>(pdKeylet);
1183
1184 // create PD
1185 createPD(ac, slePd, A1, A2);
1186
1187 // update PD with empty rules
1188 {
1189 STArray credentials(sfAcceptedCredentials, 2);
1190 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1191 ac.view().update(slePd);
1192 }
1193
1194 return true;
1195 },
1196 XRPAmount{},
1197 STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
1199
1200 testcase << "PermissionedDomain Set 2";
1202 {{"permissioned domain bad credentials size " +
1203 std::to_string(tooBig)}},
1204 [createPD](Account const& A1, Account const& A2, ApplyContext& ac) {
1205 Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
1206 auto slePd = std::make_shared<SLE>(pdKeylet);
1207
1208 // create PD
1209 createPD(ac, slePd, A1, A2);
1210
1211 // update PD
1212 {
1213 STArray credentials(sfAcceptedCredentials, tooBig);
1214
1215 for (std::size_t n = 0; n < tooBig; ++n)
1216 {
1217 auto cred = STObject::makeInnerObject(sfCredential);
1218 cred.setAccountID(sfIssuer, A2);
1219 auto credType = "cred_type2" + std::to_string(n);
1220 cred.setFieldVL(
1221 sfCredentialType,
1222 Slice(credType.c_str(), credType.size()));
1223 credentials.push_back(std::move(cred));
1224 }
1225
1226 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1227 ac.view().update(slePd);
1228 }
1229
1230 return true;
1231 },
1232 XRPAmount{},
1233 STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
1235
1236 testcase << "PermissionedDomain Set 3";
1238 {{"permissioned domain credentials aren't sorted"}},
1239 [createPD](Account const& A1, Account const& A2, ApplyContext& ac) {
1240 Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
1241 auto slePd = std::make_shared<SLE>(pdKeylet);
1242
1243 // create PD
1244 createPD(ac, slePd, A1, A2);
1245
1246 // update PD
1247 {
1248 STArray credentials(sfAcceptedCredentials, 2);
1249 for (std::size_t n = 0; n < 2; ++n)
1250 {
1251 auto cred = STObject::makeInnerObject(sfCredential);
1252 cred.setAccountID(sfIssuer, A2);
1253 auto credType =
1254 std::string("cred_type2") + std::to_string(9 - n);
1255 cred.setFieldVL(
1256 sfCredentialType,
1257 Slice(credType.c_str(), credType.size()));
1258 credentials.push_back(std::move(cred));
1259 }
1260
1261 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1262 ac.view().update(slePd);
1263 }
1264
1265 return true;
1266 },
1267 XRPAmount{},
1268 STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
1270
1271 testcase << "PermissionedDomain Set 4";
1273 {{"permissioned domain credentials aren't unique"}},
1274 [createPD](Account const& A1, Account const& A2, ApplyContext& ac) {
1275 Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10);
1276 auto slePd = std::make_shared<SLE>(pdKeylet);
1277
1278 // create PD
1279 createPD(ac, slePd, A1, A2);
1280
1281 // update PD
1282 {
1283 STArray credentials(sfAcceptedCredentials, 2);
1284 for (std::size_t n = 0; n < 2; ++n)
1285 {
1286 auto cred = STObject::makeInnerObject(sfCredential);
1287 cred.setAccountID(sfIssuer, A2);
1288 cred.setFieldVL(
1289 sfCredentialType, Slice("cred_type", 9));
1290 credentials.push_back(std::move(cred));
1291 }
1292 slePd->setFieldArray(sfAcceptedCredentials, credentials);
1293 ac.view().update(slePd);
1294 }
1295
1296 return true;
1297 },
1298 XRPAmount{},
1299 STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}},
1301 }
1302
1303public:
1304 void
1305 run() override
1306 {
1321 }
1322};
1323
1324BEAST_DEFINE_TESTSUITE(Invariants, ledger, ripple);
1325
1326} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:60
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition: suite.h:533
State information when applying a tx.
Definition: ApplyContext.h:37
ApplyView & view()
Definition: ApplyContext.h:55
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
SOTemplate const * findSOTemplateBySField(SField const &sField) const
static InnerObjectFormats const & getInstance()
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.
std::function< bool(test::jtx::Account const &a, test::jtx::Account const &b, test::jtx::Env &env)> Preclose
void run() override
Runs the suite.
void testNoDeepFreezeTrustLinesWithoutFreeze()
A currency issued by an account.
Definition: Issue.h:36
Defines the fields and their attributes within a STObject.
Definition: SOTemplate.h:114
AccountID const & getIssuer() const
Definition: STAmount.h:508
void push_back(STObject const &object)
Definition: STArray.h:212
iterator begin()
Definition: STArray.h:224
void setFieldAmount(SField const &field, STAmount const &)
Definition: STObject.cpp:789
static STObject makeInnerObject(SField const &name)
Definition: STObject.cpp:95
An immutable linear range of bytes.
Definition: Slice.h:46
Convenience class to test AMM functionality.
Definition: AMM.h:124
Immutable cryptographic account descriptor.
Definition: Account.h:39
AccountID id() const
Returns the Account ID.
Definition: Account.h:107
A transaction testing environment.
Definition: Env.h:121
Set the fee on a JTx.
Definition: fee.h:37
T invoke(T... args)
T iter_swap(T... args)
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition: Indexes.cpp:563
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:439
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:237
Keylet const & amendments() noexcept
The index of the amendment table.
Definition: Indexes.cpp:207
Keylet nftpage(Keylet const &k, uint256 const &token)
Definition: Indexes.cpp:412
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:177
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Definition: Indexes.cpp:382
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Definition: Indexes.cpp:396
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Definition: Indexes.cpp:404
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:367
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:267
Json::Value mint(jtx::Account const &account, std::uint32_t nfTokenTaxon)
Mint an NFToken.
Definition: token.cpp:34
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition: trust.cpp:32
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:30
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
FeatureBitset supported_amendments()
Definition: Env.h:74
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:118
constexpr std::uint32_t tfSetDeepFreeze
Definition: TxFlags.h:117
base_uint< 256 > uint256
Definition: base_uint.h:558
std::size_t constexpr maxPermissionedDomainCredentialsArraySize
The maximum number of credentials can be passed in array for permissioned domain.
Definition: Protocol.h:111
@ lsfHighDeepFreeze
@ lsfRequireDestTag
@ lsfDefaultRipple
@ lsfHighFreeze
@ lsfDisableMaster
@ lsfDepositAuth
@ lsfLowFreeze
@ lsfLowDeepFreeze
constexpr XRPAmount INITIAL_XRP
Configure the native currency.
std::array< keyletDesc< AccountID const & >, 6 > const directAccountKeylets
Definition: Indexes.h:381
@ tefINVARIANT_FAILED
Definition: TER.h:183
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition: View.cpp:1419
@ tecINVARIANT_FAILED
Definition: TER.h:313
@ tesSUCCESS
Definition: TER.h:244
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:54
@ tapNONE
Definition: ApplyView.h:32
TERSubset< CanCvtToTER > TER
Definition: TER.h:643
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:115
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:39
T to_string(T... args)