rippled
Loading...
Searching...
No Matches
DepositAuth_test.cpp
1#include <test/jtx.h>
2
3#include <xrpl/protocol/Feature.h>
4
5#include <algorithm>
6
7namespace xrpl {
8namespace test {
9
10// Helper function that returns the reserve on an account based on
11// the passed in number of owners.
12static XRPAmount
14{
15 return env.current()->fees().accountReserve(count);
16}
17
18// Helper function that returns true if acct has the lsfDepositAuth flag set.
19static bool
20hasDepositAuth(jtx::Env const& env, jtx::Account const& acct)
21{
22 return ((*env.le(acct))[sfFlags] & lsfDepositAuth) == lsfDepositAuth;
23}
24
26{
27 void
29 {
30 testcase("Enable");
31
32 using namespace jtx;
33 Account const alice{"alice"};
34
35 {
36 Env env(*this);
37 env.fund(XRP(10000), alice);
38
39 env(fset(alice, asfDepositAuth));
40 env.close();
41 BEAST_EXPECT(hasDepositAuth(env, alice));
42
43 env(fclear(alice, asfDepositAuth));
44 env.close();
45 BEAST_EXPECT(!hasDepositAuth(env, alice));
46 }
47 }
48
49 void
51 {
52 // Exercise IOU payments and non-direct XRP payments to an account
53 // that has the lsfDepositAuth flag set.
54 testcase("Pay IOU");
55
56 using namespace jtx;
57 Account const alice{"alice"};
58 Account const bob{"bob"};
59 Account const carol{"carol"};
60 Account const gw{"gw"};
61 IOU const USD = gw["USD"];
62
63 Env env(*this);
64
65 env.fund(XRP(10000), alice, bob, carol, gw);
66 env.close();
67 env.trust(USD(1000), alice, bob);
68 env.close();
69
70 env(pay(gw, alice, USD(150)));
71 env(offer(carol, USD(100), XRP(100)));
72 env.close();
73
74 // Make sure bob's trust line is all set up so he can receive USD.
75 env(pay(alice, bob, USD(50)));
76 env.close();
77
78 // bob sets the lsfDepositAuth flag.
80 env.close();
81
82 // None of the following payments should succeed.
83 auto failedIouPayments = [this, &env, &alice, &bob, &USD]() {
84 env.require(flags(bob, asfDepositAuth));
85
86 // Capture bob's balances before hand to confirm they don't change.
87 PrettyAmount const bobXrpBalance{env.balance(bob, XRP)};
88 PrettyAmount const bobUsdBalance{env.balance(bob, USD)};
89
90 env(pay(alice, bob, USD(50)), ter(tecNO_PERMISSION));
91 env.close();
92
93 // Note that even though alice is paying bob in XRP, the payment
94 // is still not allowed since the payment passes through an offer.
95 env(pay(alice, bob, drops(1)), sendmax(USD(1)), ter(tecNO_PERMISSION));
96 env.close();
97
98 BEAST_EXPECT(bobXrpBalance == env.balance(bob, XRP));
99 BEAST_EXPECT(bobUsdBalance == env.balance(bob, USD));
100 };
101
102 // Test when bob has an XRP balance > base reserve.
103 failedIouPayments();
104
105 // Set bob's XRP balance == base reserve. Also demonstrate that
106 // bob can make payments while his lsfDepositAuth flag is set.
107 env(pay(bob, alice, USD(25)));
108 env.close();
109
110 {
111 STAmount const bobPaysXRP{env.balance(bob, XRP) - reserve(env, 1)};
112 XRPAmount const bobPaysFee{reserve(env, 1) - reserve(env, 0)};
113 env(pay(bob, alice, bobPaysXRP), fee(bobPaysFee));
114 env.close();
115 }
116
117 // Test when bob's XRP balance == base reserve.
118 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0));
119 BEAST_EXPECT(env.balance(bob, USD) == USD(25));
120 failedIouPayments();
121
122 // Test when bob has an XRP balance == 0.
123 env(noop(bob), fee(reserve(env, 0)));
124 env.close();
125
126 BEAST_EXPECT(env.balance(bob, XRP) == XRP(0));
127 failedIouPayments();
128
129 // Give bob enough XRP for the fee to clear the lsfDepositAuth flag.
130 env(pay(alice, bob, drops(env.current()->fees().base)));
131
132 // bob clears the lsfDepositAuth and the next payment succeeds.
133 env(fclear(bob, asfDepositAuth));
134 env.close();
135
136 env(pay(alice, bob, USD(50)));
137 env.close();
138
139 env(pay(alice, bob, drops(1)), sendmax(USD(1)));
140 env.close();
141 }
142
143 void
145 {
146 // Exercise direct XRP payments to an account that has the
147 // lsfDepositAuth flag set.
148 testcase("Pay XRP");
149
150 using namespace jtx;
151 Account const alice{"alice"};
152 Account const bob{"bob"};
153
154 Env env(*this);
155 auto const baseFee = env.current()->fees().base;
156
157 env.fund(XRP(10000), alice, bob);
158 env.close();
159
160 // bob sets the lsfDepositAuth flag.
161 env(fset(bob, asfDepositAuth), fee(drops(baseFee)));
162 env.close();
163 BEAST_EXPECT(env.balance(bob, XRP) == XRP(10000) - drops(baseFee));
164
165 // bob has more XRP than the base reserve. Any XRP payment should fail.
166 env(pay(alice, bob, drops(1)), ter(tecNO_PERMISSION));
167 env.close();
168 BEAST_EXPECT(env.balance(bob, XRP) == XRP(10000) - drops(baseFee));
169
170 // Change bob's XRP balance to exactly the base reserve.
171 {
172 STAmount const bobPaysXRP{env.balance(bob, XRP) - reserve(env, 1)};
173 XRPAmount const bobPaysFee{reserve(env, 1) - reserve(env, 0)};
174 env(pay(bob, alice, bobPaysXRP), fee(bobPaysFee));
175 env.close();
176 }
177
178 // bob has exactly the base reserve. A small enough direct XRP
179 // payment should succeed.
180 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0));
181 env(pay(alice, bob, drops(1)));
182 env.close();
183
184 // bob has exactly the base reserve + 1. No payment should succeed.
185 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0) + drops(1));
186 env(pay(alice, bob, drops(1)), ter(tecNO_PERMISSION));
187 env.close();
188
189 // Take bob down to a balance of 0 XRP.
190 env(noop(bob), fee(reserve(env, 0) + drops(1)));
191 env.close();
192 BEAST_EXPECT(env.balance(bob, XRP) == drops(0));
193
194 // We should not be able to pay bob more than the base reserve.
195 env(pay(alice, bob, reserve(env, 0) + drops(1)), ter(tecNO_PERMISSION));
196 env.close();
197
198 // However a payment of exactly the base reserve should succeed.
199 env(pay(alice, bob, reserve(env, 0) + drops(0)));
200 env.close();
201 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0));
202
203 // We should be able to pay bob the base reserve one more time.
204 env(pay(alice, bob, reserve(env, 0) + drops(0)));
205 env.close();
206 BEAST_EXPECT(env.balance(bob, XRP) == (reserve(env, 0) + reserve(env, 0)));
207
208 // bob's above the threshold again. Any payment should fail.
209 env(pay(alice, bob, drops(1)), ter(tecNO_PERMISSION));
210 env.close();
211 BEAST_EXPECT(env.balance(bob, XRP) == (reserve(env, 0) + reserve(env, 0)));
212
213 // Take bob back down to a zero XRP balance.
214 env(noop(bob), fee(env.balance(bob, XRP)));
215 env.close();
216 BEAST_EXPECT(env.balance(bob, XRP) == drops(0));
217
218 // bob should not be able to clear lsfDepositAuth.
220 env.close();
221
222 // We should be able to pay bob 1 drop now.
223 env(pay(alice, bob, drops(1)));
224 env.close();
225 BEAST_EXPECT(env.balance(bob, XRP) == drops(1));
226
227 // Pay bob enough so he can afford the fee to clear lsfDepositAuth.
228 env(pay(alice, bob, drops(baseFee - 1)));
229 env.close();
230
231 // Interestingly, at this point the terINSUF_FEE_B retry grabs the
232 // request to clear lsfDepositAuth. So the balance should be zero
233 // and lsfDepositAuth should be cleared.
234 BEAST_EXPECT(env.balance(bob, XRP) == drops(0));
235 env.require(nflags(bob, asfDepositAuth));
236
237 // Since bob no longer has lsfDepositAuth set we should be able to
238 // pay him more than the base reserve.
239 env(pay(alice, bob, reserve(env, 0) + drops(1)));
240 env.close();
241 BEAST_EXPECT(env.balance(bob, XRP) == reserve(env, 0) + drops(1));
242 }
243
244 void
246 {
247 // It its current incarnation the DepositAuth flag does not change
248 // any behaviors regarding rippling and the NoRipple flag.
249 // Demonstrate that.
250 testcase("No Ripple");
251
252 using namespace jtx;
253 Account const gw1("gw1");
254 Account const gw2("gw2");
255 Account const alice("alice");
256 Account const bob("bob");
257
258 IOU const USD1(gw1["USD"]);
259 IOU const USD2(gw2["USD"]);
260
261 auto testIssuer =
262 [&](FeatureBitset const& features, bool noRipplePrev, bool noRippleNext, bool withDepositAuth) {
263 Env env(*this, features);
264
265 env.fund(XRP(10000), gw1, alice, bob);
266 env.close();
267 env(trust(gw1, alice["USD"](10), noRipplePrev ? tfSetNoRipple : 0));
268 env(trust(gw1, bob["USD"](10), noRippleNext ? tfSetNoRipple : 0));
269 env.trust(USD1(10), alice, bob);
270
271 env(pay(gw1, alice, USD1(10)));
272
273 if (withDepositAuth)
274 env(fset(gw1, asfDepositAuth));
275
276 TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY} : TER{tesSUCCESS};
277 env(pay(alice, bob, USD1(10)), path(gw1), ter(result));
278 };
279
280 auto testNonIssuer =
281 [&](FeatureBitset const& features, bool noRipplePrev, bool noRippleNext, bool withDepositAuth) {
282 Env env(*this, features);
283
284 env.fund(XRP(10000), gw1, gw2, alice);
285 env.close();
286 env(trust(alice, USD1(10), noRipplePrev ? tfSetNoRipple : 0));
287 env(trust(alice, USD2(10), noRippleNext ? tfSetNoRipple : 0));
288 env(pay(gw2, alice, USD2(10)));
289
290 if (withDepositAuth)
291 env(fset(alice, asfDepositAuth));
292
293 TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY} : TER{tesSUCCESS};
294 env(pay(gw1, gw2, USD2(10)), path(alice), sendmax(USD1(10)), ter(result));
295 };
296
297 // Test every combo of noRipplePrev, noRippleNext, and withDepositAuth
298 for (int i = 0; i < 8; ++i)
299 {
300 auto const noRipplePrev = i & 0x1;
301 auto const noRippleNext = i & 0x2;
302 auto const withDepositAuth = i & 0x4;
303 testIssuer(testable_amendments(), noRipplePrev, noRippleNext, withDepositAuth);
304
305 testNonIssuer(testable_amendments(), noRipplePrev, noRippleNext, withDepositAuth);
306 }
307 }
308
309 void
310 run() override
311 {
312 testEnable();
313 testPayIOU();
314 testPayXRP();
315 testNoRipple();
316 }
317};
318
319static Json::Value
321 jtx::Env& env,
322 jtx::Account const& acc,
324{
325 Json::Value jvParams;
326 jvParams[jss::ledger_index] = jss::validated;
327 jvParams[jss::deposit_preauth][jss::owner] = acc.human();
328 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
329 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
330 for (auto const& o : auth)
331 {
332 arr.append(o.toLEJson());
333 }
334 return env.rpc("json", "ledger_entry", to_string(jvParams));
335}
336
338{
339 void
341 {
342 testcase("Enable");
343
344 using namespace jtx;
345 Account const alice{"alice"};
346 Account const becky{"becky"};
347 {
348 // o We should be able to add and remove an entry, and
349 // o That entry should cost one reserve.
350 // o The reserve should be returned when the entry is removed.
351 Env env(*this);
352 env.fund(XRP(10000), alice, becky);
353 env.close();
354
355 // Add a DepositPreauth to alice.
356 env(deposit::auth(alice, becky));
357 env.close();
358 env.require(owners(alice, 1));
359 env.require(owners(becky, 0));
360
361 // Remove a DepositPreauth from alice.
362 env(deposit::unauth(alice, becky));
363 env.close();
364 env.require(owners(alice, 0));
365 env.require(owners(becky, 0));
366 }
367 {
368 // Verify that an account can be preauthorized and unauthorized
369 // using tickets.
370 Env env(*this);
371 env.fund(XRP(10000), alice, becky);
372 env.close();
373
374 env(ticket::create(alice, 2));
375 std::uint32_t const aliceSeq{env.seq(alice)};
376 env.close();
377 env.require(tickets(alice, 2));
378
379 // Consume the tickets from biggest seq to smallest 'cuz we can.
380 std::uint32_t aliceTicketSeq{env.seq(alice)};
381
382 // Add a DepositPreauth to alice.
383 env(deposit::auth(alice, becky), ticket::use(--aliceTicketSeq));
384 env.close();
385 // Alice uses a ticket but gains a preauth entry.
386 env.require(tickets(alice, 1));
387 env.require(owners(alice, 2));
388 BEAST_EXPECT(env.seq(alice) == aliceSeq);
389 env.require(owners(becky, 0));
390
391 // Remove a DepositPreauth from alice.
392 env(deposit::unauth(alice, becky), ticket::use(--aliceTicketSeq));
393 env.close();
394 env.require(tickets(alice, 0));
395 env.require(owners(alice, 0));
396 BEAST_EXPECT(env.seq(alice) == aliceSeq);
397 env.require(owners(becky, 0));
398 }
399 }
400
401 void
403 {
404 testcase("Invalid");
405
406 using namespace jtx;
407 Account const alice{"alice"};
408 Account const becky{"becky"};
409 Account const carol{"carol"};
410
411 Env env(*this);
412
413 // Tell env about alice, becky and carol since they are not yet funded.
414 env.memoize(alice);
415 env.memoize(becky);
416 env.memoize(carol);
417
418 // Add DepositPreauth to an unfunded account.
419 env(deposit::auth(alice, becky), seq(1), ter(terNO_ACCOUNT));
420
421 env.fund(XRP(10000), alice, becky);
422 env.close();
423
424 // Bad fee.
425 env(deposit::auth(alice, becky), fee(drops(-10)), ter(temBAD_FEE));
426 env.close();
427
428 // Bad flags.
429 env(deposit::auth(alice, becky), txflags(tfSell), ter(temINVALID_FLAG));
430 env.close();
431
432 {
433 // Neither auth not unauth.
434 Json::Value tx{deposit::auth(alice, becky)};
435 tx.removeMember(sfAuthorize.jsonName);
436 env(tx, ter(temMALFORMED));
437 env.close();
438 }
439 {
440 // Both auth and unauth.
441 Json::Value tx{deposit::auth(alice, becky)};
442 tx[sfUnauthorize.jsonName] = becky.human();
443 env(tx, ter(temMALFORMED));
444 env.close();
445 }
446 {
447 // Alice authorizes a zero account.
448 Json::Value tx{deposit::auth(alice, becky)};
449 tx[sfAuthorize.jsonName] = to_string(xrpAccount());
450 env(tx, ter(temINVALID_ACCOUNT_ID));
451 env.close();
452 }
453
454 // alice authorizes herself.
455 env(deposit::auth(alice, alice), ter(temCANNOT_PREAUTH_SELF));
456 env.close();
457
458 // alice authorizes an unfunded account.
459 env(deposit::auth(alice, carol), ter(tecNO_TARGET));
460 env.close();
461
462 // alice successfully authorizes becky.
463 env.require(owners(alice, 0));
464 env.require(owners(becky, 0));
465 env(deposit::auth(alice, becky));
466 env.close();
467 env.require(owners(alice, 1));
468 env.require(owners(becky, 0));
469
470 // alice attempts to create a duplicate authorization.
471 env(deposit::auth(alice, becky), ter(tecDUPLICATE));
472 env.close();
473 env.require(owners(alice, 1));
474 env.require(owners(becky, 0));
475
476 // carol attempts to preauthorize but doesn't have enough reserve.
477 env.fund(drops(249'999'999), carol);
478 env.close();
479
480 env(deposit::auth(carol, becky), ter(tecINSUFFICIENT_RESERVE));
481 env.close();
482 env.require(owners(carol, 0));
483 env.require(owners(becky, 0));
484
485 // carol gets enough XRP to (barely) meet the reserve.
486 env(pay(alice, carol, drops(env.current()->fees().base + 1)));
487 env.close();
488 env(deposit::auth(carol, becky));
489 env.close();
490 env.require(owners(carol, 1));
491 env.require(owners(becky, 0));
492
493 // But carol can't meet the reserve for another pre-authorization.
494 env(deposit::auth(carol, alice), ter(tecINSUFFICIENT_RESERVE));
495 env.close();
496 env.require(owners(carol, 1));
497 env.require(owners(becky, 0));
498 env.require(owners(alice, 1));
499
500 // alice attempts to remove an authorization she doesn't have.
501 env(deposit::unauth(alice, carol), ter(tecNO_ENTRY));
502 env.close();
503 env.require(owners(alice, 1));
504 env.require(owners(becky, 0));
505
506 // alice successfully removes her authorization of becky.
507 env(deposit::unauth(alice, becky));
508 env.close();
509 env.require(owners(alice, 0));
510 env.require(owners(becky, 0));
511
512 // alice removes becky again and gets an error.
513 env(deposit::unauth(alice, becky), ter(tecNO_ENTRY));
514 env.close();
515 env.require(owners(alice, 0));
516 env.require(owners(becky, 0));
517 }
518
519 void
521 {
522 testcase("Payment");
523
524 using namespace jtx;
525 Account const alice{"alice"};
526 Account const becky{"becky"};
527 Account const gw{"gw"};
528 IOU const USD(gw["USD"]);
529
530 {
531 // The initial implementation of DepositAuth had a bug where an
532 // account with the DepositAuth flag set could not make a payment
533 // to itself. That bug was fixed in the DepositPreauth amendment.
534 Env env(*this, features);
535 env.fund(XRP(5000), alice, becky, gw);
536 env.close();
537
538 env.trust(USD(1000), alice);
539 env.trust(USD(1000), becky);
540 env.close();
541
542 env(pay(gw, alice, USD(500)));
543 env.close();
544
545 env(offer(alice, XRP(100), USD(100), tfPassive), require(offers(alice, 1)));
546 env.close();
547
548 // becky pays herself USD (10) by consuming part of alice's offer.
549 // Make sure the payment works if PaymentAuth is not involved.
550 env(pay(becky, becky, USD(10)), path(~USD), sendmax(XRP(10)));
551 env.close();
552
553 // becky decides to require authorization for deposits.
554 env(fset(becky, asfDepositAuth));
555 env.close();
556
557 // becky pays herself again.
558 env(pay(becky, becky, USD(10)), path(~USD), sendmax(XRP(10)), ter(tesSUCCESS));
559 env.close();
560
561 {
562 // becky setup depositpreauth with credentials
563 char const credType[] = "abcde";
564 Account const carol{"carol"};
565 env.fund(XRP(5000), carol);
566 env.close();
567
568 bool const supportsCredentials = features[featureCredentials];
569
570 TER const expectTer(!supportsCredentials ? TER(temDISABLED) : TER(tesSUCCESS));
571
572 env(deposit::authCredentials(becky, {{carol, credType}}), ter(expectTer));
573 env.close();
574
575 // gw accept credentials
576 env(credentials::create(gw, carol, credType), ter(expectTer));
577 env.close();
578 env(credentials::accept(gw, carol, credType), ter(expectTer));
579 env.close();
580
581 auto jv = credentials::ledgerEntry(env, gw, carol, credType);
582 std::string const credIdx = supportsCredentials
583 ? jv[jss::result][jss::index].asString()
584 : "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6"
585 "EA288BE4";
586
587 env(pay(gw, becky, USD(100)), credentials::ids({credIdx}), ter(expectTer));
588 env.close();
589 }
590 }
591
592 // Make sure DepositPreauthorization works for payments.
593
594 Account const carol{"carol"};
595
596 Env env(*this, features);
597 env.fund(XRP(5000), alice, becky, carol, gw);
598 env.close();
599
600 env.trust(USD(1000), alice);
601 env.trust(USD(1000), becky);
602 env.trust(USD(1000), carol);
603 env.close();
604
605 env(pay(gw, alice, USD(1000)));
606 env.close();
607
608 // Make XRP and IOU payments from alice to becky. Should be fine.
609 env(pay(alice, becky, XRP(100)));
610 env(pay(alice, becky, USD(100)));
611 env.close();
612
613 // becky decides to require authorization for deposits.
614 env(fset(becky, asfDepositAuth));
615 env.close();
616
617 // alice can no longer pay becky.
618 env(pay(alice, becky, XRP(100)), ter(tecNO_PERMISSION));
619 env(pay(alice, becky, USD(100)), ter(tecNO_PERMISSION));
620 env.close();
621
622 // becky preauthorizes carol for deposit, which doesn't provide
623 // authorization for alice.
624 env(deposit::auth(becky, carol));
625 env.close();
626
627 // alice still can't pay becky.
628 env(pay(alice, becky, XRP(100)), ter(tecNO_PERMISSION));
629 env(pay(alice, becky, USD(100)), ter(tecNO_PERMISSION));
630 env.close();
631
632 // becky preauthorizes alice for deposit.
633 env(deposit::auth(becky, alice));
634 env.close();
635
636 // alice can now pay becky.
637 env(pay(alice, becky, XRP(100)));
638 env(pay(alice, becky, USD(100)));
639 env.close();
640
641 // alice decides to require authorization for deposits.
642 env(fset(alice, asfDepositAuth));
643 env.close();
644
645 // Even though alice is authorized to pay becky, becky is not
646 // authorized to pay alice.
647 env(pay(becky, alice, XRP(100)), ter(tecNO_PERMISSION));
648 env(pay(becky, alice, USD(100)), ter(tecNO_PERMISSION));
649 env.close();
650
651 // becky unauthorizes carol. Should have no impact on alice.
652 env(deposit::unauth(becky, carol));
653 env.close();
654
655 env(pay(alice, becky, XRP(100)));
656 env(pay(alice, becky, USD(100)));
657 env.close();
658
659 // becky unauthorizes alice. alice now can't pay becky.
660 env(deposit::unauth(becky, alice));
661 env.close();
662
663 env(pay(alice, becky, XRP(100)), ter(tecNO_PERMISSION));
664 env(pay(alice, becky, USD(100)), ter(tecNO_PERMISSION));
665 env.close();
666
667 // becky decides to remove authorization for deposits. Now
668 // alice can pay becky again.
669 env(fclear(becky, asfDepositAuth));
670 env.close();
671
672 env(pay(alice, becky, XRP(100)));
673 env(pay(alice, becky, USD(100)));
674 env.close();
675 }
676
677 void
679 {
680 using namespace jtx;
681
682 char const credType[] = "abcde";
683 Account const issuer{"issuer"};
684 Account const alice{"alice"};
685 Account const bob{"bob"};
686 Account const maria{"maria"};
687 Account const john{"john"};
688
689 {
690 testcase("Payment failure with disabled credentials rule.");
691
692 Env env(*this, testable_amendments() - featureCredentials);
693
694 env.fund(XRP(5000), issuer, bob, alice);
695 env.close();
696
697 // Bob require pre-authorization
698 env(fset(bob, asfDepositAuth));
699 env.close();
700
701 // Setup DepositPreauth object failed - amendent is not supported
702 env(deposit::authCredentials(bob, {{issuer, credType}}), ter(temDISABLED));
703 env.close();
704
705 // But can create old DepositPreauth
706 env(deposit::auth(bob, alice));
707 env.close();
708
709 // And alice can't pay with any credentials, amendment is not
710 // enabled
711 std::string const invalidIdx =
712 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
713 "01E034";
714 env(pay(alice, bob, XRP(10)), credentials::ids({invalidIdx}), ter(temDISABLED));
715 env.close();
716 }
717
718 {
719 testcase("Payment with credentials.");
720
721 Env env(*this);
722
723 env.fund(XRP(5000), issuer, alice, bob, john);
724 env.close();
725
726 // Issuer create credentials, but Alice didn't accept them yet
727 env(credentials::create(alice, issuer, credType));
728 env.close();
729
730 // Get the index of the credentials
731 auto const jv = credentials::ledgerEntry(env, alice, issuer, credType);
732 std::string const credIdx = jv[jss::result][jss::index].asString();
733
734 // Bob require pre-authorization
735 env(fset(bob, asfDepositAuth));
736 env.close();
737
738 // Bob will accept payments from accounts with credentials signed
739 // by 'issuer'
740 env(deposit::authCredentials(bob, {{issuer, credType}}));
741 env.close();
742
743 auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}});
744 BEAST_EXPECT(
745 jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) &&
746 jDP[jss::result].isMember(jss::node) && jDP[jss::result][jss::node].isMember("LedgerEntryType") &&
747 jDP[jss::result][jss::node]["LedgerEntryType"] == jss::DepositPreauth);
748
749 // Alice can't pay - empty credentials array
750 {
751 auto jv = pay(alice, bob, XRP(100));
752 jv[sfCredentialIDs.jsonName] = Json::arrayValue;
753 env(jv, ter(temMALFORMED));
754 env.close();
755 }
756
757 // Alice can't pay - not accepted credentials
758 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}), ter(tecBAD_CREDENTIALS));
759 env.close();
760
761 // Alice accept the credentials
762 env(credentials::accept(alice, issuer, credType));
763 env.close();
764
765 // Now Alice can pay
766 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}));
767 env.close();
768
769 // Alice can pay Maria without depositPreauth enabled
770 env(pay(alice, maria, XRP(250)), credentials::ids({credIdx}));
771 env.close();
772
773 // john can accept payment with old depositPreauth and valid
774 // credentials
775 env(fset(john, asfDepositAuth));
776 env(deposit::auth(john, alice));
777 env(pay(alice, john, XRP(100)), credentials::ids({credIdx}));
778 env.close();
779 }
780
781 {
782 testcase("Payment failure with invalid credentials.");
783
784 Env env(*this);
785
786 env.fund(XRP(10000), issuer, alice, bob, maria);
787 env.close();
788
789 // Issuer create credentials, but Alice didn't accept them yet
790 env(credentials::create(alice, issuer, credType));
791 env.close();
792 // Alice accept the credentials
793 env(credentials::accept(alice, issuer, credType));
794 env.close();
795 // Get the index of the credentials
796 auto const jv = credentials::ledgerEntry(env, alice, issuer, credType);
797 std::string const credIdx = jv[jss::result][jss::index].asString();
798
799 {
800 // Success as destination didn't enable pre-authorization so
801 // valid credentials will not fail
802 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}));
803 }
804
805 // Bob require pre-authorization
806 env(fset(bob, asfDepositAuth));
807 env.close();
808
809 {
810 // Fail as destination didn't setup DepositPreauth object
811 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}), ter(tecNO_PERMISSION));
812 }
813
814 // Bob setup DepositPreauth object, duplicates is not allowed
815 env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType}}), ter(temMALFORMED));
816
817 // Bob setup DepositPreauth object
818 env(deposit::authCredentials(bob, {{issuer, credType}}));
819 env.close();
820
821 {
822 std::string const invalidIdx =
823 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
824 "01E034";
825 // Alice can't pay with non-existing credentials
826 env(pay(alice, bob, XRP(100)), credentials::ids({invalidIdx}), ter(tecBAD_CREDENTIALS));
827 }
828
829 { // maria can't pay using valid credentials but issued for
830 // different account
831 env(pay(maria, bob, XRP(100)), credentials::ids({credIdx}), ter(tecBAD_CREDENTIALS));
832 }
833
834 {
835 // create another valid credential
836 char const credType2[] = "fghij";
837 env(credentials::create(alice, issuer, credType2));
838 env.close();
839 env(credentials::accept(alice, issuer, credType2));
840 env.close();
841 auto const jv = credentials::ledgerEntry(env, alice, issuer, credType2);
842 std::string const credIdx2 = jv[jss::result][jss::index].asString();
843
844 // Alice can't pay with invalid set of valid credentials
845 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx, credIdx2}), ter(tecNO_PERMISSION));
846 }
847
848 // Error, duplicate credentials
849 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx, credIdx}), ter(temMALFORMED));
850
851 // Alice can pay
852 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}));
853 env.close();
854 }
855 }
856
857 void
859 {
860 using namespace jtx;
861
862 char const credType[] = "abcde";
863 Account const issuer{"issuer"};
864 Account const alice{"alice"};
865 Account const bob{"bob"};
866 Account const maria{"maria"};
867
868 {
869 testcase("Creating / deleting with credentials.");
870
871 Env env(*this);
872
873 env.fund(XRP(5000), issuer, alice, bob);
874 env.close();
875
876 {
877 // both included [AuthorizeCredentials UnauthorizeCredentials]
878 auto jv = deposit::authCredentials(bob, {{issuer, credType}});
879 jv[sfUnauthorizeCredentials.jsonName] = Json::arrayValue;
880 env(jv, ter(temMALFORMED));
881 }
882
883 {
884 // both included [Unauthorize, AuthorizeCredentials]
885 auto jv = deposit::authCredentials(bob, {{issuer, credType}});
886 jv[sfUnauthorize.jsonName] = issuer.human();
887 env(jv, ter(temMALFORMED));
888 }
889
890 {
891 // both included [Authorize, AuthorizeCredentials]
892 auto jv = deposit::authCredentials(bob, {{issuer, credType}});
893 jv[sfAuthorize.jsonName] = issuer.human();
894 env(jv, ter(temMALFORMED));
895 }
896
897 {
898 // both included [Unauthorize, UnauthorizeCredentials]
899 auto jv = deposit::unauthCredentials(bob, {{issuer, credType}});
900 jv[sfUnauthorize.jsonName] = issuer.human();
901 env(jv, ter(temMALFORMED));
902 }
903
904 {
905 // both included [Authorize, UnauthorizeCredentials]
906 auto jv = deposit::unauthCredentials(bob, {{issuer, credType}});
907 jv[sfAuthorize.jsonName] = issuer.human();
908 env(jv, ter(temMALFORMED));
909 }
910
911 {
912 // AuthorizeCredentials is empty
913 auto jv = deposit::authCredentials(bob, {});
914 env(jv, ter(temARRAY_EMPTY));
915 }
916
917 {
918 // invalid issuer
919 auto jv = deposit::authCredentials(bob, {});
920 auto& arr(jv[sfAuthorizeCredentials.jsonName]);
922 cred[jss::Issuer] = to_string(xrpAccount());
923 cred[sfCredentialType.jsonName] = strHex(std::string_view(credType));
924 Json::Value credParent;
925 credParent[jss::Credential] = cred;
926 arr.append(std::move(credParent));
927
928 env(jv, ter(temINVALID_ACCOUNT_ID));
929 }
930
931 {
932 // empty credential type
933 auto jv = deposit::authCredentials(bob, {{issuer, {}}});
934 env(jv, ter(temMALFORMED));
935 }
936
937 {
938 // AuthorizeCredentials is larger than 8 elements
939 Account const a("a"), b("b"), c("c"), d("d"), e("e"), f("f"), g("g"), h("h"), i("i");
940 auto const& z = credType;
941 auto jv = deposit::authCredentials(
942 bob, {{a, z}, {b, z}, {c, z}, {d, z}, {e, z}, {f, z}, {g, z}, {h, z}, {i, z}});
943 env(jv, ter(temARRAY_TOO_LARGE));
944 }
945
946 {
947 // Can't create with non-existing issuer
948 Account const rick{"rick"};
949 auto jv = deposit::authCredentials(bob, {{rick, credType}});
950 env(jv, ter(tecNO_ISSUER));
951 env.close();
952 }
953
954 {
955 // not enough reserve
956 Account const john{"john"};
957 env.fund(env.current()->fees().accountReserve(0), john);
958 env.close();
959 auto jv = deposit::authCredentials(john, {{issuer, credType}});
961 }
962
963 {
964 // NO deposit object exists
965 env(deposit::unauthCredentials(bob, {{issuer, credType}}), ter(tecNO_ENTRY));
966 }
967
968 // Create DepositPreauth object
969 {
970 env(deposit::authCredentials(bob, {{issuer, credType}}));
971 env.close();
972
973 auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}});
974 BEAST_EXPECT(
975 jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) &&
976 jDP[jss::result].isMember(jss::node) && jDP[jss::result][jss::node].isMember("LedgerEntryType") &&
977 jDP[jss::result][jss::node]["LedgerEntryType"] == jss::DepositPreauth);
978
979 // Check object fields
980 BEAST_EXPECT(jDP[jss::result][jss::node][jss::Account] == bob.human());
981 auto const& credentials(jDP[jss::result][jss::node]["AuthorizeCredentials"]);
982 BEAST_EXPECT(credentials.isArray() && credentials.size() == 1);
983 for (auto const& o : credentials)
984 {
985 auto const& c(o[jss::Credential]);
986 BEAST_EXPECT(c[jss::Issuer].asString() == issuer.human());
987 BEAST_EXPECT(c["CredentialType"].asString() == strHex(std::string_view(credType)));
988 }
989
990 // can't create duplicate
991 env(deposit::authCredentials(bob, {{issuer, credType}}), ter(tecDUPLICATE));
992 }
993
994 // Delete DepositPreauth object
995 {
996 env(deposit::unauthCredentials(bob, {{issuer, credType}}));
997 env.close();
998 auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}});
999 BEAST_EXPECT(
1000 jDP.isObject() && jDP.isMember(jss::result) && jDP[jss::result].isMember(jss::error) &&
1001 jDP[jss::result][jss::error] == "entryNotFound");
1002 }
1003 }
1004 }
1005
1006 void
1008 {
1009 using namespace jtx;
1010 char const credType[] = "abcde";
1011 char const credType2[] = "fghijkl";
1012 Account const issuer{"issuer"};
1013 Account const alice{"alice"};
1014 Account const bob{"bob"};
1015 Account const gw{"gw"};
1016 IOU const USD = gw["USD"];
1017 Account const zelda{"zelda"};
1018
1019 {
1020 testcase("Payment failure with expired credentials.");
1021
1022 Env env(*this);
1023
1024 env.fund(XRP(10000), issuer, alice, bob, gw);
1025 env.close();
1026
1027 // Create credentials
1028 auto jv = credentials::create(alice, issuer, credType);
1029 // Current time in ripple epoch.
1030 // Every time ledger close, unittest timer increase by 10s
1031 uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 60;
1032 jv[sfExpiration.jsonName] = t;
1033 env(jv);
1034 env.close();
1035
1036 // Alice accept the credentials
1037 env(credentials::accept(alice, issuer, credType));
1038 env.close();
1039
1040 // Create credential which not expired
1041 jv = credentials::create(alice, issuer, credType2);
1042 uint32_t const t2 = env.current()->header().parentCloseTime.time_since_epoch().count() + 1000;
1043 jv[sfExpiration.jsonName] = t2;
1044 env(jv);
1045 env.close();
1046 env(credentials::accept(alice, issuer, credType2));
1047 env.close();
1048
1049 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1050 BEAST_EXPECT(ownerCount(env, alice) == 2);
1051
1052 // Get the index of the credentials
1053 jv = credentials::ledgerEntry(env, alice, issuer, credType);
1054 std::string const credIdx = jv[jss::result][jss::index].asString();
1055 jv = credentials::ledgerEntry(env, alice, issuer, credType2);
1056 std::string const credIdx2 = jv[jss::result][jss::index].asString();
1057
1058 // Bob require pre-authorization
1059 env(fset(bob, asfDepositAuth));
1060 env.close();
1061 // Bob setup DepositPreauth object
1062 env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType2}}));
1063 env.close();
1064
1065 {
1066 // Alice can pay
1067 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx, credIdx2}));
1068 env.close();
1069 env.close();
1070
1071 // Ledger closed, time increased, alice can't pay anymore
1072 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx, credIdx2}), ter(tecEXPIRED));
1073 env.close();
1074
1075 {
1076 // check that expired credentials were deleted
1077 auto const jDelCred = credentials::ledgerEntry(env, alice, issuer, credType);
1078 BEAST_EXPECT(
1079 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1080 jDelCred[jss::result].isMember(jss::error) &&
1081 jDelCred[jss::result][jss::error] == "entryNotFound");
1082 }
1083
1084 {
1085 // check that non-expired credential still present
1086 auto const jle = credentials::ledgerEntry(env, alice, issuer, credType2);
1087 BEAST_EXPECT(
1088 jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) &&
1089 jle[jss::result].isMember(jss::node) &&
1090 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
1091 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
1092 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
1093 jle[jss::result][jss::node][jss::Subject] == alice.human() &&
1094 jle[jss::result][jss::node]["CredentialType"] == strHex(std::string_view(credType2)));
1095 }
1096
1097 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1098 BEAST_EXPECT(ownerCount(env, alice) == 1);
1099 }
1100
1101 {
1102 auto jv = credentials::create(gw, issuer, credType);
1103 uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 40;
1104 jv[sfExpiration.jsonName] = t;
1105 env(jv);
1106 env.close();
1107 env(credentials::accept(gw, issuer, credType));
1108 env.close();
1109
1110 jv = credentials::ledgerEntry(env, gw, issuer, credType);
1111 std::string const credIdx = jv[jss::result][jss::index].asString();
1112
1113 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1114 BEAST_EXPECT(ownerCount(env, gw) == 1);
1115
1116 env.close();
1117 env.close();
1118 env.close();
1119
1120 // credentials are expired
1121 env(pay(gw, bob, USD(150)), credentials::ids({credIdx}), ter(tecEXPIRED));
1122 env.close();
1123
1124 // check that expired credentials were deleted
1125 auto const jDelCred = credentials::ledgerEntry(env, gw, issuer, credType);
1126 BEAST_EXPECT(
1127 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1128 jDelCred[jss::result].isMember(jss::error) && jDelCred[jss::result][jss::error] == "entryNotFound");
1129
1130 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1131 BEAST_EXPECT(ownerCount(env, gw) == 0);
1132 }
1133 }
1134
1135 {
1136 using namespace std::chrono;
1137
1138 testcase("Escrow failure with expired credentials.");
1139
1140 Env env(*this);
1141
1142 env.fund(XRP(5000), issuer, alice, bob, zelda);
1143 env.close();
1144
1145 // Create credentials
1146 auto jv = credentials::create(zelda, issuer, credType);
1147 uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count() + 50;
1148 jv[sfExpiration.jsonName] = t;
1149 env(jv);
1150 env.close();
1151
1152 // Zelda accept the credentials
1153 env(credentials::accept(zelda, issuer, credType));
1154 env.close();
1155
1156 // Get the index of the credentials
1157 jv = credentials::ledgerEntry(env, zelda, issuer, credType);
1158 std::string const credIdx = jv[jss::result][jss::index].asString();
1159
1160 // Bob require pre-authorization
1161 env(fset(bob, asfDepositAuth));
1162 env.close();
1163 // Bob setup DepositPreauth object
1164 env(deposit::authCredentials(bob, {{issuer, credType}}));
1165 env.close();
1166
1167 auto const seq = env.seq(alice);
1168 env(escrow::create(alice, bob, XRP(1000)), escrow::finish_time(env.now() + 1s));
1169 env.close();
1170
1171 // zelda can't finish escrow with invalid credentials
1172 {
1173 env(escrow::finish(zelda, alice, seq), credentials::ids({}), ter(temMALFORMED));
1174 env.close();
1175 }
1176
1177 {
1178 // zelda can't finish escrow with invalid credentials
1179 std::string const invalidIdx =
1180 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
1181 "01E034";
1182
1183 env(escrow::finish(zelda, alice, seq), credentials::ids({invalidIdx}), ter(tecBAD_CREDENTIALS));
1184 env.close();
1185 }
1186
1187 { // Ledger closed, time increased, zelda can't finish escrow
1188 env(escrow::finish(zelda, alice, seq), credentials::ids({credIdx}), fee(1500), ter(tecEXPIRED));
1189 env.close();
1190 }
1191
1192 // check that expired credentials were deleted
1193 auto const jDelCred = credentials::ledgerEntry(env, zelda, issuer, credType);
1194 BEAST_EXPECT(
1195 jDelCred.isObject() && jDelCred.isMember(jss::result) && jDelCred[jss::result].isMember(jss::error) &&
1196 jDelCred[jss::result][jss::error] == "entryNotFound");
1197 }
1198 }
1199
1200 void
1202 {
1203 using namespace jtx;
1204
1205 Account const stock{"stock"};
1206 Account const alice{"alice"};
1207 Account const bob{"bob"};
1208
1209 Env env(*this);
1210
1211 testcase("Sorting credentials.");
1212
1213 env.fund(XRP(5000), stock, alice, bob);
1214
1216 {"a", "a"}, {"b", "b"}, {"c", "c"}, {"d", "d"}, {"e", "e"}, {"f", "f"}, {"g", "g"}, {"h", "h"}};
1217
1218 for (auto const& c : credentials)
1219 env.fund(XRP(5000), c.issuer);
1220 env.close();
1221
1223 std::mt19937 gen(rd());
1224
1225 {
1227 for (auto const& c : credentials)
1228 pubKey2Acc.emplace(c.issuer.human(), c.issuer);
1229
1230 // check sorting in object
1231 for (int i = 0; i < 10; ++i)
1232 {
1233 std::ranges::shuffle(credentials, gen);
1234 env(deposit::authCredentials(stock, credentials));
1235 env.close();
1236
1237 auto const dp = ledgerEntryDepositPreauth(env, stock, credentials);
1238 auto const& authCred(dp[jss::result][jss::node]["AuthorizeCredentials"]);
1239 BEAST_EXPECT(authCred.isArray() && authCred.size() == credentials.size());
1241 for (auto const& o : authCred)
1242 {
1243 auto const& c(o[jss::Credential]);
1244 auto issuer = c[jss::Issuer].asString();
1245
1246 if (BEAST_EXPECT(pubKey2Acc.contains(issuer)))
1247 readCreds.emplace_back(pubKey2Acc.at(issuer), c["CredentialType"].asString());
1248 }
1249
1250 BEAST_EXPECT(std::ranges::is_sorted(readCreds));
1251
1252 env(deposit::unauthCredentials(stock, credentials));
1253 env.close();
1254 }
1255 }
1256
1257 {
1258 std::ranges::shuffle(credentials, gen);
1259 env(deposit::authCredentials(stock, credentials));
1260 env.close();
1261
1262 // check sorting in params
1263 for (int i = 0; i < 10; ++i)
1264 {
1265 std::ranges::shuffle(credentials, gen);
1266 env(deposit::authCredentials(stock, credentials), ter(tecDUPLICATE));
1267 }
1268 }
1269
1270 testcase("Check duplicate credentials.");
1271 {
1272 // check duplicates in depositPreauth params
1273 std::vector<deposit::AuthorizeCredentials> copyCredentials(credentials.begin(), credentials.end() - 1);
1274
1275 std::ranges::shuffle(copyCredentials, gen);
1276 for (auto const& c : copyCredentials)
1277 {
1278 auto credentials2 = copyCredentials;
1279 credentials2.push_back(c);
1280 env(deposit::authCredentials(stock, credentials2), ter(temMALFORMED));
1281 }
1282
1283 // create batch of credentials and save their hashes
1284 std::vector<std::string> credentialIDs;
1285 for (auto const& c : credentials)
1286 {
1287 env(credentials::create(alice, c.issuer, c.credType));
1288 env.close();
1289 env(credentials::accept(alice, c.issuer, c.credType));
1290 env.close();
1291
1292 credentialIDs.push_back(
1293 credentials::ledgerEntry(env, alice, c.issuer, c.credType)[jss::result][jss::index].asString());
1294 }
1295
1296 // check duplicates in payment params
1297 for (auto const& h : credentialIDs)
1298 {
1299 auto credentialIDs2 = credentialIDs;
1300 credentialIDs2.push_back(h);
1301
1302 env(pay(alice, bob, XRP(100)), credentials::ids(credentialIDs2), ter(temMALFORMED));
1303 }
1304 }
1305 }
1306
1307 void
1308 run() override
1309 {
1310 testEnable();
1311 testInvalid();
1312 auto const supported{jtx::testable_amendments()};
1313 testPayment(supported - featureCredentials);
1314 testPayment(supported);
1319 }
1320};
1321
1322BEAST_DEFINE_TESTSUITE(DepositAuth, app, xrpl);
1323BEAST_DEFINE_TESTSUITE(DepositPreauth, app, xrpl);
1324
1325} // namespace test
1326} // namespace xrpl
T at(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
Immutable cryptographic account descriptor.
Definition Account.h:19
std::string const & human() const
Returns the human readable public key.
Definition Account.h:94
A transaction testing environment.
Definition Env.h:119
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:98
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:249
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:261
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:240
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:158
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:284
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:792
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:131
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:533
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:319
NetClock::time_point now()
Returns the current network time.
Definition Env.h:274
Converts to IOU Issue or STAmount.
Set the fee on a JTx.
Definition fee.h:17
Match set account flags.
Definition flags.h:108
Match clear account flags.
Definition flags.h:124
Match the number of items in the account's owner directory.
Definition owners.h:48
Add a path.
Definition paths.h:37
Check a set of conditions.
Definition require.h:46
Sets the SendMax on a JTx.
Definition sendmax.h:13
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
Set a ticket sequence on a JTx.
Definition ticket.h:28
Set the flags on a JTx.
Definition txflags.h:11
T contains(T... args)
T emplace_back(T... args)
T emplace(T... args)
T end(T... args)
T is_sorted(T... args)
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:26
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:49
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:13
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:35
Json::Value unauth(Account const &account, Account const &unauth)
Remove pre-authorization for deposit.
Definition deposit.cpp:24
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:13
Json::Value unauthCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:53
auto const finish_time
Set the "FinishAfter" time tag on a JTx.
Definition escrow.h:73
Json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount)
Definition escrow.cpp:14
Json::Value finish(AccountID const &account, AccountID const &from, std::uint32_t seq)
Definition escrow.cpp:26
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
std::uint32_t ownerCount(Env const &env, Account const &account)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
FeatureBitset testable_amendments()
Definition Env.h:76
owner_count< ltOFFER > offers
Match the number of offers in the account's owner directory.
Definition owners.h:66
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
owner_count< ltTICKET > tickets
Match the number of tickets on the account.
Definition ticket.h:44
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
static bool hasDepositAuth(jtx::Env const &env, jtx::Account const &acct)
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
static Json::Value ledgerEntryDepositPreauth(jtx::Env &env, jtx::Account const &acc, std::vector< jtx::deposit::AuthorizeCredentials > const &auth)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
constexpr std::uint32_t tfSell
Definition TxFlags.h:81
@ terINSUF_FEE_B
Definition TER.h:196
@ terNO_ACCOUNT
Definition TER.h:197
constexpr std::uint32_t tfPassive
Definition TxFlags.h:78
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:96
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:65
TERSubset< CanCvtToTER > TER
Definition TER.h:620
@ temARRAY_TOO_LARGE
Definition TER.h:121
@ temBAD_FEE
Definition TER.h:72
@ temINVALID_FLAG
Definition TER.h:91
@ temCANNOT_PREAUTH_SELF
Definition TER.h:100
@ temMALFORMED
Definition TER.h:67
@ temARRAY_EMPTY
Definition TER.h:120
@ temINVALID_ACCOUNT_ID
Definition TER.h:99
@ temDISABLED
Definition TER.h:94
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecNO_ENTRY
Definition TER.h:287
@ tecNO_TARGET
Definition TER.h:285
@ tecPATH_DRY
Definition TER.h:275
@ tecBAD_CREDENTIALS
Definition TER.h:340
@ tecEXPIRED
Definition TER.h:295
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
@ tecNO_PERMISSION
Definition TER.h:286
@ tecNO_ISSUER
Definition TER.h:280
@ tecDUPLICATE
Definition TER.h:296
@ lsfDepositAuth
@ tesSUCCESS
Definition TER.h:225
T push_back(T... args)
T shuffle(T... args)
T size(T... args)
void run() override
Runs the suite.
void run() override
Runs the suite.
void testPayment(FeatureBitset features)
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...
Set the sequence number on a JTx.
Definition seq.h:14