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