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.
79 env(fset(bob, asfDepositAuth), require(flags(bob, asfDepositAuth)));
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.
219 env(fclear(bob, asfDepositAuth), ter(terINSUF_FEE_B));
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 = [&](FeatureBitset const& features,
262 bool noRipplePrev,
263 bool noRippleNext,
264 bool withDepositAuth) {
265 Env env(*this, features);
266
267 env.fund(XRP(10000), gw1, alice, bob);
268 env.close();
269 env(trust(gw1, alice["USD"](10), noRipplePrev ? tfSetNoRipple : 0));
270 env(trust(gw1, bob["USD"](10), noRippleNext ? tfSetNoRipple : 0));
271 env.trust(USD1(10), alice, bob);
272
273 env(pay(gw1, alice, USD1(10)));
274
275 if (withDepositAuth)
276 env(fset(gw1, asfDepositAuth));
277
278 TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY} : TER{tesSUCCESS};
279 env(pay(alice, bob, USD1(10)), path(gw1), ter(result));
280 };
281
282 auto testNonIssuer = [&](FeatureBitset const& features,
283 bool noRipplePrev,
284 bool noRippleNext,
285 bool withDepositAuth) {
286 Env env(*this, features);
287
288 env.fund(XRP(10000), gw1, gw2, alice);
289 env.close();
290 env(trust(alice, USD1(10), noRipplePrev ? tfSetNoRipple : 0));
291 env(trust(alice, USD2(10), noRippleNext ? tfSetNoRipple : 0));
292 env(pay(gw2, alice, USD2(10)));
293
294 if (withDepositAuth)
295 env(fset(alice, asfDepositAuth));
296
297 TER const result = (noRippleNext && noRipplePrev) ? TER{tecPATH_DRY} : TER{tesSUCCESS};
298 env(pay(gw1, gw2, USD2(10)), path(alice), sendmax(USD1(10)), ter(result));
299 };
300
301 // Test every combo of noRipplePrev, noRippleNext, and withDepositAuth
302 for (int i = 0; i < 8; ++i)
303 {
304 auto const noRipplePrev = i & 0x1;
305 auto const noRippleNext = i & 0x2;
306 auto const withDepositAuth = i & 0x4;
307 testIssuer(
308 testable_amendments(), noRipplePrev != 0, noRippleNext != 0, withDepositAuth != 0);
309
310 testNonIssuer(
311 testable_amendments(), noRipplePrev != 0, noRippleNext != 0, withDepositAuth != 0);
312 }
313 }
314
315 void
316 run() override
317 {
318 testEnable();
319 testPayIOU();
320 testPayXRP();
321 testNoRipple();
322 }
323};
324
325static Json::Value
327 jtx::Env& env,
328 jtx::Account const& acc,
330{
331 Json::Value jvParams;
332 jvParams[jss::ledger_index] = jss::validated;
333 jvParams[jss::deposit_preauth][jss::owner] = acc.human();
334 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
335 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
336 for (auto const& o : auth)
337 {
338 arr.append(o.toLEJson());
339 }
340 return env.rpc("json", "ledger_entry", to_string(jvParams));
341}
342
344{
345 void
347 {
348 testcase("Enable");
349
350 using namespace jtx;
351 Account const alice{"alice"};
352 Account const becky{"becky"};
353 {
354 // o We should be able to add and remove an entry, and
355 // o That entry should cost one reserve.
356 // o The reserve should be returned when the entry is removed.
357 Env env(*this);
358 env.fund(XRP(10000), alice, becky);
359 env.close();
360
361 // Add a DepositPreauth to alice.
362 env(deposit::auth(alice, becky));
363 env.close();
364 env.require(owners(alice, 1));
365 env.require(owners(becky, 0));
366
367 // Remove a DepositPreauth from alice.
368 env(deposit::unauth(alice, becky));
369 env.close();
370 env.require(owners(alice, 0));
371 env.require(owners(becky, 0));
372 }
373 {
374 // Verify that an account can be preauthorized and unauthorized
375 // using tickets.
376 Env env(*this);
377 env.fund(XRP(10000), alice, becky);
378 env.close();
379
380 env(ticket::create(alice, 2));
381 std::uint32_t const aliceSeq{env.seq(alice)};
382 env.close();
383 env.require(tickets(alice, 2));
384
385 // Consume the tickets from biggest seq to smallest 'cuz we can.
386 std::uint32_t aliceTicketSeq{env.seq(alice)};
387
388 // Add a DepositPreauth to alice.
389 env(deposit::auth(alice, becky), ticket::use(--aliceTicketSeq));
390 env.close();
391 // Alice uses a ticket but gains a preauth entry.
392 env.require(tickets(alice, 1));
393 env.require(owners(alice, 2));
394 BEAST_EXPECT(env.seq(alice) == aliceSeq);
395 env.require(owners(becky, 0));
396
397 // Remove a DepositPreauth from alice.
398 env(deposit::unauth(alice, becky), ticket::use(--aliceTicketSeq));
399 env.close();
400 env.require(tickets(alice, 0));
401 env.require(owners(alice, 0));
402 BEAST_EXPECT(env.seq(alice) == aliceSeq);
403 env.require(owners(becky, 0));
404 }
405 }
406
407 void
409 {
410 testcase("Invalid");
411
412 using namespace jtx;
413 Account const alice{"alice"};
414 Account const becky{"becky"};
415 Account const carol{"carol"};
416
417 Env env(*this);
418
419 // Tell env about alice, becky and carol since they are not yet funded.
420 env.memoize(alice);
421 env.memoize(becky);
422 env.memoize(carol);
423
424 // Add DepositPreauth to an unfunded account.
425 env(deposit::auth(alice, becky), seq(1), ter(terNO_ACCOUNT));
426
427 env.fund(XRP(10000), alice, becky);
428 env.close();
429
430 // Bad fee.
431 env(deposit::auth(alice, becky), fee(drops(-10)), ter(temBAD_FEE));
432 env.close();
433
434 // Bad flags.
435 env(deposit::auth(alice, becky), txflags(tfSell), ter(temINVALID_FLAG));
436 env.close();
437
438 {
439 // Neither auth not unauth.
440 Json::Value tx{deposit::auth(alice, becky)};
441 tx.removeMember(sfAuthorize.jsonName);
442 env(tx, ter(temMALFORMED));
443 env.close();
444 }
445 {
446 // Both auth and unauth.
447 Json::Value tx{deposit::auth(alice, becky)};
448 tx[sfUnauthorize.jsonName] = becky.human();
449 env(tx, ter(temMALFORMED));
450 env.close();
451 }
452 {
453 // Alice authorizes a zero account.
454 Json::Value tx{deposit::auth(alice, becky)};
455 tx[sfAuthorize.jsonName] = to_string(xrpAccount());
456 env(tx, ter(temINVALID_ACCOUNT_ID));
457 env.close();
458 }
459
460 // alice authorizes herself.
461 env(deposit::auth(alice, alice), ter(temCANNOT_PREAUTH_SELF));
462 env.close();
463
464 // alice authorizes an unfunded account.
465 env(deposit::auth(alice, carol), ter(tecNO_TARGET));
466 env.close();
467
468 // alice successfully authorizes becky.
469 env.require(owners(alice, 0));
470 env.require(owners(becky, 0));
471 env(deposit::auth(alice, becky));
472 env.close();
473 env.require(owners(alice, 1));
474 env.require(owners(becky, 0));
475
476 // alice attempts to create a duplicate authorization.
477 env(deposit::auth(alice, becky), ter(tecDUPLICATE));
478 env.close();
479 env.require(owners(alice, 1));
480 env.require(owners(becky, 0));
481
482 // carol attempts to preauthorize but doesn't have enough reserve.
483 env.fund(drops(249'999'999), carol);
484 env.close();
485
486 env(deposit::auth(carol, becky), ter(tecINSUFFICIENT_RESERVE));
487 env.close();
488 env.require(owners(carol, 0));
489 env.require(owners(becky, 0));
490
491 // carol gets enough XRP to (barely) meet the reserve.
492 env(pay(alice, carol, drops(env.current()->fees().base + 1)));
493 env.close();
494 env(deposit::auth(carol, becky));
495 env.close();
496 env.require(owners(carol, 1));
497 env.require(owners(becky, 0));
498
499 // But carol can't meet the reserve for another pre-authorization.
500 env(deposit::auth(carol, alice), ter(tecINSUFFICIENT_RESERVE));
501 env.close();
502 env.require(owners(carol, 1));
503 env.require(owners(becky, 0));
504 env.require(owners(alice, 1));
505
506 // alice attempts to remove an authorization she doesn't have.
507 env(deposit::unauth(alice, carol), ter(tecNO_ENTRY));
508 env.close();
509 env.require(owners(alice, 1));
510 env.require(owners(becky, 0));
511
512 // alice successfully removes her authorization of becky.
513 env(deposit::unauth(alice, becky));
514 env.close();
515 env.require(owners(alice, 0));
516 env.require(owners(becky, 0));
517
518 // alice removes becky again and gets an error.
519 env(deposit::unauth(alice, becky), ter(tecNO_ENTRY));
520 env.close();
521 env.require(owners(alice, 0));
522 env.require(owners(becky, 0));
523 }
524
525 void
527 {
528 testcase("Payment");
529
530 using namespace jtx;
531 Account const alice{"alice"};
532 Account const becky{"becky"};
533 Account const gw{"gw"};
534 IOU const USD(gw["USD"]);
535
536 {
537 // The initial implementation of DepositAuth had a bug where an
538 // account with the DepositAuth flag set could not make a payment
539 // to itself. That bug was fixed in the DepositPreauth amendment.
540 Env env(*this, features);
541 env.fund(XRP(5000), alice, becky, gw);
542 env.close();
543
544 env.trust(USD(1000), alice);
545 env.trust(USD(1000), becky);
546 env.close();
547
548 env(pay(gw, alice, USD(500)));
549 env.close();
550
551 env(offer(alice, XRP(100), USD(100), tfPassive), require(offers(alice, 1)));
552 env.close();
553
554 // becky pays herself USD (10) by consuming part of alice's offer.
555 // Make sure the payment works if PaymentAuth is not involved.
556 env(pay(becky, becky, USD(10)), path(~USD), sendmax(XRP(10)));
557 env.close();
558
559 // becky decides to require authorization for deposits.
560 env(fset(becky, asfDepositAuth));
561 env.close();
562
563 // becky pays herself again.
564 env(pay(becky, becky, USD(10)), path(~USD), sendmax(XRP(10)), ter(tesSUCCESS));
565 env.close();
566
567 {
568 // becky setup depositpreauth with credentials
569 char const credType[] = "abcde";
570 Account const carol{"carol"};
571 env.fund(XRP(5000), carol);
572 env.close();
573
574 bool const supportsCredentials = features[featureCredentials];
575
576 TER const expectTer(!supportsCredentials ? TER(temDISABLED) : TER(tesSUCCESS));
577
578 env(deposit::authCredentials(becky, {{carol, credType}}), ter(expectTer));
579 env.close();
580
581 // gw accept credentials
582 env(credentials::create(gw, carol, credType), ter(expectTer));
583 env.close();
584 env(credentials::accept(gw, carol, credType), ter(expectTer));
585 env.close();
586
587 auto jv = credentials::ledgerEntry(env, gw, carol, credType);
588 std::string const credIdx = supportsCredentials
589 ? jv[jss::result][jss::index].asString()
590 : "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6"
591 "EA288BE4";
592
593 env(pay(gw, becky, USD(100)), credentials::ids({credIdx}), ter(expectTer));
594 env.close();
595 }
596 }
597
598 // Make sure DepositPreauthorization works for payments.
599
600 Account const carol{"carol"};
601
602 Env env(*this, features);
603 env.fund(XRP(5000), alice, becky, carol, gw);
604 env.close();
605
606 env.trust(USD(1000), alice);
607 env.trust(USD(1000), becky);
608 env.trust(USD(1000), carol);
609 env.close();
610
611 env(pay(gw, alice, USD(1000)));
612 env.close();
613
614 // Make XRP and IOU payments from alice to becky. Should be fine.
615 env(pay(alice, becky, XRP(100)));
616 env(pay(alice, becky, USD(100)));
617 env.close();
618
619 // becky decides to require authorization for deposits.
620 env(fset(becky, asfDepositAuth));
621 env.close();
622
623 // alice can no longer pay becky.
624 env(pay(alice, becky, XRP(100)), ter(tecNO_PERMISSION));
625 env(pay(alice, becky, USD(100)), ter(tecNO_PERMISSION));
626 env.close();
627
628 // becky preauthorizes carol for deposit, which doesn't provide
629 // authorization for alice.
630 env(deposit::auth(becky, carol));
631 env.close();
632
633 // alice still can't pay becky.
634 env(pay(alice, becky, XRP(100)), ter(tecNO_PERMISSION));
635 env(pay(alice, becky, USD(100)), ter(tecNO_PERMISSION));
636 env.close();
637
638 // becky preauthorizes alice for deposit.
639 env(deposit::auth(becky, alice));
640 env.close();
641
642 // alice can now pay becky.
643 env(pay(alice, becky, XRP(100)));
644 env(pay(alice, becky, USD(100)));
645 env.close();
646
647 // alice decides to require authorization for deposits.
648 env(fset(alice, asfDepositAuth));
649 env.close();
650
651 // Even though alice is authorized to pay becky, becky is not
652 // authorized to pay alice.
653 env(pay(becky, alice, XRP(100)), ter(tecNO_PERMISSION));
654 env(pay(becky, alice, USD(100)), ter(tecNO_PERMISSION));
655 env.close();
656
657 // becky unauthorizes carol. Should have no impact on alice.
658 env(deposit::unauth(becky, carol));
659 env.close();
660
661 env(pay(alice, becky, XRP(100)));
662 env(pay(alice, becky, USD(100)));
663 env.close();
664
665 // becky unauthorizes alice. alice now can't pay becky.
666 env(deposit::unauth(becky, alice));
667 env.close();
668
669 env(pay(alice, becky, XRP(100)), ter(tecNO_PERMISSION));
670 env(pay(alice, becky, USD(100)), ter(tecNO_PERMISSION));
671 env.close();
672
673 // becky decides to remove authorization for deposits. Now
674 // alice can pay becky again.
675 env(fclear(becky, asfDepositAuth));
676 env.close();
677
678 env(pay(alice, becky, XRP(100)));
679 env(pay(alice, becky, USD(100)));
680 env.close();
681 }
682
683 void
685 {
686 using namespace jtx;
687
688 char const credType[] = "abcde";
689 Account const issuer{"issuer"};
690 Account const alice{"alice"};
691 Account const bob{"bob"};
692 Account const maria{"maria"};
693 Account const john{"john"};
694
695 {
696 testcase("Payment failure with disabled credentials rule.");
697
698 Env env(*this, testable_amendments() - featureCredentials);
699
700 env.fund(XRP(5000), issuer, bob, alice);
701 env.close();
702
703 // Bob require pre-authorization
704 env(fset(bob, asfDepositAuth));
705 env.close();
706
707 // Setup DepositPreauth object failed - amendent is not supported
708 env(deposit::authCredentials(bob, {{issuer, credType}}), ter(temDISABLED));
709 env.close();
710
711 // But can create old DepositPreauth
712 env(deposit::auth(bob, alice));
713 env.close();
714
715 // And alice can't pay with any credentials, amendment is not
716 // enabled
717 std::string const invalidIdx =
718 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
719 "01E034";
720 env(pay(alice, bob, XRP(10)), credentials::ids({invalidIdx}), ter(temDISABLED));
721 env.close();
722 }
723
724 {
725 testcase("Payment with credentials.");
726
727 Env env(*this);
728
729 env.fund(XRP(5000), issuer, alice, bob, john);
730 env.close();
731
732 // Issuer create credentials, but Alice didn't accept them yet
733 env(credentials::create(alice, issuer, credType));
734 env.close();
735
736 // Get the index of the credentials
737 auto const jv = credentials::ledgerEntry(env, alice, issuer, credType);
738 std::string const credIdx = jv[jss::result][jss::index].asString();
739
740 // Bob require pre-authorization
741 env(fset(bob, asfDepositAuth));
742 env.close();
743
744 // Bob will accept payments from accounts with credentials signed
745 // by 'issuer'
746 env(deposit::authCredentials(bob, {{issuer, credType}}));
747 env.close();
748
749 auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}});
750 BEAST_EXPECT(
751 jDP.isObject() && jDP.isMember(jss::result) &&
752 !jDP[jss::result].isMember(jss::error) && jDP[jss::result].isMember(jss::node) &&
753 jDP[jss::result][jss::node].isMember("LedgerEntryType") &&
754 jDP[jss::result][jss::node]["LedgerEntryType"] == jss::DepositPreauth);
755
756 // Alice can't pay - empty credentials array
757 {
758 auto jv = pay(alice, bob, XRP(100));
759 jv[sfCredentialIDs.jsonName] = Json::arrayValue;
760 env(jv, ter(temMALFORMED));
761 env.close();
762 }
763
764 // Alice can't pay - not accepted credentials
765 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}), ter(tecBAD_CREDENTIALS));
766 env.close();
767
768 // Alice accept the credentials
769 env(credentials::accept(alice, issuer, credType));
770 env.close();
771
772 // Now Alice can pay
773 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}));
774 env.close();
775
776 // Alice can pay Maria without depositPreauth enabled
777 env(pay(alice, maria, XRP(250)), credentials::ids({credIdx}));
778 env.close();
779
780 // john can accept payment with old depositPreauth and valid
781 // credentials
782 env(fset(john, asfDepositAuth));
783 env(deposit::auth(john, alice));
784 env(pay(alice, john, XRP(100)), credentials::ids({credIdx}));
785 env.close();
786 }
787
788 {
789 testcase("Payment failure with invalid credentials.");
790
791 Env env(*this);
792
793 env.fund(XRP(10000), issuer, alice, bob, maria);
794 env.close();
795
796 // Issuer create credentials, but Alice didn't accept them yet
797 env(credentials::create(alice, issuer, credType));
798 env.close();
799 // Alice accept the credentials
800 env(credentials::accept(alice, issuer, credType));
801 env.close();
802 // Get the index of the credentials
803 auto const jv = credentials::ledgerEntry(env, alice, issuer, credType);
804 std::string const credIdx = jv[jss::result][jss::index].asString();
805
806 {
807 // Success as destination didn't enable pre-authorization so
808 // valid credentials will not fail
809 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}));
810 }
811
812 // Bob require pre-authorization
813 env(fset(bob, asfDepositAuth));
814 env.close();
815
816 {
817 // Fail as destination didn't setup DepositPreauth object
818 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}), ter(tecNO_PERMISSION));
819 }
820
821 // Bob setup DepositPreauth object, duplicates is not allowed
822 env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType}}),
824
825 // Bob setup DepositPreauth object
826 env(deposit::authCredentials(bob, {{issuer, credType}}));
827 env.close();
828
829 {
830 std::string const invalidIdx =
831 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
832 "01E034";
833 // Alice can't pay with non-existing credentials
834 env(pay(alice, bob, XRP(100)),
835 credentials::ids({invalidIdx}),
837 }
838
839 { // maria can't pay using valid credentials but issued for
840 // different account
841 env(pay(maria, bob, XRP(100)),
842 credentials::ids({credIdx}),
844 }
845
846 {
847 // create another valid credential
848 char const credType2[] = "fghij";
849 env(credentials::create(alice, issuer, credType2));
850 env.close();
851 env(credentials::accept(alice, issuer, credType2));
852 env.close();
853 auto const jv = credentials::ledgerEntry(env, alice, issuer, credType2);
854 std::string const credIdx2 = jv[jss::result][jss::index].asString();
855
856 // Alice can't pay with invalid set of valid credentials
857 env(pay(alice, bob, XRP(100)),
858 credentials::ids({credIdx, credIdx2}),
860 }
861
862 // Error, duplicate credentials
863 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx, credIdx}), ter(temMALFORMED));
864
865 // Alice can pay
866 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx}));
867 env.close();
868 }
869 }
870
871 void
873 {
874 using namespace jtx;
875
876 char const credType[] = "abcde";
877 Account const issuer{"issuer"};
878 Account const alice{"alice"};
879 Account const bob{"bob"};
880 Account const maria{"maria"};
881
882 {
883 testcase("Creating / deleting with credentials.");
884
885 Env env(*this);
886
887 env.fund(XRP(5000), issuer, alice, bob);
888 env.close();
889
890 {
891 // both included [AuthorizeCredentials UnauthorizeCredentials]
892 auto jv = deposit::authCredentials(bob, {{issuer, credType}});
893 jv[sfUnauthorizeCredentials.jsonName] = Json::arrayValue;
894 env(jv, ter(temMALFORMED));
895 }
896
897 {
898 // both included [Unauthorize, AuthorizeCredentials]
899 auto jv = deposit::authCredentials(bob, {{issuer, credType}});
900 jv[sfUnauthorize.jsonName] = issuer.human();
901 env(jv, ter(temMALFORMED));
902 }
903
904 {
905 // both included [Authorize, AuthorizeCredentials]
906 auto jv = deposit::authCredentials(bob, {{issuer, credType}});
907 jv[sfAuthorize.jsonName] = issuer.human();
908 env(jv, ter(temMALFORMED));
909 }
910
911 {
912 // both included [Unauthorize, UnauthorizeCredentials]
913 auto jv = deposit::unauthCredentials(bob, {{issuer, credType}});
914 jv[sfUnauthorize.jsonName] = issuer.human();
915 env(jv, ter(temMALFORMED));
916 }
917
918 {
919 // both included [Authorize, UnauthorizeCredentials]
920 auto jv = deposit::unauthCredentials(bob, {{issuer, credType}});
921 jv[sfAuthorize.jsonName] = issuer.human();
922 env(jv, ter(temMALFORMED));
923 }
924
925 {
926 // AuthorizeCredentials is empty
927 auto jv = deposit::authCredentials(bob, {});
928 env(jv, ter(temARRAY_EMPTY));
929 }
930
931 {
932 // invalid issuer
933 auto jv = deposit::authCredentials(bob, {});
934 auto& arr(jv[sfAuthorizeCredentials.jsonName]);
936 cred[jss::Issuer] = to_string(xrpAccount());
937 cred[sfCredentialType.jsonName] = strHex(std::string_view(credType));
938 Json::Value credParent;
939 credParent[jss::Credential] = cred;
940 arr.append(std::move(credParent));
941
942 env(jv, ter(temINVALID_ACCOUNT_ID));
943 }
944
945 {
946 // empty credential type
947 auto jv = deposit::authCredentials(bob, {{issuer, {}}});
948 env(jv, ter(temMALFORMED));
949 }
950
951 {
952 // AuthorizeCredentials is larger than 8 elements
953 Account const a("a"), b("b"), c("c"), d("d"), e("e"), f("f"), g("g"), h("h"),
954 i("i");
955 auto const& z = credType;
956 auto jv = deposit::authCredentials(
957 bob, {{a, z}, {b, z}, {c, z}, {d, z}, {e, z}, {f, z}, {g, z}, {h, z}, {i, z}});
958 env(jv, ter(temARRAY_TOO_LARGE));
959 }
960
961 {
962 // Can't create with non-existing issuer
963 Account const rick{"rick"};
964 auto jv = deposit::authCredentials(bob, {{rick, credType}});
965 env(jv, ter(tecNO_ISSUER));
966 env.close();
967 }
968
969 {
970 // not enough reserve
971 Account const john{"john"};
972 env.fund(env.current()->fees().accountReserve(0), john);
973 env.close();
974 auto jv = deposit::authCredentials(john, {{issuer, credType}});
976 }
977
978 {
979 // NO deposit object exists
980 env(deposit::unauthCredentials(bob, {{issuer, credType}}), ter(tecNO_ENTRY));
981 }
982
983 // Create DepositPreauth object
984 {
985 env(deposit::authCredentials(bob, {{issuer, credType}}));
986 env.close();
987
988 auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}});
989 BEAST_EXPECT(
990 jDP.isObject() && jDP.isMember(jss::result) &&
991 !jDP[jss::result].isMember(jss::error) &&
992 jDP[jss::result].isMember(jss::node) &&
993 jDP[jss::result][jss::node].isMember("LedgerEntryType") &&
994 jDP[jss::result][jss::node]["LedgerEntryType"] == jss::DepositPreauth);
995
996 // Check object fields
997 BEAST_EXPECT(jDP[jss::result][jss::node][jss::Account] == bob.human());
998 auto const& credentials(jDP[jss::result][jss::node]["AuthorizeCredentials"]);
999 BEAST_EXPECT(credentials.isArray() && credentials.size() == 1);
1000 for (auto const& o : credentials)
1001 {
1002 auto const& c(o[jss::Credential]);
1003 BEAST_EXPECT(c[jss::Issuer].asString() == issuer.human());
1004 BEAST_EXPECT(
1005 c["CredentialType"].asString() == strHex(std::string_view(credType)));
1006 }
1007
1008 // can't create duplicate
1009 env(deposit::authCredentials(bob, {{issuer, credType}}), ter(tecDUPLICATE));
1010 }
1011
1012 // Delete DepositPreauth object
1013 {
1014 env(deposit::unauthCredentials(bob, {{issuer, credType}}));
1015 env.close();
1016 auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}});
1017 BEAST_EXPECT(
1018 jDP.isObject() && jDP.isMember(jss::result) &&
1019 jDP[jss::result].isMember(jss::error) &&
1020 jDP[jss::result][jss::error] == "entryNotFound");
1021 }
1022 }
1023 }
1024
1025 void
1027 {
1028 using namespace jtx;
1029 char const credType[] = "abcde";
1030 char const credType2[] = "fghijkl";
1031 Account const issuer{"issuer"};
1032 Account const alice{"alice"};
1033 Account const bob{"bob"};
1034 Account const gw{"gw"};
1035 IOU const USD = gw["USD"];
1036 Account const zelda{"zelda"};
1037
1038 {
1039 testcase("Payment failure with expired credentials.");
1040
1041 Env env(*this);
1042
1043 env.fund(XRP(10000), issuer, alice, bob, gw);
1044 env.close();
1045
1046 // Create credentials
1047 auto jv = credentials::create(alice, issuer, credType);
1048 // Current time in ripple epoch.
1049 // Every time ledger close, unittest timer increase by 10s
1050 uint32_t const t =
1051 env.current()->header().parentCloseTime.time_since_epoch().count() + 60;
1052 jv[sfExpiration.jsonName] = t;
1053 env(jv);
1054 env.close();
1055
1056 // Alice accept the credentials
1057 env(credentials::accept(alice, issuer, credType));
1058 env.close();
1059
1060 // Create credential which not expired
1061 jv = credentials::create(alice, issuer, credType2);
1062 uint32_t const t2 =
1063 env.current()->header().parentCloseTime.time_since_epoch().count() + 1000;
1064 jv[sfExpiration.jsonName] = t2;
1065 env(jv);
1066 env.close();
1067 env(credentials::accept(alice, issuer, credType2));
1068 env.close();
1069
1070 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1071 BEAST_EXPECT(ownerCount(env, alice) == 2);
1072
1073 // Get the index of the credentials
1074 jv = credentials::ledgerEntry(env, alice, issuer, credType);
1075 std::string const credIdx = jv[jss::result][jss::index].asString();
1076 jv = credentials::ledgerEntry(env, alice, issuer, credType2);
1077 std::string const credIdx2 = jv[jss::result][jss::index].asString();
1078
1079 // Bob require pre-authorization
1080 env(fset(bob, asfDepositAuth));
1081 env.close();
1082 // Bob setup DepositPreauth object
1083 env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType2}}));
1084 env.close();
1085
1086 {
1087 // Alice can pay
1088 env(pay(alice, bob, XRP(100)), credentials::ids({credIdx, credIdx2}));
1089 env.close();
1090 env.close();
1091
1092 // Ledger closed, time increased, alice can't pay anymore
1093 env(pay(alice, bob, XRP(100)),
1094 credentials::ids({credIdx, credIdx2}),
1095 ter(tecEXPIRED));
1096 env.close();
1097
1098 {
1099 // check that expired credentials were deleted
1100 auto const jDelCred = credentials::ledgerEntry(env, alice, issuer, credType);
1101 BEAST_EXPECT(
1102 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1103 jDelCred[jss::result].isMember(jss::error) &&
1104 jDelCred[jss::result][jss::error] == "entryNotFound");
1105 }
1106
1107 {
1108 // check that non-expired credential still present
1109 auto const jle = credentials::ledgerEntry(env, alice, issuer, credType2);
1110 BEAST_EXPECT(
1111 jle.isObject() && jle.isMember(jss::result) &&
1112 !jle[jss::result].isMember(jss::error) &&
1113 jle[jss::result].isMember(jss::node) &&
1114 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
1115 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
1116 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
1117 jle[jss::result][jss::node][jss::Subject] == alice.human() &&
1118 jle[jss::result][jss::node]["CredentialType"] ==
1119 strHex(std::string_view(credType2)));
1120 }
1121
1122 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1123 BEAST_EXPECT(ownerCount(env, alice) == 1);
1124 }
1125
1126 {
1127 auto jv = credentials::create(gw, issuer, credType);
1128 uint32_t const t =
1129 env.current()->header().parentCloseTime.time_since_epoch().count() + 40;
1130 jv[sfExpiration.jsonName] = t;
1131 env(jv);
1132 env.close();
1133 env(credentials::accept(gw, issuer, credType));
1134 env.close();
1135
1136 jv = credentials::ledgerEntry(env, gw, issuer, credType);
1137 std::string const credIdx = jv[jss::result][jss::index].asString();
1138
1139 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1140 BEAST_EXPECT(ownerCount(env, gw) == 1);
1141
1142 env.close();
1143 env.close();
1144 env.close();
1145
1146 // credentials are expired
1147 env(pay(gw, bob, USD(150)), credentials::ids({credIdx}), ter(tecEXPIRED));
1148 env.close();
1149
1150 // check that expired credentials were deleted
1151 auto const jDelCred = credentials::ledgerEntry(env, gw, issuer, credType);
1152 BEAST_EXPECT(
1153 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1154 jDelCred[jss::result].isMember(jss::error) &&
1155 jDelCred[jss::result][jss::error] == "entryNotFound");
1156
1157 BEAST_EXPECT(ownerCount(env, issuer) == 0);
1158 BEAST_EXPECT(ownerCount(env, gw) == 0);
1159 }
1160 }
1161
1162 {
1163 using namespace std::chrono;
1164
1165 testcase("Escrow failure with expired credentials.");
1166
1167 Env env(*this);
1168
1169 env.fund(XRP(5000), issuer, alice, bob, zelda);
1170 env.close();
1171
1172 // Create credentials
1173 auto jv = credentials::create(zelda, issuer, credType);
1174 uint32_t const t =
1175 env.current()->header().parentCloseTime.time_since_epoch().count() + 50;
1176 jv[sfExpiration.jsonName] = t;
1177 env(jv);
1178 env.close();
1179
1180 // Zelda accept the credentials
1181 env(credentials::accept(zelda, issuer, credType));
1182 env.close();
1183
1184 // Get the index of the credentials
1185 jv = credentials::ledgerEntry(env, zelda, issuer, credType);
1186 std::string const credIdx = jv[jss::result][jss::index].asString();
1187
1188 // Bob require pre-authorization
1189 env(fset(bob, asfDepositAuth));
1190 env.close();
1191 // Bob setup DepositPreauth object
1192 env(deposit::authCredentials(bob, {{issuer, credType}}));
1193 env.close();
1194
1195 auto const seq = env.seq(alice);
1196 env(escrow::create(alice, bob, XRP(1000)), escrow::finish_time(env.now() + 1s));
1197 env.close();
1198
1199 // zelda can't finish escrow with invalid credentials
1200 {
1201 env(escrow::finish(zelda, alice, seq), credentials::ids({}), ter(temMALFORMED));
1202 env.close();
1203 }
1204
1205 {
1206 // zelda can't finish escrow with invalid credentials
1207 std::string const invalidIdx =
1208 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
1209 "01E034";
1210
1211 env(escrow::finish(zelda, alice, seq),
1212 credentials::ids({invalidIdx}),
1214 env.close();
1215 }
1216
1217 { // Ledger closed, time increased, zelda can't finish escrow
1218 env(escrow::finish(zelda, alice, seq),
1219 credentials::ids({credIdx}),
1220 fee(1500),
1221 ter(tecEXPIRED));
1222 env.close();
1223 }
1224
1225 // check that expired credentials were deleted
1226 auto const jDelCred = credentials::ledgerEntry(env, zelda, issuer, credType);
1227 BEAST_EXPECT(
1228 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1229 jDelCred[jss::result].isMember(jss::error) &&
1230 jDelCred[jss::result][jss::error] == "entryNotFound");
1231 }
1232 }
1233
1234 void
1236 {
1237 using namespace jtx;
1238
1239 Account const stock{"stock"};
1240 Account const alice{"alice"};
1241 Account const bob{"bob"};
1242
1243 Env env(*this);
1244
1245 testcase("Sorting credentials.");
1246
1247 env.fund(XRP(5000), stock, alice, bob);
1248
1250 {"a", "a"},
1251 {"b", "b"},
1252 {"c", "c"},
1253 {"d", "d"},
1254 {"e", "e"},
1255 {"f", "f"},
1256 {"g", "g"},
1257 {"h", "h"}};
1258
1259 for (auto const& c : credentials)
1260 env.fund(XRP(5000), c.issuer);
1261 env.close();
1262
1264 std::mt19937 gen(rd());
1265
1266 {
1268 for (auto const& c : credentials)
1269 pubKey2Acc.emplace(c.issuer.human(), c.issuer);
1270
1271 // check sorting in object
1272 for (int i = 0; i < 10; ++i)
1273 {
1274 std::ranges::shuffle(credentials, gen);
1275 env(deposit::authCredentials(stock, credentials));
1276 env.close();
1277
1278 auto const dp = ledgerEntryDepositPreauth(env, stock, credentials);
1279 auto const& authCred(dp[jss::result][jss::node]["AuthorizeCredentials"]);
1280 BEAST_EXPECT(authCred.isArray() && authCred.size() == credentials.size());
1282 for (auto const& o : authCred)
1283 {
1284 auto const& c(o[jss::Credential]);
1285 auto issuer = c[jss::Issuer].asString();
1286
1287 if (BEAST_EXPECT(pubKey2Acc.contains(issuer)))
1288 {
1289 readCreds.emplace_back(
1290 pubKey2Acc.at(issuer), c["CredentialType"].asString());
1291 }
1292 }
1293
1294 BEAST_EXPECT(std::ranges::is_sorted(readCreds));
1295
1296 env(deposit::unauthCredentials(stock, credentials));
1297 env.close();
1298 }
1299 }
1300
1301 {
1302 std::ranges::shuffle(credentials, gen);
1303 env(deposit::authCredentials(stock, credentials));
1304 env.close();
1305
1306 // check sorting in params
1307 for (int i = 0; i < 10; ++i)
1308 {
1309 std::ranges::shuffle(credentials, gen);
1310 env(deposit::authCredentials(stock, credentials), ter(tecDUPLICATE));
1311 }
1312 }
1313
1314 testcase("Check duplicate credentials.");
1315 {
1316 // check duplicates in depositPreauth params
1318 credentials.begin(), credentials.end() - 1);
1319
1320 std::ranges::shuffle(copyCredentials, gen);
1321 for (auto const& c : copyCredentials)
1322 {
1323 auto credentials2 = copyCredentials;
1324 credentials2.push_back(c);
1325 env(deposit::authCredentials(stock, credentials2), ter(temMALFORMED));
1326 }
1327
1328 // create batch of credentials and save their hashes
1329 std::vector<std::string> credentialIDs;
1330 for (auto const& c : credentials)
1331 {
1332 env(credentials::create(alice, c.issuer, c.credType));
1333 env.close();
1334 env(credentials::accept(alice, c.issuer, c.credType));
1335 env.close();
1336
1337 credentialIDs.push_back(
1339 env, alice, c.issuer, c.credType)[jss::result][jss::index]
1340 .asString());
1341 }
1342
1343 // check duplicates in payment params
1344 for (auto const& h : credentialIDs)
1345 {
1346 auto credentialIDs2 = credentialIDs;
1347 credentialIDs2.push_back(h);
1348
1349 env(pay(alice, bob, XRP(100)), credentials::ids(credentialIDs2), ter(temMALFORMED));
1350 }
1351 }
1352 }
1353
1354 void
1355 run() override
1356 {
1357 testEnable();
1358 testInvalid();
1359 auto const supported{jtx::testable_amendments()};
1360 testPayment(supported - featureCredentials);
1361 testPayment(supported);
1366 }
1367};
1368
1369BEAST_DEFINE_TESTSUITE(DepositAuth, app, xrpl);
1370BEAST_DEFINE_TESTSUITE(DepositPreauth, app, xrpl);
1371
1372} // namespace test
1373} // 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:150
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:122
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:100
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:258
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:270
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:249
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:168
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:301
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:847
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:141
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:588
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:329
NetClock::time_point now()
Returns the current network time.
Definition Env.h:282
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:52
Add a path.
Definition paths.h:38
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:53
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:95
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:78
owner_count< ltOFFER > offers
Match the number of offers in the account's owner directory.
Definition owners.h:70
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
@ terINSUF_FEE_B
Definition TER.h:196
@ terNO_ACCOUNT
Definition TER.h:197
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
TERSubset< CanCvtToTER > TER
Definition TER.h:622
@ 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
@ 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