xrpld
Loading...
Searching...
No Matches
AccountDelete_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/acctdelete.h>
6#include <test/jtx/amount.h>
7#include <test/jtx/balance.h>
8#include <test/jtx/check.h>
9#include <test/jtx/credentials.h>
10#include <test/jtx/deposit.h>
11#include <test/jtx/did.h>
12#include <test/jtx/escrow.h>
13#include <test/jtx/fee.h>
14#include <test/jtx/flags.h>
15#include <test/jtx/multisign.h>
16#include <test/jtx/noop.h>
17#include <test/jtx/offer.h>
18#include <test/jtx/owners.h>
19#include <test/jtx/pay.h>
20#include <test/jtx/regkey.h>
21#include <test/jtx/sig.h>
22#include <test/jtx/ter.h>
23#include <test/jtx/ticket.h>
24#include <test/jtx/trust.h>
25#include <test/jtx/txflags.h>
26#include <test/jtx/vault.h>
27
28#include <xrpl/basics/base_uint.h>
29#include <xrpl/basics/chrono.h>
30#include <xrpl/basics/strHex.h>
31#include <xrpl/beast/unit_test/suite.h>
32#include <xrpl/ledger/ReadView.h>
33#include <xrpl/protocol/Feature.h>
34#include <xrpl/protocol/Indexes.h>
35#include <xrpl/protocol/Issue.h>
36#include <xrpl/protocol/Keylet.h>
37#include <xrpl/protocol/PublicKey.h>
38#include <xrpl/protocol/SField.h>
39#include <xrpl/protocol/STAmount.h>
40#include <xrpl/protocol/SeqProxy.h>
41#include <xrpl/protocol/TER.h>
42#include <xrpl/protocol/TxFlags.h>
43#include <xrpl/protocol/jss.h>
44
45#include <chrono>
46#include <cstdint>
47#include <memory>
48#include <string>
49
50namespace xrpl::test {
51
53{
54private:
55 // Helper function that verifies the expected DeliveredAmount is present.
56 //
57 // NOTE: the function _infers_ the transaction to operate on by calling
58 // env.tx(), which returns the result from the most recent transaction.
59 void
61 {
62 // Get the hash for the most recent transaction.
63 std::string const txHash{
64 env.tx()->getJson(JsonOptions::Values::None)[jss::hash].asString()};
65
66 // Verify DeliveredAmount and delivered_amount metadata are correct.
67 // We can't use env.meta() here, because meta() doesn't include
68 // delivered_amount.
69 env.close();
70 json::Value const meta = env.rpc("tx", txHash)[jss::result][jss::meta];
71
72 // Expect there to be a DeliveredAmount field.
73 if (!BEAST_EXPECT(meta.isMember(sfDeliveredAmount.jsonName)))
74 return;
75
76 // DeliveredAmount and delivered_amount should both be present and
77 // equal amount.
78 json::Value const jsonExpect{amount.getJson(JsonOptions::Values::None)};
79 BEAST_EXPECT(meta[sfDeliveredAmount.jsonName] == jsonExpect);
80 BEAST_EXPECT(meta[jss::delivered_amount] == jsonExpect);
81 }
82
83 // Helper function to create a payment channel.
84 static json::Value
86 jtx::Account const& account,
87 jtx::Account const& to,
88 STAmount const& amount,
89 NetClock::duration const& settleDelay,
90 NetClock::time_point const& cancelAfter,
91 PublicKey const& pk)
92 {
93 json::Value jv;
94 jv[jss::TransactionType] = jss::PaymentChannelCreate;
95 jv[jss::Account] = account.human();
96 jv[jss::Destination] = to.human();
97 jv[jss::Amount] = amount.getJson(JsonOptions::Values::None);
98 jv[sfSettleDelay.jsonName] = settleDelay.count();
99 jv[sfCancelAfter.jsonName] = cancelAfter.time_since_epoch().count() + 2;
100 jv[sfPublicKey.jsonName] = strHex(pk.slice());
101 return jv;
102 };
103
104public:
105 void
107 {
108 using namespace jtx;
109
110 testcase("Basics");
111
112 Env env{*this};
113 Account const alice("alice");
114 Account const becky("becky");
115 Account const carol("carol");
116 Account const gw("gw");
117
118 env.fund(XRP(10000), alice, becky, carol, gw);
119 env.close();
120
121 // Alice can't delete her account and then give herself the XRP.
122 env(acctdelete(alice, alice), Ter(temDST_IS_SRC));
123
124 // alice can't delete her account with a negative fee.
125 env(acctdelete(alice, becky), Fee(drops(-1)), Ter(temBAD_FEE));
126
127 // Invalid flags.
128 env(acctdelete(alice, becky), Txflags(tfImmediateOrCancel), Ter(temINVALID_FLAG));
129
130 // Account deletion has a high fee. Make sure the fee requirement
131 // behaves as we expect.
132 auto const acctDelFee{drops(env.current()->fees().increment)};
133 env(acctdelete(alice, becky), Ter(telINSUF_FEE_P));
134
135 // Try a fee one drop less than the required amount.
136 env(acctdelete(alice, becky), Fee(acctDelFee - drops(1)), Ter(telINSUF_FEE_P));
137
138 // alice's account is created too recently to be deleted.
139 env(acctdelete(alice, becky), Fee(acctDelFee), Ter(tecTOO_SOON));
140
141 // Give becky a trustline. She is no longer deletable.
142 env(trust(becky, gw["USD"](1000)));
143 env.close();
144
145 // Give carol a deposit pre-authorization, an offer, a ticket,
146 // a signer list, and a DID. Even with all that she's still deletable.
147 env(deposit::auth(carol, becky));
148 std::uint32_t const carolOfferSeq{env.seq(carol)};
149 env(offer(carol, gw["USD"](51), XRP(51)));
150 std::uint32_t const carolTicketSeq{env.seq(carol) + 1};
151 env(ticket::create(carol, 1));
152 env(signers(carol, 1, {{alice, 1}, {becky, 1}}));
153 env(did::setValid(carol));
154
155 // Deleting should fail with TOO_SOON, which is a relatively
156 // cheap check compared to validating the contents of her directory.
157 env(acctdelete(alice, becky), Fee(acctDelFee), Ter(tecTOO_SOON));
158
159 // Close enough ledgers to almost be able to delete alice's account.
160 incLgrSeqForAccDel(env, alice, 1);
161
162 // alice's account is still created too recently to be deleted.
163 env(acctdelete(alice, becky), Fee(acctDelFee), Ter(tecTOO_SOON));
164
165 // The most recent delete attempt advanced alice's sequence. So
166 // close two ledgers and her account should be deletable.
167 env.close();
168 env.close();
169
170 {
171 auto const aliceOldBalance{env.balance(alice)};
172 auto const beckyOldBalance{env.balance(becky)};
173
174 // Verify that alice's account exists but she has no directory.
175 BEAST_EXPECT(env.closed()->exists(keylet::account(alice.id())));
176 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(alice.id())));
177
178 env(acctdelete(alice, becky), Fee(acctDelFee));
179 verifyDeliveredAmount(env, aliceOldBalance - acctDelFee);
180 env.close();
181
182 // Verify that alice's account and directory are actually gone.
183 BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
184 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(alice.id())));
185
186 // Verify that alice's XRP, minus the fee, was transferred to becky.
187 BEAST_EXPECT(env.balance(becky) == aliceOldBalance + beckyOldBalance - acctDelFee);
188 }
189
190 // Attempt to delete becky's account but get stopped by the trust line.
191 env(acctdelete(becky, carol), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
192 env.close();
193
194 // Verify that becky's account is still there by giving her a regular
195 // key. This has the side effect of setting the lsfPasswordSpent bit
196 // on her account root.
197 Account const beck("beck");
198 env(regkey(becky, beck), Fee(drops(0)));
199 env.close();
200
201 // Show that the lsfPasswordSpent bit is set by attempting to change
202 // becky's regular key for free again. That fails.
203 Account const reb("reb");
204 env(regkey(becky, reb), Sig(becky), Fee(drops(0)), Ter(telINSUF_FEE_P));
205
206 // Close enough ledgers that becky's failing regkey transaction is
207 // no longer retried.
208 for (int i = 0; i < 8; ++i)
209 env.close();
210
211 {
212 auto const beckyOldBalance{env.balance(becky)};
213 auto const carolOldBalance{env.balance(carol)};
214
215 // Verify that Carol's account, directory, deposit
216 // pre-authorization, offer, ticket, and signer list exist.
217 BEAST_EXPECT(env.closed()->exists(keylet::account(carol.id())));
218 BEAST_EXPECT(env.closed()->exists(keylet::ownerDir(carol.id())));
219 BEAST_EXPECT(env.closed()->exists(keylet::depositPreauth(carol.id(), becky.id())));
220 BEAST_EXPECT(env.closed()->exists(
221 keylet::offer(carol.id(), SeqProxy::rawSequence(carolOfferSeq))));
222 BEAST_EXPECT(env.closed()->exists(
223 keylet::ticket(carol.id(), SeqProxy::rawTicket(carolTicketSeq))));
224 BEAST_EXPECT(env.closed()->exists(keylet::signerList(carol.id())));
225
226 // Delete carol's account even with stuff in her directory. Show
227 // that multisigning for the delete does not increase carol's fee.
228 env(acctdelete(carol, becky), Fee(acctDelFee), Msig(alice));
229 verifyDeliveredAmount(env, carolOldBalance - acctDelFee);
230 env.close();
231
232 // Verify that Carol's account, directory, and other stuff are gone.
233 BEAST_EXPECT(!env.closed()->exists(keylet::account(carol.id())));
234 BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(carol.id())));
235 BEAST_EXPECT(!env.closed()->exists(keylet::depositPreauth(carol.id(), becky.id())));
236 BEAST_EXPECT(!env.closed()->exists(
237 keylet::offer(carol.id(), SeqProxy::rawSequence(carolOfferSeq))));
238 BEAST_EXPECT(!env.closed()->exists(
239 keylet::ticket(carol.id(), SeqProxy::rawTicket(carolTicketSeq))));
240 BEAST_EXPECT(!env.closed()->exists(keylet::signerList(carol.id())));
241
242 // Verify that Carol's XRP, minus the fee, was transferred to becky.
243 BEAST_EXPECT(env.balance(becky) == carolOldBalance + beckyOldBalance - acctDelFee);
244
245 // Since becky received an influx of XRP, her lsfPasswordSpent bit
246 // is cleared and she can change her regular key for free again.
247 env(regkey(becky, reb), Sig(becky), Fee(drops(0)));
248 }
249 }
250
251 void
253 {
254 // The code that deletes consecutive directory entries uses a
255 // peculiarity of the implementation. Make sure that peculiarity
256 // behaves as expected across owner directory pages.
257 using namespace jtx;
258
259 testcase("Directories");
260
261 Env env{*this};
262 Account const alice("alice");
263 Account const gw("gw");
264
265 env.fund(XRP(10000), alice, gw);
266 env.close();
267
268 // Alice creates enough offers to require two owner directories.
269 for (int i{0}; i < 45; ++i)
270 {
271 env(offer(alice, gw["USD"](1), XRP(1)));
272 env.close();
273 }
274 env.require(offers(alice, 45));
275
276 // Close enough ledgers to be able to delete alice's account.
277 incLgrSeqForAccDel(env, alice);
278
279 // Verify that both directory nodes exist.
280 Keylet const aliceRootKey{keylet::ownerDir(alice.id())};
281 Keylet const alicePageKey{keylet::page(aliceRootKey, 1)};
282 BEAST_EXPECT(env.closed()->exists(aliceRootKey));
283 BEAST_EXPECT(env.closed()->exists(alicePageKey));
284
285 // Delete alice's account.
286 auto const acctDelFee{drops(env.current()->fees().increment)};
287 auto const aliceBalance{env.balance(alice)};
288 env(acctdelete(alice, gw), Fee(acctDelFee));
289 verifyDeliveredAmount(env, aliceBalance - acctDelFee);
290 env.close();
291
292 // Both of alice's directory nodes should be gone.
293 BEAST_EXPECT(!env.closed()->exists(aliceRootKey));
294 BEAST_EXPECT(!env.closed()->exists(alicePageKey));
295 }
296
297 void
299 {
300 using namespace jtx;
301
302 testcase("Owned types");
303
304 // We want to test PayChannels with the backlink.
305 Env env{*this, testableAmendments()};
306 Account const alice("alice");
307 Account const becky("becky");
308 Account const gw("gw");
309
310 env.fund(XRP(100000), alice, becky, gw);
311 env.close();
312
313 // Give alice and becky a bunch of offers that we have to search
314 // through before we figure out that there's a non-deletable
315 // entry in their directory.
316 for (int i{0}; i < 200; ++i)
317 {
318 env(offer(alice, gw["USD"](1), XRP(1)));
319 env(offer(becky, gw["USD"](1), XRP(1)));
320 env.close();
321 }
322 env.require(offers(alice, 200));
323 env.require(offers(becky, 200));
324
325 // Close enough ledgers to be able to delete alice's and becky's
326 // accounts.
327 incLgrSeqForAccDel(env, alice);
328 incLgrSeqForAccDel(env, becky);
329
330 // alice writes a check to becky. Until that check is cashed or
331 // canceled it will prevent alice's and becky's accounts from being
332 // deleted.
333 uint256 const checkId = keylet::check(alice, SeqProxy::rawSequence(env.seq(alice))).key;
334 env(check::create(alice, becky, XRP(1)));
335 env.close();
336
337 auto const acctDelFee{drops(env.current()->fees().increment)};
338 env(acctdelete(alice, gw), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
339 env(acctdelete(becky, gw), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
340 env.close();
341
342 // Cancel the check, but add an escrow. Again, with the escrow
343 // on board, alice and becky should not be able to delete their
344 // accounts.
345 env(check::cancel(becky, checkId));
346 env.close();
347
348 using namespace std::chrono_literals;
349 std::uint32_t const escrowSeq{env.seq(alice)};
350 env(escrow::create(alice, becky, XRP(333)),
351 escrow::kFinishTime(env.now() + 3s),
352 escrow::kCancelTime(env.now() + 4s));
353 env.close();
354
355 // alice and becky should be unable to delete their accounts because
356 // of the escrow.
357 env(acctdelete(alice, gw), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
358 env(acctdelete(becky, gw), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
359 env.close();
360
361 // Now cancel the escrow, but create a payment channel between
362 // alice and becky.
363
364 bool const withTokenEscrow = env.current()->rules().enabled(featureTokenEscrow);
365 if (withTokenEscrow)
366 {
367 Account const gw1("gw1");
368 Account const carol("carol");
369 auto const usd = gw1["USD"];
370 env.fund(XRP(100000), carol, gw1);
371 env(fset(gw1, asfAllowTrustLineLocking));
372 env.close();
373 env.trust(usd(10000), carol);
374 env.close();
375 env(pay(gw1, carol, usd(100)));
376 env.close();
377
378 std::uint32_t const escrowSeq{env.seq(carol)};
379 env(escrow::create(carol, becky, usd(1)),
380 escrow::kFinishTime(env.now() + 3s),
381 escrow::kCancelTime(env.now() + 4s));
382 env.close();
383
384 incLgrSeqForAccDel(env, gw1);
385
386 env(acctdelete(gw1, becky), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
387 env.close();
388
389 env(escrow::cancel(becky, carol, escrowSeq));
390 env.close();
391 }
392
393 env(escrow::cancel(becky, alice, escrowSeq));
394 env.close();
395
396 Keylet const alicePayChanKey{
397 keylet::payChannel(alice, becky, SeqProxy::rawSequence(env.seq(alice)))};
398
399 env(payChanCreate(alice, becky, XRP(57), 4s, env.now() + 2s, alice.pk()));
400 env.close();
401
402 // With the PayChannel in place becky and alice should not be
403 // able to delete her account
404 auto const beckyBalance{env.balance(becky)};
405 env(acctdelete(alice, gw), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
406 env(acctdelete(becky, gw), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
407 env.close();
408
409 // Alice cancels her PayChannel, which will leave her with only offers
410 // in her directory.
411
412 // Lambda to close a PayChannel.
413 auto payChanClose =
414 [](jtx::Account const& account, Keylet const& payChanKeylet, PublicKey const& pk) {
415 json::Value jv;
416 jv[jss::TransactionType] = jss::PaymentChannelClaim;
417 jv[jss::Flags] = tfClose;
418 jv[jss::Account] = account.human();
419 jv[sfChannel.jsonName] = to_string(payChanKeylet.key);
420 jv[sfPublicKey.jsonName] = strHex(pk.slice());
421 return jv;
422 };
423 env(payChanClose(alice, alicePayChanKey, alice.pk()));
424 env.close();
425
426 // gw creates a PayChannel with alice as the destination, this should
427 // prevent alice from deleting her account.
428 Keylet const gwPayChanKey{
429 keylet::payChannel(gw, alice, SeqProxy::rawSequence(env.seq(gw)))};
430
431 env(payChanCreate(gw, alice, XRP(68), 4s, env.now() + 2s, alice.pk()));
432 env.close();
433
434 // alice can't delete her account because of the PayChannel.
435 env(acctdelete(alice, gw), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
436 env.close();
437
438 // alice closes the PayChannel which should (finally) allow her to
439 // delete her account.
440 env(payChanClose(alice, gwPayChanKey, alice.pk()));
441 env.close();
442
443 // Now alice can successfully delete her account.
444 auto const aliceBalance{env.balance(alice)};
445 env(acctdelete(alice, gw), Fee(acctDelFee));
446 verifyDeliveredAmount(env, aliceBalance - acctDelFee);
447 env.close();
448 }
449
450 void
452 {
453 // Put enough offers in an account that we refuse to delete the account.
454 using namespace jtx;
455
456 testcase("Too many offers");
457
458 Env env{*this};
459 Account const alice("alice");
460 Account const gw("gw");
461
462 // Fund alice well so she can afford the reserve on the offers.
463 env.fund(XRP(10000000), alice, gw);
464 env.close();
465
466 // To increase the number of Books affected, change the currency of
467 // each offer.
468 std::string currency{"AAA"};
469
470 // Alice creates 1001 offers. This is one greater than the number of
471 // directory entries an AccountDelete will remove.
472 std::uint32_t const offerSeq0{env.seq(alice)};
473 static constexpr int kOfferCount{1001};
474 for (int i{0}; i < kOfferCount; ++i)
475 {
476 env(offer(alice, gw[currency](1), XRP(1)));
477 env.close();
478
479 // Increment to next currency.
480 ++currency[0];
481 if (currency[0] > 'Z')
482 {
483 currency[0] = 'A';
484 ++currency[1];
485 }
486 if (currency[1] > 'Z')
487 {
488 currency[1] = 'A';
489 ++currency[2];
490 }
491 if (currency[2] > 'Z')
492 {
493 currency[0] = 'A';
494 currency[1] = 'A';
495 currency[2] = 'A';
496 }
497 }
498
499 // Close enough ledgers to be able to delete alice's account.
500 incLgrSeqForAccDel(env, alice);
501
502 // Verify the existence of the expected ledger entries.
503 Keylet const aliceOwnerDirKey{keylet::ownerDir(alice.id())};
504 {
505 std::shared_ptr<ReadView const> const closed{env.closed()};
506 BEAST_EXPECT(closed->exists(keylet::account(alice.id())));
507 BEAST_EXPECT(closed->exists(aliceOwnerDirKey));
508
509 // alice's directory nodes.
510 for (std::uint32_t i{0}; i < ((kOfferCount / 32) + 1); ++i)
511 BEAST_EXPECT(closed->exists(keylet::page(aliceOwnerDirKey, i)));
512
513 // alice's offers.
514 for (std::uint32_t i{0}; i < kOfferCount; ++i)
515 {
516 BEAST_EXPECT(closed->exists(
517 keylet::offer(alice.id(), SeqProxy::rawSequence(offerSeq0 + i))));
518 }
519 }
520
521 // Delete alice's account. Should fail because she has too many
522 // offers in her directory.
523 auto const acctDelFee{drops(env.current()->fees().increment)};
524
525 env(acctdelete(alice, gw), Fee(acctDelFee), Ter(tefTOO_BIG));
526
527 // Cancel one of alice's offers. Then the account delete can succeed.
528 env.require(offers(alice, kOfferCount));
529 env(offerCancel(alice, offerSeq0));
530 env.close();
531 env.require(offers(alice, kOfferCount - 1));
532
533 // alice successfully deletes her account.
534 auto const alicePreDelBal{env.balance(alice)};
535 env(acctdelete(alice, gw), Fee(acctDelFee));
536 verifyDeliveredAmount(env, alicePreDelBal - acctDelFee);
537 env.close();
538
539 // Verify that alice's account root is gone as well as her directory
540 // nodes and all of her offers.
541 {
542 std::shared_ptr<ReadView const> const closed{env.closed()};
543 BEAST_EXPECT(!closed->exists(keylet::account(alice.id())));
544 BEAST_EXPECT(!closed->exists(aliceOwnerDirKey));
545
546 // alice's former directory nodes.
547 for (std::uint32_t i{0}; i < ((kOfferCount / 32) + 1); ++i)
548 BEAST_EXPECT(!closed->exists(keylet::page(aliceOwnerDirKey, i)));
549
550 // alice's former offers.
551 for (std::uint32_t i{0}; i < kOfferCount; ++i)
552 {
553 BEAST_EXPECT(!closed->exists(
554 keylet::offer(alice.id(), SeqProxy::rawSequence(offerSeq0 + i))));
555 }
556 }
557 }
558
559 void
561 {
562 // Show that a trust line that is implicitly created by offer crossing
563 // prevents an account from being deleted.
564 using namespace jtx;
565
566 testcase("Implicitly created trust line");
567
568 Env env{*this};
569 Account const alice{"alice"};
570 Account const gw{"gw"};
571 auto const bux{gw["BUX"]};
572
573 env.fund(XRP(10000), alice, gw);
574 env.close();
575
576 // alice creates an offer that, if crossed, will implicitly create
577 // a trust line.
578 env(offer(alice, bux(30), XRP(30)));
579 env.close();
580
581 // gw crosses alice's offer. alice should end up with BUX(30).
582 env(offer(gw, XRP(30), bux(30)));
583 env.close();
584 env.require(Balance(alice, bux(30)));
585
586 // Close enough ledgers to be able to delete alice's account.
587 incLgrSeqForAccDel(env, alice);
588
589 // alice and gw can't delete their accounts because of the implicitly
590 // created trust line.
591 auto const acctDelFee{drops(env.current()->fees().increment)};
592 env(acctdelete(alice, gw), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
593 env.close();
594
595 env(acctdelete(gw, alice), Fee(acctDelFee), Ter(tecHAS_OBLIGATIONS));
596 env.close();
597 {
598 std::shared_ptr<ReadView const> const closed{env.closed()};
599 BEAST_EXPECT(closed->exists(keylet::account(alice.id())));
600 BEAST_EXPECT(closed->exists(keylet::account(gw.id())));
601 }
602 }
603
604 void
606 {
607 // See what happens when an account with a balance less than the
608 // incremental reserve tries to delete itself.
609 using namespace jtx;
610
611 testcase("Balance too small for fee");
612
613 Env env{*this};
614 Account const alice("alice");
615
616 // Note that the fee structure for unit tests does not match the fees
617 // on the production network (October 2019). Unit tests have a base
618 // reserve of 200 XRP.
619 env.fund(env.current()->fees().reserve, noripple(alice));
620 env.close();
621
622 // Burn a chunk of alice's funds so she only has 1 XRP remaining in
623 // her account.
624 env(noop(alice), Fee(env.balance(alice) - XRP(1)));
625 env.close();
626
627 auto const acctDelFee{drops(env.current()->fees().increment)};
628 BEAST_EXPECT(acctDelFee > env.balance(alice));
629
630 // alice attempts to delete her account even though she can't pay
631 // the full fee. She specifies a fee that is larger than her balance.
632 //
633 // The balance of env.master should not change.
634 auto const masterBalance{env.balance(env.master)};
635 env(acctdelete(alice, env.master), Fee(acctDelFee), Ter(terINSUF_FEE_B));
636 env.close();
637 {
638 std::shared_ptr<ReadView const> const closed{env.closed()};
639 BEAST_EXPECT(closed->exists(keylet::account(alice.id())));
640 BEAST_EXPECT(env.balance(env.master) == masterBalance);
641 }
642
643 // alice again attempts to delete her account. This time she specifies
644 // her current balance in XRP. Again the transaction fails.
645 BEAST_EXPECT(env.balance(alice) == XRP(1));
646 env(acctdelete(alice, env.master), Fee(XRP(1)), Ter(telINSUF_FEE_P));
647 env.close();
648 {
649 std::shared_ptr<ReadView const> const closed{env.closed()};
650 BEAST_EXPECT(closed->exists(keylet::account(alice.id())));
651 BEAST_EXPECT(env.balance(env.master) == masterBalance);
652 }
653 }
654
655 void
657 {
658 testcase("With Tickets");
659
660 using namespace test::jtx;
661
662 Account const alice{"alice"};
663 Account const bob{"bob"};
664
665 Env env{*this};
666 env.fund(XRP(100000), alice, bob);
667 env.close();
668
669 // bob grabs as many tickets as he is allowed to have.
670 std::uint32_t const ticketSeq{env.seq(bob) + 1};
671 env(ticket::create(bob, 250));
672 env.close();
673 env.require(Owners(bob, 250));
674
675 {
676 std::shared_ptr<ReadView const> const closed{env.closed()};
677 BEAST_EXPECT(closed->exists(keylet::account(bob.id())));
678 for (std::uint32_t i = 0; i < 250; ++i)
679 {
680 BEAST_EXPECT(
681 closed->exists(keylet::ticket(bob.id(), SeqProxy::rawTicket(ticketSeq + i))));
682 }
683 }
684
685 // Close enough ledgers to be able to delete bob's account.
686 incLgrSeqForAccDel(env, bob);
687
688 // bob deletes his account using a ticket. bob's account and all
689 // of his tickets should be removed from the ledger.
690 auto const acctDelFee{drops(env.current()->fees().increment)};
691 auto const bobOldBalance{env.balance(bob)};
692 env(acctdelete(bob, alice), ticket::Use(ticketSeq), Fee(acctDelFee));
693 verifyDeliveredAmount(env, bobOldBalance - acctDelFee);
694 env.close();
695 {
696 std::shared_ptr<ReadView const> const closed{env.closed()};
697 BEAST_EXPECT(!closed->exists(keylet::account(bob.id())));
698 for (std::uint32_t i = 0; i < 250; ++i)
699 {
700 BEAST_EXPECT(
701 !closed->exists(keylet::ticket(bob.id(), SeqProxy::rawTicket(ticketSeq + i))));
702 }
703 }
704 }
705
706 void
708 {
709 testcase("Destination Constraints");
710
711 using namespace test::jtx;
712
713 Account const alice{"alice"};
714 Account const becky{"becky"};
715 Account const carol{"carol"};
716 Account const daria{"daria"};
717
718 Env env{*this, features};
719 env.fund(XRP(100000), alice, becky, carol);
720 env.close();
721
722 // alice sets the lsfDepositAuth flag on her account. This should
723 // prevent becky from deleting her account while using alice as the
724 // destination.
725 env(fset(alice, asfDepositAuth));
726
727 // carol requires a destination tag.
728 env(fset(carol, asfRequireDest));
729 env.close();
730
731 // Need to create a pseudo-account
732 Vault const vault{env};
733 auto [tx, keylet] = vault.create({.owner = alice, .asset = xrpIssue()});
734 env(tx);
735 env.close();
736 auto const sleVault = env.le(keylet);
737 if (!BEAST_EXPECT(sleVault))
738 return;
739 Account const vaultPseudo{"vaultPseudo", sleVault->at(sfAccount)};
740
741 // Close enough ledgers to be able to delete becky's account.
742 incLgrSeqForAccDel(env, becky);
743
744 // becky attempts to delete her account using daria as the destination.
745 // Since daria is not in the ledger the delete attempt fails.
746 auto const acctDelFee{drops(env.current()->fees().increment)};
747 env(acctdelete(becky, daria), Fee(acctDelFee), Ter(tecNO_DST));
748 env.close();
749
750 // becky attempts to delete her account, but carol requires a
751 // destination tag which becky has omitted.
752 env(acctdelete(becky, carol), Fee(acctDelFee), Ter(tecDST_TAG_NEEDED));
753 env.close();
754
755 // becky attempts to delete her account, but alice won't take her XRP,
756 // so the delete is blocked.
757 env(acctdelete(becky, alice), Fee(acctDelFee), Ter(tecNO_PERMISSION));
758 env.close();
759
760 // becky attempts to delete her account using a pseudo-account as the
761 // destination, which fails since pseudo-accounts have deposit auth enabled.
762 env(acctdelete(becky, vaultPseudo), Fee(acctDelFee), Ter(tecNO_PERMISSION));
763
764 // alice preauthorizes deposits from becky. Now becky can delete her
765 // account and forward the leftovers to alice.
766 env(deposit::auth(alice, becky));
767 env.close();
768
769 auto const beckyOldBalance{env.balance(becky)};
770 env(acctdelete(becky, alice), Fee(acctDelFee));
771 verifyDeliveredAmount(env, beckyOldBalance - acctDelFee);
772 env.close();
773 }
774
775 void
777 {
778 {
779 testcase("Destination Constraints with DepositPreauth and Credentials");
780
781 using namespace test::jtx;
782
783 Account const alice{"alice"};
784 Account const becky{"becky"};
785 Account const carol{"carol"};
786 Account const daria{"daria"};
787
788 char const credType[] = "abcd";
789
790 Env env{*this};
791 env.fund(XRP(100000), alice, becky, carol, daria);
792 env.close();
793
794 // carol issue credentials for becky
795 env(credentials::create(becky, carol, credType));
796 env.close();
797
798 // get credentials index
799 auto const jv = credentials::ledgerEntry(env, becky, carol, credType);
800 std::string const credIdx = jv[jss::result][jss::index].asString();
801
802 // Close enough ledgers to be able to delete becky's account.
803 incLgrSeqForAccDel(env, becky);
804
805 auto const acctDelFee{drops(env.current()->fees().increment)};
806
807 // becky use credentials but they aren't accepted
808 env(acctdelete(becky, alice),
809 credentials::Ids({credIdx}),
810 Fee(acctDelFee),
812 env.close();
813
814 {
815 // alice sets the lsfDepositAuth flag on her account. This
816 // should prevent becky from deleting her account while using
817 // alice as the destination.
818 env(fset(alice, asfDepositAuth));
819 env.close();
820 }
821
822 // Fail, credentials still not accepted
823 env(acctdelete(becky, alice),
824 credentials::Ids({credIdx}),
825 Fee(acctDelFee),
827 env.close();
828
829 // becky accept the credentials
830 env(credentials::accept(becky, carol, credType));
831 env.close();
832
833 // Fail, credentials doesn’t belong to carol
834 env(acctdelete(carol, alice),
835 credentials::Ids({credIdx}),
836 Fee(acctDelFee),
838
839 // Fail, no depositPreauth for provided credentials
840 env(acctdelete(becky, alice),
841 credentials::Ids({credIdx}),
842 Fee(acctDelFee),
844 env.close();
845
846 // alice create DepositPreauth Object
847 env(deposit::authCredentials(alice, {{.issuer = carol, .credType = credType}}));
848 env.close();
849
850 // becky attempts to delete her account, but alice won't take her
851 // XRP, so the delete is blocked.
852 env(acctdelete(becky, alice), Fee(acctDelFee), Ter(tecNO_PERMISSION));
853
854 // becky use empty credentials and can't delete account
855 env(acctdelete(becky, alice), Fee(acctDelFee), credentials::Ids({}), Ter(temMALFORMED));
856
857 // becky use bad credentials and can't delete account
858 env(acctdelete(becky, alice),
859 credentials::Ids({"48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6E"
860 "A288BE4"}),
861 Fee(acctDelFee),
863 env.close();
864
865 // becky use credentials and can delete account
866 env(acctdelete(becky, alice), credentials::Ids({credIdx}), Fee(acctDelFee));
867 env.close();
868
869 {
870 // check that credential object deleted too
871 auto const jNoCred = credentials::ledgerEntry(env, becky, carol, credType);
872 BEAST_EXPECT(
873 jNoCred.isObject() && jNoCred.isMember(jss::result) &&
874 jNoCred[jss::result].isMember(jss::error) &&
875 jNoCred[jss::result][jss::error] == "entryNotFound");
876 }
877
878 testcase("Credentials that aren't required");
879 { // carol issue credentials for daria
880 env(credentials::create(daria, carol, credType));
881 env.close();
882 env(credentials::accept(daria, carol, credType));
883 env.close();
884 std::string const credDaria =
885 credentials::ledgerEntry(env, daria, carol, credType)[jss::result][jss::index]
886 .asString();
887
888 // daria use valid credentials, which aren't required and can
889 // delete her account
890 env(acctdelete(daria, carol), credentials::Ids({credDaria}), Fee(acctDelFee));
891 env.close();
892
893 // check that credential object deleted too
894 auto const jNoCred = credentials::ledgerEntry(env, daria, carol, credType);
895
896 BEAST_EXPECT(
897 jNoCred.isObject() && jNoCred.isMember(jss::result) &&
898 jNoCred[jss::result].isMember(jss::error) &&
899 jNoCred[jss::result][jss::error] == "entryNotFound");
900 }
901
902 {
903 Account const eaton{"eaton"};
904 Account const fred{"fred"};
905
906 env.fund(XRP(5000), eaton, fred);
907
908 // carol issue credentials for eaton
909 env(credentials::create(eaton, carol, credType));
910 env.close();
911 env(credentials::accept(eaton, carol, credType));
912 env.close();
913 std::string const credEaton =
914 credentials::ledgerEntry(env, eaton, carol, credType)[jss::result][jss::index]
915 .asString();
916
917 // fred make pre-authorization through authorized account
918 env(fset(fred, asfDepositAuth));
919 env.close();
920 env(deposit::auth(fred, eaton));
921 env.close();
922
923 // Close enough ledgers to be able to delete becky's account.
924 incLgrSeqForAccDel(env, eaton);
925 auto const acctDelFee{drops(env.current()->fees().increment)};
926
927 // eaton use valid credentials, but he already authorized
928 // through "Authorized" field.
929 env(acctdelete(eaton, fred), credentials::Ids({credEaton}), Fee(acctDelFee));
930 env.close();
931
932 // check that credential object deleted too
933 auto const jNoCred = credentials::ledgerEntry(env, eaton, carol, credType);
934
935 BEAST_EXPECT(
936 jNoCred.isObject() && jNoCred.isMember(jss::result) &&
937 jNoCred[jss::result].isMember(jss::error) &&
938 jNoCred[jss::result][jss::error] == "entryNotFound");
939 }
940
941 testcase("Expired credentials");
942 {
943 Account const john{"john"};
944
945 env.fund(XRP(10000), john);
946 env.close();
947
948 auto jv = credentials::create(john, carol, credType);
949 uint32_t const t =
950 env.current()->header().parentCloseTime.time_since_epoch().count() + 20;
951 jv[sfExpiration.jsonName] = t;
952 env(jv);
953 env.close();
954 env(credentials::accept(john, carol, credType));
955 env.close();
956 jv = credentials::ledgerEntry(env, john, carol, credType);
957 std::string const credIdx = jv[jss::result][jss::index].asString();
958
959 incLgrSeqForAccDel(env, john);
960
961 // credentials are expired
962 // john use credentials but can't delete account
963 env(acctdelete(john, alice),
964 credentials::Ids({credIdx}),
965 Fee(acctDelFee),
966 Ter(tecEXPIRED));
967 env.close();
968
969 {
970 // check that expired credential object deleted
971 auto jv = credentials::ledgerEntry(env, john, carol, credType);
972 BEAST_EXPECT(
973 jv.isObject() && jv.isMember(jss::result) &&
974 jv[jss::result].isMember(jss::error) &&
975 jv[jss::result][jss::error] == "entryNotFound");
976 }
977 }
978 }
979
980 {
981 testcase("Credentials feature disabled");
982 using namespace test::jtx;
983
984 Account const alice{"alice"};
985 Account const becky{"becky"};
986 Account const carol{"carol"};
987
988 Env env{*this, testableAmendments() - featureCredentials};
989 env.fund(XRP(100000), alice, becky, carol);
990 env.close();
991
992 // alice sets the lsfDepositAuth flag on her account. This should
993 // prevent becky from deleting her account while using alice as the
994 // destination.
995 env(fset(alice, asfDepositAuth));
996 env.close();
997
998 // Close enough ledgers to be able to delete becky's account.
999 incLgrSeqForAccDel(env, becky);
1000
1001 auto const acctDelFee{drops(env.current()->fees().increment)};
1002
1003 std::string const credIdx =
1004 "098B7F1B146470A1C5084DC7832C04A72939E3EBC58E68AB8B579BA072B0CE"
1005 "CB";
1006
1007 // and can't delete even with old DepositPreauth
1008 env(deposit::auth(alice, becky));
1009 env.close();
1010
1011 env(acctdelete(becky, alice),
1012 credentials::Ids({credIdx}),
1013 Fee(acctDelFee),
1014 Ter(temDISABLED));
1015 env.close();
1016 }
1017 }
1018
1019 void
1021 {
1022 {
1023 testcase("Deleting Issuer deletes issued credentials");
1024
1025 using namespace test::jtx;
1026
1027 Account const alice{"alice"};
1028 Account const becky{"becky"};
1029 Account const carol{"carol"};
1030
1031 char const credType[] = "abcd";
1032
1033 Env env{*this};
1034 env.fund(XRP(100000), alice, becky, carol);
1035 env.close();
1036
1037 // carol issue credentials for becky
1038 env(credentials::create(becky, carol, credType));
1039 env.close();
1040 env(credentials::accept(becky, carol, credType));
1041 env.close();
1042
1043 // get credentials index
1044 auto const jv = credentials::ledgerEntry(env, becky, carol, credType);
1045 std::string const credIdx = jv[jss::result][jss::index].asString();
1046
1047 // Close enough ledgers to be able to delete carol's account.
1048 incLgrSeqForAccDel(env, carol);
1049
1050 auto const acctDelFee{drops(env.current()->fees().increment)};
1051 env(acctdelete(carol, alice), Fee(acctDelFee));
1052 env.close();
1053
1054 { // check that credential object deleted too
1055 BEAST_EXPECT(!env.le(credIdx));
1056 auto const jv = credentials::ledgerEntry(env, becky, carol, credType);
1057 BEAST_EXPECT(
1058 jv.isObject() && jv.isMember(jss::result) &&
1059 jv[jss::result].isMember(jss::error) &&
1060 jv[jss::result][jss::error] == "entryNotFound");
1061 }
1062 }
1063
1064 {
1065 testcase("Deleting Subject deletes issued credentials");
1066
1067 using namespace test::jtx;
1068
1069 Account const alice{"alice"};
1070 Account const becky{"becky"};
1071 Account const carol{"carol"};
1072
1073 char const credType[] = "abcd";
1074
1075 Env env{*this};
1076 env.fund(XRP(100000), alice, becky, carol);
1077 env.close();
1078
1079 // carol issue credentials for becky
1080 env(credentials::create(becky, carol, credType));
1081 env.close();
1082 env(credentials::accept(becky, carol, credType));
1083 env.close();
1084
1085 // get credentials index
1086 auto const jv = credentials::ledgerEntry(env, becky, carol, credType);
1087 std::string const credIdx = jv[jss::result][jss::index].asString();
1088
1089 // Close enough ledgers to be able to delete carol's account.
1090 incLgrSeqForAccDel(env, becky);
1091
1092 auto const acctDelFee{drops(env.current()->fees().increment)};
1093 env(acctdelete(becky, alice), Fee(acctDelFee));
1094 env.close();
1095
1096 { // check that credential object deleted too
1097 BEAST_EXPECT(!env.le(credIdx));
1098 auto const jv = credentials::ledgerEntry(env, becky, carol, credType);
1099 BEAST_EXPECT(
1100 jv.isObject() && jv.isMember(jss::result) &&
1101 jv[jss::result].isMember(jss::error) &&
1102 jv[jss::result][jss::error] == "entryNotFound");
1103 }
1104 }
1105 }
1106
1107 void
1108 run() override
1109 {
1110 auto const all{jtx::testableAmendments()};
1111 testBasics();
1118 testDest(all);
1119 testDest(all - fixCleanup3_3_0);
1122 }
1123};
1124
1126
1127} // namespace xrpl::test
A testsuite class.
Definition suite.h:52
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Represents a JSON value.
Definition json_value.h:117
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
std::chrono::time_point< NetClock > time_point
Definition chrono.h:48
std::chrono::duration< rep, period > duration
Definition chrono.h:47
A public key.
Definition PublicKey.h:53
Slice slice() const noexcept
Definition PublicKey.h:115
json::Value getJson(JsonOptions=JsonOptions::Values::None) const override
Definition STAmount.cpp:734
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:62
static constexpr SeqProxy rawTicket(std::uint32_t v)
Factory function to return a ticket-based SeqProxy.
Definition SeqProxy.h:74
void testDest(FeatureBitset features)
void run() override
Runs the suite.
static json::Value payChanCreate(jtx::Account const &account, jtx::Account const &to, STAmount const &amount, NetClock::duration const &settleDelay, NetClock::time_point const &cancelAfter, PublicKey const &pk)
void verifyDeliveredAmount(jtx::Env &env, STAmount const &amount)
Immutable cryptographic account descriptor.
Definition jtx/Account.h:21
std::string const & human() const
Returns the human readable public key.
PublicKey const & pk() const
Return the public key.
Definition jtx/Account.h:84
AccountID id() const
Returns the Account ID.
A transaction testing environment.
Definition Env.h:161
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
SLE::const_pointer le(Account const &account) const
Return an account root.
Definition Env.cpp:311
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition Env.cpp:127
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:323
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:302
Account const & master
Definition Env.h:165
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:1056
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:201
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:354
std::shared_ptr< STTx const > tx() const
Return the tx data for the last JTx.
Definition Env.cpp:560
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:764
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:377
NetClock::time_point now()
Returns the current network time.
Definition Env.h:326
Set the fee on a JTx.
Definition fee.h:20
Set a multisignature on a JTx.
Definition multisign.h:53
Match the number of items in the account's owner directory.
Definition owners.h:55
Set the regular signature on a JTx.
Definition sig.h:19
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:18
Set the flags on a JTx.
Definition txflags.h:14
Set a ticket sequence on a JTx.
Definition ticket.h:36
Keylet computation functions.
Definition Indexes.h:40
Keylet payChannel(AccountID const &src, AccountID const &dst, SeqProxy const &seq) noexcept
A PaymentChannel.
Definition Indexes.cpp:394
Keylet offer(AccountID const &id, SeqProxy const &seq) noexcept
An offer from an account.
Definition Indexes.cpp:276
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition Indexes.cpp:344
Keylet signerList(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:326
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:373
Keylet check(AccountID const &id, SeqProxy const &seq) noexcept
A Check.
Definition Indexes.cpp:338
Keylet ticket(AccountID const &id, SeqProxy const &ticketSeq)
A ticket belonging to an account.
Definition Indexes.cpp:310
Keylet page(uint256 const &root, std::uint64_t const index=0) noexcept
A page in a directory.
Definition Indexes.cpp:379
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
json::Value cancel(jtx::Account const &dest, uint256 const &checkId)
Cancel a check.
Definition check.cpp:39
json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:29
json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:16
json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:56
json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:38
json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:16
json::Value setValid(jtx::Account const &account)
Definition dids.cpp:25
json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount)
Definition escrow.cpp:24
json::Value cancel(AccountID const &account, Account const &from, std::uint32_t seq)
Definition escrow.cpp:48
auto const kCancelTime
Set the "CancelAfter" time tag on a JTx.
Definition escrow.h:83
auto const kFinishTime
Set the "FinishAfter" time tag on a JTx.
Definition escrow.h:78
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:16
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
json::Value regkey(Account const &account, DisabledT)
Disable the regular key.
Definition regkey.cpp:13
json::Value offerCancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:31
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
json::Value noop(Account const &account)
The null transaction.
Definition noop.h:14
FeatureBitset testableAmendments()
Definition Env.h:92
json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
void incLgrSeqForAccDel(jtx::Env &env, jtx::Account const &acc, std::uint32_t margin=0)
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
Definition Env.h:86
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:14
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:18
OwnerCount< ltOFFER > offers
Match the number of offers in the account's owner directory.
Definition owners.h:137
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
json::Value signers(Account const &account, std::uint32_t quorum, std::vector< Signer > const &v)
Definition multisign.cpp:31
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:15
BEAST_DEFINE_TESTSUITE_PRIO(AccountDelete, app, xrpl, 2)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ telINSUF_FEE_P
Definition TER.h:43
@ terINSUF_FEE_B
Definition TER.h:212
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:108
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
@ tefTOO_BIG
Definition TER.h:176
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
@ temBAD_FEE
Definition TER.h:80
@ temINVALID_FLAG
Definition TER.h:99
@ temDST_IS_SRC
Definition TER.h:96
@ temMALFORMED
Definition TER.h:75
@ temDISABLED
Definition TER.h:102
@ tecTOO_SOON
Definition TER.h:321
@ tecBAD_CREDENTIALS
Definition TER.h:362
@ tecEXPIRED
Definition TER.h:317
@ tecNO_PERMISSION
Definition TER.h:308
@ tecDST_TAG_NEEDED
Definition TER.h:312
@ tecHAS_OBLIGATIONS
Definition TER.h:320
@ tecNO_DST
Definition TER.h:293
BaseUInt< 256 > uint256
Definition base_uint.h:580
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
uint256 key
Definition Keylet.h:21
T time_since_epoch(T... args)