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