rippled
Loading...
Searching...
No Matches
Escrow_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx.h>
21
22#include <xrpld/app/tx/applySteps.h>
23
24#include <xrpl/ledger/Dir.h>
25#include <xrpl/protocol/Feature.h>
26#include <xrpl/protocol/Indexes.h>
27#include <xrpl/protocol/TxFlags.h>
28#include <xrpl/protocol/jss.h>
29
30#include <algorithm>
31#include <iterator>
32
33namespace ripple {
34namespace test {
35
37{
38 void
40 {
41 testcase("Enablement");
42
43 using namespace jtx;
44 using namespace std::chrono;
45
46 Env env(*this, features);
47 auto const baseFee = env.current()->fees().base;
48 env.fund(XRP(5000), "alice", "bob");
49 env(escrow::create("alice", "bob", XRP(1000)),
50 escrow::finish_time(env.now() + 1s));
51 env.close();
52
53 auto const seq1 = env.seq("alice");
54
55 env(escrow::create("alice", "bob", XRP(1000)),
57 escrow::finish_time(env.now() + 1s),
58 fee(baseFee * 150));
59 env.close();
60 env(escrow::finish("bob", "alice", seq1),
63 fee(baseFee * 150));
64
65 auto const seq2 = env.seq("alice");
66
67 env(escrow::create("alice", "bob", XRP(1000)),
69 escrow::finish_time(env.now() + 1s),
70 escrow::cancel_time(env.now() + 2s),
71 fee(baseFee * 150));
72 env.close();
73 env(escrow::cancel("bob", "alice", seq2), fee(baseFee * 150));
74 }
75
76 void
78 {
79 using namespace jtx;
80 using namespace std::chrono;
81
82 {
83 testcase("Timing: Finish Only");
84 Env env(*this, features);
85 auto const baseFee = env.current()->fees().base;
86 env.fund(XRP(5000), "alice", "bob");
87 env.close();
88
89 // We create an escrow that can be finished in the future
90 auto const ts = env.now() + 97s;
91
92 auto const seq = env.seq("alice");
93 env(escrow::create("alice", "bob", XRP(1000)),
95
96 // Advance the ledger, verifying that the finish won't complete
97 // prematurely.
98 for (; env.now() < ts; env.close())
99 env(escrow::finish("bob", "alice", seq),
100 fee(baseFee * 150),
102
103 env(escrow::finish("bob", "alice", seq), fee(baseFee * 150));
104 }
105
106 {
107 testcase("Timing: Cancel Only");
108 Env env(*this, features);
109 auto const baseFee = env.current()->fees().base;
110 env.fund(XRP(5000), "alice", "bob");
111 env.close();
112
113 // We create an escrow that can be cancelled in the future
114 auto const ts = env.now() + 117s;
115
116 auto const seq = env.seq("alice");
117 env(escrow::create("alice", "bob", XRP(1000)),
120
121 // Advance the ledger, verifying that the cancel won't complete
122 // prematurely.
123 for (; env.now() < ts; env.close())
124 env(escrow::cancel("bob", "alice", seq),
125 fee(baseFee * 150),
127
128 // Verify that a finish won't work anymore.
129 env(escrow::finish("bob", "alice", seq),
132 fee(baseFee * 150),
134
135 // Verify that the cancel will succeed
136 env(escrow::cancel("bob", "alice", seq), fee(baseFee * 150));
137 }
138
139 {
140 testcase("Timing: Finish and Cancel -> Finish");
141 Env env(*this, features);
142 auto const baseFee = env.current()->fees().base;
143 env.fund(XRP(5000), "alice", "bob");
144 env.close();
145
146 // We create an escrow that can be cancelled in the future
147 auto const fts = env.now() + 117s;
148 auto const cts = env.now() + 192s;
149
150 auto const seq = env.seq("alice");
151 env(escrow::create("alice", "bob", XRP(1000)),
154
155 // Advance the ledger, verifying that the finish and cancel won't
156 // complete prematurely.
157 for (; env.now() < fts; env.close())
158 {
159 env(escrow::finish("bob", "alice", seq),
160 fee(baseFee * 150),
162 env(escrow::cancel("bob", "alice", seq),
163 fee(baseFee * 150),
165 }
166
167 // Verify that a cancel still won't work
168 env(escrow::cancel("bob", "alice", seq),
169 fee(baseFee * 150),
171
172 // And verify that a finish will
173 env(escrow::finish("bob", "alice", seq), fee(baseFee * 150));
174 }
175
176 {
177 testcase("Timing: Finish and Cancel -> Cancel");
178 Env env(*this, features);
179 auto const baseFee = env.current()->fees().base;
180 env.fund(XRP(5000), "alice", "bob");
181 env.close();
182
183 // We create an escrow that can be cancelled in the future
184 auto const fts = env.now() + 109s;
185 auto const cts = env.now() + 184s;
186
187 auto const seq = env.seq("alice");
188 env(escrow::create("alice", "bob", XRP(1000)),
191
192 // Advance the ledger, verifying that the finish and cancel won't
193 // complete prematurely.
194 for (; env.now() < fts; env.close())
195 {
196 env(escrow::finish("bob", "alice", seq),
197 fee(baseFee * 150),
199 env(escrow::cancel("bob", "alice", seq),
200 fee(baseFee * 150),
202 }
203
204 // Continue advancing, verifying that the cancel won't complete
205 // prematurely. At this point a finish would succeed.
206 for (; env.now() < cts; env.close())
207 env(escrow::cancel("bob", "alice", seq),
208 fee(baseFee * 150),
210
211 // Verify that finish will no longer work, since we are past the
212 // cancel activation time.
213 env(escrow::finish("bob", "alice", seq),
214 fee(baseFee * 150),
216
217 // And verify that a cancel will succeed.
218 env(escrow::cancel("bob", "alice", seq), fee(baseFee * 150));
219 }
220 }
221
222 void
224 {
225 testcase("Tags");
226
227 using namespace jtx;
228 using namespace std::chrono;
229
230 Env env(*this, features);
231
232 auto const alice = Account("alice");
233 auto const bob = Account("bob");
234
235 env.fund(XRP(5000), alice, bob);
236
237 // Check to make sure that we correctly detect if tags are really
238 // required:
239 env(fset(bob, asfRequireDest));
240 env(escrow::create(alice, bob, XRP(1000)),
241 escrow::finish_time(env.now() + 1s),
243
244 // set source and dest tags
245 auto const seq = env.seq(alice);
246
247 env(escrow::create(alice, bob, XRP(1000)),
248 escrow::finish_time(env.now() + 1s),
249 stag(1),
250 dtag(2));
251
252 auto const sle = env.le(keylet::escrow(alice.id(), seq));
253 BEAST_EXPECT(sle);
254 BEAST_EXPECT((*sle)[sfSourceTag] == 1);
255 BEAST_EXPECT((*sle)[sfDestinationTag] == 2);
256 if (features[fixIncludeKeyletFields])
257 {
258 BEAST_EXPECT((*sle)[sfSequence] == seq);
259 }
260 else
261 {
262 BEAST_EXPECT(!sle->isFieldPresent(sfSequence));
263 }
264 }
265
266 void
268 {
269 testcase("Disallow XRP");
270
271 using namespace jtx;
272 using namespace std::chrono;
273
274 {
275 // Respect the "asfDisallowXRP" account flag:
276 Env env(*this, features - featureDepositAuth);
277
278 env.fund(XRP(5000), "bob", "george");
279 env(fset("george", asfDisallowXRP));
280 env(escrow::create("bob", "george", XRP(10)),
281 escrow::finish_time(env.now() + 1s),
283 }
284 {
285 // Ignore the "asfDisallowXRP" account flag, which we should
286 // have been doing before.
287 Env env(*this, features);
288
289 env.fund(XRP(5000), "bob", "george");
290 env(fset("george", asfDisallowXRP));
291 env(escrow::create("bob", "george", XRP(10)),
292 escrow::finish_time(env.now() + 1s));
293 }
294 }
295
296 void
298 {
299 using namespace jtx;
300 using namespace std::chrono;
301
302 testcase("RequiresConditionOrFinishAfter");
303
304 Env env(*this, features);
305 auto const baseFee = env.current()->fees().base;
306 env.fund(XRP(5000), "alice", "bob", "carol");
307 env.close();
308
309 // Creating an escrow with only a cancel time is not allowed:
310 env(escrow::create("alice", "bob", XRP(100)),
311 escrow::cancel_time(env.now() + 90s),
312 fee(baseFee * 150),
314
315 // Creating an escrow with only a cancel time and a condition is
316 // allowed:
317 auto const seq = env.seq("alice");
318 env(escrow::create("alice", "bob", XRP(100)),
319 escrow::cancel_time(env.now() + 90s),
321 fee(baseFee * 150));
322 env.close();
323 env(escrow::finish("carol", "alice", seq),
326 fee(baseFee * 150));
327 BEAST_EXPECT(env.balance("bob") == XRP(5100));
328
329 // Creating an escrow with only a cancel time and a finish time is
330 // allowed:
331 auto const seqFt = env.seq("alice");
332 env(escrow::create("alice", "bob", XRP(100)),
333 escrow::finish_time(env.now()), // Set finish time to now so that
334 // we can call finish immediately.
335 escrow::cancel_time(env.now() + 50s),
336 fee(baseFee * 150));
337 env.close();
338 env(escrow::finish("carol", "alice", seqFt), fee(150 * baseFee));
339 BEAST_EXPECT(
340 env.balance("bob") ==
341 XRP(5200)); // 5100 (from last transaction) + 100
342 }
343
344 void
346 {
347 testcase("Failure Cases");
348
349 using namespace jtx;
350 using namespace std::chrono;
351
352 Env env(*this, features);
353 auto const baseFee = env.current()->fees().base;
354 env.fund(XRP(5000), "alice", "bob", "gw");
355 env.close();
356
357 // temINVALID_FLAG
358 env(escrow::create("alice", "bob", XRP(1000)),
359 escrow::finish_time(env.now() + 5s),
362
363 // Finish time is in the past
364 env(escrow::create("alice", "bob", XRP(1000)),
365 escrow::finish_time(env.now() - 5s),
367
368 // Cancel time is in the past
369 env(escrow::create("alice", "bob", XRP(1000)),
371 escrow::cancel_time(env.now() - 5s),
373
374 // no destination account
375 env(escrow::create("alice", "carol", XRP(1000)),
376 escrow::finish_time(env.now() + 1s),
377 ter(tecNO_DST));
378
379 env.fund(XRP(5000), "carol");
380
381 // Using non-XRP:
382 bool const withTokenEscrow =
383 env.current()->rules().enabled(featureTokenEscrow);
384 {
385 // tecNO_PERMISSION: token escrow is enabled but the issuer did not
386 // set the asfAllowTrustLineLocking flag
387 auto const txResult =
388 withTokenEscrow ? ter(tecNO_PERMISSION) : ter(temBAD_AMOUNT);
389 env(escrow::create("alice", "carol", Account("alice")["USD"](500)),
390 escrow::finish_time(env.now() + 5s),
391 txResult);
392 }
393
394 // Sending zero or no XRP:
395 env(escrow::create("alice", "carol", XRP(0)),
396 escrow::finish_time(env.now() + 1s),
398 env(escrow::create("alice", "carol", XRP(-1000)),
399 escrow::finish_time(env.now() + 1s),
401
402 // Fail if neither CancelAfter nor FinishAfter are specified:
403 env(escrow::create("alice", "carol", XRP(1)), ter(temBAD_EXPIRATION));
404
405 // Fail if neither a FinishTime nor a condition are attached:
406 env(escrow::create("alice", "carol", XRP(1)),
407 escrow::cancel_time(env.now() + 1s),
409
410 // Fail if FinishAfter has already passed:
411 env(escrow::create("alice", "carol", XRP(1)),
412 escrow::finish_time(env.now() - 1s),
414
415 // If both CancelAfter and FinishAfter are set, then CancelAfter must
416 // be strictly later than FinishAfter.
417 env(escrow::create("alice", "carol", XRP(1)),
419 escrow::finish_time(env.now() + 10s),
420 escrow::cancel_time(env.now() + 10s),
422
423 env(escrow::create("alice", "carol", XRP(1)),
425 escrow::finish_time(env.now() + 10s),
426 escrow::cancel_time(env.now() + 5s),
428
429 // Carol now requires the use of a destination tag
430 env(fset("carol", asfRequireDest));
431
432 // missing destination tag
433 env(escrow::create("alice", "carol", XRP(1)),
435 escrow::cancel_time(env.now() + 1s),
437
438 // Success!
439 env(escrow::create("alice", "carol", XRP(1)),
441 escrow::cancel_time(env.now() + 1s),
442 dtag(1));
443
444 { // Fail if the sender wants to send more than he has:
445 auto const accountReserve = drops(env.current()->fees().reserve);
446 auto const accountIncrement =
447 drops(env.current()->fees().increment);
448
449 env.fund(accountReserve + accountIncrement + XRP(50), "daniel");
450 env(escrow::create("daniel", "bob", XRP(51)),
451 escrow::finish_time(env.now() + 1s),
453
454 env.fund(accountReserve + accountIncrement + XRP(50), "evan");
455 env(escrow::create("evan", "bob", XRP(50)),
456 escrow::finish_time(env.now() + 1s),
458
459 env.fund(accountReserve, "frank");
460 env(escrow::create("frank", "bob", XRP(1)),
461 escrow::finish_time(env.now() + 1s),
463 }
464
465 { // Specify incorrect sequence number
466 env.fund(XRP(5000), "hannah");
467 auto const seq = env.seq("hannah");
468 env(escrow::create("hannah", "hannah", XRP(10)),
469 escrow::finish_time(env.now() + 1s),
470 fee(150 * baseFee));
471 env.close();
472 env(escrow::finish("hannah", "hannah", seq + 7),
473 fee(150 * baseFee),
475 }
476
477 { // Try to specify a condition for a non-conditional payment
478 env.fund(XRP(5000), "ivan");
479 auto const seq = env.seq("ivan");
480
481 env(escrow::create("ivan", "ivan", XRP(10)),
482 escrow::finish_time(env.now() + 1s));
483 env.close();
484 env(escrow::finish("ivan", "ivan", seq),
487 fee(150 * baseFee),
489 }
490 }
491
492 void
494 {
495 testcase("Lockup");
496
497 using namespace jtx;
498 using namespace std::chrono;
499
500 {
501 // Unconditional
502 Env env(*this, features);
503 auto const baseFee = env.current()->fees().base;
504 env.fund(XRP(5000), "alice", "bob");
505 auto const seq = env.seq("alice");
506 env(escrow::create("alice", "alice", XRP(1000)),
507 escrow::finish_time(env.now() + 5s));
508 env.require(balance("alice", XRP(4000) - drops(baseFee)));
509
510 // Not enough time has elapsed for a finish and canceling isn't
511 // possible.
512 env(escrow::cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
513 env(escrow::finish("bob", "alice", seq), ter(tecNO_PERMISSION));
514 env.close();
515
516 // Cancel continues to not be possible
517 env(escrow::cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
518
519 // Finish should succeed. Verify funds.
520 env(escrow::finish("bob", "alice", seq));
521 env.require(balance("alice", XRP(5000) - drops(baseFee)));
522 }
523 {
524 // Unconditionally pay from Alice to Bob. Zelda (neither source nor
525 // destination) signs all cancels and finishes. This shows that
526 // Escrow will make a payment to Bob with no intervention from Bob.
527 Env env(*this, features);
528 auto const baseFee = env.current()->fees().base;
529 env.fund(XRP(5000), "alice", "bob", "zelda");
530 auto const seq = env.seq("alice");
531 env(escrow::create("alice", "bob", XRP(1000)),
532 escrow::finish_time(env.now() + 5s));
533 env.require(balance("alice", XRP(4000) - drops(baseFee)));
534
535 // Not enough time has elapsed for a finish and canceling isn't
536 // possible.
537 env(escrow::cancel("zelda", "alice", seq), ter(tecNO_PERMISSION));
538 env(escrow::finish("zelda", "alice", seq), ter(tecNO_PERMISSION));
539 env.close();
540
541 // Cancel continues to not be possible
542 env(escrow::cancel("zelda", "alice", seq), ter(tecNO_PERMISSION));
543
544 // Finish should succeed. Verify funds.
545 env(escrow::finish("zelda", "alice", seq));
546 env.close();
547
548 env.require(balance("alice", XRP(4000) - drops(baseFee)));
549 env.require(balance("bob", XRP(6000)));
550 env.require(balance("zelda", XRP(5000) - drops(4 * baseFee)));
551 }
552 {
553 // Bob sets DepositAuth so only Bob can finish the escrow.
554 Env env(*this, features);
555 auto const baseFee = env.current()->fees().base;
556
557 env.fund(XRP(5000), "alice", "bob", "zelda");
558 env(fset("bob", asfDepositAuth));
559 env.close();
560
561 auto const seq = env.seq("alice");
562 env(escrow::create("alice", "bob", XRP(1000)),
563 escrow::finish_time(env.now() + 5s));
564 env.require(balance("alice", XRP(4000) - drops(baseFee)));
565
566 // Not enough time has elapsed for a finish and canceling isn't
567 // possible.
568 env(escrow::cancel("zelda", "alice", seq), ter(tecNO_PERMISSION));
569 env(escrow::cancel("alice", "alice", seq), ter(tecNO_PERMISSION));
570 env(escrow::cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
571 env(escrow::finish("zelda", "alice", seq), ter(tecNO_PERMISSION));
572 env(escrow::finish("alice", "alice", seq), ter(tecNO_PERMISSION));
573 env(escrow::finish("bob", "alice", seq), ter(tecNO_PERMISSION));
574 env.close();
575
576 // Cancel continues to not be possible. Finish will only succeed for
577 // Bob, because of DepositAuth.
578 env(escrow::cancel("zelda", "alice", seq), ter(tecNO_PERMISSION));
579 env(escrow::cancel("alice", "alice", seq), ter(tecNO_PERMISSION));
580 env(escrow::cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
581 env(escrow::finish("zelda", "alice", seq), ter(tecNO_PERMISSION));
582 env(escrow::finish("alice", "alice", seq), ter(tecNO_PERMISSION));
583 env(escrow::finish("bob", "alice", seq));
584 env.close();
585
586 env.require(balance("alice", XRP(4000) - (baseFee * 5)));
587 env.require(balance("bob", XRP(6000) - (baseFee * 5)));
588 env.require(balance("zelda", XRP(5000) - (baseFee * 4)));
589 }
590 {
591 // Bob sets DepositAuth but preauthorizes Zelda, so Zelda can
592 // finish the escrow.
593 Env env(*this, features);
594 auto const baseFee = env.current()->fees().base;
595
596 env.fund(XRP(5000), "alice", "bob", "zelda");
597 env(fset("bob", asfDepositAuth));
598 env.close();
599 env(deposit::auth("bob", "zelda"));
600 env.close();
601
602 auto const seq = env.seq("alice");
603 env(escrow::create("alice", "bob", XRP(1000)),
604 escrow::finish_time(env.now() + 5s));
605 env.require(balance("alice", XRP(4000) - drops(baseFee)));
606 env.close();
607
608 // DepositPreauth allows Finish to succeed for either Zelda or
609 // Bob. But Finish won't succeed for Alice since she is not
610 // preauthorized.
611 env(escrow::finish("alice", "alice", seq), ter(tecNO_PERMISSION));
612 env(escrow::finish("zelda", "alice", seq));
613 env.close();
614
615 env.require(balance("alice", XRP(4000) - (baseFee * 2)));
616 env.require(balance("bob", XRP(6000) - (baseFee * 2)));
617 env.require(balance("zelda", XRP(5000) - (baseFee * 1)));
618 }
619 {
620 // Conditional
621 Env env(*this, features);
622 auto const baseFee = env.current()->fees().base;
623 env.fund(XRP(5000), "alice", "bob");
624 auto const seq = env.seq("alice");
625 env(escrow::create("alice", "alice", XRP(1000)),
627 escrow::finish_time(env.now() + 5s));
628 env.require(balance("alice", XRP(4000) - drops(baseFee)));
629
630 // Not enough time has elapsed for a finish and canceling isn't
631 // possible.
632 env(escrow::cancel("alice", "alice", seq), ter(tecNO_PERMISSION));
633 env(escrow::cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
634 env(escrow::finish("alice", "alice", seq), ter(tecNO_PERMISSION));
635 env(escrow::finish("alice", "alice", seq),
638 fee(150 * baseFee),
640 env(escrow::finish("bob", "alice", seq), ter(tecNO_PERMISSION));
641 env(escrow::finish("bob", "alice", seq),
644 fee(150 * baseFee),
646 env.close();
647
648 // Cancel continues to not be possible. Finish is possible but
649 // requires the fulfillment associated with the escrow.
650 env(escrow::cancel("alice", "alice", seq), ter(tecNO_PERMISSION));
651 env(escrow::cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
652 env(escrow::finish("bob", "alice", seq),
654 env(escrow::finish("alice", "alice", seq),
656 env.close();
657
658 env(escrow::finish("bob", "alice", seq),
661 fee(150 * baseFee));
662 }
663 {
664 // Self-escrowed conditional with DepositAuth.
665 Env env(*this, features);
666 auto const baseFee = env.current()->fees().base;
667
668 env.fund(XRP(5000), "alice", "bob");
669 auto const seq = env.seq("alice");
670 env(escrow::create("alice", "alice", XRP(1000)),
672 escrow::finish_time(env.now() + 5s));
673 env.require(balance("alice", XRP(4000) - drops(baseFee)));
674 env.close();
675
676 // Finish is now possible but requires the cryptocondition.
677 env(escrow::finish("bob", "alice", seq),
679 env(escrow::finish("alice", "alice", seq),
681
682 // Enable deposit authorization. After this only Alice can finish
683 // the escrow.
684 env(fset("alice", asfDepositAuth));
685 env.close();
686
687 env(escrow::finish("alice", "alice", seq),
690 fee(150 * baseFee),
692 env(escrow::finish("bob", "alice", seq),
695 fee(150 * baseFee),
697 env(escrow::finish("alice", "alice", seq),
700 fee(150 * baseFee));
701 }
702 {
703 // Self-escrowed conditional with DepositAuth and DepositPreauth.
704 Env env(*this, features);
705 auto const baseFee = env.current()->fees().base;
706
707 env.fund(XRP(5000), "alice", "bob", "zelda");
708 auto const seq = env.seq("alice");
709 env(escrow::create("alice", "alice", XRP(1000)),
711 escrow::finish_time(env.now() + 5s));
712 env.require(balance("alice", XRP(4000) - drops(baseFee)));
713 env.close();
714
715 // Alice preauthorizes Zelda for deposit, even though Alice has not
716 // set the lsfDepositAuth flag (yet).
717 env(deposit::auth("alice", "zelda"));
718 env.close();
719
720 // Finish is now possible but requires the cryptocondition.
721 env(escrow::finish("alice", "alice", seq),
723 env(escrow::finish("bob", "alice", seq),
725 env(escrow::finish("zelda", "alice", seq),
727
728 // Alice enables deposit authorization. After this only Alice or
729 // Zelda (because Zelda is preauthorized) can finish the escrow.
730 env(fset("alice", asfDepositAuth));
731 env.close();
732
733 env(escrow::finish("alice", "alice", seq),
736 fee(150 * baseFee),
738 env(escrow::finish("bob", "alice", seq),
741 fee(150 * baseFee),
743 env(escrow::finish("zelda", "alice", seq),
746 fee(150 * baseFee));
747 }
748 }
749
750 void
752 {
753 testcase("Escrow with CryptoConditions");
754
755 using namespace jtx;
756 using namespace std::chrono;
757
758 { // Test cryptoconditions
759 Env env(*this, features);
760 auto const baseFee = env.current()->fees().base;
761 env.fund(XRP(5000), "alice", "bob", "carol");
762 auto const seq = env.seq("alice");
763 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
764 env(escrow::create("alice", "carol", XRP(1000)),
766 escrow::cancel_time(env.now() + 1s));
767 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
768 env.require(balance("alice", XRP(4000) - drops(baseFee)));
769 env.require(balance("carol", XRP(5000)));
770 env(escrow::cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
771 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
772
773 // Attempt to finish without a fulfillment
774 env(escrow::finish("bob", "alice", seq),
776 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
777
778 // Attempt to finish with a condition instead of a fulfillment
779 env(escrow::finish("bob", "alice", seq),
782 fee(150 * baseFee),
784 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
785 env(escrow::finish("bob", "alice", seq),
788 fee(150 * baseFee),
790 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
791 env(escrow::finish("bob", "alice", seq),
794 fee(150 * baseFee),
796 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
797
798 // Attempt to finish with an incorrect condition and various
799 // combinations of correct and incorrect fulfillments.
800 env(escrow::finish("bob", "alice", seq),
803 fee(150 * baseFee),
805 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
806 env(escrow::finish("bob", "alice", seq),
809 fee(150 * baseFee),
811 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
812 env(escrow::finish("bob", "alice", seq),
815 fee(150 * baseFee),
817 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
818
819 // Attempt to finish with the correct condition & fulfillment
820 env(escrow::finish("bob", "alice", seq),
823 fee(150 * baseFee));
824
825 // SLE removed on finish
826 BEAST_EXPECT(!env.le(keylet::escrow(Account("alice").id(), seq)));
827 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
828 env.require(balance("carol", XRP(6000)));
829 env(escrow::cancel("bob", "alice", seq), ter(tecNO_TARGET));
830 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
831 env(escrow::cancel("bob", "carol", 1), ter(tecNO_TARGET));
832 }
833 { // Test cancel when condition is present
834 Env env(*this, features);
835 auto const baseFee = env.current()->fees().base;
836 env.fund(XRP(5000), "alice", "bob", "carol");
837 auto const seq = env.seq("alice");
838 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
839 env(escrow::create("alice", "carol", XRP(1000)),
841 escrow::cancel_time(env.now() + 1s));
842 env.close();
843 env.require(balance("alice", XRP(4000) - drops(baseFee)));
844 // balance restored on cancel
845 env(escrow::cancel("bob", "alice", seq));
846 env.require(balance("alice", XRP(5000) - drops(baseFee)));
847 // SLE removed on cancel
848 BEAST_EXPECT(!env.le(keylet::escrow(Account("alice").id(), seq)));
849 }
850 {
851 Env env(*this, features);
852 auto const baseFee = env.current()->fees().base;
853 env.fund(XRP(5000), "alice", "bob", "carol");
854 env.close();
855 auto const seq = env.seq("alice");
856 env(escrow::create("alice", "carol", XRP(1000)),
858 escrow::cancel_time(env.now() + 1s));
859 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
860 // cancel fails before expiration
861 env(escrow::cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
862 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
863 env.close();
864 // finish fails after expiration
865 env(escrow::finish("bob", "alice", seq),
868 fee(150 * baseFee),
870 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
871 env.require(balance("carol", XRP(5000)));
872 }
873 { // Test long & short conditions during creation
874 Env env(*this, features);
875 env.fund(XRP(5000), "alice", "bob", "carol");
876
878 v.resize(escrow::cb1.size() + 2, 0x78);
879 std::memcpy(v.data() + 1, escrow::cb1.data(), escrow::cb1.size());
880
881 auto const p = v.data();
882 auto const s = v.size();
883
884 auto const ts = env.now() + 1s;
885
886 // All these are expected to fail, because the
887 // condition we pass in is malformed in some way
888 env(escrow::create("alice", "carol", XRP(1000)),
892 env(escrow::create("alice", "carol", XRP(1000)),
893 escrow::condition(Slice{p, s - 1}),
896 env(escrow::create("alice", "carol", XRP(1000)),
897 escrow::condition(Slice{p, s - 2}),
900 env(escrow::create("alice", "carol", XRP(1000)),
901 escrow::condition(Slice{p + 1, s - 1}),
904 env(escrow::create("alice", "carol", XRP(1000)),
905 escrow::condition(Slice{p + 1, s - 3}),
908 env(escrow::create("alice", "carol", XRP(1000)),
909 escrow::condition(Slice{p + 2, s - 2}),
912 env(escrow::create("alice", "carol", XRP(1000)),
913 escrow::condition(Slice{p + 2, s - 3}),
916
917 auto const seq = env.seq("alice");
918 auto const baseFee = env.current()->fees().base;
919 env(escrow::create("alice", "carol", XRP(1000)),
920 escrow::condition(Slice{p + 1, s - 2}),
922 fee(10 * baseFee));
923 env(escrow::finish("bob", "alice", seq),
926 fee(150 * baseFee));
927 env.require(balance("alice", XRP(4000) - drops(10 * baseFee)));
928 env.require(balance("bob", XRP(5000) - drops(150 * baseFee)));
929 env.require(balance("carol", XRP(6000)));
930 }
931 { // Test long and short conditions & fulfillments during finish
932 Env env(*this, features);
933 env.fund(XRP(5000), "alice", "bob", "carol");
934
936 cv.resize(escrow::cb2.size() + 2, 0x78);
937 std::memcpy(cv.data() + 1, escrow::cb2.data(), escrow::cb2.size());
938
939 auto const cp = cv.data();
940 auto const cs = cv.size();
941
943 fv.resize(escrow::fb2.size() + 2, 0x13);
944 std::memcpy(fv.data() + 1, escrow::fb2.data(), escrow::fb2.size());
945
946 auto const fp = fv.data();
947 auto const fs = fv.size();
948
949 auto const ts = env.now() + 1s;
950
951 // All these are expected to fail, because the
952 // condition we pass in is malformed in some way
953 env(escrow::create("alice", "carol", XRP(1000)),
954 escrow::condition(Slice{cp, cs}),
957 env(escrow::create("alice", "carol", XRP(1000)),
958 escrow::condition(Slice{cp, cs - 1}),
961 env(escrow::create("alice", "carol", XRP(1000)),
962 escrow::condition(Slice{cp, cs - 2}),
965 env(escrow::create("alice", "carol", XRP(1000)),
966 escrow::condition(Slice{cp + 1, cs - 1}),
969 env(escrow::create("alice", "carol", XRP(1000)),
970 escrow::condition(Slice{cp + 1, cs - 3}),
973 env(escrow::create("alice", "carol", XRP(1000)),
974 escrow::condition(Slice{cp + 2, cs - 2}),
977 env(escrow::create("alice", "carol", XRP(1000)),
978 escrow::condition(Slice{cp + 2, cs - 3}),
981
982 auto const seq = env.seq("alice");
983 auto const baseFee = env.current()->fees().base;
984 env(escrow::create("alice", "carol", XRP(1000)),
985 escrow::condition(Slice{cp + 1, cs - 2}),
987 fee(10 * baseFee));
988
989 // Now, try to fulfill using the same sequence of
990 // malformed conditions.
991 env(escrow::finish("bob", "alice", seq),
992 escrow::condition(Slice{cp, cs}),
993 escrow::fulfillment(Slice{fp, fs}),
994 fee(150 * baseFee),
996 env(escrow::finish("bob", "alice", seq),
997 escrow::condition(Slice{cp, cs - 1}),
998 escrow::fulfillment(Slice{fp, fs}),
999 fee(150 * baseFee),
1001 env(escrow::finish("bob", "alice", seq),
1002 escrow::condition(Slice{cp, cs - 2}),
1003 escrow::fulfillment(Slice{fp, fs}),
1004 fee(150 * baseFee),
1006 env(escrow::finish("bob", "alice", seq),
1007 escrow::condition(Slice{cp + 1, cs - 1}),
1008 escrow::fulfillment(Slice{fp, fs}),
1009 fee(150 * baseFee),
1011 env(escrow::finish("bob", "alice", seq),
1012 escrow::condition(Slice{cp + 1, cs - 3}),
1013 escrow::fulfillment(Slice{fp, fs}),
1014 fee(150 * baseFee),
1016 env(escrow::finish("bob", "alice", seq),
1017 escrow::condition(Slice{cp + 2, cs - 2}),
1018 escrow::fulfillment(Slice{fp, fs}),
1019 fee(150 * baseFee),
1021 env(escrow::finish("bob", "alice", seq),
1022 escrow::condition(Slice{cp + 2, cs - 3}),
1023 escrow::fulfillment(Slice{fp, fs}),
1024 fee(150 * baseFee),
1026
1027 // Now, using the correct condition, try malformed fulfillments:
1028 env(escrow::finish("bob", "alice", seq),
1029 escrow::condition(Slice{cp + 1, cs - 2}),
1030 escrow::fulfillment(Slice{fp, fs}),
1031 fee(150 * baseFee),
1033 env(escrow::finish("bob", "alice", seq),
1034 escrow::condition(Slice{cp + 1, cs - 2}),
1035 escrow::fulfillment(Slice{fp, fs - 1}),
1036 fee(150 * baseFee),
1038 env(escrow::finish("bob", "alice", seq),
1039 escrow::condition(Slice{cp + 1, cs - 2}),
1040 escrow::fulfillment(Slice{fp, fs - 2}),
1041 fee(150 * baseFee),
1043 env(escrow::finish("bob", "alice", seq),
1044 escrow::condition(Slice{cp + 1, cs - 2}),
1045 escrow::fulfillment(Slice{fp + 1, fs - 1}),
1046 fee(150 * baseFee),
1048 env(escrow::finish("bob", "alice", seq),
1049 escrow::condition(Slice{cp + 1, cs - 2}),
1050 escrow::fulfillment(Slice{fp + 1, fs - 3}),
1051 fee(150 * baseFee),
1053 env(escrow::finish("bob", "alice", seq),
1054 escrow::condition(Slice{cp + 1, cs - 2}),
1055 escrow::fulfillment(Slice{fp + 1, fs - 3}),
1056 fee(150 * baseFee),
1058 env(escrow::finish("bob", "alice", seq),
1059 escrow::condition(Slice{cp + 1, cs - 2}),
1060 escrow::fulfillment(Slice{fp + 2, fs - 2}),
1061 fee(150 * baseFee),
1063 env(escrow::finish("bob", "alice", seq),
1064 escrow::condition(Slice{cp + 1, cs - 2}),
1065 escrow::fulfillment(Slice{fp + 2, fs - 3}),
1066 fee(150 * baseFee),
1068
1069 // Now try for the right one
1070 env(escrow::finish("bob", "alice", seq),
1073 fee(150 * baseFee));
1074 env.require(balance("alice", XRP(4000) - drops(10 * baseFee)));
1075 env.require(balance("carol", XRP(6000)));
1076 }
1077 { // Test empty condition during creation and
1078 // empty condition & fulfillment during finish
1079 Env env(*this, features);
1080 env.fund(XRP(5000), "alice", "bob", "carol");
1081
1082 env(escrow::create("alice", "carol", XRP(1000)),
1084 escrow::cancel_time(env.now() + 1s),
1085 ter(temMALFORMED));
1086
1087 auto const seq = env.seq("alice");
1088 auto const baseFee = env.current()->fees().base;
1089 env(escrow::create("alice", "carol", XRP(1000)),
1091 escrow::cancel_time(env.now() + 1s));
1092
1093 env(escrow::finish("bob", "alice", seq),
1096 fee(150 * baseFee),
1098 env(escrow::finish("bob", "alice", seq),
1101 fee(150 * baseFee),
1103 env(escrow::finish("bob", "alice", seq),
1106 fee(150 * baseFee),
1108
1109 // Assemble finish that is missing the Condition or the Fulfillment
1110 // since either both must be present, or neither can:
1111 env(escrow::finish("bob", "alice", seq),
1113 ter(temMALFORMED));
1114 env(escrow::finish("bob", "alice", seq),
1116 ter(temMALFORMED));
1117
1118 // Now finish it.
1119 env(escrow::finish("bob", "alice", seq),
1122 fee(150 * baseFee));
1123 env.require(balance("carol", XRP(6000)));
1124 env.require(balance("alice", XRP(4000) - drops(baseFee)));
1125 }
1126 { // Test a condition other than PreimageSha256, which
1127 // would require a separate amendment
1128 Env env(*this, features);
1129 env.fund(XRP(5000), "alice", "bob");
1130
1132 {0xA2, 0x2B, 0x80, 0x20, 0x42, 0x4A, 0x70, 0x49, 0x49,
1133 0x52, 0x92, 0x67, 0xB6, 0x21, 0xB3, 0xD7, 0x91, 0x19,
1134 0xD7, 0x29, 0xB2, 0x38, 0x2C, 0xED, 0x8B, 0x29, 0x6C,
1135 0x3C, 0x02, 0x8F, 0xA9, 0x7D, 0x35, 0x0F, 0x6D, 0x07,
1136 0x81, 0x03, 0x06, 0x34, 0xD2, 0x82, 0x02, 0x03, 0xC8}};
1137
1138 // FIXME: this transaction should, eventually, return temDISABLED
1139 // instead of temMALFORMED.
1140 env(escrow::create("alice", "bob", XRP(1000)),
1142 escrow::cancel_time(env.now() + 1s),
1143 ter(temMALFORMED));
1144 }
1145 }
1146
1147 void
1149 {
1150 using namespace jtx;
1151 using namespace std::chrono;
1152
1153 auto const alice = Account("alice");
1154 auto const bruce = Account("bruce");
1155 auto const carol = Account("carol");
1156
1157 {
1158 testcase("Metadata to self");
1159
1160 Env env(*this, features);
1161 env.fund(XRP(5000), alice, bruce, carol);
1162 auto const aseq = env.seq(alice);
1163 auto const bseq = env.seq(bruce);
1164
1165 env(escrow::create(alice, alice, XRP(1000)),
1166 escrow::finish_time(env.now() + 1s),
1167 escrow::cancel_time(env.now() + 500s));
1168 BEAST_EXPECT(
1169 (*env.meta())[sfTransactionResult] ==
1170 static_cast<std::uint8_t>(tesSUCCESS));
1171 env.close(5s);
1172 auto const aa = env.le(keylet::escrow(alice.id(), aseq));
1173 BEAST_EXPECT(aa);
1174
1175 {
1176 ripple::Dir aod(*env.current(), keylet::ownerDir(alice.id()));
1177 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1178 BEAST_EXPECT(
1179 std::find(aod.begin(), aod.end(), aa) != aod.end());
1180 }
1181
1182 env(escrow::create(bruce, bruce, XRP(1000)),
1183 escrow::finish_time(env.now() + 1s),
1184 escrow::cancel_time(env.now() + 2s));
1185 BEAST_EXPECT(
1186 (*env.meta())[sfTransactionResult] ==
1187 static_cast<std::uint8_t>(tesSUCCESS));
1188 env.close(5s);
1189 auto const bb = env.le(keylet::escrow(bruce.id(), bseq));
1190 BEAST_EXPECT(bb);
1191
1192 {
1193 ripple::Dir bod(*env.current(), keylet::ownerDir(bruce.id()));
1194 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1195 BEAST_EXPECT(
1196 std::find(bod.begin(), bod.end(), bb) != bod.end());
1197 }
1198
1199 env.close(5s);
1200 env(escrow::finish(alice, alice, aseq));
1201 {
1202 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq)));
1203 BEAST_EXPECT(
1204 (*env.meta())[sfTransactionResult] ==
1205 static_cast<std::uint8_t>(tesSUCCESS));
1206
1207 ripple::Dir aod(*env.current(), keylet::ownerDir(alice.id()));
1208 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0);
1209 BEAST_EXPECT(
1210 std::find(aod.begin(), aod.end(), aa) == aod.end());
1211
1212 ripple::Dir bod(*env.current(), keylet::ownerDir(bruce.id()));
1213 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1214 BEAST_EXPECT(
1215 std::find(bod.begin(), bod.end(), bb) != bod.end());
1216 }
1217
1218 env.close(5s);
1219 env(escrow::cancel(bruce, bruce, bseq));
1220 {
1221 BEAST_EXPECT(!env.le(keylet::escrow(bruce.id(), bseq)));
1222 BEAST_EXPECT(
1223 (*env.meta())[sfTransactionResult] ==
1224 static_cast<std::uint8_t>(tesSUCCESS));
1225
1226 ripple::Dir bod(*env.current(), keylet::ownerDir(bruce.id()));
1227 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 0);
1228 BEAST_EXPECT(
1229 std::find(bod.begin(), bod.end(), bb) == bod.end());
1230 }
1231 }
1232 {
1233 testcase("Metadata to other");
1234
1235 Env env(*this, features);
1236 env.fund(XRP(5000), alice, bruce, carol);
1237 auto const aseq = env.seq(alice);
1238 auto const bseq = env.seq(bruce);
1239
1240 env(escrow::create(alice, bruce, XRP(1000)),
1241 escrow::finish_time(env.now() + 1s));
1242 BEAST_EXPECT(
1243 (*env.meta())[sfTransactionResult] ==
1244 static_cast<std::uint8_t>(tesSUCCESS));
1245 env.close(5s);
1246 env(escrow::create(bruce, carol, XRP(1000)),
1247 escrow::finish_time(env.now() + 1s),
1248 escrow::cancel_time(env.now() + 2s));
1249 BEAST_EXPECT(
1250 (*env.meta())[sfTransactionResult] ==
1251 static_cast<std::uint8_t>(tesSUCCESS));
1252 env.close(5s);
1253
1254 auto const ab = env.le(keylet::escrow(alice.id(), aseq));
1255 BEAST_EXPECT(ab);
1256
1257 auto const bc = env.le(keylet::escrow(bruce.id(), bseq));
1258 BEAST_EXPECT(bc);
1259
1260 {
1261 ripple::Dir aod(*env.current(), keylet::ownerDir(alice.id()));
1262 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1263 BEAST_EXPECT(
1264 std::find(aod.begin(), aod.end(), ab) != aod.end());
1265
1266 ripple::Dir bod(*env.current(), keylet::ownerDir(bruce.id()));
1267 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
1268 BEAST_EXPECT(
1269 std::find(bod.begin(), bod.end(), ab) != bod.end());
1270 BEAST_EXPECT(
1271 std::find(bod.begin(), bod.end(), bc) != bod.end());
1272
1273 ripple::Dir cod(*env.current(), keylet::ownerDir(carol.id()));
1274 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1275 BEAST_EXPECT(
1276 std::find(cod.begin(), cod.end(), bc) != cod.end());
1277 }
1278
1279 env.close(5s);
1280 env(escrow::finish(alice, alice, aseq));
1281 {
1282 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq)));
1283 BEAST_EXPECT(env.le(keylet::escrow(bruce.id(), bseq)));
1284
1285 ripple::Dir aod(*env.current(), keylet::ownerDir(alice.id()));
1286 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0);
1287 BEAST_EXPECT(
1288 std::find(aod.begin(), aod.end(), ab) == aod.end());
1289
1290 ripple::Dir bod(*env.current(), keylet::ownerDir(bruce.id()));
1291 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1292 BEAST_EXPECT(
1293 std::find(bod.begin(), bod.end(), ab) == bod.end());
1294 BEAST_EXPECT(
1295 std::find(bod.begin(), bod.end(), bc) != bod.end());
1296
1297 ripple::Dir cod(*env.current(), keylet::ownerDir(carol.id()));
1298 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1299 }
1300
1301 env.close(5s);
1302 env(escrow::cancel(bruce, bruce, bseq));
1303 {
1304 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq)));
1305 BEAST_EXPECT(!env.le(keylet::escrow(bruce.id(), bseq)));
1306
1307 ripple::Dir aod(*env.current(), keylet::ownerDir(alice.id()));
1308 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0);
1309 BEAST_EXPECT(
1310 std::find(aod.begin(), aod.end(), ab) == aod.end());
1311
1312 ripple::Dir bod(*env.current(), keylet::ownerDir(bruce.id()));
1313 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 0);
1314 BEAST_EXPECT(
1315 std::find(bod.begin(), bod.end(), ab) == bod.end());
1316 BEAST_EXPECT(
1317 std::find(bod.begin(), bod.end(), bc) == bod.end());
1318
1319 ripple::Dir cod(*env.current(), keylet::ownerDir(carol.id()));
1320 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 0);
1321 }
1322 }
1323 }
1324
1325 void
1327 {
1328 testcase("Consequences");
1329
1330 using namespace jtx;
1331 using namespace std::chrono;
1332 Env env(*this, features);
1333 auto const baseFee = env.current()->fees().base;
1334
1335 env.memoize("alice");
1336 env.memoize("bob");
1337 env.memoize("carol");
1338
1339 {
1340 auto const jtx = env.jt(
1341 escrow::create("alice", "carol", XRP(1000)),
1342 escrow::finish_time(env.now() + 1s),
1343 seq(1),
1344 fee(baseFee));
1345 auto const pf = preflight(
1346 env.app(),
1347 env.current()->rules(),
1348 *jtx.stx,
1349 tapNONE,
1350 env.journal);
1351 BEAST_EXPECT(pf.ter == tesSUCCESS);
1352 BEAST_EXPECT(!pf.consequences.isBlocker());
1353 BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
1354 BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(1000));
1355 }
1356
1357 {
1358 auto const jtx =
1359 env.jt(escrow::cancel("bob", "alice", 3), seq(1), fee(baseFee));
1360 auto const pf = preflight(
1361 env.app(),
1362 env.current()->rules(),
1363 *jtx.stx,
1364 tapNONE,
1365 env.journal);
1366 BEAST_EXPECT(pf.ter == tesSUCCESS);
1367 BEAST_EXPECT(!pf.consequences.isBlocker());
1368 BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
1369 BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0));
1370 }
1371
1372 {
1373 auto const jtx =
1374 env.jt(escrow::finish("bob", "alice", 3), seq(1), fee(baseFee));
1375 auto const pf = preflight(
1376 env.app(),
1377 env.current()->rules(),
1378 *jtx.stx,
1379 tapNONE,
1380 env.journal);
1381 BEAST_EXPECT(pf.ter == tesSUCCESS);
1382 BEAST_EXPECT(!pf.consequences.isBlocker());
1383 BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
1384 BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0));
1385 }
1386 }
1387
1388 void
1390 {
1391 testcase("Escrow with tickets");
1392
1393 using namespace jtx;
1394 using namespace std::chrono;
1395 Account const alice{"alice"};
1396 Account const bob{"bob"};
1397
1398 {
1399 // Create escrow and finish using tickets.
1400 Env env(*this, features);
1401 auto const baseFee = env.current()->fees().base;
1402 env.fund(XRP(5000), alice, bob);
1403 env.close();
1404
1405 // alice creates a ticket.
1406 std::uint32_t const aliceTicket{env.seq(alice) + 1};
1407 env(ticket::create(alice, 1));
1408
1409 // bob creates a bunch of tickets because he will be burning
1410 // through them with tec transactions. Just because we can
1411 // we'll use them up starting from largest and going smaller.
1412 constexpr static std::uint32_t bobTicketCount{20};
1413 env(ticket::create(bob, bobTicketCount));
1414 env.close();
1415 std::uint32_t bobTicket{env.seq(bob)};
1416 env.require(tickets(alice, 1));
1417 env.require(tickets(bob, bobTicketCount));
1418
1419 // Note that from here on all transactions use tickets. No account
1420 // root sequences should change.
1421 std::uint32_t const aliceRootSeq{env.seq(alice)};
1422 std::uint32_t const bobRootSeq{env.seq(bob)};
1423
1424 // alice creates an escrow that can be finished in the future
1425 auto const ts = env.now() + 97s;
1426
1427 std::uint32_t const escrowSeq = aliceTicket;
1428 env(escrow::create(alice, bob, XRP(1000)),
1430 ticket::use(aliceTicket));
1431 BEAST_EXPECT(env.seq(alice) == aliceRootSeq);
1432 env.require(tickets(alice, 0));
1433 env.require(tickets(bob, bobTicketCount));
1434
1435 // Advance the ledger, verifying that the finish won't complete
1436 // prematurely. Note that each tec consumes one of bob's tickets.
1437 for (; env.now() < ts; env.close())
1438 {
1439 env(escrow::finish(bob, alice, escrowSeq),
1440 fee(150 * baseFee),
1441 ticket::use(--bobTicket),
1443 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1444 }
1445
1446 // bob tries to re-use a ticket, which is rejected.
1447 env(escrow::finish(bob, alice, escrowSeq),
1448 fee(150 * baseFee),
1449 ticket::use(bobTicket),
1450 ter(tefNO_TICKET));
1451
1452 // bob uses one of his remaining tickets. Success!
1453 env(escrow::finish(bob, alice, escrowSeq),
1454 fee(150 * baseFee),
1455 ticket::use(--bobTicket));
1456 env.close();
1457 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1458 }
1459 {
1460 // Create escrow and cancel using tickets.
1461 Env env(*this, features);
1462 auto const baseFee = env.current()->fees().base;
1463 env.fund(XRP(5000), alice, bob);
1464 env.close();
1465
1466 // alice creates a ticket.
1467 std::uint32_t const aliceTicket{env.seq(alice) + 1};
1468 env(ticket::create(alice, 1));
1469
1470 // bob creates a bunch of tickets because he will be burning
1471 // through them with tec transactions.
1472 constexpr std::uint32_t bobTicketCount{20};
1473 std::uint32_t bobTicket{env.seq(bob) + 1};
1474 env(ticket::create(bob, bobTicketCount));
1475 env.close();
1476 env.require(tickets(alice, 1));
1477 env.require(tickets(bob, bobTicketCount));
1478
1479 // Note that from here on all transactions use tickets. No account
1480 // root sequences should change.
1481 std::uint32_t const aliceRootSeq{env.seq(alice)};
1482 std::uint32_t const bobRootSeq{env.seq(bob)};
1483
1484 // alice creates an escrow that can be finished in the future.
1485 auto const ts = env.now() + 117s;
1486
1487 std::uint32_t const escrowSeq = aliceTicket;
1488 env(escrow::create(alice, bob, XRP(1000)),
1491 ticket::use(aliceTicket));
1492 BEAST_EXPECT(env.seq(alice) == aliceRootSeq);
1493 env.require(tickets(alice, 0));
1494 env.require(tickets(bob, bobTicketCount));
1495
1496 // Advance the ledger, verifying that the cancel won't complete
1497 // prematurely.
1498 for (; env.now() < ts; env.close())
1499 {
1500 env(escrow::cancel(bob, alice, escrowSeq),
1501 fee(150 * baseFee),
1502 ticket::use(bobTicket++),
1504 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1505 }
1506
1507 // Verify that a finish won't work anymore.
1508 env(escrow::finish(bob, alice, escrowSeq),
1511 fee(150 * baseFee),
1512 ticket::use(bobTicket++),
1514 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1515
1516 // Verify that the cancel succeeds.
1517 env(escrow::cancel(bob, alice, escrowSeq),
1518 fee(150 * baseFee),
1519 ticket::use(bobTicket++));
1520 env.close();
1521 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1522
1523 // Verify that bob actually consumed his tickets.
1524 env.require(tickets(bob, env.seq(bob) - bobTicket));
1525 }
1526 }
1527
1528 void
1530 {
1531 testcase("Test with credentials");
1532
1533 using namespace jtx;
1534 using namespace std::chrono;
1535
1536 Account const alice{"alice"};
1537 Account const bob{"bob"};
1538 Account const carol{"carol"};
1539 Account const dillon{"dillon "};
1540 Account const zelda{"zelda"};
1541
1542 char const credType[] = "abcde";
1543
1544 {
1545 // Credentials amendment not enabled
1546 Env env(*this, features - featureCredentials);
1547 env.fund(XRP(5000), alice, bob);
1548 env.close();
1549
1550 auto const seq = env.seq(alice);
1551 env(escrow::create(alice, bob, XRP(1000)),
1552 escrow::finish_time(env.now() + 1s));
1553 env.close();
1554
1555 env(fset(bob, asfDepositAuth));
1556 env.close();
1557 env(deposit::auth(bob, alice));
1558 env.close();
1559
1560 std::string const credIdx =
1561 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
1562 "E4";
1563 env(escrow::finish(bob, alice, seq),
1564 credentials::ids({credIdx}),
1565 ter(temDISABLED));
1566 }
1567
1568 {
1569 Env env(*this, features);
1570
1571 env.fund(XRP(5000), alice, bob, carol, dillon, zelda);
1572 env.close();
1573
1574 env(credentials::create(carol, zelda, credType));
1575 env.close();
1576 auto const jv =
1577 credentials::ledgerEntry(env, carol, zelda, credType);
1578 std::string const credIdx = jv[jss::result][jss::index].asString();
1579
1580 auto const seq = env.seq(alice);
1581 env(escrow::create(alice, bob, XRP(1000)),
1582 escrow::finish_time(env.now() + 50s));
1583 env.close();
1584
1585 // Bob require preauthorization
1586 env(fset(bob, asfDepositAuth));
1587 env.close();
1588
1589 // Fail, credentials not accepted
1590 env(escrow::finish(carol, alice, seq),
1591 credentials::ids({credIdx}),
1593
1594 env.close();
1595
1596 env(credentials::accept(carol, zelda, credType));
1597 env.close();
1598
1599 // Fail, credentials doesn’t belong to root account
1600 env(escrow::finish(dillon, alice, seq),
1601 credentials::ids({credIdx}),
1603
1604 // Fail, no depositPreauth
1605 env(escrow::finish(carol, alice, seq),
1606 credentials::ids({credIdx}),
1608
1609 env(deposit::authCredentials(bob, {{zelda, credType}}));
1610 env.close();
1611
1612 // Success
1613 env.close();
1614 env(escrow::finish(carol, alice, seq), credentials::ids({credIdx}));
1615 env.close();
1616 }
1617
1618 {
1619 testcase("Escrow with credentials without depositPreauth");
1620 using namespace std::chrono;
1621
1622 Env env(*this, features);
1623
1624 env.fund(XRP(5000), alice, bob, carol, dillon, zelda);
1625 env.close();
1626
1627 env(credentials::create(carol, zelda, credType));
1628 env.close();
1629 env(credentials::accept(carol, zelda, credType));
1630 env.close();
1631 auto const jv =
1632 credentials::ledgerEntry(env, carol, zelda, credType);
1633 std::string const credIdx = jv[jss::result][jss::index].asString();
1634
1635 auto const seq = env.seq(alice);
1636 env(escrow::create(alice, bob, XRP(1000)),
1637 escrow::finish_time(env.now() + 50s));
1638 // time advance
1639 env.close();
1640 env.close();
1641 env.close();
1642 env.close();
1643 env.close();
1644 env.close();
1645
1646 // Succeed, Bob doesn't require preauthorization
1647 env(escrow::finish(carol, alice, seq), credentials::ids({credIdx}));
1648 env.close();
1649
1650 {
1651 char const credType2[] = "fghijk";
1652
1653 env(credentials::create(bob, zelda, credType2));
1654 env.close();
1655 env(credentials::accept(bob, zelda, credType2));
1656 env.close();
1657 auto const credIdxBob =
1659 env, bob, zelda, credType2)[jss::result][jss::index]
1660 .asString();
1661
1662 auto const seq = env.seq(alice);
1663 env(escrow::create(alice, bob, XRP(1000)),
1664 escrow::finish_time(env.now() + 1s));
1665 env.close();
1666
1667 // Bob require preauthorization
1668 env(fset(bob, asfDepositAuth));
1669 env.close();
1670 env(deposit::authCredentials(bob, {{zelda, credType}}));
1671 env.close();
1672
1673 // Use any valid credentials if account == dst
1674 env(escrow::finish(bob, alice, seq),
1675 credentials::ids({credIdxBob}));
1676 env.close();
1677 }
1678 }
1679 }
1680
1681 void
1683 {
1684 testEnablement(features);
1685 testTiming(features);
1686 testTags(features);
1687 testDisallowXRP(features);
1689 testFails(features);
1690 testLockup(features);
1691 testEscrowConditions(features);
1692 testMetaAndOwnership(features);
1693 testConsequences(features);
1694 testEscrowWithTickets(features);
1695 testCredentials(features);
1696 }
1697
1698public:
1699 void
1700 run() override
1701 {
1702 using namespace test::jtx;
1705 testWithFeats(all - featureTokenEscrow);
1706 testTags(all - fixIncludeKeyletFields);
1707 }
1708};
1709
1710BEAST_DEFINE_TESTSUITE(Escrow, app, ripple);
1711
1712} // namespace test
1713} // namespace ripple
std::string asString() const
Returns the unquoted string value.
A testsuite class.
Definition suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
A class that simplifies iterating ledger directory pages.
Definition Dir.h:41
const_iterator end() const
Definition Dir.cpp:52
const_iterator begin() const
Definition Dir.cpp:34
An immutable linear range of bytes.
Definition Slice.h:46
Immutable cryptographic account descriptor.
Definition Account.h:39
A transaction testing environment.
Definition Env.h:121
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:269
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:547
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:331
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:122
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
Definition Env.h:508
NetClock::time_point now()
Returns the current network time.
Definition Env.h:284
Application & app()
Definition Env.h:261
beast::Journal const journal
Definition Env.h:162
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:290
std::shared_ptr< STObject const > meta()
Return metadata for the last JTx.
Definition Env.cpp:504
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:184
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:157
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:278
A balance matches.
Definition balance.h:39
Set the fee on a JTx.
Definition fee.h:37
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:35
Set a ticket sequence on a JTx.
Definition ticket.h:48
Set the flags on a JTx.
Definition txflags.h:31
T data(T... args)
T distance(T... args)
T find(T... args)
T memcpy(T... args)
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Definition Indexes.cpp:389
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:374
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:32
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:48
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:78
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:32
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:54
std::array< std::uint8_t, 7 > const fb2
Definition escrow.h:78
std::array< std::uint8_t, 8 > const fb3
Definition escrow.h:88
std::array< std::uint8_t, 39 > const cb1
Definition escrow.h:71
std::array< std::uint8_t, 39 > const cb3
Definition escrow.h:91
Json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount)
Definition escrow.cpp:33
auto const finish_time
Set the "FinishAfter" time tag on a JTx.
Definition escrow.h:98
Json::Value cancel(AccountID const &account, Account const &from, std::uint32_t seq)
Definition escrow.cpp:57
std::array< std::uint8_t, 39 > const cb2
Definition escrow.h:81
auto const cancel_time
Set the "CancelAfter" time tag on a JTx.
Definition escrow.h:101
std::array< std::uint8_t, 4 > const fb1
Definition escrow.h:69
Json::Value finish(AccountID const &account, AccountID const &from, std::uint32_t seq)
Definition escrow.cpp:45
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:31
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:29
FeatureBitset testable_amendments()
Definition Env.h:74
owner_count< ltTICKET > tickets
Match the number of tickets on the account.
Definition ticket.h:64
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:111
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:85
PreflightResult preflight(Application &app, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
constexpr std::uint32_t asfRequireDest
Definition TxFlags.h:77
constexpr std::uint32_t tfPassive
Definition TxFlags.h:98
@ tefNO_TICKET
Definition TER.h:185
@ tecCRYPTOCONDITION_ERROR
Definition TER.h:312
@ tecNO_DST
Definition TER.h:290
@ tecUNFUNDED
Definition TER.h:295
@ tecNO_TARGET
Definition TER.h:304
@ tecBAD_CREDENTIALS
Definition TER.h:359
@ tecNO_PERMISSION
Definition TER.h:305
@ tecDST_TAG_NEEDED
Definition TER.h:309
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tesSUCCESS
Definition TER.h:244
@ tapNONE
Definition ApplyView.h:31
constexpr std::uint32_t asfDisallowXRP
Definition TxFlags.h:79
@ temBAD_AMOUNT
Definition TER.h:89
@ temMALFORMED
Definition TER.h:87
@ temBAD_EXPIRATION
Definition TER.h:91
@ temINVALID_FLAG
Definition TER.h:111
@ temDISABLED
Definition TER.h:114
T resize(T... args)
T size(T... args)
void run() override
Runs the suite.
void testLockup(FeatureBitset features)
void testCredentials(FeatureBitset features)
void testFails(FeatureBitset features)
void testEscrowConditions(FeatureBitset features)
void testTags(FeatureBitset features)
void testMetaAndOwnership(FeatureBitset features)
void testConsequences(FeatureBitset features)
void testEnablement(FeatureBitset features)
void testRequiresConditionOrFinishAfter(FeatureBitset features)
void testWithFeats(FeatureBitset features)
void testEscrowWithTickets(FeatureBitset features)
void testDisallowXRP(FeatureBitset features)
void testTiming(FeatureBitset features)
Set the destination tag on a JTx.
Definition tag.h:32
Set the sequence number on a JTx.
Definition seq.h:34
Set the source tag on a JTx.
Definition tag.h:47