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