xrpld
Loading...
Searching...
No Matches
CheckMPT_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/balance.h>
7#include <test/jtx/check.h>
8#include <test/jtx/fee.h>
9#include <test/jtx/flags.h>
10#include <test/jtx/invoice_id.h>
11#include <test/jtx/mpt.h>
12#include <test/jtx/multisign.h>
13#include <test/jtx/offer.h>
14#include <test/jtx/owners.h>
15#include <test/jtx/pay.h>
16#include <test/jtx/regkey.h>
17#include <test/jtx/sig.h>
18#include <test/jtx/ter.h>
19#include <test/jtx/ticket.h>
20#include <test/jtx/txflags.h>
21
22#include <xrpl/basics/UnorderedContainers.h>
23#include <xrpl/basics/chrono.h>
24#include <xrpl/basics/contract.h>
25#include <xrpl/beast/unit_test/suite.h>
26#include <xrpl/core/ServiceRegistry.h>
27#include <xrpl/ledger/helpers/DirectoryHelpers.h>
28#include <xrpl/protocol/AccountID.h>
29#include <xrpl/protocol/Feature.h>
30#include <xrpl/protocol/Indexes.h>
31#include <xrpl/protocol/Issue.h>
32#include <xrpl/protocol/KeyType.h>
33#include <xrpl/protocol/LedgerFormats.h>
34#include <xrpl/protocol/SField.h>
35#include <xrpl/protocol/STAmount.h>
36#include <xrpl/protocol/TER.h>
37#include <xrpl/protocol/TxFlags.h>
38#include <xrpl/protocol/UintTypes.h>
39#include <xrpl/protocol/XRPAmount.h>
40#include <xrpl/protocol/jss.h>
41
42#include <cstddef>
43#include <cstdint>
44#include <iostream>
45#include <ostream>
46#include <stdexcept>
47#include <string>
48#include <utility>
49#include <vector>
50
51namespace xrpl {
52
54{
55 // Helper function that returns the Checks on an account.
58 {
60 forEachItem(*env.current(), account, [&result](SLE::const_ref sle) {
61 if (sle && sle->getType() == ltCHECK)
62 result.push_back(sle);
63 });
64 return result;
65 }
66
67 // Helper function that verifies the expected DeliveredAmount is present.
68 //
69 // NOTE: the function _infers_ the transaction to operate on by calling
70 // env.tx(), which returns the result from the most recent transaction.
71 void
73 {
74 // Get the hash for the most recent transaction.
75 std::string const txHash{
76 env.tx()->getJson(JsonOptions::Values::None)[jss::hash].asString()};
77
78 // Verify DeliveredAmount and delivered_amount metadata are correct.
79 env.close();
80 json::Value const meta = env.rpc("tx", txHash)[jss::result][jss::meta];
81
82 // Expect there to be a DeliveredAmount field.
83 if (!BEAST_EXPECT(meta.isMember(sfDeliveredAmount.jsonName)))
84 return;
85
86 // DeliveredAmount and delivered_amount should both be present and
87 // equal amount.
88 BEAST_EXPECT(meta[sfDeliveredAmount.jsonName] == amount.getJson(JsonOptions::Values::None));
89 BEAST_EXPECT(meta[jss::delivered_amount] == amount.getJson(JsonOptions::Values::None));
90 }
91
92 void
94 {
95 // Explore many of the valid ways to create a check.
96 testcase("Create valid");
97
98 using namespace test::jtx;
99
100 Account const gw{"gateway"};
101 Account const alice{"alice"};
102 Account const bob{"bob"};
103
104 Env env{*this, features};
105
106 STAmount const startBalance{XRP(1'000).value()};
107 env.fund(startBalance, gw, alice, bob);
108
109 MPT const usd = MPTTester({.env = env, .issuer = gw});
110
111 // Note that no MPToken has been set up for alice, but alice can
112 // still write a check for USD. You don't have to have the funds
113 // necessary to cover a check in order to write a check.
114 auto writeTwoChecks = [&env, &usd, this](Account const& from, Account const& to) {
115 std::uint32_t const fromOwnerCount{ownerCount(env, from)};
116 std::uint32_t const toOwnerCount{ownerCount(env, to)};
117
118 std::size_t const fromCkCount{checksOnAccount(env, from).size()};
119 std::size_t const toCkCount{checksOnAccount(env, to).size()};
120
121 env(check::create(from, to, XRP(2000)));
122 env.close();
123
124 env(check::create(from, to, usd(50)));
125 env.close();
126
127 BEAST_EXPECT(checksOnAccount(env, from).size() == fromCkCount + 2);
128 BEAST_EXPECT(checksOnAccount(env, to).size() == toCkCount + 2);
129
130 env.require(Owners(from, fromOwnerCount + 2));
131 env.require(Owners(to, to == from ? fromOwnerCount + 2 : toOwnerCount));
132 };
133 // from to
134 writeTwoChecks(alice, bob);
135 writeTwoChecks(gw, alice);
136 writeTwoChecks(alice, gw);
137
138 // Now try adding the various optional fields. There's no
139 // expected interaction between these optional fields; other than
140 // the expiration, they are just plopped into the ledger. So I'm
141 // not looking at interactions.
142 using namespace std::chrono_literals;
143 std::size_t const aliceCount{checksOnAccount(env, alice).size()};
144 std::size_t const bobCount{checksOnAccount(env, bob).size()};
145 env(check::create(alice, bob, usd(50)), Expiration(env.now() + 1s));
146 env.close();
147
148 env(check::create(alice, bob, usd(50)), SourceTag(2));
149 env.close();
150 env(check::create(alice, bob, usd(50)), DestTag(3));
151 env.close();
152 env(check::create(alice, bob, usd(50)), InvoiceId(uint256{4}));
153 env.close();
154 env(check::create(alice, bob, usd(50)),
155 Expiration(env.now() + 1s),
156 SourceTag(12),
157 DestTag(13),
158 InvoiceId(uint256{4}));
159 env.close();
160
161 BEAST_EXPECT(checksOnAccount(env, alice).size() == aliceCount + 5);
162 BEAST_EXPECT(checksOnAccount(env, bob).size() == bobCount + 5);
163
164 // Use a regular key and also multisign to create a check.
165 Account const alie{"alie", KeyType::Ed25519};
166 env(regkey(alice, alie));
167 env.close();
168
169 Account const bogie{"bogie", KeyType::Secp256k1};
170 Account const demon{"demon", KeyType::Ed25519};
171 env(signers(alice, 2, {{bogie, 1}, {demon, 1}}), Sig(alie));
172 env.close();
173
174 // alice uses her regular key to create a check.
175 env(check::create(alice, bob, usd(50)), Sig(alie));
176 env.close();
177 BEAST_EXPECT(checksOnAccount(env, alice).size() == aliceCount + 6);
178 BEAST_EXPECT(checksOnAccount(env, bob).size() == bobCount + 6);
179
180 // alice uses multisigning to create a check.
181 XRPAmount const baseFeeDrops{env.current()->fees().base};
182 env(check::create(alice, bob, usd(50)), Msig(bogie, demon), Fee(3 * baseFeeDrops));
183 env.close();
184 BEAST_EXPECT(checksOnAccount(env, alice).size() == aliceCount + 7);
185 BEAST_EXPECT(checksOnAccount(env, bob).size() == bobCount + 7);
186 }
187
188 void
190 {
191 testcase("Create valid with disallow incoming");
192
193 using namespace test::jtx;
194
195 Account const gw{"gateway"};
196 Account const alice{"alice"};
197 Account const bob{"bob"};
198
199 Env env{*this, features};
200
201 STAmount const startBalance{XRP(1'000).value()};
202 env.fund(startBalance, gw, alice, bob);
203
204 MPT const usd = MPTTester({.env = env, .issuer = gw});
205
206 /*
207 * Attempt to create two checks from `from` to `to` and
208 * require they both result in error/success code `expected`
209 */
210 auto writeTwoChecksDI = [&env, &usd, this](
211 Account const& from, Account const& to, TER expected) {
212 std::uint32_t const fromOwnerCount{ownerCount(env, from)};
213 std::uint32_t const toOwnerCount{ownerCount(env, to)};
214
215 std::size_t const fromCkCount{checksOnAccount(env, from).size()};
216 std::size_t const toCkCount{checksOnAccount(env, to).size()};
217
218 env(check::create(from, to, XRP(2000)), Ter(expected));
219 env.close();
220
221 env(check::create(from, to, usd(50)), Ter(expected));
222 env.close();
223
224 if (expected == tesSUCCESS)
225 {
226 BEAST_EXPECT(checksOnAccount(env, from).size() == fromCkCount + 2);
227 BEAST_EXPECT(checksOnAccount(env, to).size() == toCkCount + 2);
228
229 env.require(Owners(from, fromOwnerCount + 2));
230 env.require(Owners(to, to == from ? fromOwnerCount + 2 : toOwnerCount));
231 return;
232 }
233
234 BEAST_EXPECT(checksOnAccount(env, from).size() == fromCkCount);
235 BEAST_EXPECT(checksOnAccount(env, to).size() == toCkCount);
236
237 env.require(Owners(from, fromOwnerCount));
238 env.require(Owners(to, to == from ? fromOwnerCount : toOwnerCount));
239 };
240
241 // enable the DisallowIncoming flag on both bob and alice
242 env(fset(bob, asfDisallowIncomingCheck));
243 env(fset(alice, asfDisallowIncomingCheck));
244 env.close();
245
246 // both alice and bob can't receive checks
247 writeTwoChecksDI(alice, bob, tecNO_PERMISSION);
248 writeTwoChecksDI(gw, alice, tecNO_PERMISSION);
249
250 // remove the flag from alice but not from bob
251 env(fclear(alice, asfDisallowIncomingCheck));
252 env.close();
253
254 // now bob can send alice a cheque but not visa-versa
255 writeTwoChecksDI(bob, alice, tesSUCCESS);
256 writeTwoChecksDI(alice, bob, tecNO_PERMISSION);
257
258 // remove bob's flag too
259 env(fclear(bob, asfDisallowIncomingCheck));
260 env.close();
261
262 // now they can send checks freely
263 writeTwoChecksDI(bob, alice, tesSUCCESS);
264 writeTwoChecksDI(alice, bob, tesSUCCESS);
265 }
266
267 void
269 {
270 // Explore many of the invalid ways to create a check.
271 testcase("Create invalid");
272
273 using namespace test::jtx;
274
275 Account const gw1{"gateway1"};
276 Account const gwF{"gatewayFrozen"};
277 Account const alice{"alice"};
278 Account const bob{"bob"};
279
280 Env env{*this, features};
281
282 STAmount const startBalance{XRP(1'000).value()};
283 env.fund(startBalance, gw1, gwF, alice, bob);
284
285 auto usdm = MPTTester({.env = env, .issuer = gw1, .flags = kMptDexFlags | tfMPTCanLock});
286 MPT const usd = usdm;
287
288 // Bad fee.
289 env(check::create(alice, bob, usd(50)), Fee(drops(-10)), Ter(temBAD_FEE));
290 env.close();
291
292 // Bad flags.
293 env(check::create(alice, bob, usd(50)), Txflags(tfImmediateOrCancel), Ter(temINVALID_FLAG));
294 env.close();
295
296 // Check to self.
297 env(check::create(alice, alice, XRP(10)), Ter(temREDUNDANT));
298 env.close();
299
300 // Bad amount.
301 env(check::create(alice, bob, drops(-1)), Ter(temBAD_AMOUNT));
302 env.close();
303
304 env(check::create(alice, bob, drops(0)), Ter(temBAD_AMOUNT));
305 env.close();
306
307 env(check::create(alice, bob, drops(1)));
308 env.close();
309
310 env(check::create(alice, bob, usd(-1)), Ter(temBAD_AMOUNT));
311 env.close();
312
313 env(check::create(alice, bob, usd(0)), Ter(temBAD_AMOUNT));
314 env.close();
315
316 env(check::create(alice, bob, usd(1)));
317 env.close();
318 {
319 MPT const bad(makeMptID(0, xrpAccount()));
320 env(check::create(alice, bob, bad(2)), Ter(temBAD_CURRENCY));
321 env.close();
322 }
323
324 // Bad expiration.
325 env(check::create(alice, bob, usd(50)),
326 Expiration(NetClock::time_point{}),
327 Ter(temBAD_EXPIRATION));
328 env.close();
329
330 // Destination does not exist.
331 Account const bogie{"bogie"};
332 env(check::create(alice, bogie, usd(50)), Ter(tecNO_DST));
333 env.close();
334
335 // Require destination tag.
336 env(fset(bob, asfRequireDest));
337 env.close();
338
339 env(check::create(alice, bob, usd(50)), Ter(tecDST_TAG_NEEDED));
340 env.close();
341
342 env(check::create(alice, bob, usd(50)), DestTag(11));
343 env.close();
344
345 env(fclear(bob, asfRequireDest));
346 env.close();
347 {
348 // Globally frozen asset.
349 env.close();
350 auto usfm =
351 MPTTester({.env = env, .issuer = gwF, .flags = kMptDexFlags | tfMPTCanLock});
352 MPT const usf = usfm;
353 usfm.set({.flags = tfMPTLock});
354
355 env(check::create(alice, bob, usf(50)), Ter(tecLOCKED));
356 env.close();
357
358 usfm.set({.flags = tfMPTUnlock});
359
360 env(check::create(alice, bob, usf(50)));
361 env.close();
362 }
363 {
364 // Frozen MPT. Check creation should be similar to payment
365 // behavior in the face of locked MPT.
366 usdm.authorizeHolders({alice, bob});
367 env(pay(gw1, alice, usd(25)));
368 env(pay(gw1, bob, usd(25)));
369 env.close();
370
371 usdm.set({.holder = alice, .flags = tfMPTLock});
372 // Setting MPT locked prevents alice from
373 // creating a check for USD ore receiving a check. This is different
374 // from IOU where alice can receive checks from bob or gw.
375 env.close();
376 env(check::create(alice, bob, usd(50)), Ter(tecLOCKED));
377 env.close();
378 // Note that IOU returns tecPATH_DRY in this case.
379 // IOU's internal error is terNO_LINE, which is
380 // considered ter re-triable and changed to tecPATH_DRY.
381 env(pay(alice, bob, usd(1)), Ter(tecPATH_DRY));
382 env.close();
383 env(check::create(bob, alice, usd(50)), Ter(tecLOCKED));
384 env.close();
385 env(pay(bob, alice, usd(1)), Ter(tecPATH_DRY));
386 env.close();
387 env(check::create(gw1, alice, usd(50)), Ter(tecLOCKED));
388 env.close();
389 env(pay(gw1, alice, usd(1)));
390 env.close();
391
392 // Clear that lock. Now check creation works.
393 usdm.set({.holder = alice, .flags = tfMPTUnlock});
394 env(check::create(alice, bob, usd(50)));
395 env.close();
396 env(check::create(bob, alice, usd(50)));
397 env.close();
398 env(check::create(gw1, alice, usd(50)));
399 env.close();
400 }
401
402 // Expired expiration.
403 env(check::create(alice, bob, usd(50)), Expiration(env.now()), Ter(tecEXPIRED));
404 env.close();
405
406 using namespace std::chrono_literals;
407 env(check::create(alice, bob, usd(50)), Expiration(env.now() + 1s));
408 env.close();
409
410 // Insufficient reserve.
411 Account const cheri{"cheri"};
412 env.fund(env.current()->fees().accountReserve(1) - drops(1), cheri);
413
414 env(check::create(cheri, bob, usd(50)),
415 Fee(drops(env.current()->fees().base)),
417 env.close();
418
419 env(pay(bob, cheri, drops(env.current()->fees().base + 1)));
420 env.close();
421
422 env(check::create(cheri, bob, usd(50)));
423 env.close();
424 }
425
426 void
428 {
429 // Explore many of the valid ways to cash a check for an MPT.
430 testcase("Cash MPT");
431
432 using namespace test::jtx;
433
434 Account const gw{"gateway"};
435 Account const alice{"alice"};
436 Account const bob{"bob"};
437 {
438 // Simple MPT check cashed with Amount (with failures).
439 Env env{*this, features};
440
441 env.fund(XRP(1'000), gw, alice, bob);
442
443 MPT const usd =
444 MPTTester({.env = env, .issuer = gw, .holders = {alice}, .maxAmt = 105});
445
446 // alice writes the check before she gets the funds.
447 uint256 const chkId1{getCheckIndex(alice, env.seq(alice))};
448 env(check::create(alice, bob, usd(100)));
449 env.close();
450
451 // bob attempts to cash the check. Should fail.
452 env(check::cash(bob, chkId1, usd(100)), Ter(tecPATH_PARTIAL));
453 env.close();
454
455 // alice gets almost enough funds. bob tries and fails again.
456 env(pay(gw, alice, usd(95)));
457 env.close();
458 env(check::cash(bob, chkId1, usd(100)), Ter(tecPATH_PARTIAL));
459 env.close();
460
461 // alice gets the last of the necessary funds.
462 env(pay(gw, alice, usd(5)));
463 env.close();
464
465 // bob for more than the check's SendMax.
466 env.close();
467 env(check::cash(bob, chkId1, usd(105)), Ter(tecPATH_PARTIAL));
468 env.close();
469
470 // bob asks for exactly the check amount and the check clears.
471 // MPT is authorized automatically
472 env(check::cash(bob, chkId1, usd(100)));
473 env.close();
474 env.require(Balance(alice, usd(0)));
475 env.require(Balance(bob, usd(100)));
476 BEAST_EXPECT(checksOnAccount(env, alice).empty());
477 BEAST_EXPECT(checksOnAccount(env, bob).empty());
478 BEAST_EXPECT(ownerCount(env, alice) == 1);
479 BEAST_EXPECT(ownerCount(env, bob) == 1);
480
481 // bob tries to cash the same check again, which fails.
482 env(check::cash(bob, chkId1, usd(100)), Ter(tecNO_ENTRY));
483 env.close();
484
485 // bob pays alice USD(70) so he can try another case.
486 env(pay(bob, alice, usd(70)));
487 env.close();
488
489 uint256 const chkId2{getCheckIndex(alice, env.seq(alice))};
490 env(check::create(alice, bob, usd(70)));
491 env.close();
492 BEAST_EXPECT(checksOnAccount(env, alice).size() == 1);
493 BEAST_EXPECT(checksOnAccount(env, bob).size() == 1);
494
495 // bob cashes the check for less than the face amount. That works,
496 // consumes the check, and bob receives as much as he asked for.
497 env(check::cash(bob, chkId2, usd(50)));
498 env.close();
499 env.require(Balance(alice, usd(20)));
500 env.require(Balance(bob, usd(80)));
501 BEAST_EXPECT(checksOnAccount(env, alice).empty());
502 BEAST_EXPECT(checksOnAccount(env, bob).empty());
503 BEAST_EXPECT(ownerCount(env, alice) == 1);
504 BEAST_EXPECT(ownerCount(env, bob) == 1);
505
506 // alice writes two checks for USD(20), although she only has
507 // USD(20).
508 uint256 const chkId3{getCheckIndex(alice, env.seq(alice))};
509 env(check::create(alice, bob, usd(20)));
510 env.close();
511 uint256 const chkId4{getCheckIndex(alice, env.seq(alice))};
512 env(check::create(alice, bob, usd(20)));
513 env.close();
514 BEAST_EXPECT(checksOnAccount(env, alice).size() == 2);
515 BEAST_EXPECT(checksOnAccount(env, bob).size() == 2);
516
517 // bob cashes the second check for the face amount.
518 env(check::cash(bob, chkId4, usd(20)));
519 env.close();
520 env.require(Balance(alice, usd(0)));
521 env.require(Balance(bob, usd(100)));
522 BEAST_EXPECT(checksOnAccount(env, alice).size() == 1);
523 BEAST_EXPECT(checksOnAccount(env, bob).size() == 1);
524 BEAST_EXPECT(ownerCount(env, alice) == 2);
525 BEAST_EXPECT(ownerCount(env, bob) == 1);
526
527 // bob is not allowed to cash the last check for USD(0), he must
528 // use check::cancel instead.
529 env(check::cash(bob, chkId3, usd(0)), Ter(temBAD_AMOUNT));
530 env.close();
531 env.require(Balance(alice, usd(0)));
532 env.require(Balance(bob, usd(100)));
533 BEAST_EXPECT(checksOnAccount(env, alice).size() == 1);
534 BEAST_EXPECT(checksOnAccount(env, bob).size() == 1);
535 BEAST_EXPECT(ownerCount(env, alice) == 2);
536 BEAST_EXPECT(ownerCount(env, bob) == 1);
537
538 {
539 // Unlike IOU, cashing a check exceeding the MPT limit doesn't
540 // work. Show that at work.
541 //
542 // MPT limit is USD(105). Show that
543 // neither a payment to bob or caching can exceed that limit.
544
545 // Payment of 200 USD fails.
546 env(pay(gw, bob, usd(200)), Ter(tecPATH_PARTIAL));
547 env.close();
548
549 uint256 const chkId20{getCheckIndex(gw, env.seq(gw))};
550 env(check::create(gw, bob, usd(200)));
551 env.close();
552
553 // Cashing a check for 200 USD fails.
554 env(check::cash(bob, chkId20, usd(200)), Ter(tecPATH_PARTIAL));
555 env.close();
556 env.require(Balance(bob, usd(100)));
557
558 // Clean up this most recent experiment so the rest of the
559 // tests work.
560 env(pay(bob, gw, usd(100)));
561 env(check::cancel(bob, chkId20));
562 }
563
564 // ... so bob cancels alice's remaining check.
565 env(check::cancel(bob, chkId3));
566 env.close();
567 env.require(Balance(alice, usd(0)));
568 env.require(Balance(bob, usd(0)));
569 BEAST_EXPECT(checksOnAccount(env, alice).empty());
570 BEAST_EXPECT(checksOnAccount(env, bob).empty());
571 BEAST_EXPECT(ownerCount(env, alice) == 1);
572 BEAST_EXPECT(ownerCount(env, bob) == 1);
573 }
574 {
575 // Simple MPT check cashed with DeliverMin (with failures).
576 Env env{*this, features};
577
578 env.fund(XRP(1'000), gw, alice, bob);
579
580 MPT const usd =
581 MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 20});
582
583 env(pay(gw, alice, usd(8)));
584 env.close();
585
586 // alice creates several checks ahead of time.
587 uint256 const chkId9{getCheckIndex(alice, env.seq(alice))};
588 env(check::create(alice, bob, usd(9)));
589 env.close();
590 uint256 const chkId8{getCheckIndex(alice, env.seq(alice))};
591 env(check::create(alice, bob, usd(8)));
592 env.close();
593 uint256 const chkId7{getCheckIndex(alice, env.seq(alice))};
594 env(check::create(alice, bob, usd(7)));
595 env.close();
596 uint256 const chkId6{getCheckIndex(alice, env.seq(alice))};
597 env(check::create(alice, bob, usd(6)));
598 env.close();
599
600 // bob attempts to cash a check for the amount on the check.
601 // Should fail, since alice doesn't have the funds.
602 env(check::cash(bob, chkId9, check::DeliverMin(usd(9))), Ter(tecPATH_PARTIAL));
603 env.close();
604
605 // bob sets a DeliverMin of 7 and gets all that alice has.
606 env(check::cash(bob, chkId9, check::DeliverMin(usd(7))));
607 verifyDeliveredAmount(env, usd(8));
608 env.require(Balance(alice, usd(0)));
609 env.require(Balance(bob, usd(8)));
610 BEAST_EXPECT(checksOnAccount(env, alice).size() == 3);
611 BEAST_EXPECT(checksOnAccount(env, bob).size() == 3);
612 BEAST_EXPECT(ownerCount(env, alice) == 4);
613 BEAST_EXPECT(ownerCount(env, bob) == 1);
614
615 // bob pays alice USD(7) so he can use another check.
616 env(pay(bob, alice, usd(7)));
617 env.close();
618
619 // Using DeliverMin for the SendMax value of the check (and no
620 // transfer fees) should work just like setting Amount.
621 env(check::cash(bob, chkId7, check::DeliverMin(usd(7))));
622 verifyDeliveredAmount(env, usd(7));
623 env.require(Balance(alice, usd(0)));
624 env.require(Balance(bob, usd(8)));
625 BEAST_EXPECT(checksOnAccount(env, alice).size() == 2);
626 BEAST_EXPECT(checksOnAccount(env, bob).size() == 2);
627 BEAST_EXPECT(ownerCount(env, alice) == 3);
628 BEAST_EXPECT(ownerCount(env, bob) == 1);
629
630 // bob pays alice USD(8) so he can use the last two checks.
631 env(pay(bob, alice, usd(8)));
632 env.close();
633
634 // alice has USD(8). If bob uses the check for USD(6) and uses a
635 // DeliverMin of 4, he should get the SendMax value of the check.
636 env(check::cash(bob, chkId6, check::DeliverMin(usd(4))));
637 verifyDeliveredAmount(env, usd(6));
638 env.require(Balance(alice, usd(2)));
639 env.require(Balance(bob, usd(6)));
640 BEAST_EXPECT(checksOnAccount(env, alice).size() == 1);
641 BEAST_EXPECT(checksOnAccount(env, bob).size() == 1);
642 BEAST_EXPECT(ownerCount(env, alice) == 2);
643 BEAST_EXPECT(ownerCount(env, bob) == 1);
644
645 // bob cashes the last remaining check setting a DeliverMin.
646 // of exactly alice's remaining USD.
647 env(check::cash(bob, chkId8, check::DeliverMin(usd(2))));
648 verifyDeliveredAmount(env, usd(2));
649 env.require(Balance(alice, usd(0)));
650 env.require(Balance(bob, usd(8)));
651 BEAST_EXPECT(checksOnAccount(env, alice).empty());
652 BEAST_EXPECT(checksOnAccount(env, bob).empty());
653 BEAST_EXPECT(ownerCount(env, alice) == 1);
654 BEAST_EXPECT(ownerCount(env, bob) == 1);
655 }
656 {
657 // Examine the effects of the asfRequireAuth flag.
658 Env env(*this, features);
659
660 env.fund(XRP(1000), gw, alice, bob);
661 auto usdm = MPTTester(
662 {.env = env,
663 .issuer = gw,
664 .holders = {alice},
665 .flags = kMptDexFlags | tfMPTRequireAuth,
666 .maxAmt = 20});
667 MPT const usd = usdm;
668 usdm.authorize({.holder = alice});
669 env.close();
670 env(pay(gw, alice, usd(8)));
671 env.close();
672
673 // alice writes a check to bob for USD. bob can't cash it
674 // because he is not authorized to hold gw["USD"].
675 uint256 const chkId{getCheckIndex(alice, env.seq(alice))};
676 env(check::create(alice, bob, usd(7)));
677 env.close();
678
679 env(check::cash(bob, chkId, usd(7)), Ter(tecNO_AUTH));
680 env.close();
681
682 // Now give bob MPT for USD. bob still can't cash the
683 // check because he is not authorized.
684 usdm.authorize({.account = bob});
685 env.close();
686
687 env(check::cash(bob, chkId, usd(7)), Ter(tecNO_AUTH));
688 env.close();
689
690 // bob gets authorization to hold USD.
691 usdm.authorize({.holder = bob});
692 env.close();
693
694 env(check::cash(bob, chkId, check::DeliverMin(usd(4))));
695 STAmount const bobGot = usd(7);
696 verifyDeliveredAmount(env, bobGot);
697 env.require(Balance(alice, usd(8) - bobGot));
698 env.require(Balance(bob, bobGot));
699
700 BEAST_EXPECT(checksOnAccount(env, alice).empty());
701 BEAST_EXPECT(checksOnAccount(env, bob).empty());
702 BEAST_EXPECT(ownerCount(env, alice) == 1);
703 BEAST_EXPECT(ownerCount(env, bob) == 1);
704 }
705
706 {
707 Env env{*this, features};
708
709 env.fund(XRP(1'000), gw, alice, bob);
710
711 MPT const usd =
712 MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 20});
713
714 // alice creates her checks ahead of time.
715 uint256 const chkId1{getCheckIndex(alice, env.seq(alice))};
716 env(check::create(alice, bob, usd(1)));
717 env.close();
718
719 uint256 const chkId2{getCheckIndex(alice, env.seq(alice))};
720 env(check::create(alice, bob, usd(2)));
721 env.close();
722
723 env(pay(gw, alice, usd(8)));
724 env.close();
725
726 // Give bob a regular key and signers
727 Account const bobby{"bobby", KeyType::Secp256k1};
728 env(regkey(bob, bobby));
729 env.close();
730
731 Account const bogie{"bogie", KeyType::Secp256k1};
732 Account const demon{"demon", KeyType::Ed25519};
733 env(signers(bob, 2, {{bogie, 1}, {demon, 1}}), Sig(bobby));
734 env.close();
735
736 int const signersCount = 1;
737 BEAST_EXPECT(ownerCount(env, bob) == signersCount + 1);
738
739 // bob uses his regular key to cash a check.
740 env(check::cash(bob, chkId1, (usd(1))), Sig(bobby));
741 env.close();
742 env.require(Balance(alice, usd(7)));
743 env.require(Balance(bob, usd(1)));
744 BEAST_EXPECT(checksOnAccount(env, alice).size() == 1);
745 BEAST_EXPECT(checksOnAccount(env, bob).size() == 1);
746 BEAST_EXPECT(ownerCount(env, alice) == 2);
747 BEAST_EXPECT(ownerCount(env, bob) == signersCount + 1);
748
749 // bob uses multisigning to cash a check.
750 XRPAmount const baseFeeDrops{env.current()->fees().base};
751 env(check::cash(bob, chkId2, (usd(2))), Msig(bogie, demon), Fee(3 * baseFeeDrops));
752 env.close();
753 env.require(Balance(alice, usd(5)));
754 env.require(Balance(bob, usd(3)));
755 BEAST_EXPECT(checksOnAccount(env, alice).empty());
756 BEAST_EXPECT(checksOnAccount(env, bob).empty());
757 BEAST_EXPECT(ownerCount(env, alice) == 1);
758 BEAST_EXPECT(ownerCount(env, bob) == signersCount + 1);
759 }
760 }
761
762 void
764 {
765 // Look at behavior when the issuer charges a transfer fee.
766 testcase("Cash with transfer fee");
767
768 using namespace test::jtx;
769
770 Account const gw{"gateway"};
771 Account const alice{"alice"};
772 Account const bob{"bob"};
773
774 Env env{*this, features};
775
776 env.fund(XRP(1'000), gw, alice, bob);
777
778 // Set gw's transfer rate and see the consequences when cashing a check.
779 MPT const usd = MPTTester(
780 {.env = env,
781 .issuer = gw,
782 .holders = {alice, bob},
783 .transferFee = 25'000,
784 .maxAmt = 1'000});
785
786 env.close();
787 env(pay(gw, alice, usd(1'000)));
788 env.close();
789
790 // alice writes a check with a SendMax of USD(125). The most bob
791 // can get is USD(100) because of the transfer rate.
792 uint256 const chkId125{getCheckIndex(alice, env.seq(alice))};
793 env(check::create(alice, bob, usd(125)));
794 env.close();
795
796 // alice writes another check that won't get cashed until the transfer
797 // rate changes so we can see the rate applies when the check is
798 // cashed, not when it is created.
799#if 0
800 uint256 const chkId120{getCheckIndex(alice, env.Seq(alice))};
801 env(check::create(alice, bob, USD(120)));
802 env.close();
803#endif
804
805 // bob attempts to cash the check for face value. Should fail.
806 env(check::cash(bob, chkId125, usd(125)), Ter(tecPATH_PARTIAL));
807 env.close();
808 env(check::cash(bob, chkId125, check::DeliverMin(usd(101))), Ter(tecPATH_PARTIAL));
809 env.close();
810
811 // bob decides that he'll accept anything USD(75) or up.
812 // He gets USD(100).
813 env(check::cash(bob, chkId125, check::DeliverMin(usd(75))));
814 verifyDeliveredAmount(env, usd(100));
815 env.require(Balance(alice, usd(1'000 - 125)));
816 env.require(Balance(bob, usd(0 + 100)));
817 BEAST_EXPECT(checksOnAccount(env, alice).empty());
818 BEAST_EXPECT(checksOnAccount(env, bob).empty());
819
820#if 0
821 // Adjust gw's rate...
822 env(rate(gw, 1.2));
823 env.close();
824
825 // bob cashes the second check for less than the face value. The new
826 // rate applies to the actual value transferred.
827 env(check::cash(bob, chkId120, USD(50)));
828 env.close();
829 env.Require(Balance(alice, USD(1000 - 125 - 60)));
830 env.Require(Balance(bob, USD(0 + 100 + 50)));
831 BEAST_EXPECT(checksOnAccount(env, alice).size() == 0);
832 BEAST_EXPECT(checksOnAccount(env, bob).size() == 0);
833#endif
834 }
835
836 void
838 {
839 // Explore many of the ways to fail at cashing a check.
840 testcase("Cash invalid");
841
842 using namespace test::jtx;
843
844 Account const gw{"gateway"};
845 Account const alice{"alice"};
846 Account const bob{"bob"};
847 Account const zoe{"zoe"};
848 std::int64_t maxAmt{20};
849
850 Env env(*this, features);
851
852 env.fund(XRP(1000), gw, alice, bob, zoe);
853
854 auto usdm = MPTTester(
855 {.env = env,
856 .issuer = gw,
857 .holders = {alice},
858 .flags = kMptDexFlags | tfMPTCanLock,
859 .maxAmt = maxAmt});
860 MPT const usd = usdm;
861
862 env(pay(gw, alice, usd(20)));
863 env.close();
864
865 usdm.authorize({.account = bob});
866
867 // bob tries to cash a non-existent check from alice.
868 {
869 uint256 const chkId{getCheckIndex(alice, env.seq(alice))};
870 env(check::cash(bob, chkId, usd(20)), Ter(tecNO_ENTRY));
871 env.close();
872 }
873
874 // alice creates her checks ahead of time.
875 uint256 const chkIdU{getCheckIndex(alice, env.seq(alice))};
876 env(check::create(alice, bob, usd(20)));
877 env.close();
878
879 uint256 const chkIdX{getCheckIndex(alice, env.seq(alice))};
880 env(check::create(alice, bob, XRP(10)));
881 env.close();
882
883 using namespace std::chrono_literals;
884 uint256 const chkIdExp{getCheckIndex(alice, env.seq(alice))};
885 env(check::create(alice, bob, XRP(10)), Expiration(env.now() + 1s));
886 env.close();
887
888 uint256 const chkIdFroz1{getCheckIndex(alice, env.seq(alice))};
889 env(check::create(alice, bob, usd(1)));
890 env.close();
891
892 uint256 const chkIdFroz2{getCheckIndex(alice, env.seq(alice))};
893 env(check::create(alice, bob, usd(2)));
894 env.close();
895
896 uint256 const chkIdFroz3{getCheckIndex(alice, env.seq(alice))};
897 env(check::create(alice, bob, usd(3)));
898 env.close();
899
900 uint256 const chkIdNoDest1{getCheckIndex(alice, env.seq(alice))};
901 env(check::create(alice, bob, usd(1)));
902 env.close();
903
904 uint256 const chkIdHasDest2{getCheckIndex(alice, env.seq(alice))};
905 env(check::create(alice, bob, usd(2)), DestTag(7));
906 env.close();
907
908 // Same set of failing cases for both MPT and XRP check cashing.
909 auto failingCases = [&env, &gw, &alice, &bob](
910 uint256 const& chkId, STAmount const& amount) {
911 // Bad fee.
912 env(check::cash(bob, chkId, amount), Fee(drops(-10)), Ter(temBAD_FEE));
913 env.close();
914
915 // Bad flags.
916 env(check::cash(bob, chkId, amount),
917 Txflags(tfImmediateOrCancel),
918 Ter(temINVALID_FLAG));
919 env.close();
920
921 // Missing both Amount and DeliverMin.
922 {
923 json::Value tx{check::cash(bob, chkId, amount)};
924 tx.removeMember(sfAmount.jsonName);
925 env(tx, Ter(temMALFORMED));
926 env.close();
927 }
928 // Both Amount and DeliverMin present.
929 {
930 json::Value tx{check::cash(bob, chkId, amount)};
931 tx[sfDeliverMin.jsonName] = amount.getJson(JsonOptions::Values::None);
932 env(tx, Ter(temMALFORMED));
933 env.close();
934 }
935
936 // Negative or zero amount.
937 {
938 STAmount neg{amount};
939 neg.negate();
940 env(check::cash(bob, chkId, neg), Ter(temBAD_AMOUNT));
941 env.close();
942 env(check::cash(bob, chkId, amount.zeroed()), Ter(temBAD_AMOUNT));
943 env.close();
944 }
945
946 // Bad currency.
947 if (!amount.native())
948 {
949 Issue const badIssue{badCurrency(), amount.getIssuer()};
950 STAmount badAmount{amount};
951 badAmount.setIssue(Issue{badCurrency(), amount.getIssuer()});
952 env(check::cash(bob, chkId, badAmount), Ter(temBAD_CURRENCY));
953 env.close();
954 }
955
956 // Not destination cashing check.
957 env(check::cash(alice, chkId, amount), Ter(tecNO_PERMISSION));
958 env.close();
959 env(check::cash(gw, chkId, amount), Ter(tecNO_PERMISSION));
960 env.close();
961
962 // Currency mismatch.
963 {
964 MPT const eur = MPTTester({.env = env, .issuer = gw});
965 STAmount const badAmount{eur, amount};
966 env(check::cash(bob, chkId, badAmount), Ter(temMALFORMED));
967 env.close();
968 }
969
970 // Issuer mismatch.
971 // Every MPT is unique. There is no USD MPT with different issuers.
972
973 // Amount bigger than SendMax.
974 env(check::cash(bob, chkId, amount + amount), Ter(tecPATH_PARTIAL));
975 env.close();
976
977 // DeliverMin bigger than SendMax.
978 env(check::cash(bob, chkId, check::DeliverMin(amount + amount)), Ter(tecPATH_PARTIAL));
979 env.close();
980 };
981
982 failingCases(chkIdX, XRP(10));
983 failingCases(chkIdU, usd(20));
984
985 // Verify that those two checks really were cashable.
986 env(check::cash(bob, chkIdU, usd(20)));
987 env.close();
988 env(check::cash(bob, chkIdX, check::DeliverMin(XRP(10))));
989 verifyDeliveredAmount(env, XRP(10));
990
991 // Try to cash an expired check.
992 env(check::cash(bob, chkIdExp, XRP(10)), Ter(tecEXPIRED));
993 env.close();
994
995 // Cancel the expired check. Anyone can cancel an expired check.
996 env(check::cancel(zoe, chkIdExp));
997 env.close();
998
999 // Can we cash a check with frozen MPT?
1000 {
1001 env(pay(bob, alice, usd(20)));
1002 env.close();
1003 env.require(Balance(alice, usd(20)));
1004 env.require(Balance(bob, usd(0)));
1005
1006 // Global freeze
1007 usdm.set({.flags = tfMPTLock});
1008
1009 // MPTLocked flag is set and the account is not the issuer of MPT
1010 env(check::cash(bob, chkIdFroz1, usd(1)), Ter(tecPATH_PARTIAL));
1011 env.close();
1012 env(check::cash(bob, chkIdFroz1, check::DeliverMin(usd(1))), Ter(tecPATH_PARTIAL));
1013 env.close();
1014
1015 usdm.set({.flags = tfMPTUnlock});
1016
1017 // No longer frozen. Success.
1018 env(check::cash(bob, chkIdFroz1, usd(1)));
1019 env.close();
1020 env.require(Balance(alice, usd(19)));
1021 env.require(Balance(bob, usd(1)));
1022
1023 // Freeze individual MPT.
1024 usdm.set({.holder = alice, .flags = tfMPTLock});
1025 env(check::cash(bob, chkIdFroz2, usd(2)), Ter(tecPATH_PARTIAL));
1026 env.close();
1027 env(check::cash(bob, chkIdFroz2, check::DeliverMin(usd(1))), Ter(tecPATH_PARTIAL));
1028 env.close();
1029
1030 // Clear that freeze. Now check cashing works.
1031 usdm.set({.holder = alice, .flags = tfMPTUnlock});
1032 env(check::cash(bob, chkIdFroz2, usd(2)));
1033 env.close();
1034 env.require(Balance(alice, usd(17)));
1035 env.require(Balance(bob, usd(3)));
1036
1037 // Freeze bob's MPT. bob can't cash the check.
1038 usdm.set({.holder = bob, .flags = tfMPTLock});
1039 env(check::cash(bob, chkIdFroz3, usd(3)), Ter(tecLOCKED));
1040 env.close();
1041 env(check::cash(bob, chkIdFroz3, check::DeliverMin(usd(1))), Ter(tecLOCKED));
1042 env.close();
1043
1044 // Clear that freeze. Now check cashing works again.
1045 usdm.set({.holder = bob, .flags = tfMPTUnlock});
1046 env.close();
1047 env(check::cash(bob, chkIdFroz3, check::DeliverMin(usd(1))));
1048 verifyDeliveredAmount(env, usd(3));
1049 env.require(Balance(alice, usd(14)));
1050 env.require(Balance(bob, usd(6)));
1051 }
1052 {
1053 // Set the RequireDest flag on bob's account (after the check
1054 // was created) then cash a check without a destination tag.
1055 env(fset(bob, asfRequireDest));
1056 env.close();
1057 env(check::cash(bob, chkIdNoDest1, usd(1)), Ter(tecDST_TAG_NEEDED));
1058 env.close();
1059 env(check::cash(bob, chkIdNoDest1, check::DeliverMin(usd(1))), Ter(tecDST_TAG_NEEDED));
1060 env.close();
1061
1062 // bob can cash a check with a destination tag.
1063 env(check::cash(bob, chkIdHasDest2, usd(2)));
1064 env.close();
1065
1066 env.require(Balance(alice, usd(12)));
1067 env.require(Balance(bob, usd(8)));
1068
1069 // Clear the RequireDest flag on bob's account so he can
1070 // cash the check with no DestinationTag.
1071 env(fclear(bob, asfRequireDest));
1072 env.close();
1073 env(check::cash(bob, chkIdNoDest1, usd(1)));
1074 env.close();
1075 env.require(Balance(alice, usd(11)));
1076 env.require(Balance(bob, usd(9)));
1077 }
1078
1079 // OutstandingAmount exceeds MaximumAmount
1080 {
1081 // Already at maximum
1082 BEAST_EXPECT(env.balance(gw, usdm) == usdm(-maxAmt));
1083
1084 uint256 const chkId{getCheckIndex(gw, env.seq(gw))};
1085 env(check::create(gw, bob, usdm(10)));
1086 env.close();
1087
1088 // Exceeds MaximumAmount (20 + 10) = 30 > 20
1089 env(check::cash(bob, chkId, usdm(10)), Ter(tecPATH_PARTIAL));
1090 env.close();
1091
1092 // Redeem some tokens (20 - 9) = 11
1093 env(pay(alice, gw, usdm(9)));
1094 env.close();
1095
1096 // Still exceeds MaximumAmount (11 + 10) = 21 > 20
1097 env(check::cash(bob, chkId, usdm(10)), Ter(tecPATH_PARTIAL));
1098 env.close();
1099 }
1100 }
1101
1102 void
1104 {
1105 // Explore many of the ways to cancel a check.
1106 testcase("Cancel valid");
1107
1108 using namespace test::jtx;
1109
1110 Account const gw{"gateway"};
1111 Account const alice{"alice"};
1112 Account const bob{"bob"};
1113 Account const zoe{"zoe"};
1114
1115 {
1116 Env env{*this, features};
1117
1118 env.fund(XRP(1'000), gw, alice, bob, zoe);
1119
1120 MPT const usd = MPTTester({.env = env, .issuer = gw});
1121
1122 // alice creates her checks ahead of time.
1123 // Three ordinary checks with no expiration.
1124 uint256 const chkId1{getCheckIndex(alice, env.seq(alice))};
1125 env(check::create(alice, bob, usd(10)));
1126 env.close();
1127
1128 uint256 const chkId2{getCheckIndex(alice, env.seq(alice))};
1129 env(check::create(alice, bob, XRP(10)));
1130 env.close();
1131
1132 uint256 const chkId3{getCheckIndex(alice, env.seq(alice))};
1133 env(check::create(alice, bob, usd(10)));
1134 env.close();
1135
1136 // Three checks that expire in 10 minutes.
1137 using namespace std::chrono_literals;
1138 uint256 const chkIdNotExp1{getCheckIndex(alice, env.seq(alice))};
1139 env(check::create(alice, bob, XRP(10)), Expiration(env.now() + 600s));
1140 env.close();
1141
1142 uint256 const chkIdNotExp2{getCheckIndex(alice, env.seq(alice))};
1143 env(check::create(alice, bob, usd(10)), Expiration(env.now() + 600s));
1144 env.close();
1145
1146 uint256 const chkIdNotExp3{getCheckIndex(alice, env.seq(alice))};
1147 env(check::create(alice, bob, XRP(10)), Expiration(env.now() + 600s));
1148 env.close();
1149
1150 // Three checks that expire in one second.
1151 uint256 const chkIdExp1{getCheckIndex(alice, env.seq(alice))};
1152 env(check::create(alice, bob, usd(10)), Expiration(env.now() + 1s));
1153 env.close();
1154
1155 uint256 const chkIdExp2{getCheckIndex(alice, env.seq(alice))};
1156 env(check::create(alice, bob, XRP(10)), Expiration(env.now() + 1s));
1157 env.close();
1158
1159 uint256 const chkIdExp3{getCheckIndex(alice, env.seq(alice))};
1160 env(check::create(alice, bob, usd(10)), Expiration(env.now() + 1s));
1161 env.close();
1162
1163 // Two checks to cancel using a regular key and using multisigning.
1164 uint256 const chkIdReg{getCheckIndex(alice, env.seq(alice))};
1165 env(check::create(alice, bob, usd(10)));
1166 env.close();
1167
1168 uint256 const chkIdMSig{getCheckIndex(alice, env.seq(alice))};
1169 env(check::create(alice, bob, XRP(10)));
1170 env.close();
1171 BEAST_EXPECT(checksOnAccount(env, alice).size() == 11);
1172 BEAST_EXPECT(ownerCount(env, alice) == 11);
1173
1174 // Creator, destination, and an outsider cancel the checks.
1175 env(check::cancel(alice, chkId1));
1176 env.close();
1177 BEAST_EXPECT(checksOnAccount(env, alice).size() == 10);
1178 BEAST_EXPECT(ownerCount(env, alice) == 10);
1179
1180 env(check::cancel(bob, chkId2));
1181 env.close();
1182 BEAST_EXPECT(checksOnAccount(env, alice).size() == 9);
1183 BEAST_EXPECT(ownerCount(env, alice) == 9);
1184
1185 env(check::cancel(zoe, chkId3), Ter(tecNO_PERMISSION));
1186 env.close();
1187 BEAST_EXPECT(checksOnAccount(env, alice).size() == 9);
1188 BEAST_EXPECT(ownerCount(env, alice) == 9);
1189
1190 // Creator, destination, and an outsider cancel unexpired checks.
1191 env(check::cancel(alice, chkIdNotExp1));
1192 env.close();
1193 BEAST_EXPECT(checksOnAccount(env, alice).size() == 8);
1194 BEAST_EXPECT(ownerCount(env, alice) == 8);
1195
1196 env(check::cancel(bob, chkIdNotExp2));
1197 env.close();
1198 BEAST_EXPECT(checksOnAccount(env, alice).size() == 7);
1199 BEAST_EXPECT(ownerCount(env, alice) == 7);
1200
1201 env(check::cancel(zoe, chkIdNotExp3), Ter(tecNO_PERMISSION));
1202 env.close();
1203 BEAST_EXPECT(checksOnAccount(env, alice).size() == 7);
1204 BEAST_EXPECT(ownerCount(env, alice) == 7);
1205
1206 // Creator, destination, and an outsider cancel expired checks.
1207 env(check::cancel(alice, chkIdExp1));
1208 env.close();
1209 BEAST_EXPECT(checksOnAccount(env, alice).size() == 6);
1210 BEAST_EXPECT(ownerCount(env, alice) == 6);
1211
1212 env(check::cancel(bob, chkIdExp2));
1213 env.close();
1214 BEAST_EXPECT(checksOnAccount(env, alice).size() == 5);
1215 BEAST_EXPECT(ownerCount(env, alice) == 5);
1216
1217 env(check::cancel(zoe, chkIdExp3));
1218 env.close();
1219 BEAST_EXPECT(checksOnAccount(env, alice).size() == 4);
1220 BEAST_EXPECT(ownerCount(env, alice) == 4);
1221
1222 // Use a regular key and also multisign to cancel checks.
1223 Account const alie{"alie", KeyType::Ed25519};
1224 env(regkey(alice, alie));
1225 env.close();
1226
1227 Account const bogie{"bogie", KeyType::Secp256k1};
1228 Account const demon{"demon", KeyType::Ed25519};
1229 env(signers(alice, 2, {{bogie, 1}, {demon, 1}}), Sig(alie));
1230 env.close();
1231
1232 int const signersCount{1};
1233
1234 // alice uses her regular key to cancel a check.
1235 env(check::cancel(alice, chkIdReg), Sig(alie));
1236 env.close();
1237 BEAST_EXPECT(checksOnAccount(env, alice).size() == 3);
1238 BEAST_EXPECT(ownerCount(env, alice) == signersCount + 3);
1239
1240 // alice uses multisigning to cancel a check.
1241 XRPAmount const baseFeeDrops{env.current()->fees().base};
1242 env(check::cancel(alice, chkIdMSig), Msig(bogie, demon), Fee(3 * baseFeeDrops));
1243 env.close();
1244 BEAST_EXPECT(checksOnAccount(env, alice).size() == 2);
1245 BEAST_EXPECT(ownerCount(env, alice) == signersCount + 2);
1246
1247 // Creator and destination cancel the remaining unexpired checks.
1248 env(check::cancel(alice, chkId3), Sig(alice));
1249 env.close();
1250 BEAST_EXPECT(checksOnAccount(env, alice).size() == 1);
1251 BEAST_EXPECT(ownerCount(env, alice) == signersCount + 1);
1252
1253 env(check::cancel(bob, chkIdNotExp3));
1254 env.close();
1255 BEAST_EXPECT(checksOnAccount(env, alice).empty());
1256 BEAST_EXPECT(ownerCount(env, alice) == signersCount + 0);
1257 }
1258 }
1259
1260 void
1262 {
1263 testcase("With Tickets");
1264
1265 using namespace test::jtx;
1266
1267 Account const gw{"gw"};
1268 Account const alice{"alice"};
1269 Account const bob{"bob"};
1270
1271 Env env{*this, features};
1272 env.fund(XRP(1'000), gw, alice, bob);
1273 env.close();
1274
1275 MPT const usd =
1276 MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 1'000});
1277
1278 // alice and bob grab enough tickets for all the following
1279 // transactions. Note that once the tickets are acquired alice's
1280 // and bob's account sequence numbers should not advance.
1281 std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
1282 env(ticket::create(alice, 10));
1283 std::uint32_t const aliceSeq{env.seq(alice)};
1284
1285 std::uint32_t bobTicketSeq{env.seq(bob) + 1};
1286 env(ticket::create(bob, 10));
1287 std::uint32_t const bobSeq{env.seq(bob)};
1288
1289 env.close();
1290 // MPT + 10 tickets
1291 env.require(Owners(alice, 11));
1292 env.require(Owners(bob, 11));
1293
1294 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1295 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1296
1297 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
1298 BEAST_EXPECT(env.seq(bob) == bobSeq);
1299
1300 env(pay(gw, alice, usd(900)));
1301 env.close();
1302
1303 // alice creates four checks; two XRP, two MPT. Bob will cash
1304 // one of each and cancel one of each.
1305 uint256 const chkIdXrp1{getCheckIndex(alice, aliceTicketSeq)};
1306 env(check::create(alice, bob, XRP(200)), ticket::Use(aliceTicketSeq++));
1307
1308 uint256 const chkIdXrp2{getCheckIndex(alice, aliceTicketSeq)};
1309 env(check::create(alice, bob, XRP(300)), ticket::Use(aliceTicketSeq++));
1310
1311 uint256 const chkIdUsd1{getCheckIndex(alice, aliceTicketSeq)};
1312 env(check::create(alice, bob, usd(200)), ticket::Use(aliceTicketSeq++));
1313
1314 uint256 const chkIdUsd2{getCheckIndex(alice, aliceTicketSeq)};
1315 env(check::create(alice, bob, usd(300)), ticket::Use(aliceTicketSeq++));
1316
1317 env.close();
1318 // Alice used four tickets but created four checks.
1319 env.require(Owners(alice, 11));
1320 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1321 BEAST_EXPECT(checksOnAccount(env, alice).size() == 4);
1322 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1323
1324 env.require(Owners(bob, 11));
1325 BEAST_EXPECT(env.seq(bob) == bobSeq);
1326
1327 // Bob cancels two of alice's checks.
1328 env(check::cancel(bob, chkIdXrp1), ticket::Use(bobTicketSeq++));
1329 env(check::cancel(bob, chkIdUsd2), ticket::Use(bobTicketSeq++));
1330 env.close();
1331
1332 env.require(Owners(alice, 9));
1333 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1334 BEAST_EXPECT(checksOnAccount(env, alice).size() == 2);
1335 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1336
1337 env.require(Owners(bob, 9));
1338 BEAST_EXPECT(env.seq(bob) == bobSeq);
1339
1340 // Bob cashes alice's two remaining checks.
1341 env(check::cash(bob, chkIdXrp2, XRP(300)), ticket::Use(bobTicketSeq++));
1342 env(check::cash(bob, chkIdUsd1, usd(200)), ticket::Use(bobTicketSeq++));
1343 env.close();
1344
1345 auto const baseFee = env.current()->fees().base;
1346 env.require(Owners(alice, 7));
1347 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1348 BEAST_EXPECT(checksOnAccount(env, alice).empty());
1349 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1350 env.require(Balance(alice, usd(700)));
1351 env.require(Balance(alice, XRP(700) - 6 * baseFee));
1352 env.require(Owners(bob, 7));
1353 BEAST_EXPECT(env.seq(bob) == bobSeq);
1354 env.require(Balance(bob, usd(200)));
1355 env.require(Balance(bob, XRP(1'300) - 6 * baseFee));
1356 }
1357
1358 void
1360 {
1361 // Explore automatic MPT creation when a check is cashed.
1362
1363 testcase("MPT Creation");
1364
1365 using namespace test::jtx;
1366
1367 Env env{*this, features};
1368
1369 // An account that independently tracks its owner count.
1370 struct AccountOwns
1371 {
1372 using iterator = hash_map<std::string, MPTTester>::iterator;
1374 Env& env;
1375 Account const acct;
1376 std::size_t owners{0};
1378 bool const isIssuer;
1379 bool const requireAuth;
1380
1381 AccountOwns(
1383 Env& e,
1384 Account a,
1385 bool isIssuer,
1386 bool requireAuth = false)
1387 : suite(s), env(e), acct(std::move(a)), isIssuer(isIssuer), requireAuth(requireAuth)
1388 {
1389 }
1390
1391 void
1392 verifyOwners(std::uint32_t line, bool print = false) const
1393 {
1394 if (print)
1395 {
1396 std::cout << acct.name() << " " << ownerCount(env, acct) << " " << owners
1397 << std::endl;
1398 }
1399 suite.expect(
1400 ownerCount(env, acct) == owners, "Owner count mismatch", __FILE__, line);
1401 }
1402
1403 // Operators to make using the class more convenient.
1404 operator Account() const
1405 {
1406 return acct;
1407 }
1408
1409 operator xrpl::AccountID() const
1410 {
1411 return acct.id();
1412 }
1413
1417 MPT
1418 operator[](std::string const& s)
1419 {
1420 if (!isIssuer)
1421 Throw<std::runtime_error>("AccountOwns: must be issuer");
1422 if (auto const& it = mpts.find(s); it != mpts.end())
1423 return it->second[s];
1424 auto flags = kMptDexFlags | tfMPTCanLock;
1425 if (requireAuth)
1426 flags |= tfMPTRequireAuth;
1427 auto [it, _] =
1428 mpts.emplace(s, MPTTester({.env = env, .issuer = acct, .flags = flags}));
1429 (void)_;
1430 ++owners;
1431
1432 return it->second[s];
1433 }
1434
1435 iterator
1436 getIt(MPT const& mpt)
1437 {
1438 if (!isIssuer)
1439 Throw<std::runtime_error>("AccountOwns::set must be issuer");
1440 auto it = mpts.find(mpt.name);
1441 if (it == mpts.end())
1442 Throw<std::runtime_error>("AccountOwns::set mpt doesn't exist");
1443 return it;
1444 }
1445
1446 void
1447 set(MPT const& mpt, std::uint32_t flag)
1448 {
1449 auto it = getIt(mpt);
1450 it->second.set({.flags = flag});
1451 }
1452
1453 void
1454 authorize(MPT const& mpt, AccountOwns& id)
1455 {
1456 auto it = getIt(mpt);
1457 it->second.authorize({.account = id});
1458 ++id.owners;
1459 }
1460
1461 void
1462 cleanup(MPT const& mpt, AccountOwns& id)
1463 {
1464 auto it = getIt(mpt);
1465 // redeem to the issuer
1466 if (auto const redeem = it->second.getBalance(id))
1467 pay(it, id, acct, redeem);
1468 // delete mptoken
1469 it->second.authorize({.account = id, .flags = tfMPTUnauthorize});
1470 --id.owners;
1471 }
1472
1473 void
1474 pay(iterator& it, Account const& src, Account const& dst, std::uint64_t amount)
1475 {
1476 if (env.le(keylet::account(dst))->isFlag(lsfDepositAuth))
1477 {
1478 env(fclear(dst, asfDepositAuth));
1479 it->second.pay(src, dst, amount);
1480 env(fset(dst, asfDepositAuth));
1481 }
1482 else
1483 {
1484 it->second.pay(src, dst, amount);
1485 }
1486 }
1487
1488 void
1489 pay(Account const& src, Account const& dst, PrettyAmount amount)
1490 {
1491 auto it = getIt(amount.name());
1492 pay(it, src, dst, amount.value().mpt().value());
1493 }
1494 };
1495
1496 AccountOwns alice{*this, env, "alice", false};
1497 AccountOwns bob{*this, env, "bob", false};
1498 AccountOwns gw1{*this, env, "gw1", true};
1499
1500 // Fund with noripple so the accounts do not have any flags set.
1501 env.fund(XRP(5000), noripple(alice, bob));
1502 env.close();
1503
1504 // Automatic MPT creation should fail if the check destination
1505 // can't afford the reserve for the trust line.
1506 {
1507 // Fund gw1 with noripple (even though that's atypical for a
1508 // gateway) so it does not have any flags set. We'll set flags
1509 // on gw1 later.
1510 env.fund(XRP(5'000), noripple(gw1));
1511 env.close();
1512
1513 MPT const cK8 = gw1["CK8"];
1514 gw1.verifyOwners(__LINE__);
1515
1516 Account const yui{"yui"};
1517
1518 // Note the reserve in unit tests is 200 XRP, not 20. So here
1519 // we're just barely giving yui enough XRP to meet the
1520 // account reserve.
1521 env.fund(XRP(200), yui);
1522 env.close();
1523
1524 uint256 const chkId{getCheckIndex(gw1, env.seq(gw1))};
1525 env(check::create(gw1, yui, cK8(99)));
1526 env.close();
1527
1528 env(check::cash(yui, chkId, cK8(99)), Ter(tecINSUFFICIENT_RESERVE));
1529 env.close();
1530 alice.verifyOwners(__LINE__);
1531
1532 // Give yui enough XRP to meet the trust line's reserve. Cashing
1533 // the check succeeds and creates the trust line.
1534 env(pay(env.master, yui, XRP(51)));
1535 env.close();
1536 env(check::cash(yui, chkId, cK8(99)));
1537 verifyDeliveredAmount(env, cK8(99));
1538 env.close();
1539 BEAST_EXPECT(ownerCount(env, yui) == 1);
1540
1541 // The automatic trust line does not take a reserve from gw1.
1542 // Since gw1's check was consumed it has no owners.
1543 gw1.verifyOwners(__LINE__);
1544 }
1545
1546 // We'll be looking at the effects of various account root flags and
1547 // MPT flags.
1548
1549 // Automatically create MPT using
1550 // o Offers and
1551 // o Check cashing
1552
1553 //----------- No account root flags, check written by issuer -----------
1554 {
1555 // No account root flags on any participant.
1556 // Automatic trust line from issuer to destination.
1557
1558 BEAST_EXPECT((*env.le(gw1))[sfFlags] == 0);
1559 BEAST_EXPECT((*env.le(alice))[sfFlags] == 0);
1560 BEAST_EXPECT((*env.le(bob))[sfFlags] == 0);
1561
1562 // Use offers to automatically create MPT
1563 MPT const oF1 = gw1["OF1"];
1564 env(offer(gw1, XRP(98), oF1(98)));
1565 env.close();
1566 BEAST_EXPECT(env.le(keylet::mptoken(oF1.issuanceID, alice)) == nullptr);
1567 env(offer(alice, oF1(98), XRP(98)));
1568 ++alice.owners;
1569 env.close();
1570
1571 // Both offers should be consumed.
1572 // Since gw1's offer was consumed and the trust line was not
1573 // created by gw1, gw1's owner count should be 0.
1574 gw1.verifyOwners(__LINE__);
1575
1576 // alice's automatically created MPT bumps her owner count.
1577 alice.verifyOwners(__LINE__);
1578
1579 // Use check cashing to automatically create the trust line.
1580 MPT const cK1 = gw1["CK1"];
1581 uint256 const chkId{getCheckIndex(gw1, env.seq(gw1))};
1582 env(check::create(gw1, alice, cK1(98)));
1583 env.close();
1584 BEAST_EXPECT(env.le(keylet::mptoken(cK1.issuanceID, alice)) == nullptr);
1585 env(check::cash(alice, chkId, cK1(98)));
1586 ++alice.owners;
1587 verifyDeliveredAmount(env, cK1(98));
1588 env.close();
1589
1590 // gw1's check should be consumed.
1591 // Since gw1's check was consumed and the trust line was not
1592 // created by gw1, gw1's owner count should be 0.
1593 gw1.verifyOwners(__LINE__);
1594
1595 // alice's automatically created trust line bumps her owner count.
1596 alice.verifyOwners(__LINE__);
1597
1598 // cmpTrustLines(gw1, alice, OF1, CK1);
1599 }
1600 //--------- No account root flags, check written by non-issuer ---------
1601 {
1602 // No account root flags on any participant.
1603
1604 // Use offers to automatically create MPT.
1605 // Transfer of assets using offers does not require rippling.
1606 // So bob's offer is successfully crossed which creates MPT.
1607 MPT const oF1 = gw1["OF1"];
1608 env(offer(alice, XRP(97), oF1(97)));
1609 env.close();
1610 BEAST_EXPECT(env.le(keylet::mptoken(oF1, bob)) == nullptr);
1611 env(offer(bob, oF1(97), XRP(97)));
1612 ++bob.owners;
1613 env.close();
1614
1615 // Both offers should be consumed.
1616 env.require(Balance(alice, oF1(1)));
1617 env.require(Balance(bob, oF1(97)));
1618
1619 // bob now has an owner count of 1 due to new MPT.
1620 gw1.verifyOwners(__LINE__);
1621 alice.verifyOwners(__LINE__);
1622 bob.verifyOwners(__LINE__);
1623
1624 // Use check cashing to automatically create MPT.
1625 //
1626 // Unlike IOU where cashing a check (unlike crossing offers)
1627 // requires rippling through the currency's issuer, rippling doesn't
1628 // impact MPT. Even though gw1 does not have rippling enabled, the
1629 // check cash succeeds for MPT and MPT is created.
1630 MPT const cK1 = gw1["CK1"];
1631 uint256 const chkId{getCheckIndex(alice, env.seq(alice))};
1632 env(check::create(alice, bob, cK1(97)));
1633 env.close();
1634 BEAST_EXPECT(env.le(keylet::mptoken(cK1, bob)) == nullptr);
1635 env(check::cash(bob, chkId, cK1(97)));
1636 ++bob.owners;
1637 env.close();
1638
1639 BEAST_EXPECT(env.le(keylet::mptoken(oF1, bob)) != nullptr);
1640
1641 gw1.verifyOwners(__LINE__);
1642 alice.verifyOwners(__LINE__);
1643 bob.verifyOwners(__LINE__);
1644 }
1645
1646 //------------- lsfDefaultRipple, check written by issuer --------------
1647 {
1648 // gw1 enables rippling.
1649 // This doesn't impact automatic MPT creation.
1650 env(fset(gw1, asfDefaultRipple));
1651 env.close();
1652
1653 // Use offers to automatically create the trust line.
1654 MPT const oF2 = gw1["OF2"];
1655 env(offer(gw1, XRP(96), oF2(96)));
1656 env.close();
1657 BEAST_EXPECT(env.le(keylet::mptoken(oF2, alice)) == nullptr);
1658 env(offer(alice, oF2(96), XRP(96)));
1659 ++alice.owners;
1660 env.close();
1661
1662 // Both offers should be consumed.
1663 // Since gw1's offer was consumed, gw1 owner count doesn't change.
1664 gw1.verifyOwners(__LINE__);
1665
1666 // alice's automatically created MPT bumps her owner count.
1667 alice.verifyOwners(__LINE__);
1668
1669 // Use check cashing to automatically create MPT.
1670 MPT const cK2 = gw1["CK2"];
1671 uint256 const chkId{getCheckIndex(gw1, env.seq(gw1))};
1672 env(check::create(gw1, alice, cK2(96)));
1673 env.close();
1674 BEAST_EXPECT(env.le(keylet::mptoken(cK2, alice)) == nullptr);
1675 env(check::cash(alice, chkId, cK2(96)));
1676 ++alice.owners;
1677 verifyDeliveredAmount(env, cK2(96));
1678 env.close();
1679
1680 // gw1's check should be consumed.
1681 // Since gw1's check was consumed and MPT was not
1682 // created by gw1, gw1's owner count doesn't change.
1683 gw1.verifyOwners(__LINE__);
1684
1685 // alice's automatically created trust line bumps her owner count.
1686 alice.verifyOwners(__LINE__);
1687 }
1688
1689 //----------- lsfDefaultRipple, check written by non-issuer ------------
1690 {
1691 // gw1 enabled rippling doesn't impact MPT, so automatic MPT from
1692 // non-issuer to non-issuer should work.
1693
1694 // Use offers to automatically create MPT.
1695 MPT const oF2 = gw1["OF2"];
1696 env(offer(alice, XRP(95), oF2(95)));
1697 env.close();
1698 // alice already has OF2 MPT
1699 BEAST_EXPECT(env.le(keylet::mptoken(oF2, alice)) != nullptr);
1700 env(offer(bob, oF2(95), XRP(95)));
1701 ++bob.owners;
1702 env.close();
1703
1704 // bob's owner count should increase due to the new MPT.
1705 gw1.verifyOwners(__LINE__);
1706 alice.verifyOwners(__LINE__);
1707 bob.verifyOwners(__LINE__);
1708
1709 // Use check cashing to automatically create MPT.
1710 MPT const cK2 = gw1["CK2"];
1711 uint256 const chkId{getCheckIndex(alice, env.seq(alice))};
1712 env(check::create(alice, bob, cK2(95)));
1713 env.close();
1714 BEAST_EXPECT(env.le(keylet::mptoken(cK2, bob)) == nullptr);
1715 env(check::cash(bob, chkId, cK2(95)));
1716 ++bob.owners;
1717 verifyDeliveredAmount(env, cK2(95));
1718 env.close();
1719
1720 // bob's owner count should increase due to the new MPT.
1721 gw1.verifyOwners(__LINE__);
1722 alice.verifyOwners(__LINE__);
1723 bob.verifyOwners(__LINE__);
1724 }
1725
1726 //-------------- lsfDepositAuth, check written by issuer ---------------
1727 {
1728 // Both offers and checks ignore the lsfDepositAuth flag, since
1729 // the destination signs the transaction that delivers their funds.
1730 // So setting lsfDepositAuth on all the participants should not
1731 // change any outcomes.
1732 //
1733 // Automatic MPT from issuer to non-issuer should still work.
1734 env(fset(gw1, asfDepositAuth));
1735 env(fset(alice, asfDepositAuth));
1736 env(fset(bob, asfDepositAuth));
1737 env.close();
1738
1739 // Use offers to automatically create MPT.
1740 MPT const oF3 = gw1["OF3"];
1741 env(offer(gw1, XRP(94), oF3(94)));
1742 env.close();
1743 BEAST_EXPECT(env.le(keylet::mptoken(oF3, alice)) == nullptr);
1744 env(offer(alice, oF3(94), XRP(94)));
1745 ++alice.owners;
1746 env.close();
1747
1748 // Both offers should be consumed.
1749 // Since gw1's offer was consumed and MPT was not
1750 // created by gw1, gw1's owner count doesn't change.
1751 gw1.verifyOwners(__LINE__);
1752
1753 // alice's automatically created MPT bumps her owner count.
1754 alice.verifyOwners(__LINE__);
1755
1756 // Use check cashing to automatically create MPT.
1757 MPT const cK3 = gw1["CK3"];
1758 uint256 const chkId{getCheckIndex(gw1, env.seq(gw1))};
1759 env(check::create(gw1, alice, cK3(94)));
1760 env.close();
1761 BEAST_EXPECT(env.le(keylet::mptoken(cK3, alice)) == nullptr);
1762 env(check::cash(alice, chkId, cK3(94)));
1763 ++alice.owners;
1764 verifyDeliveredAmount(env, cK3(94));
1765 env.close();
1766
1767 // gw1's check should be consumed.
1768 // Since gw1's check was consumed and MPT was not
1769 // created by gw1, gw1's owner count doesn't change.
1770 gw1.verifyOwners(__LINE__);
1771
1772 // alice's automatically created trust line bumps her owner count.
1773 alice.verifyOwners(__LINE__);
1774 }
1775
1776 //------------ lsfDepositAuth, check written by non-issuer -------------
1777 {
1778 // The presence of the lsfDepositAuth flag should not affect
1779 // automatic MPT creation.
1780
1781 // Use offers to automatically create MPT.
1782 MPT const oF3 = gw1["OF3"];
1783 env(offer(alice, XRP(93), oF3(93)));
1784 env.close();
1785 BEAST_EXPECT(env.le(keylet::mptoken(oF3, alice)) != nullptr);
1786 env(offer(bob, oF3(93), XRP(93)));
1787 ++bob.owners;
1788 env.close();
1789
1790 // bob's owner count should increase due to the new MPT.
1791 gw1.verifyOwners(__LINE__);
1792 alice.verifyOwners(__LINE__);
1793 bob.verifyOwners(__LINE__);
1794
1795 // Use check cashing to automatically create MPT.
1796 MPT const cK3 = gw1["CK3"];
1797 uint256 const chkId{getCheckIndex(alice, env.seq(alice))};
1798 env(check::create(alice, bob, cK3(93)));
1799 env.close();
1800 BEAST_EXPECT(env.le(keylet::mptoken(cK3, bob)) == nullptr);
1801 env(check::cash(bob, chkId, cK3(93)));
1802 ++bob.owners;
1803 verifyDeliveredAmount(env, cK3(93));
1804 env.close();
1805
1806 // bob's owner count should increase due to the new MPT.
1807 gw1.verifyOwners(__LINE__);
1808 alice.verifyOwners(__LINE__);
1809 bob.verifyOwners(__LINE__);
1810 }
1811
1812 //-------------- lsfGlobalFreeze, check written by issuer --------------
1813 {
1814 // Set lsfGlobalFreeze on gw1. That should not stop any automatic
1815 // MPT from being created.
1816 env(fset(gw1, asfGlobalFreeze));
1817 env.close();
1818
1819 // Use offers to automatically create MPT.
1820 MPT const oF4 = gw1["OF4"];
1821 env(offer(gw1, XRP(92), oF4(92)));
1822 env.close();
1823 BEAST_EXPECT(env.le(keylet::mptoken(oF4, alice)) == nullptr);
1824 env(offer(alice, oF4(92), XRP(92)));
1825 ++alice.owners;
1826 env.close();
1827
1828 // alice's owner count should increase do to the new MPT.
1829 gw1.verifyOwners(__LINE__);
1830 alice.verifyOwners(__LINE__);
1831 bob.verifyOwners(__LINE__);
1832
1833 // Use check cashing to automatically create MPT.
1834 MPT const cK4 = gw1["CK4"];
1835 uint256 const chkId{getCheckIndex(gw1, env.seq(gw1))};
1836 env(check::create(gw1, bob, cK4(92)));
1837 env.close();
1838 BEAST_EXPECT(env.le(keylet::mptoken(cK4, bob)) == nullptr);
1839 env(check::cash(bob, chkId, cK4(92)));
1840 verifyDeliveredAmount(env, cK4(92));
1841 ++bob.owners;
1842 env.close();
1843
1844 // bob's owner count should increase due to the new MPT.
1845 gw1.verifyOwners(__LINE__);
1846 alice.verifyOwners(__LINE__);
1847 bob.verifyOwners(__LINE__);
1848
1849 // clean up
1850 gw1.cleanup(oF4, alice);
1851 gw1.cleanup(cK4, bob);
1852 }
1853
1854 //-------------- lsfMPTLock, check written by issuer --------------
1855 {
1856 // Set lsfMPTLock on gw1. That should stop any automatic
1857 // MPT from being created.
1858
1859 // Use offers to automatically create MPT.
1860 MPT const oF4 = gw1["OF4"];
1861 gw1.set(oF4, tfMPTLock);
1862 env(offer(gw1, XRP(92), oF4(92)), Ter(tecLOCKED));
1863 env.close();
1864 BEAST_EXPECT(env.le(keylet::mptoken(oF4, alice)) == nullptr);
1865 env(offer(alice, oF4(92), XRP(92)), Ter(tecLOCKED));
1866 env.close();
1867
1868 // No one's owner count should have changed.
1869 gw1.verifyOwners(__LINE__);
1870 alice.verifyOwners(__LINE__);
1871 bob.verifyOwners(__LINE__);
1872
1873 // Use check cashing to automatically create MPT.
1874 MPT const cK4 = gw1["CK4"];
1875 gw1.set(cK4, tfMPTLock);
1876 uint256 const chkId{getCheckIndex(gw1, env.seq(gw1))};
1877 env(check::create(gw1, alice, cK4(92)), Ter(tecLOCKED));
1878 env.close();
1879 BEAST_EXPECT(env.le(keylet::mptoken(cK4, alice)) == nullptr);
1880 env(check::cash(alice, chkId, cK4(92)), Ter(tecNO_ENTRY));
1881 env.close();
1882
1883 // No one's owner count should have changed.
1884 gw1.verifyOwners(__LINE__);
1885 alice.verifyOwners(__LINE__);
1886 bob.verifyOwners(__LINE__);
1887
1888 // Because gw1 has set tfMPTLock, neither MPT
1889 // is created.
1890 BEAST_EXPECT(env.le(keylet::mptoken(oF4, alice)) == nullptr);
1891 BEAST_EXPECT(env.le(keylet::mptoken(cK4, alice)) == nullptr);
1892
1893 // clear global freeze
1894 gw1.set(oF4, tfMPTUnlock);
1895 gw1.set(cK4, tfMPTUnlock);
1896 }
1897
1898 //------------ lsfGlobalFreeze, check written by non-issuer ------------
1899 {
1900 // lsfGlobalFreeze flag set on gw1 should not stop
1901 // automatic MPT creation between non-issuers.
1902
1903 // Use offers to automatically create MPT.
1904 MPT const oF4 = gw1["OF4"];
1905 gw1.authorize(oF4, alice);
1906 gw1.pay(gw1, alice, oF4(91));
1907 env(offer(alice, XRP(91), oF4(91)));
1908 env.close();
1909 BEAST_EXPECT(env.le(keylet::mptoken(oF4, alice)) != nullptr);
1910 env(offer(bob, oF4(91), XRP(91)));
1911 ++bob.owners;
1912 env.close();
1913
1914 // alice's owner count should increase since it created MPT.
1915 // bob's owner count should increase due to the new MPT.
1916 gw1.verifyOwners(__LINE__);
1917 alice.verifyOwners(__LINE__);
1918 bob.verifyOwners(__LINE__);
1919
1920 // Use check cashing to automatically create the trust line.
1921 MPT const cK4 = gw1["CK4"];
1922 uint256 const chkId{getCheckIndex(alice, env.seq(alice))};
1923 env(check::create(alice, bob, cK4(91)));
1924 env.close();
1925 BEAST_EXPECT(env.le(keylet::mptoken(cK4, bob)) == nullptr);
1926 gw1.authorize(cK4, alice);
1927 gw1.pay(gw1, alice, cK4(91));
1928 env(check::cash(bob, chkId, cK4(91)));
1929 ++bob.owners;
1930 env.close();
1931
1932 // alice's owner count should increase since it created MPT.
1933 // bob's owner count should increase due to the new MPT.
1934 gw1.verifyOwners(__LINE__);
1935 alice.verifyOwners(__LINE__);
1936 bob.verifyOwners(__LINE__);
1937
1938 // cleanup
1939 gw1.cleanup(oF4, alice);
1940 gw1.cleanup(cK4, alice);
1941 gw1.cleanup(oF4, bob);
1942 gw1.cleanup(cK4, bob);
1943 }
1944
1945 //------------ lsfMPTLock, check written by non-issuer ------------
1946 {
1947 // Since gw1 has the lsfMPTLock flag set, there should be
1948 // no automatic MPT creation between non-issuers.
1949
1950 // Use offers to automatically create MPT.
1951 MPT const oF4 = gw1["OF4"];
1952 gw1.set(oF4, tfMPTLock);
1953 env(offer(alice, XRP(91), oF4(91)), Ter(tecLOCKED));
1954 env.close();
1955 BEAST_EXPECT(env.le(keylet::mptoken(oF4, alice)) == nullptr);
1956 env(offer(bob, oF4(91), XRP(91)), Ter(tecLOCKED));
1957 env.close();
1958
1959 // No one's owner count should have changed.
1960 gw1.verifyOwners(__LINE__);
1961 alice.verifyOwners(__LINE__);
1962 bob.verifyOwners(__LINE__);
1963
1964 // Use check cashing to automatically create the trust line.
1965 MPT const cK4 = gw1["CK4"];
1966 gw1.set(cK4, tfMPTLock);
1967 uint256 const chkId{getCheckIndex(alice, env.seq(alice))};
1968 env(check::create(alice, bob, cK4(91)), Ter(tecLOCKED));
1969 env.close();
1970 BEAST_EXPECT(env.le(keylet::mptoken(cK4, bob)) == nullptr);
1971 env(check::cash(bob, chkId, cK4(91)), Ter(tecNO_ENTRY));
1972 env.close();
1973
1974 // No one's owner count should have changed.
1975 gw1.verifyOwners(__LINE__);
1976 alice.verifyOwners(__LINE__);
1977 bob.verifyOwners(__LINE__);
1978
1979 // Because gw1 has set lsfGlobalFreeze, neither trust line
1980 // is created.
1981 BEAST_EXPECT(env.le(keylet::mptoken(oF4, bob)) == nullptr);
1982 BEAST_EXPECT(env.le(keylet::mptoken(cK4, bob)) == nullptr);
1983
1984 gw1.set(oF4, tfMPTUnlock);
1985 gw1.set(cK4, tfMPTUnlock);
1986 }
1987
1988 //-------------- lsfRequireAuth, check written by issuer ---------------
1989
1990 // We want to test the lsfRequireAuth flag, but we can't set that
1991 // flag on an account that already has MPT. So we'll fund
1992 // a new gateway and use that.
1993 AccountOwns gw2{*this, env, "gw2", true};
1994 {
1995 env.fund(XRP(5'000), gw2);
1996 env.close();
1997
1998 // Set lsfRequireAuth on gw2. That should not stop any automatic
1999 // MPT from being created.
2000 env(fset(gw2, asfRequireAuth));
2001 env.close();
2002
2003 // Use offers to automatically create MPT.
2004 MPT const oF5 = gw2["OF5"];
2005 env(offer(gw2, XRP(92), oF5(92)));
2006 env.close();
2007 BEAST_EXPECT(env.le(keylet::mptoken(oF5, alice)) == nullptr);
2008 env(offer(alice, oF5(92), XRP(92)));
2009 ++alice.owners;
2010 env.close();
2011
2012 // alice's owner count should increase due to the new MPT.
2013 gw2.verifyOwners(__LINE__);
2014 alice.verifyOwners(__LINE__);
2015 bob.verifyOwners(__LINE__);
2016
2017 // Use check cashing to automatically create MPT.
2018 MPT const cK5 = gw2["CK5"];
2019 uint256 const chkId{getCheckIndex(gw2, env.seq(gw2))};
2020 env(check::create(gw2, alice, cK5(92)));
2021 env.close();
2022 BEAST_EXPECT(env.le(keylet::mptoken(cK5, alice)) == nullptr);
2023 env(check::cash(alice, chkId, cK5(92)));
2024 verifyDeliveredAmount(env, cK5(92));
2025 ++alice.owners;
2026 env.close();
2027
2028 // alice's owner count should increase due to the new MPT.
2029 gw2.verifyOwners(__LINE__);
2030 alice.verifyOwners(__LINE__);
2031 bob.verifyOwners(__LINE__);
2032
2033 // cleanup
2034 gw2.cleanup(oF5, alice);
2035 gw2.cleanup(cK5, alice);
2036 }
2037
2038 // Fund new gw to test since gw2 has MPTokenIssuance already created.
2039 // Set RequireAuth flag.
2040 AccountOwns gw3{*this, env, "gw3", true, true};
2041 {
2042 env.fund(XRP(5'000), gw3);
2043 env.close();
2044 // Use offers to automatically create the trust line.
2045 MPT const oF5 = gw3["OF5"];
2046 std::uint32_t const gw3OfferSeq = {env.seq(gw3)};
2047 env(offer(gw3, XRP(92), oF5(92)));
2048 ++gw3.owners;
2049 env.close();
2050 BEAST_EXPECT(env.le(keylet::mptoken(oF5, alice)) == nullptr);
2051 env(offer(alice, oF5(92), XRP(92)), Ter(tecNO_AUTH));
2052 env.close();
2053
2054 // gw3 should still own the offer, but no one else's owner
2055 // count should have changed.
2056 gw3.verifyOwners(__LINE__);
2057 alice.verifyOwners(__LINE__);
2058 bob.verifyOwners(__LINE__);
2059
2060 // Since we don't need it anymore, remove gw3's offer.
2061 env(offerCancel(gw3, gw3OfferSeq));
2062 --gw3.owners;
2063 env.close();
2064 gw3.verifyOwners(__LINE__);
2065
2066 // Use check cashing to automatically create the trust line.
2067 MPT const cK5 = gw3["CK5"];
2068 uint256 const chkId{getCheckIndex(gw3, env.seq(gw3))};
2069 env(check::create(gw3, alice, cK5(92)));
2070 ++gw3.owners;
2071 env.close();
2072 BEAST_EXPECT(env.le(keylet::mptoken(cK5, alice)) == nullptr);
2073 env(check::cash(alice, chkId, cK5(92)), Ter(tecNO_AUTH));
2074 env.close();
2075
2076 // gw3 should still own the check, but no one else's owner
2077 // count should have changed.
2078 gw3.verifyOwners(__LINE__);
2079 alice.verifyOwners(__LINE__);
2080 bob.verifyOwners(__LINE__);
2081
2082 // Because gw3 has set lsfRequireAuth, neither trust line
2083 // is created.
2084 BEAST_EXPECT(env.le(keylet::mptoken(oF5, alice)) == nullptr);
2085 BEAST_EXPECT(env.le(keylet::mptoken(cK5, alice)) == nullptr);
2086
2087 // Since we don't need it anymore, remove gw3's check.
2088 env(check::cancel(gw3, chkId));
2089 --gw3.owners;
2090 env.close();
2091 gw3.verifyOwners(__LINE__);
2092 }
2093
2094 //------------ lsfRequireAuth, check written by non-issuer -------------
2095 {
2096 // gw2 lsfRequireAuth flag set should not affect
2097 // automatic MPT creation between non-issuers.
2098
2099 // Use offers to automatically create MPT.
2100 MPT const oF5 = gw2["OF5"];
2101 gw2.authorize(oF5, alice);
2102 gw2.pay(gw2, alice, oF5(91));
2103 env(offer(alice, XRP(91), oF5(91)));
2104 env.close();
2105 env(offer(bob, oF5(91), XRP(91)));
2106 ++bob.owners;
2107 env.close();
2108
2109 // bob's owner count should increase due to the new MPT.
2110 gw2.verifyOwners(__LINE__);
2111 alice.verifyOwners(__LINE__);
2112 bob.verifyOwners(__LINE__);
2113
2114 // Use check cashing to automatically create the trust line.
2115 MPT const cK5 = gw2["CK5"];
2116 gw2.authorize(cK5, alice);
2117 gw2.pay(gw2, alice, cK5(91));
2118 uint256 const chkId{getCheckIndex(alice, env.seq(alice))};
2119 env(check::create(alice, bob, cK5(91)));
2120 env.close();
2121 BEAST_EXPECT(env.le(keylet::mptoken(cK5, bob)) == nullptr);
2122 env(check::cash(bob, chkId, cK5(91)));
2123 ++bob.owners;
2124 env.close();
2125
2126 // bob's owner count should increase due to the new MPT.
2127 gw2.verifyOwners(__LINE__);
2128 alice.verifyOwners(__LINE__);
2129 bob.verifyOwners(__LINE__);
2130 }
2131
2132 //------------ lsfMPTRequireAuth, check written by non-issuer
2133 //-------------
2134 {
2135 // Since gw3 has the lsfMPTRequireAuth flag set, there should be
2136 // no automatic MPT creation between non-issuers.
2137
2138 // Use offers to automatically create the trust line.
2139 MPT const oF5 = gw3["OF5"];
2140 env(offer(alice, XRP(91), oF5(91)), Ter(tecUNFUNDED_OFFER));
2141 env.close();
2142 env(offer(bob, oF5(91), XRP(91)), Ter(tecNO_AUTH));
2143 BEAST_EXPECT(env.le(keylet::mptoken(oF5, bob)) == nullptr);
2144 env.close();
2145
2146 gw3.verifyOwners(__LINE__);
2147 alice.verifyOwners(__LINE__);
2148 bob.verifyOwners(__LINE__);
2149
2150 // Use check cashing to automatically create the trust line.
2151 MPT const cK5 = gw3["CK5"];
2152 uint256 const chkId{getCheckIndex(alice, env.seq(alice))};
2153 env(check::create(alice, bob, cK5(91)));
2154 env.close();
2155 BEAST_EXPECT(env.le(keylet::mptoken(cK5, bob)) == nullptr);
2156 env(check::cash(bob, chkId, cK5(91)), Ter(tecPATH_PARTIAL));
2157 env.close();
2158
2159 // Delete alice's check since it is no longer needed.
2160 env(check::cancel(alice, chkId));
2161 env.close();
2162
2163 // No one's owner count should have changed.
2164 gw3.verifyOwners(__LINE__);
2165 alice.verifyOwners(__LINE__);
2166 bob.verifyOwners(__LINE__);
2167
2168 // Because gw3 has set lsfRequireAuth, neither trust line
2169 // is created.
2170 BEAST_EXPECT(env.le(keylet::mptoken(oF5, bob)) == nullptr);
2171 BEAST_EXPECT(env.le(keylet::mptoken(cK5, bob)) == nullptr);
2172 }
2173 }
2174
2175 void
2177 {
2178 testCreateValid(features);
2180 testCreateInvalid(features);
2181 testCashMPT(features);
2182 testCashXferFee(features);
2183 testCashInvalid(features);
2184 testCancelValid(features);
2185 testWithTickets(features);
2186 }
2187
2188public:
2189 void
2190 run() override
2191 {
2192 using namespace test::jtx;
2193 auto const sa = testableAmendments();
2194 testWithFeats(sa);
2195
2196 testMPTCreation(sa);
2197 }
2198};
2199
2201
2202} // namespace xrpl
A testsuite class.
Definition suite.h:50
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:223
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Represents a JSON value.
Definition json_value.h:130
Value removeMember(char const *key)
Remove and return the named member.
bool isMember(char const *key) const
Return true if the object has a member named key.
void testCashXferFee(FeatureBitset features)
void testWithTickets(FeatureBitset features)
void run() override
Runs the suite.
void verifyDeliveredAmount(test::jtx::Env &env, STAmount const &amount)
void testCreateInvalid(FeatureBitset features)
void testCancelValid(FeatureBitset features)
static std::vector< SLE::const_pointer > checksOnAccount(test::jtx::Env &env, test::jtx::Account account)
void testWithFeats(FeatureBitset features)
void testMPTCreation(FeatureBitset features)
void testCashInvalid(FeatureBitset features)
void testCreateDisallowIncoming(FeatureBitset features)
void testCreateValid(FeatureBitset features)
void testCashMPT(FeatureBitset features)
A currency issued by an account.
Definition Issue.h:13
std::chrono::time_point< NetClock > time_point
Definition chrono.h:46
void setIssue(Asset const &asset)
Set the Issue for this amount.
Definition STAmount.cpp:407
void negate()
Definition STAmount.h:568
json::Value getJson(JsonOptions=JsonOptions::Values::None) const override
Definition STAmount.cpp:734
std::shared_ptr< STLedgerEntry const > const & const_ref
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
A transaction testing environment.
Definition Env.h:143
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
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:864
std::shared_ptr< STTx const > tx() const
Return the tx data for the last JTx.
Definition Env.cpp:533
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
T emplace(T... args)
T end(T... args)
T endl(T... args)
T find(T... args)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:533
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
std::unordered_map< Key, Value, Hash, Pred, Allocator > hash_map
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
@ temBAD_CURRENCY
Definition TER.h:76
@ temBAD_EXPIRATION
Definition TER.h:77
@ temBAD_FEE
Definition TER.h:78
@ temINVALID_FLAG
Definition TER.h:97
@ temMALFORMED
Definition TER.h:73
@ temBAD_AMOUNT
Definition TER.h:75
@ temREDUNDANT
Definition TER.h:98
TERSubset< CanCvtToTER > TER
Definition TER.h:634
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(SLE::const_ref)> const &f)
Iterate all items in the given directory.
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, std::uint8_t depth=0)
Check if the account lacks required authorization for MPT.
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecLOCKED
Definition TER.h:356
@ tecPATH_PARTIAL
Definition TER.h:280
@ tecNO_ENTRY
Definition TER.h:304
@ tecPATH_DRY
Definition TER.h:292
@ tecNO_AUTH
Definition TER.h:298
@ tecUNFUNDED_OFFER
Definition TER.h:282
@ tecEXPIRED
Definition TER.h:312
@ tecINSUFFICIENT_RESERVE
Definition TER.h:305
@ tecNO_PERMISSION
Definition TER.h:303
@ tecDST_TAG_NEEDED
Definition TER.h:307
@ tecNO_DST
Definition TER.h:288
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
BaseUInt< 256 > uint256
Definition base_uint.h:562
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Definition Indexes.cpp:172
@ tesSUCCESS
Definition TER.h:240
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49