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