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