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