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, supported_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 // Lambda to create an escrow.
339 auto escrowCreate = [](jtx::Account const& account,
340 jtx::Account const& to,
341 STAmount const& amount,
342 NetClock::time_point const& cancelAfter) {
343 Json::Value jv;
344 jv[jss::TransactionType] = jss::EscrowCreate;
345 jv[jss::Account] = account.human();
346 jv[jss::Destination] = to.human();
347 jv[jss::Amount] = amount.getJson(JsonOptions::none);
348 jv[sfFinishAfter.jsonName] =
349 cancelAfter.time_since_epoch().count() + 1;
350 jv[sfCancelAfter.jsonName] =
351 cancelAfter.time_since_epoch().count() + 2;
352 return jv;
353 };
354
355 using namespace std::chrono_literals;
356 std::uint32_t const escrowSeq{env.seq(alice)};
357 env(escrowCreate(alice, becky, XRP(333), env.now() + 2s));
358 env.close();
359
360 // alice and becky should be unable to delete their accounts because
361 // of the escrow.
362 env(acctdelete(alice, gw), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
363 env(acctdelete(becky, gw), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
364 env.close();
365
366 // Now cancel the escrow, but create a payment channel between
367 // alice and becky.
368
369 // Lambda to cancel an escrow.
370 auto escrowCancel =
371 [](Account const& account, Account const& from, std::uint32_t seq) {
372 Json::Value jv;
373 jv[jss::TransactionType] = jss::EscrowCancel;
374 jv[jss::Account] = account.human();
375 jv[sfOwner.jsonName] = from.human();
376 jv[sfOfferSequence.jsonName] = seq;
377 return jv;
378 };
379 env(escrowCancel(becky, alice, escrowSeq));
380 env.close();
381
382 Keylet const alicePayChanKey{
383 keylet::payChan(alice, becky, env.seq(alice))};
384
385 env(payChanCreate(
386 alice, becky, XRP(57), 4s, env.now() + 2s, alice.pk()));
387 env.close();
388
389 // An old-style PayChannel does not add a back link from the
390 // destination. So with the PayChannel in place becky should be
391 // able to delete her account, but alice should not.
392 auto const beckyBalance{env.balance(becky)};
393 env(acctdelete(alice, gw), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
394 env(acctdelete(becky, gw), fee(acctDelFee));
395 verifyDeliveredAmount(env, beckyBalance - acctDelFee);
396 env.close();
397
398 // Alice cancels her PayChannel which will leave her with only offers
399 // in her directory.
400
401 // Lambda to close a PayChannel.
402 auto payChanClose = [](jtx::Account const& account,
403 Keylet const& payChanKeylet,
404 PublicKey const& pk) {
405 Json::Value jv;
406 jv[jss::TransactionType] = jss::PaymentChannelClaim;
407 jv[jss::Flags] = tfClose;
408 jv[jss::Account] = account.human();
409 jv[sfChannel.jsonName] = to_string(payChanKeylet.key);
410 jv[sfPublicKey.jsonName] = strHex(pk.slice());
411 return jv;
412 };
413 env(payChanClose(alice, alicePayChanKey, alice.pk()));
414 env.close();
415
416 // Now enable the amendment so PayChannels add a backlink from the
417 // destination.
418 env.enableFeature(fixPayChanRecipientOwnerDir);
419 env.close();
420
421 // gw creates a PayChannel with alice as the destination. With the
422 // amendment passed this should prevent alice from deleting her
423 // account.
424 Keylet const gwPayChanKey{keylet::payChan(gw, alice, env.seq(gw))};
425
426 env(payChanCreate(gw, alice, XRP(68), 4s, env.now() + 2s, alice.pk()));
427 env.close();
428
429 // alice can't delete her account because of the PayChannel.
430 env(acctdelete(alice, gw), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
431 env.close();
432
433 // alice closes the PayChannel which should (finally) allow her to
434 // delete her account.
435 env(payChanClose(alice, gwPayChanKey, alice.pk()));
436 env.close();
437
438 // Now alice can successfully delete her account.
439 auto const aliceBalance{env.balance(alice)};
440 env(acctdelete(alice, gw), fee(acctDelFee));
441 verifyDeliveredAmount(env, aliceBalance - acctDelFee);
442 env.close();
443 }
444
445 void
447 {
448 // Create an account with an old-style PayChannel. Delete the
449 // destination of the PayChannel then resurrect the destination.
450 // The PayChannel should still work.
451 using namespace jtx;
452
453 testcase("Resurrection");
454
455 // We need an old-style PayChannel that doesn't provide a backlink
456 // from the destination. So don't enable the amendment with that fix.
457 Env env{*this, supported_amendments() - fixPayChanRecipientOwnerDir};
458 Account const alice("alice");
459 Account const becky("becky");
460
461 env.fund(XRP(10000), alice, becky);
462 env.close();
463
464 // Verify that becky's account root is present.
465 Keylet const beckyAcctKey{keylet::account(becky.id())};
466 BEAST_EXPECT(env.closed()->exists(beckyAcctKey));
467
468 using namespace std::chrono_literals;
469 Keylet const payChanKey{keylet::payChan(alice, becky, env.seq(alice))};
470 auto const payChanXRP = XRP(37);
471
472 env(payChanCreate(
473 alice, becky, payChanXRP, 4s, env.now() + 1h, alice.pk()));
474 env.close();
475 BEAST_EXPECT(env.closed()->exists(payChanKey));
476
477 // Close enough ledgers to be able to delete becky's account.
478 incLgrSeqForAccDel(env, becky);
479
480 auto const beckyPreDelBalance{env.balance(becky)};
481
482 auto const acctDelFee{drops(env.current()->fees().increment)};
483 env(acctdelete(becky, alice), fee(acctDelFee));
484 verifyDeliveredAmount(env, beckyPreDelBalance - acctDelFee);
485 env.close();
486
487 // Verify that becky's account root is gone.
488 BEAST_EXPECT(!env.closed()->exists(beckyAcctKey));
489
490 // All it takes is a large enough XRP payment to resurrect
491 // becky's account. Try too small a payment.
492 env(pay(alice,
493 becky,
494 drops(env.current()->fees().accountReserve(0)) - XRP(1)),
496 env.close();
497
498 // Actually resurrect becky's account.
499 env(pay(alice, becky, XRP(10)));
500 env.close();
501
502 // becky's account root should be back.
503 BEAST_EXPECT(env.closed()->exists(beckyAcctKey));
504 BEAST_EXPECT(env.balance(becky) == XRP(10));
505
506 // becky's resurrected account can be the destination of alice's
507 // PayChannel.
508 auto payChanClaim = [&]() {
509 Json::Value jv;
510 jv[jss::TransactionType] = jss::PaymentChannelClaim;
511 jv[jss::Account] = alice.human();
512 jv[sfChannel.jsonName] = to_string(payChanKey.key);
513 jv[sfBalance.jsonName] =
514 payChanXRP.value().getJson(JsonOptions::none);
515 return jv;
516 };
517 env(payChanClaim());
518 env.close();
519
520 BEAST_EXPECT(env.balance(becky) == XRP(10) + payChanXRP);
521 }
522
523 void
525 {
526 // Start with the featureDeletableAccounts amendment disabled.
527 // Then enable the amendment and delete an account.
528 using namespace jtx;
529
530 testcase("Amendment enable");
531
532 Env env{*this, supported_amendments() - featureDeletableAccounts};
533 Account const alice("alice");
534 Account const becky("becky");
535
536 env.fund(XRP(10000), alice, becky);
537 env.close();
538
539 // Close enough ledgers to be able to delete alice's account.
540 incLgrSeqForAccDel(env, alice);
541
542 // Verify that alice's account root is present.
543 Keylet const aliceAcctKey{keylet::account(alice.id())};
544 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
545
546 auto const alicePreDelBal{env.balance(alice)};
547 auto const beckyPreDelBal{env.balance(becky)};
548
549 auto const acctDelFee{drops(env.current()->fees().increment)};
550 env(acctdelete(alice, becky), fee(acctDelFee), ter(temDISABLED));
551 env.close();
552
553 // Verify that alice's account root is still present and alice and
554 // becky both have their XRP.
555 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
556 BEAST_EXPECT(env.balance(alice) == alicePreDelBal);
557 BEAST_EXPECT(env.balance(becky) == beckyPreDelBal);
558
559 // When the amendment is enabled the previous transaction is
560 // retried into the new open ledger and succeeds.
561 env.enableFeature(featureDeletableAccounts);
562 env.close();
563
564 // alice's account is still in the most recently closed ledger.
565 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
566
567 // Verify that alice's account root is gone from the current ledger
568 // and becky has alice's XRP.
569 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
570 BEAST_EXPECT(
571 env.balance(becky) == alicePreDelBal + beckyPreDelBal - acctDelFee);
572
573 env.close();
574 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
575 }
576
577 void
579 {
580 // Put enough offers in an account that we refuse to delete the account.
581 using namespace jtx;
582
583 testcase("Too many offers");
584
585 Env env{*this};
586 Account const alice("alice");
587 Account const gw("gw");
588
589 // Fund alice well so she can afford the reserve on the offers.
590 env.fund(XRP(10000000), alice, gw);
591 env.close();
592
593 // To increase the number of Books affected, change the currency of
594 // each offer.
595 std::string currency{"AAA"};
596
597 // Alice creates 1001 offers. This is one greater than the number of
598 // directory entries an AccountDelete will remove.
599 std::uint32_t const offerSeq0{env.seq(alice)};
600 constexpr int offerCount{1001};
601 for (int i{0}; i < offerCount; ++i)
602 {
603 env(offer(alice, gw[currency](1), XRP(1)));
604 env.close();
605
606 // Increment to next currency.
607 ++currency[0];
608 if (currency[0] > 'Z')
609 {
610 currency[0] = 'A';
611 ++currency[1];
612 }
613 if (currency[1] > 'Z')
614 {
615 currency[1] = 'A';
616 ++currency[2];
617 }
618 if (currency[2] > 'Z')
619 {
620 currency[0] = 'A';
621 currency[1] = 'A';
622 currency[2] = 'A';
623 }
624 }
625
626 // Close enough ledgers to be able to delete alice's account.
627 incLgrSeqForAccDel(env, alice);
628
629 // Verify the existence of the expected ledger entries.
630 Keylet const aliceOwnerDirKey{keylet::ownerDir(alice.id())};
631 {
632 std::shared_ptr<ReadView const> closed{env.closed()};
633 BEAST_EXPECT(closed->exists(keylet::account(alice.id())));
634 BEAST_EXPECT(closed->exists(aliceOwnerDirKey));
635
636 // alice's directory nodes.
637 for (std::uint32_t i{0}; i < ((offerCount / 32) + 1); ++i)
638 BEAST_EXPECT(closed->exists(keylet::page(aliceOwnerDirKey, i)));
639
640 // alice's offers.
641 for (std::uint32_t i{0}; i < offerCount; ++i)
642 BEAST_EXPECT(
643 closed->exists(keylet::offer(alice.id(), offerSeq0 + i)));
644 }
645
646 // Delete alice's account. Should fail because she has too many
647 // offers in her directory.
648 auto const acctDelFee{drops(env.current()->fees().increment)};
649
650 env(acctdelete(alice, gw), fee(acctDelFee), ter(tefTOO_BIG));
651
652 // Cancel one of alice's offers. Then the account delete can succeed.
653 env.require(offers(alice, offerCount));
654 env(offer_cancel(alice, offerSeq0));
655 env.close();
656 env.require(offers(alice, offerCount - 1));
657
658 // alice successfully deletes her account.
659 auto const alicePreDelBal{env.balance(alice)};
660 env(acctdelete(alice, gw), fee(acctDelFee));
661 verifyDeliveredAmount(env, alicePreDelBal - acctDelFee);
662 env.close();
663
664 // Verify that alice's account root is gone as well as her directory
665 // nodes and all of her offers.
666 {
667 std::shared_ptr<ReadView const> closed{env.closed()};
668 BEAST_EXPECT(!closed->exists(keylet::account(alice.id())));
669 BEAST_EXPECT(!closed->exists(aliceOwnerDirKey));
670
671 // alice's former directory nodes.
672 for (std::uint32_t i{0}; i < ((offerCount / 32) + 1); ++i)
673 BEAST_EXPECT(
674 !closed->exists(keylet::page(aliceOwnerDirKey, i)));
675
676 // alice's former offers.
677 for (std::uint32_t i{0}; i < offerCount; ++i)
678 BEAST_EXPECT(
679 !closed->exists(keylet::offer(alice.id(), offerSeq0 + i)));
680 }
681 }
682
683 void
685 {
686 // Show that a trust line that is implicitly created by offer crossing
687 // prevents an account from being deleted.
688 using namespace jtx;
689
690 testcase("Implicitly created trust line");
691
692 Env env{*this};
693 Account const alice{"alice"};
694 Account const gw{"gw"};
695 auto const BUX{gw["BUX"]};
696
697 env.fund(XRP(10000), alice, gw);
698 env.close();
699
700 // alice creates an offer that, if crossed, will implicitly create
701 // a trust line.
702 env(offer(alice, BUX(30), XRP(30)));
703 env.close();
704
705 // gw crosses alice's offer. alice should end up with BUX(30).
706 env(offer(gw, XRP(30), BUX(30)));
707 env.close();
708 env.require(balance(alice, BUX(30)));
709
710 // Close enough ledgers to be able to delete alice's account.
711 incLgrSeqForAccDel(env, alice);
712
713 // alice and gw can't delete their accounts because of the implicitly
714 // created trust line.
715 auto const acctDelFee{drops(env.current()->fees().increment)};
716 env(acctdelete(alice, gw), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
717 env.close();
718
719 env(acctdelete(gw, alice), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
720 env.close();
721 {
722 std::shared_ptr<ReadView const> closed{env.closed()};
723 BEAST_EXPECT(closed->exists(keylet::account(alice.id())));
724 BEAST_EXPECT(closed->exists(keylet::account(gw.id())));
725 }
726 }
727
728 void
730 {
731 // See what happens when an account with a balance less than the
732 // incremental reserve tries to delete itself.
733 using namespace jtx;
734
735 testcase("Balance too small for fee");
736
737 Env env{*this};
738 Account const alice("alice");
739
740 // Note that the fee structure for unit tests does not match the fees
741 // on the production network (October 2019). Unit tests have a base
742 // reserve of 200 XRP.
743 env.fund(env.current()->fees().accountReserve(0), noripple(alice));
744 env.close();
745
746 // Burn a chunk of alice's funds so she only has 1 XRP remaining in
747 // her account.
748 env(noop(alice), fee(env.balance(alice) - XRP(1)));
749 env.close();
750
751 auto const acctDelFee{drops(env.current()->fees().increment)};
752 BEAST_EXPECT(acctDelFee > env.balance(alice));
753
754 // alice attempts to delete her account even though she can't pay
755 // the full fee. She specifies a fee that is larger than her balance.
756 //
757 // The balance of env.master should not change.
758 auto const masterBalance{env.balance(env.master)};
759 env(acctdelete(alice, env.master),
760 fee(acctDelFee),
762 env.close();
763 {
764 std::shared_ptr<ReadView const> const closed{env.closed()};
765 BEAST_EXPECT(closed->exists(keylet::account(alice.id())));
766 BEAST_EXPECT(env.balance(env.master) == masterBalance);
767 }
768
769 // alice again attempts to delete her account. This time she specifies
770 // her current balance in XRP. Again the transaction fails.
771 BEAST_EXPECT(env.balance(alice) == XRP(1));
772 env(acctdelete(alice, env.master), fee(XRP(1)), ter(telINSUF_FEE_P));
773 env.close();
774 {
775 std::shared_ptr<ReadView const> closed{env.closed()};
776 BEAST_EXPECT(closed->exists(keylet::account(alice.id())));
777 BEAST_EXPECT(env.balance(env.master) == masterBalance);
778 }
779 }
780
781 void
783 {
784 testcase("With Tickets");
785
786 using namespace test::jtx;
787
788 Account const alice{"alice"};
789 Account const bob{"bob"};
790
791 Env env{*this};
792 env.fund(XRP(100000), alice, bob);
793 env.close();
794
795 // bob grabs as many tickets as he is allowed to have.
796 std::uint32_t const ticketSeq{env.seq(bob) + 1};
797 env(ticket::create(bob, 250));
798 env.close();
799 env.require(owners(bob, 250));
800
801 {
802 std::shared_ptr<ReadView const> closed{env.closed()};
803 BEAST_EXPECT(closed->exists(keylet::account(bob.id())));
804 for (std::uint32_t i = 0; i < 250; ++i)
805 {
806 BEAST_EXPECT(
807 closed->exists(keylet::ticket(bob.id(), ticketSeq + i)));
808 }
809 }
810
811 // Close enough ledgers to be able to delete bob's account.
812 incLgrSeqForAccDel(env, bob);
813
814 // bob deletes his account using a ticket. bob's account and all
815 // of his tickets should be removed from the ledger.
816 auto const acctDelFee{drops(env.current()->fees().increment)};
817 auto const bobOldBalance{env.balance(bob)};
818 env(acctdelete(bob, alice), ticket::use(ticketSeq), fee(acctDelFee));
819 verifyDeliveredAmount(env, bobOldBalance - acctDelFee);
820 env.close();
821 {
822 std::shared_ptr<ReadView const> closed{env.closed()};
823 BEAST_EXPECT(!closed->exists(keylet::account(bob.id())));
824 for (std::uint32_t i = 0; i < 250; ++i)
825 {
826 BEAST_EXPECT(
827 !closed->exists(keylet::ticket(bob.id(), ticketSeq + i)));
828 }
829 }
830 }
831
832 void
834 {
835 testcase("Destination Constraints");
836
837 using namespace test::jtx;
838
839 Account const alice{"alice"};
840 Account const becky{"becky"};
841 Account const carol{"carol"};
842 Account const daria{"daria"};
843
844 Env env{*this};
845 env.fund(XRP(100000), alice, becky, carol);
846 env.close();
847
848 // alice sets the lsfDepositAuth flag on her account. This should
849 // prevent becky from deleting her account while using alice as the
850 // destination.
851 env(fset(alice, asfDepositAuth));
852
853 // carol requires a destination tag.
854 env(fset(carol, asfRequireDest));
855 env.close();
856
857 // Close enough ledgers to be able to delete becky's account.
858 incLgrSeqForAccDel(env, becky);
859
860 // becky attempts to delete her account using daria as the destination.
861 // Since daria is not in the ledger the delete attempt fails.
862 auto const acctDelFee{drops(env.current()->fees().increment)};
863 env(acctdelete(becky, daria), fee(acctDelFee), ter(tecNO_DST));
864 env.close();
865
866 // becky attempts to delete her account, but carol requires a
867 // destination tag which becky has omitted.
868 env(acctdelete(becky, carol), fee(acctDelFee), ter(tecDST_TAG_NEEDED));
869 env.close();
870
871 // becky attempts to delete her account, but alice won't take her XRP,
872 // so the delete is blocked.
873 env(acctdelete(becky, alice), fee(acctDelFee), ter(tecNO_PERMISSION));
874 env.close();
875
876 // alice preauthorizes deposits from becky. Now becky can delete her
877 // account and forward the leftovers to alice.
878 env(deposit::auth(alice, becky));
879 env.close();
880
881 auto const beckyOldBalance{env.balance(becky)};
882 env(acctdelete(becky, alice), fee(acctDelFee));
883 verifyDeliveredAmount(env, beckyOldBalance - acctDelFee);
884 env.close();
885 }
886
887 void
889 {
890 {
891 testcase(
892 "Destination Constraints with DepositPreauth and Credentials");
893
894 using namespace test::jtx;
895
896 Account const alice{"alice"};
897 Account const becky{"becky"};
898 Account const carol{"carol"};
899 Account const daria{"daria"};
900
901 char const credType[] = "abcd";
902
903 Env env{*this};
904 env.fund(XRP(100000), alice, becky, carol, daria);
905 env.close();
906
907 // carol issue credentials for becky
908 env(credentials::create(becky, carol, credType));
909 env.close();
910
911 // get credentials index
912 auto const jv =
913 credentials::ledgerEntry(env, becky, carol, credType);
914 std::string const credIdx = jv[jss::result][jss::index].asString();
915
916 // Close enough ledgers to be able to delete becky's account.
917 incLgrSeqForAccDel(env, becky);
918
919 auto const acctDelFee{drops(env.current()->fees().increment)};
920
921 // becky use credentials but they aren't accepted
922 env(acctdelete(becky, alice),
923 credentials::ids({credIdx}),
924 fee(acctDelFee),
926 env.close();
927
928 {
929 // alice sets the lsfDepositAuth flag on her account. This
930 // should prevent becky from deleting her account while using
931 // alice as the destination.
932 env(fset(alice, asfDepositAuth));
933 env.close();
934 }
935
936 // Fail, credentials still not accepted
937 env(acctdelete(becky, alice),
938 credentials::ids({credIdx}),
939 fee(acctDelFee),
941 env.close();
942
943 // becky accept the credentials
944 env(credentials::accept(becky, carol, credType));
945 env.close();
946
947 // Fail, credentials doesn’t belong to carol
948 env(acctdelete(carol, alice),
949 credentials::ids({credIdx}),
950 fee(acctDelFee),
952
953 // Fail, no depositPreauth for provided credentials
954 env(acctdelete(becky, alice),
955 credentials::ids({credIdx}),
956 fee(acctDelFee),
958 env.close();
959
960 // alice create DepositPreauth Object
961 env(deposit::authCredentials(alice, {{carol, credType}}));
962 env.close();
963
964 // becky attempts to delete her account, but alice won't take her
965 // XRP, so the delete is blocked.
966 env(acctdelete(becky, alice),
967 fee(acctDelFee),
969
970 // becky use empty credentials and can't delete account
971 env(acctdelete(becky, alice),
972 fee(acctDelFee),
975
976 // becky use bad credentials and can't delete account
977 env(acctdelete(becky, alice),
979 {"48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6E"
980 "A288BE4"}),
981 fee(acctDelFee),
983 env.close();
984
985 // becky use credentials and can delete account
986 env(acctdelete(becky, alice),
987 credentials::ids({credIdx}),
988 fee(acctDelFee));
989 env.close();
990
991 {
992 // check that credential object deleted too
993 auto const jNoCred =
994 credentials::ledgerEntry(env, becky, carol, credType);
995 BEAST_EXPECT(
996 jNoCred.isObject() && jNoCred.isMember(jss::result) &&
997 jNoCred[jss::result].isMember(jss::error) &&
998 jNoCred[jss::result][jss::error] == "entryNotFound");
999 }
1000
1001 testcase("Credentials that aren't required");
1002 { // carol issue credentials for daria
1003 env(credentials::create(daria, carol, credType));
1004 env.close();
1005 env(credentials::accept(daria, carol, credType));
1006 env.close();
1007 std::string const credDaria =
1009 env, daria, carol, credType)[jss::result][jss::index]
1010 .asString();
1011
1012 // daria use valid credentials, which aren't required and can
1013 // delete her account
1014 env(acctdelete(daria, carol),
1015 credentials::ids({credDaria}),
1016 fee(acctDelFee));
1017 env.close();
1018
1019 // check that credential object deleted too
1020 auto const jNoCred =
1021 credentials::ledgerEntry(env, daria, carol, credType);
1022
1023 BEAST_EXPECT(
1024 jNoCred.isObject() && jNoCred.isMember(jss::result) &&
1025 jNoCred[jss::result].isMember(jss::error) &&
1026 jNoCred[jss::result][jss::error] == "entryNotFound");
1027 }
1028
1029 {
1030 Account const eaton{"eaton"};
1031 Account const fred{"fred"};
1032
1033 env.fund(XRP(5000), eaton, fred);
1034
1035 // carol issue credentials for eaton
1036 env(credentials::create(eaton, carol, credType));
1037 env.close();
1038 env(credentials::accept(eaton, carol, credType));
1039 env.close();
1040 std::string const credEaton =
1042 env, eaton, carol, credType)[jss::result][jss::index]
1043 .asString();
1044
1045 // fred make preauthorization through authorized account
1046 env(fset(fred, asfDepositAuth));
1047 env.close();
1048 env(deposit::auth(fred, eaton));
1049 env.close();
1050
1051 // Close enough ledgers to be able to delete becky's account.
1052 incLgrSeqForAccDel(env, eaton);
1053 auto const acctDelFee{drops(env.current()->fees().increment)};
1054
1055 // eaton use valid credentials, but he already authorized
1056 // through "Authorized" field.
1057 env(acctdelete(eaton, fred),
1058 credentials::ids({credEaton}),
1059 fee(acctDelFee));
1060 env.close();
1061
1062 // check that credential object deleted too
1063 auto const jNoCred =
1064 credentials::ledgerEntry(env, eaton, carol, credType);
1065
1066 BEAST_EXPECT(
1067 jNoCred.isObject() && jNoCred.isMember(jss::result) &&
1068 jNoCred[jss::result].isMember(jss::error) &&
1069 jNoCred[jss::result][jss::error] == "entryNotFound");
1070 }
1071
1072 testcase("Expired credentials");
1073 {
1074 Account const john{"john"};
1075
1076 env.fund(XRP(10000), john);
1077 env.close();
1078
1079 auto jv = credentials::create(john, carol, credType);
1080 uint32_t const t = env.current()
1081 ->info()
1082 .parentCloseTime.time_since_epoch()
1083 .count() +
1084 20;
1085 jv[sfExpiration.jsonName] = t;
1086 env(jv);
1087 env.close();
1088 env(credentials::accept(john, carol, credType));
1089 env.close();
1090 jv = credentials::ledgerEntry(env, john, carol, credType);
1091 std::string const credIdx =
1092 jv[jss::result][jss::index].asString();
1093
1094 incLgrSeqForAccDel(env, john);
1095
1096 // credentials are expired
1097 // john use credentials but can't delete account
1098 env(acctdelete(john, alice),
1099 credentials::ids({credIdx}),
1100 fee(acctDelFee),
1101 ter(tecEXPIRED));
1102 env.close();
1103
1104 {
1105 // check that expired credential object deleted
1106 auto jv =
1107 credentials::ledgerEntry(env, john, carol, credType);
1108 BEAST_EXPECT(
1109 jv.isObject() && jv.isMember(jss::result) &&
1110 jv[jss::result].isMember(jss::error) &&
1111 jv[jss::result][jss::error] == "entryNotFound");
1112 }
1113 }
1114 }
1115
1116 {
1117 testcase("Credentials feature disabled");
1118 using namespace test::jtx;
1119
1120 Account const alice{"alice"};
1121 Account const becky{"becky"};
1122 Account const carol{"carol"};
1123
1124 Env env{*this, supported_amendments() - featureCredentials};
1125 env.fund(XRP(100000), alice, becky, carol);
1126 env.close();
1127
1128 // alice sets the lsfDepositAuth flag on her account. This should
1129 // prevent becky from deleting her account while using alice as the
1130 // destination.
1131 env(fset(alice, asfDepositAuth));
1132 env.close();
1133
1134 // Close enough ledgers to be able to delete becky's account.
1135 incLgrSeqForAccDel(env, becky);
1136
1137 auto const acctDelFee{drops(env.current()->fees().increment)};
1138
1139 std::string const credIdx =
1140 "098B7F1B146470A1C5084DC7832C04A72939E3EBC58E68AB8B579BA072B0CE"
1141 "CB";
1142
1143 // and can't delete even with old DepositPreauth
1144 env(deposit::auth(alice, becky));
1145 env.close();
1146
1147 env(acctdelete(becky, alice),
1148 credentials::ids({credIdx}),
1149 fee(acctDelFee),
1150 ter(temDISABLED));
1151 env.close();
1152 }
1153 }
1154
1155 void
1157 {
1158 {
1159 testcase("Deleting Issuer deletes issued credentials");
1160
1161 using namespace test::jtx;
1162
1163 Account const alice{"alice"};
1164 Account const becky{"becky"};
1165 Account const carol{"carol"};
1166
1167 char const credType[] = "abcd";
1168
1169 Env env{*this};
1170 env.fund(XRP(100000), alice, becky, carol);
1171 env.close();
1172
1173 // carol issue credentials for becky
1174 env(credentials::create(becky, carol, credType));
1175 env.close();
1176 env(credentials::accept(becky, carol, credType));
1177 env.close();
1178
1179 // get credentials index
1180 auto const jv =
1181 credentials::ledgerEntry(env, becky, carol, credType);
1182 std::string const credIdx = jv[jss::result][jss::index].asString();
1183
1184 // Close enough ledgers to be able to delete carol's account.
1185 incLgrSeqForAccDel(env, carol);
1186
1187 auto const acctDelFee{drops(env.current()->fees().increment)};
1188 env(acctdelete(carol, alice), fee(acctDelFee));
1189 env.close();
1190
1191 { // check that credential object deleted too
1192 BEAST_EXPECT(!env.le(credIdx));
1193 auto const jv =
1194 credentials::ledgerEntry(env, becky, carol, credType);
1195 BEAST_EXPECT(
1196 jv.isObject() && jv.isMember(jss::result) &&
1197 jv[jss::result].isMember(jss::error) &&
1198 jv[jss::result][jss::error] == "entryNotFound");
1199 }
1200 }
1201
1202 {
1203 testcase("Deleting Subject deletes issued credentials");
1204
1205 using namespace test::jtx;
1206
1207 Account const alice{"alice"};
1208 Account const becky{"becky"};
1209 Account const carol{"carol"};
1210
1211 char const credType[] = "abcd";
1212
1213 Env env{*this};
1214 env.fund(XRP(100000), alice, becky, carol);
1215 env.close();
1216
1217 // carol issue credentials for becky
1218 env(credentials::create(becky, carol, credType));
1219 env.close();
1220 env(credentials::accept(becky, carol, credType));
1221 env.close();
1222
1223 // get credentials index
1224 auto const jv =
1225 credentials::ledgerEntry(env, becky, carol, credType);
1226 std::string const credIdx = jv[jss::result][jss::index].asString();
1227
1228 // Close enough ledgers to be able to delete carol's account.
1229 incLgrSeqForAccDel(env, becky);
1230
1231 auto const acctDelFee{drops(env.current()->fees().increment)};
1232 env(acctdelete(becky, alice), fee(acctDelFee));
1233 env.close();
1234
1235 { // check that credential object deleted too
1236 BEAST_EXPECT(!env.le(credIdx));
1237 auto const jv =
1238 credentials::ledgerEntry(env, becky, carol, credType);
1239 BEAST_EXPECT(
1240 jv.isObject() && jv.isMember(jss::result) &&
1241 jv[jss::result].isMember(jss::error) &&
1242 jv[jss::result][jss::error] == "entryNotFound");
1243 }
1244 }
1245 }
1246
1247 void
1248 run() override
1249 {
1250 testBasics();
1259 testDest();
1262 }
1263};
1264
1265BEAST_DEFINE_TESTSUITE_PRIO(AccountDelete, app, ripple, 2);
1266
1267} // namespace test
1268} // namespace ripple
Represents a JSON value.
Definition: json_value.h:150
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:482
bool isMember(char const *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:962
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
A public key.
Definition: PublicKey.h:62
Slice slice() const noexcept
Definition: PublicKey.h:123
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition: STAmount.cpp:639
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:90
AccountID id() const
Returns the Account ID.
Definition: Account.h:107
static Account const master
The master account.
Definition: Account.h:48
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:114
A transaction testing environment.
Definition: Env.h:121
std::shared_ptr< STTx const > tx() const
Return the tx data for the last JTx.
Definition: Env.cpp:460
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:117
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:779
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:233
A balance matches.
Definition: balance.h:39
Set the fee on a JTx.
Definition: fee.h:37
Set a multisignature on a JTx.
Definition: multisign.h: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:170
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.
Definition: TestHelpers.h:450
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(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
void incLgrSeqForAccDel(jtx::Env &env, jtx::Account const &acc, std::uint32_t margin=0)
Definition: acctdelete.cpp:44
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition: offer.cpp:29
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
Definition: acctdelete.cpp:31
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
FeatureBitset supported_amendments()
Definition: Env.h:74
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition: offer.cpp:46
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
constexpr std::uint32_t asfDepositAuth
Definition: TxFlags.h:85
@ telINSUF_FEE_P
Definition: TER.h:57
constexpr std::uint32_t asfRequireDest
Definition: TxFlags.h:77
constexpr std::uint32_t tfImmediateOrCancel
Definition: TxFlags.h:98
@ 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:138
@ terINSUF_FEE_B
Definition: TER.h:216
@ temBAD_FEE
Definition: TER.h:92
@ temMALFORMED
Definition: TER.h:87
@ temINVALID_FLAG
Definition: TER.h:111
@ temDISABLED
Definition: TER.h:114
@ temDST_IS_SRC
Definition: TER.h:108
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:39
uint256 key
Definition: Keylet.h:40
Set the sequence number on a JTx.
Definition: seq.h:34
T time_since_epoch(T... args)