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