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