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