xrpld
Loading...
Searching...
No Matches
Escrow_test.cpp
1
2#include <test/jtx/Env.h>
3#include <test/jtx/amount.h>
4#include <test/jtx/balance.h>
5#include <test/jtx/credentials.h>
6#include <test/jtx/deposit.h>
7#include <test/jtx/escrow.h>
8#include <test/jtx/fee.h>
9#include <test/jtx/flags.h>
10#include <test/jtx/seq.h>
11#include <test/jtx/tag.h>
12#include <test/jtx/ter.h>
13#include <test/jtx/ticket.h>
14#include <test/jtx/txflags.h>
15
16#include <xrpl/basics/Slice.h>
17#include <xrpl/beast/unit_test/suite.h>
18#include <xrpl/ledger/ApplyView.h>
19#include <xrpl/ledger/Dir.h>
20#include <xrpl/protocol/Feature.h>
21#include <xrpl/protocol/Indexes.h>
22#include <xrpl/protocol/SField.h>
23#include <xrpl/protocol/TER.h>
24#include <xrpl/protocol/TxFlags.h>
25#include <xrpl/protocol/jss.h>
26#include <xrpl/tx/applySteps.h>
27
28#include <algorithm>
29#include <array>
30#include <cstdint>
31#include <cstring>
32#include <iterator>
33#include <vector>
34
35namespace xrpl::test {
36
38{
39 void
41 {
42 testcase("Enablement");
43
44 using namespace jtx;
45 using namespace std::chrono;
46
47 Env env(*this, features);
48 auto const baseFee = env.current()->fees().base;
49 env.fund(XRP(5000), "alice", "bob");
50 env(escrow::create("alice", "bob", XRP(1000)), escrow::kFinishTime(env.now() + 1s));
51 env.close();
52
53 auto const seq1 = env.seq("alice");
54
55 env(escrow::create("alice", "bob", XRP(1000)),
57 escrow::kFinishTime(env.now() + 1s),
58 Fee(baseFee * 150));
59 env.close();
60 env(escrow::finish("bob", "alice", seq1),
63 Fee(baseFee * 150));
64
65 auto const seq2 = env.seq("alice");
66
67 env(escrow::create("alice", "bob", XRP(1000)),
69 escrow::kFinishTime(env.now() + 1s),
70 escrow::kCancelTime(env.now() + 2s),
71 Fee(baseFee * 150));
72 env.close();
73 env(escrow::cancel("bob", "alice", seq2), Fee(baseFee * 150));
74 }
75
76 void
78 {
79 using namespace jtx;
80 using namespace std::chrono;
81
82 {
83 testcase("Timing: Finish Only");
84 Env env(*this, features);
85 auto const baseFee = env.current()->fees().base;
86 env.fund(XRP(5000), "alice", "bob");
87 env.close();
88
89 // We create an escrow that can be finished in the future
90 auto const ts = env.now() + 97s;
91
92 auto const seq = env.seq("alice");
93 env(escrow::create("alice", "bob", XRP(1000)), escrow::kFinishTime(ts));
94
95 // Advance the ledger, verifying that the finish won't complete
96 // prematurely.
97 for (; env.now() < ts; env.close())
98 env(escrow::finish("bob", "alice", seq), Fee(baseFee * 150), Ter(tecNO_PERMISSION));
99
100 env(escrow::finish("bob", "alice", seq), Fee(baseFee * 150));
101 }
102
103 {
104 testcase("Timing: Cancel Only");
105 Env env(*this, features);
106 auto const baseFee = env.current()->fees().base;
107 env.fund(XRP(5000), "alice", "bob");
108 env.close();
109
110 // We create an escrow that can be cancelled in the future
111 auto const ts = env.now() + 117s;
112
113 auto const seq = env.seq("alice");
114 env(escrow::create("alice", "bob", XRP(1000)),
117
118 // Advance the ledger, verifying that the cancel won't complete
119 // prematurely.
120 for (; env.now() < ts; env.close())
121 env(escrow::cancel("bob", "alice", seq), Fee(baseFee * 150), Ter(tecNO_PERMISSION));
122
123 // Verify that a finish won't work anymore.
124 env(escrow::finish("bob", "alice", seq),
127 Fee(baseFee * 150),
129
130 // Verify that the cancel will succeed
131 env(escrow::cancel("bob", "alice", seq), Fee(baseFee * 150));
132 }
133
134 {
135 testcase("Timing: Finish and Cancel -> Finish");
136 Env env(*this, features);
137 auto const baseFee = env.current()->fees().base;
138 env.fund(XRP(5000), "alice", "bob");
139 env.close();
140
141 // We create an escrow that can be cancelled in the future
142 auto const fts = env.now() + 117s;
143 auto const cts = env.now() + 192s;
144
145 auto const seq = env.seq("alice");
146 env(escrow::create("alice", "bob", XRP(1000)),
149
150 // Advance the ledger, verifying that the finish and cancel won't
151 // complete prematurely.
152 for (; env.now() < fts; env.close())
153 {
154 env(escrow::finish("bob", "alice", seq), Fee(baseFee * 150), Ter(tecNO_PERMISSION));
155 env(escrow::cancel("bob", "alice", seq), Fee(baseFee * 150), Ter(tecNO_PERMISSION));
156 }
157
158 // Verify that a cancel still won't work
159 env(escrow::cancel("bob", "alice", seq), Fee(baseFee * 150), Ter(tecNO_PERMISSION));
160
161 // And verify that a finish will
162 env(escrow::finish("bob", "alice", seq), Fee(baseFee * 150));
163 }
164
165 {
166 testcase("Timing: Finish and Cancel -> Cancel");
167 Env env(*this, features);
168 auto const baseFee = env.current()->fees().base;
169 env.fund(XRP(5000), "alice", "bob");
170 env.close();
171
172 // We create an escrow that can be cancelled in the future
173 auto const fts = env.now() + 109s;
174 auto const cts = env.now() + 184s;
175
176 auto const seq = env.seq("alice");
177 env(escrow::create("alice", "bob", XRP(1000)),
180
181 // Advance the ledger, verifying that the finish and cancel won't
182 // complete prematurely.
183 for (; env.now() < fts; env.close())
184 {
185 env(escrow::finish("bob", "alice", seq), Fee(baseFee * 150), Ter(tecNO_PERMISSION));
186 env(escrow::cancel("bob", "alice", seq), Fee(baseFee * 150), Ter(tecNO_PERMISSION));
187 }
188
189 // Continue advancing, verifying that the cancel won't complete
190 // prematurely. At this point a finish would succeed.
191 for (; env.now() < cts; env.close())
192 env(escrow::cancel("bob", "alice", seq), Fee(baseFee * 150), Ter(tecNO_PERMISSION));
193
194 // Verify that finish will no longer work, since we are past the
195 // cancel activation time.
196 env(escrow::finish("bob", "alice", seq), Fee(baseFee * 150), Ter(tecNO_PERMISSION));
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::kFinishTime(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::kFinishTime(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)), escrow::kFinishTime(env.now() + 1s));
263 }
264 }
265
266 void
268 {
269 using namespace jtx;
270 using namespace std::chrono;
271
272 testcase("RequiresConditionOrFinishAfter");
273
274 Env env(*this, features);
275 auto const baseFee = env.current()->fees().base;
276 env.fund(XRP(5000), "alice", "bob", "carol");
277 env.close();
278
279 // Creating an escrow with only a cancel time is not allowed:
280 env(escrow::create("alice", "bob", XRP(100)),
281 escrow::kCancelTime(env.now() + 90s),
282 Fee(baseFee * 150),
284
285 // Creating an escrow with only a cancel time and a kCondition is
286 // allowed:
287 auto const seq = env.seq("alice");
288 env(escrow::create("alice", "bob", XRP(100)),
289 escrow::kCancelTime(env.now() + 90s),
291 Fee(baseFee * 150));
292 env.close();
293 env(escrow::finish("carol", "alice", seq),
296 Fee(baseFee * 150));
297 BEAST_EXPECT(env.balance("bob") == XRP(5100));
298
299 // Creating an escrow with only a cancel time and a finish time is
300 // allowed:
301 auto const seqFt = env.seq("alice");
302 env(escrow::create("alice", "bob", XRP(100)),
303 escrow::kFinishTime(env.now()), // Set finish time to now so that
304 // we can call finish immediately.
305 escrow::kCancelTime(env.now() + 50s),
306 Fee(baseFee * 150));
307 env.close();
308 env(escrow::finish("carol", "alice", seqFt), Fee(150 * baseFee));
309 BEAST_EXPECT(env.balance("bob") == XRP(5200)); // 5100 (from last transaction) + 100
310 }
311
312 void
314 {
315 testcase("Failure Cases");
316
317 using namespace jtx;
318 using namespace std::chrono;
319
320 Env env(*this, features);
321 auto const baseFee = env.current()->fees().base;
322 env.fund(XRP(5000), "alice", "bob", "gw");
323 env.close();
324
325 // temINVALID_FLAG
326 env(escrow::create("alice", "bob", XRP(1000)),
327 escrow::kFinishTime(env.now() + 5s),
328 Txflags(tfPassive),
330
331 // Finish time is in the past
332 env(escrow::create("alice", "bob", XRP(1000)),
333 escrow::kFinishTime(env.now() - 5s),
335
336 // Cancel time is in the past
337 env(escrow::create("alice", "bob", XRP(1000)),
339 escrow::kCancelTime(env.now() - 5s),
341
342 // no destination account
343 env(escrow::create("alice", "carol", XRP(1000)),
344 escrow::kFinishTime(env.now() + 1s),
345 Ter(tecNO_DST));
346
347 env.fund(XRP(5000), "carol");
348
349 // Using non-XRP:
350 bool const withTokenEscrow = env.current()->rules().enabled(featureTokenEscrow);
351 {
352 // tecNO_PERMISSION: token escrow is enabled but the issuer did not
353 // set the asfAllowTrustLineLocking flag
354 auto const txResult = withTokenEscrow ? Ter(tecNO_PERMISSION) : Ter(temBAD_AMOUNT);
355 env(escrow::create("alice", "carol", Account("alice")["USD"](500)),
356 escrow::kFinishTime(env.now() + 5s),
357 txResult);
358 }
359
360 // Sending zero or no XRP:
361 env(escrow::create("alice", "carol", XRP(0)),
362 escrow::kFinishTime(env.now() + 1s),
364 env(escrow::create("alice", "carol", XRP(-1000)),
365 escrow::kFinishTime(env.now() + 1s),
367
368 // Fail if neither CancelAfter nor FinishAfter are specified:
369 env(escrow::create("alice", "carol", XRP(1)), Ter(temBAD_EXPIRATION));
370
371 // Fail if neither a FinishTime nor a kCondition are attached:
372 env(escrow::create("alice", "carol", XRP(1)),
373 escrow::kCancelTime(env.now() + 1s),
375
376 // Fail if FinishAfter has already passed:
377 env(escrow::create("alice", "carol", XRP(1)),
378 escrow::kFinishTime(env.now() - 1s),
380
381 // If both CancelAfter and FinishAfter are set, then CancelAfter must
382 // be strictly later than FinishAfter.
383 env(escrow::create("alice", "carol", XRP(1)),
385 escrow::kFinishTime(env.now() + 10s),
386 escrow::kCancelTime(env.now() + 10s),
388
389 env(escrow::create("alice", "carol", XRP(1)),
391 escrow::kFinishTime(env.now() + 10s),
392 escrow::kCancelTime(env.now() + 5s),
394
395 // Carol now requires the use of a destination tag
396 env(fset("carol", asfRequireDest));
397
398 // missing destination tag
399 env(escrow::create("alice", "carol", XRP(1)),
401 escrow::kCancelTime(env.now() + 1s),
403
404 // Success!
405 env(escrow::create("alice", "carol", XRP(1)),
407 escrow::kCancelTime(env.now() + 1s),
408 Dtag(1));
409
410 { // Fail if the sender wants to send more than he has:
411 auto const accountReserve = drops(env.current()->fees().reserve);
412 auto const accountIncrement = drops(env.current()->fees().increment);
413
414 env.fund(accountReserve + accountIncrement + XRP(50), "daniel");
415 env(escrow::create("daniel", "bob", XRP(51)),
416 escrow::kFinishTime(env.now() + 1s),
418
419 env.fund(accountReserve + accountIncrement + XRP(50), "evan");
420 env(escrow::create("evan", "bob", XRP(50)),
421 escrow::kFinishTime(env.now() + 1s),
423
424 env.fund(accountReserve, "frank");
425 env(escrow::create("frank", "bob", XRP(1)),
426 escrow::kFinishTime(env.now() + 1s),
428 }
429
430 { // Specify incorrect sequence number
431 env.fund(XRP(5000), "hannah");
432 auto const seq = env.seq("hannah");
433 env(escrow::create("hannah", "hannah", XRP(10)),
434 escrow::kFinishTime(env.now() + 1s),
435 Fee(150 * baseFee));
436 env.close();
437 env(escrow::finish("hannah", "hannah", seq + 7), Fee(150 * baseFee), Ter(tecNO_TARGET));
438 }
439
440 { // Try to specify a kCondition for a non-conditional payment
441 env.fund(XRP(5000), "ivan");
442 auto const seq = env.seq("ivan");
443
444 env(escrow::create("ivan", "ivan", XRP(10)), escrow::kFinishTime(env.now() + 1s));
445 env.close();
446 env(escrow::finish("ivan", "ivan", seq),
449 Fee(150 * baseFee),
451 }
452 }
453
454 void
456 {
457 testcase("Lockup");
458
459 using namespace jtx;
460 using namespace std::chrono;
461
462 {
463 // Unconditional
464 Env env(*this, features);
465 auto const baseFee = env.current()->fees().base;
466 env.fund(XRP(5000), "alice", "bob");
467 auto const seq = env.seq("alice");
468 env(escrow::create("alice", "alice", XRP(1000)), escrow::kFinishTime(env.now() + 5s));
469 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
470
471 // Not enough time has elapsed for a finish and canceling isn't
472 // possible.
473 env(escrow::cancel("bob", "alice", seq), Ter(tecNO_PERMISSION));
474 env(escrow::finish("bob", "alice", seq), Ter(tecNO_PERMISSION));
475 env.close();
476
477 // Cancel continues to not be possible
478 env(escrow::cancel("bob", "alice", seq), Ter(tecNO_PERMISSION));
479
480 // Finish should succeed. Verify funds.
481 env(escrow::finish("bob", "alice", seq));
482 env.require(Balance("alice", XRP(5000) - drops(baseFee)));
483 }
484 {
485 // Unconditionally pay from Alice to Bob. Zelda (neither source nor
486 // destination) signs all cancels and finishes. This shows that
487 // Escrow will make a payment to Bob with no intervention from Bob.
488 Env env(*this, features);
489 auto const baseFee = env.current()->fees().base;
490 env.fund(XRP(5000), "alice", "bob", "zelda");
491 auto const seq = env.seq("alice");
492 env(escrow::create("alice", "bob", XRP(1000)), escrow::kFinishTime(env.now() + 5s));
493 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
494
495 // Not enough time has elapsed for a finish and canceling isn't
496 // possible.
497 env(escrow::cancel("zelda", "alice", seq), Ter(tecNO_PERMISSION));
498 env(escrow::finish("zelda", "alice", seq), Ter(tecNO_PERMISSION));
499 env.close();
500
501 // Cancel continues to not be possible
502 env(escrow::cancel("zelda", "alice", seq), Ter(tecNO_PERMISSION));
503
504 // Finish should succeed. Verify funds.
505 env(escrow::finish("zelda", "alice", seq));
506 env.close();
507
508 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
509 env.require(Balance("bob", XRP(6000)));
510 env.require(Balance("zelda", XRP(5000) - drops(4 * baseFee)));
511 }
512 {
513 // Bob sets DepositAuth so only Bob can finish the escrow.
514 Env env(*this, features);
515 auto const baseFee = env.current()->fees().base;
516
517 env.fund(XRP(5000), "alice", "bob", "zelda");
518 env(fset("bob", asfDepositAuth));
519 env.close();
520
521 auto const seq = env.seq("alice");
522 env(escrow::create("alice", "bob", XRP(1000)), escrow::kFinishTime(env.now() + 5s));
523 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
524
525 // Not enough time has elapsed for a finish and canceling isn't
526 // possible.
527 env(escrow::cancel("zelda", "alice", seq), Ter(tecNO_PERMISSION));
528 env(escrow::cancel("alice", "alice", seq), Ter(tecNO_PERMISSION));
529 env(escrow::cancel("bob", "alice", seq), Ter(tecNO_PERMISSION));
530 env(escrow::finish("zelda", "alice", seq), Ter(tecNO_PERMISSION));
531 env(escrow::finish("alice", "alice", seq), Ter(tecNO_PERMISSION));
532 env(escrow::finish("bob", "alice", seq), Ter(tecNO_PERMISSION));
533 env.close();
534
535 // Cancel continues to not be possible. Finish will only succeed for
536 // Bob, because of DepositAuth.
537 env(escrow::cancel("zelda", "alice", seq), Ter(tecNO_PERMISSION));
538 env(escrow::cancel("alice", "alice", seq), Ter(tecNO_PERMISSION));
539 env(escrow::cancel("bob", "alice", seq), Ter(tecNO_PERMISSION));
540 env(escrow::finish("zelda", "alice", seq), Ter(tecNO_PERMISSION));
541 env(escrow::finish("alice", "alice", seq), Ter(tecNO_PERMISSION));
542 env(escrow::finish("bob", "alice", seq));
543 env.close();
544
545 env.require(Balance("alice", XRP(4000) - (baseFee * 5)));
546 env.require(Balance("bob", XRP(6000) - (baseFee * 5)));
547 env.require(Balance("zelda", XRP(5000) - (baseFee * 4)));
548 }
549 {
550 // Bob sets DepositAuth but preauthorizes Zelda, so Zelda can
551 // finish the escrow.
552 Env env(*this, features);
553 auto const baseFee = env.current()->fees().base;
554
555 env.fund(XRP(5000), "alice", "bob", "zelda");
556 env(fset("bob", asfDepositAuth));
557 env.close();
558 env(deposit::auth("bob", "zelda"));
559 env.close();
560
561 auto const seq = env.seq("alice");
562 env(escrow::create("alice", "bob", XRP(1000)), escrow::kFinishTime(env.now() + 5s));
563 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
564 env.close();
565
566 // DepositPreauth allows Finish to succeed for either Zelda or
567 // Bob. But Finish won't succeed for Alice since she is not
568 // preauthorized.
569 env(escrow::finish("alice", "alice", seq), Ter(tecNO_PERMISSION));
570 env(escrow::finish("zelda", "alice", seq));
571 env.close();
572
573 env.require(Balance("alice", XRP(4000) - (baseFee * 2)));
574 env.require(Balance("bob", XRP(6000) - (baseFee * 2)));
575 env.require(Balance("zelda", XRP(5000) - (baseFee * 1)));
576 }
577 {
578 // Conditional
579 Env env(*this, features);
580 auto const baseFee = env.current()->fees().base;
581 env.fund(XRP(5000), "alice", "bob");
582 auto const seq = env.seq("alice");
583 env(escrow::create("alice", "alice", XRP(1000)),
585 escrow::kFinishTime(env.now() + 5s));
586 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
587
588 // Not enough time has elapsed for a finish and canceling isn't
589 // possible.
590 env(escrow::cancel("alice", "alice", seq), Ter(tecNO_PERMISSION));
591 env(escrow::cancel("bob", "alice", seq), Ter(tecNO_PERMISSION));
592 env(escrow::finish("alice", "alice", seq), Ter(tecNO_PERMISSION));
593 env(escrow::finish("alice", "alice", seq),
596 Fee(150 * baseFee),
598 env(escrow::finish("bob", "alice", seq), Ter(tecNO_PERMISSION));
599 env(escrow::finish("bob", "alice", seq),
602 Fee(150 * baseFee),
604 env.close();
605
606 // Cancel continues to not be possible. Finish is possible but
607 // requires the kFulfillment associated with the escrow.
608 env(escrow::cancel("alice", "alice", seq), Ter(tecNO_PERMISSION));
609 env(escrow::cancel("bob", "alice", seq), Ter(tecNO_PERMISSION));
610 env(escrow::finish("bob", "alice", seq), Ter(tecCRYPTOCONDITION_ERROR));
611 env(escrow::finish("alice", "alice", seq), Ter(tecCRYPTOCONDITION_ERROR));
612 env.close();
613
614 env(escrow::finish("bob", "alice", seq),
617 Fee(150 * baseFee));
618 }
619 {
620 // Self-escrowed conditional with DepositAuth.
621 Env env(*this, features);
622 auto const baseFee = env.current()->fees().base;
623
624 env.fund(XRP(5000), "alice", "bob");
625 auto const seq = env.seq("alice");
626 env(escrow::create("alice", "alice", XRP(1000)),
628 escrow::kFinishTime(env.now() + 5s));
629 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
630 env.close();
631
632 // Finish is now possible but requires the cryptocondition.
633 env(escrow::finish("bob", "alice", seq), Ter(tecCRYPTOCONDITION_ERROR));
634 env(escrow::finish("alice", "alice", seq), Ter(tecCRYPTOCONDITION_ERROR));
635
636 // Enable deposit authorization. After this only Alice can finish
637 // the escrow.
638 env(fset("alice", asfDepositAuth));
639 env.close();
640
641 env(escrow::finish("alice", "alice", seq),
644 Fee(150 * baseFee),
646 env(escrow::finish("bob", "alice", seq),
649 Fee(150 * baseFee),
651 env(escrow::finish("alice", "alice", seq),
654 Fee(150 * baseFee));
655 }
656 {
657 // Self-escrowed conditional with DepositAuth and DepositPreauth.
658 Env env(*this, features);
659 auto const baseFee = env.current()->fees().base;
660
661 env.fund(XRP(5000), "alice", "bob", "zelda");
662 auto const seq = env.seq("alice");
663 env(escrow::create("alice", "alice", XRP(1000)),
665 escrow::kFinishTime(env.now() + 5s));
666 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
667 env.close();
668
669 // Alice preauthorizes Zelda for deposit, even though Alice has not
670 // set the lsfDepositAuth flag (yet).
671 env(deposit::auth("alice", "zelda"));
672 env.close();
673
674 // Finish is now possible but requires the cryptocondition.
675 env(escrow::finish("alice", "alice", seq), Ter(tecCRYPTOCONDITION_ERROR));
676 env(escrow::finish("bob", "alice", seq), Ter(tecCRYPTOCONDITION_ERROR));
677 env(escrow::finish("zelda", "alice", seq), Ter(tecCRYPTOCONDITION_ERROR));
678
679 // Alice enables deposit authorization. After this only Alice or
680 // Zelda (because Zelda is preauthorized) can finish the escrow.
681 env(fset("alice", asfDepositAuth));
682 env.close();
683
684 env(escrow::finish("alice", "alice", seq),
687 Fee(150 * baseFee),
689 env(escrow::finish("bob", "alice", seq),
692 Fee(150 * baseFee),
694 env(escrow::finish("zelda", "alice", seq),
697 Fee(150 * baseFee));
698 }
699 }
700
701 void
703 {
704 testcase("Escrow with CryptoConditions");
705
706 using namespace jtx;
707 using namespace std::chrono;
708
709 { // Test cryptoconditions
710 Env env(*this, features);
711 auto const baseFee = env.current()->fees().base;
712 env.fund(XRP(5000), "alice", "bob", "carol");
713 auto const seq = env.seq("alice");
714 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
715 env(escrow::create("alice", "carol", XRP(1000)),
717 escrow::kCancelTime(env.now() + 1s));
718 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
719 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
720 env.require(Balance("carol", XRP(5000)));
721 env(escrow::cancel("bob", "alice", seq), Ter(tecNO_PERMISSION));
722 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
723
724 // Attempt to finish without a kFulfillment
725 env(escrow::finish("bob", "alice", seq), Ter(tecCRYPTOCONDITION_ERROR));
726 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
727
728 // Attempt to finish with a kCondition instead of a kFulfillment
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 an incorrect kCondition and various
749 // combinations of correct and incorrect fulfillments.
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 the correct kCondition & kFulfillment
770 env(escrow::finish("bob", "alice", seq),
773 Fee(150 * baseFee));
774
775 // SLE removed on finish
776 BEAST_EXPECT(!env.le(keylet::escrow(Account("alice").id(), seq)));
777 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
778 env.require(Balance("carol", XRP(6000)));
779 env(escrow::cancel("bob", "alice", seq), Ter(tecNO_TARGET));
780 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
781 env(escrow::cancel("bob", "carol", 1), Ter(tecNO_TARGET));
782 }
783 { // Test cancel when kCondition is present
784 Env env(*this, features);
785 auto const baseFee = env.current()->fees().base;
786 env.fund(XRP(5000), "alice", "bob", "carol");
787 auto const seq = env.seq("alice");
788 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
789 env(escrow::create("alice", "carol", XRP(1000)),
791 escrow::kCancelTime(env.now() + 1s));
792 env.close();
793 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
794 // balance restored on cancel
795 env(escrow::cancel("bob", "alice", seq));
796 env.require(Balance("alice", XRP(5000) - drops(baseFee)));
797 // SLE removed on cancel
798 BEAST_EXPECT(!env.le(keylet::escrow(Account("alice").id(), seq)));
799 }
800 {
801 Env env(*this, features);
802 auto const baseFee = env.current()->fees().base;
803 env.fund(XRP(5000), "alice", "bob", "carol");
804 env.close();
805 auto const seq = env.seq("alice");
806 env(escrow::create("alice", "carol", XRP(1000)),
808 escrow::kCancelTime(env.now() + 1s));
809 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
810 // cancel fails before expiration
811 env(escrow::cancel("bob", "alice", seq), Ter(tecNO_PERMISSION));
812 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
813 env.close();
814 // finish fails after expiration
815 env(escrow::finish("bob", "alice", seq),
818 Fee(150 * baseFee),
820 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
821 env.require(Balance("carol", XRP(5000)));
822 }
823 { // Test long & short conditions during creation
824 Env env(*this, features);
825 env.fund(XRP(5000), "alice", "bob", "carol");
826
828 v.resize(escrow::kCb1.size() + 2, 0x78);
829 std::memcpy(v.data() + 1, escrow::kCb1.data(), escrow::kCb1.size());
830
831 auto const p = v.data();
832 auto const s = v.size();
833
834 auto const ts = env.now() + 1s;
835
836 // All these are expected to fail, because the
837 // kCondition we pass in is malformed in some way
838 env(escrow::create("alice", "carol", XRP(1000)),
842 env(escrow::create("alice", "carol", XRP(1000)),
843 escrow::kCondition(Slice{p, s - 1}),
846 env(escrow::create("alice", "carol", XRP(1000)),
847 escrow::kCondition(Slice{p, s - 2}),
850 env(escrow::create("alice", "carol", XRP(1000)),
851 escrow::kCondition(Slice{p + 1, s - 1}),
854 env(escrow::create("alice", "carol", XRP(1000)),
855 escrow::kCondition(Slice{p + 1, s - 3}),
858 env(escrow::create("alice", "carol", XRP(1000)),
859 escrow::kCondition(Slice{p + 2, s - 2}),
862 env(escrow::create("alice", "carol", XRP(1000)),
863 escrow::kCondition(Slice{p + 2, s - 3}),
866
867 auto const seq = env.seq("alice");
868 auto const baseFee = env.current()->fees().base;
869 env(escrow::create("alice", "carol", XRP(1000)),
870 escrow::kCondition(Slice{p + 1, s - 2}),
872 Fee(10 * baseFee));
873 env(escrow::finish("bob", "alice", seq),
876 Fee(150 * baseFee));
877 env.require(Balance("alice", XRP(4000) - drops(10 * baseFee)));
878 env.require(Balance("bob", XRP(5000) - drops(150 * baseFee)));
879 env.require(Balance("carol", XRP(6000)));
880 }
881 { // Test long and short conditions & fulfillments during finish
882 Env env(*this, features);
883 env.fund(XRP(5000), "alice", "bob", "carol");
884
886 cv.resize(escrow::kCb2.size() + 2, 0x78);
887 std::memcpy(cv.data() + 1, escrow::kCb2.data(), escrow::kCb2.size());
888
889 auto const cp = cv.data();
890 auto const cs = cv.size();
891
893 fv.resize(escrow::kFb2.size() + 2, 0x13);
894 std::memcpy(fv.data() + 1, escrow::kFb2.data(), escrow::kFb2.size());
895
896 auto const fp = fv.data();
897 auto const fs = fv.size();
898
899 auto const ts = env.now() + 1s;
900
901 // All these are expected to fail, because the
902 // kCondition we pass in is malformed in some way
903 env(escrow::create("alice", "carol", XRP(1000)),
904 escrow::kCondition(Slice{cp, cs}),
907 env(escrow::create("alice", "carol", XRP(1000)),
908 escrow::kCondition(Slice{cp, cs - 1}),
911 env(escrow::create("alice", "carol", XRP(1000)),
912 escrow::kCondition(Slice{cp, cs - 2}),
915 env(escrow::create("alice", "carol", XRP(1000)),
916 escrow::kCondition(Slice{cp + 1, cs - 1}),
919 env(escrow::create("alice", "carol", XRP(1000)),
920 escrow::kCondition(Slice{cp + 1, cs - 3}),
923 env(escrow::create("alice", "carol", XRP(1000)),
924 escrow::kCondition(Slice{cp + 2, cs - 2}),
927 env(escrow::create("alice", "carol", XRP(1000)),
928 escrow::kCondition(Slice{cp + 2, cs - 3}),
931
932 auto const seq = env.seq("alice");
933 auto const baseFee = env.current()->fees().base;
934 env(escrow::create("alice", "carol", XRP(1000)),
935 escrow::kCondition(Slice{cp + 1, cs - 2}),
937 Fee(10 * baseFee));
938
939 // Now, try to fulfill using the same sequence of
940 // malformed conditions.
941 env(escrow::finish("bob", "alice", seq),
942 escrow::kCondition(Slice{cp, cs}),
944 Fee(150 * baseFee),
946 env(escrow::finish("bob", "alice", seq),
947 escrow::kCondition(Slice{cp, cs - 1}),
949 Fee(150 * baseFee),
951 env(escrow::finish("bob", "alice", seq),
952 escrow::kCondition(Slice{cp, cs - 2}),
954 Fee(150 * baseFee),
956 env(escrow::finish("bob", "alice", seq),
957 escrow::kCondition(Slice{cp + 1, cs - 1}),
959 Fee(150 * baseFee),
961 env(escrow::finish("bob", "alice", seq),
962 escrow::kCondition(Slice{cp + 1, cs - 3}),
964 Fee(150 * baseFee),
966 env(escrow::finish("bob", "alice", seq),
967 escrow::kCondition(Slice{cp + 2, cs - 2}),
969 Fee(150 * baseFee),
971 env(escrow::finish("bob", "alice", seq),
972 escrow::kCondition(Slice{cp + 2, cs - 3}),
974 Fee(150 * baseFee),
976
977 // Now, using the correct kCondition, try malformed fulfillments:
978 env(escrow::finish("bob", "alice", seq),
979 escrow::kCondition(Slice{cp + 1, cs - 2}),
981 Fee(150 * baseFee),
983 env(escrow::finish("bob", "alice", seq),
984 escrow::kCondition(Slice{cp + 1, cs - 2}),
985 escrow::kFulfillment(Slice{fp, fs - 1}),
986 Fee(150 * baseFee),
988 env(escrow::finish("bob", "alice", seq),
989 escrow::kCondition(Slice{cp + 1, cs - 2}),
990 escrow::kFulfillment(Slice{fp, fs - 2}),
991 Fee(150 * baseFee),
993 env(escrow::finish("bob", "alice", seq),
994 escrow::kCondition(Slice{cp + 1, cs - 2}),
995 escrow::kFulfillment(Slice{fp + 1, fs - 1}),
996 Fee(150 * baseFee),
998 env(escrow::finish("bob", "alice", seq),
999 escrow::kCondition(Slice{cp + 1, cs - 2}),
1000 escrow::kFulfillment(Slice{fp + 1, fs - 3}),
1001 Fee(150 * baseFee),
1003 env(escrow::finish("bob", "alice", seq),
1004 escrow::kCondition(Slice{cp + 1, cs - 2}),
1005 escrow::kFulfillment(Slice{fp + 1, fs - 3}),
1006 Fee(150 * baseFee),
1008 env(escrow::finish("bob", "alice", seq),
1009 escrow::kCondition(Slice{cp + 1, cs - 2}),
1010 escrow::kFulfillment(Slice{fp + 2, fs - 2}),
1011 Fee(150 * baseFee),
1013 env(escrow::finish("bob", "alice", seq),
1014 escrow::kCondition(Slice{cp + 1, cs - 2}),
1015 escrow::kFulfillment(Slice{fp + 2, fs - 3}),
1016 Fee(150 * baseFee),
1018
1019 // Now try for the right one
1020 env(escrow::finish("bob", "alice", seq),
1023 Fee(150 * baseFee));
1024 env.require(Balance("alice", XRP(4000) - drops(10 * baseFee)));
1025 env.require(Balance("carol", XRP(6000)));
1026 }
1027 { // Test empty kCondition during creation and
1028 // empty kCondition & kFulfillment during finish
1029 Env env(*this, features);
1030 env.fund(XRP(5000), "alice", "bob", "carol");
1031
1032 env(escrow::create("alice", "carol", XRP(1000)),
1034 escrow::kCancelTime(env.now() + 1s),
1035 Ter(temMALFORMED));
1036
1037 auto const seq = env.seq("alice");
1038 auto const baseFee = env.current()->fees().base;
1039 env(escrow::create("alice", "carol", XRP(1000)),
1041 escrow::kCancelTime(env.now() + 1s));
1042
1043 env(escrow::finish("bob", "alice", seq),
1046 Fee(150 * baseFee),
1048 env(escrow::finish("bob", "alice", seq),
1051 Fee(150 * baseFee),
1053 env(escrow::finish("bob", "alice", seq),
1056 Fee(150 * baseFee),
1058
1059 // Assemble finish that is missing the Condition or the Fulfillment
1060 // since either both must be present, or neither can:
1061 env(escrow::finish("bob", "alice", seq),
1063 Ter(temMALFORMED));
1064 env(escrow::finish("bob", "alice", seq),
1066 Ter(temMALFORMED));
1067
1068 // Now finish it.
1069 env(escrow::finish("bob", "alice", seq),
1072 Fee(150 * baseFee));
1073 env.require(Balance("carol", XRP(6000)));
1074 env.require(Balance("alice", XRP(4000) - drops(baseFee)));
1075 }
1076 { // Test a kCondition other than PreimageSha256, which
1077 // would require a separate amendment
1078 Env env(*this, features);
1079 env.fund(XRP(5000), "alice", "bob");
1080
1081 std::array<std::uint8_t, 45> const cb = {
1082 {0xA2, 0x2B, 0x80, 0x20, 0x42, 0x4A, 0x70, 0x49, 0x49, 0x52, 0x92, 0x67,
1083 0xB6, 0x21, 0xB3, 0xD7, 0x91, 0x19, 0xD7, 0x29, 0xB2, 0x38, 0x2C, 0xED,
1084 0x8B, 0x29, 0x6C, 0x3C, 0x02, 0x8F, 0xA9, 0x7D, 0x35, 0x0F, 0x6D, 0x07,
1085 0x81, 0x03, 0x06, 0x34, 0xD2, 0x82, 0x02, 0x03, 0xC8}};
1086
1087 // FIXME: this transaction should, eventually, return temDISABLED
1088 // instead of temMALFORMED.
1089 env(escrow::create("alice", "bob", XRP(1000)),
1091 escrow::kCancelTime(env.now() + 1s),
1092 Ter(temMALFORMED));
1093 }
1094 }
1095
1096 void
1098 {
1099 using namespace jtx;
1100 using namespace std::chrono;
1101
1102 auto const alice = Account("alice");
1103 auto const bruce = Account("bruce");
1104 auto const carol = Account("carol");
1105
1106 {
1107 testcase("Metadata to self");
1108
1109 Env env(*this, features);
1110 env.fund(XRP(5000), alice, bruce, carol);
1111 auto const aseq = env.seq(alice);
1112 auto const bseq = env.seq(bruce);
1113
1114 env(escrow::create(alice, alice, XRP(1000)),
1115 escrow::kFinishTime(env.now() + 1s),
1116 escrow::kCancelTime(env.now() + 500s));
1117 BEAST_EXPECT(
1118 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1119 env.close(5s);
1120 auto const aa = env.le(keylet::escrow(alice.id(), aseq));
1121 BEAST_EXPECT(aa);
1122
1123 {
1124 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1125 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1126 BEAST_EXPECT(
1127 // NOLINTNEXTLINE(modernize-use-ranges)
1128 std::find(aod.begin(), aod.end(), aa) != aod.end());
1129 }
1130
1131 env(escrow::create(bruce, bruce, XRP(1000)),
1132 escrow::kFinishTime(env.now() + 1s),
1133 escrow::kCancelTime(env.now() + 2s));
1134 BEAST_EXPECT(
1135 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1136 env.close(5s);
1137 auto const bb = env.le(keylet::escrow(bruce.id(), bseq));
1138 BEAST_EXPECT(bb);
1139
1140 {
1141 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id()));
1142 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1143 BEAST_EXPECT(
1144 // NOLINTNEXTLINE(modernize-use-ranges)
1145 std::find(bod.begin(), bod.end(), bb) != bod.end());
1146 }
1147
1148 env.close(5s);
1149 env(escrow::finish(alice, alice, aseq));
1150 {
1151 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq)));
1152 BEAST_EXPECT(
1153 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1154
1155 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1156 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0);
1157 BEAST_EXPECT(
1158 // NOLINTNEXTLINE(modernize-use-ranges)
1159 std::find(aod.begin(), aod.end(), aa) == aod.end());
1160
1161 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id()));
1162 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1163 BEAST_EXPECT(
1164 // NOLINTNEXTLINE(modernize-use-ranges)
1165 std::find(bod.begin(), bod.end(), bb) != bod.end());
1166 }
1167
1168 env.close(5s);
1169 env(escrow::cancel(bruce, bruce, bseq));
1170 {
1171 BEAST_EXPECT(!env.le(keylet::escrow(bruce.id(), bseq)));
1172 BEAST_EXPECT(
1173 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1174
1175 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id()));
1176 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 0);
1177 BEAST_EXPECT(
1178 // NOLINTNEXTLINE(modernize-use-ranges)
1179 std::find(bod.begin(), bod.end(), bb) == bod.end());
1180 }
1181 }
1182 {
1183 testcase("Metadata to other");
1184
1185 Env env(*this, features);
1186 env.fund(XRP(5000), alice, bruce, carol);
1187 auto const aseq = env.seq(alice);
1188 auto const bseq = env.seq(bruce);
1189
1190 env(escrow::create(alice, bruce, XRP(1000)), escrow::kFinishTime(env.now() + 1s));
1191 BEAST_EXPECT(
1192 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1193 env.close(5s);
1194 env(escrow::create(bruce, carol, XRP(1000)),
1195 escrow::kFinishTime(env.now() + 1s),
1196 escrow::kCancelTime(env.now() + 2s));
1197 BEAST_EXPECT(
1198 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1199 env.close(5s);
1200
1201 auto const ab = env.le(keylet::escrow(alice.id(), aseq));
1202 BEAST_EXPECT(ab);
1203
1204 auto const bc = env.le(keylet::escrow(bruce.id(), bseq));
1205 BEAST_EXPECT(bc);
1206
1207 {
1208 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1209 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1210 BEAST_EXPECT(
1211 // NOLINTNEXTLINE(modernize-use-ranges)
1212 std::find(aod.begin(), aod.end(), ab) != aod.end());
1213
1214 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id()));
1215 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
1216 BEAST_EXPECT(
1217 // NOLINTNEXTLINE(modernize-use-ranges)
1218 std::find(bod.begin(), bod.end(), ab) != bod.end());
1219 BEAST_EXPECT(
1220 // NOLINTNEXTLINE(modernize-use-ranges)
1221 std::find(bod.begin(), bod.end(), bc) != bod.end());
1222
1223 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1224 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1225 BEAST_EXPECT(
1226 // NOLINTNEXTLINE(modernize-use-ranges)
1227 std::find(cod.begin(), cod.end(), bc) != cod.end());
1228 }
1229
1230 env.close(5s);
1231 env(escrow::finish(alice, alice, aseq));
1232 {
1233 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq)));
1234 BEAST_EXPECT(env.le(keylet::escrow(bruce.id(), bseq)));
1235
1236 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1237 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0);
1238 BEAST_EXPECT(
1239 // NOLINTNEXTLINE(modernize-use-ranges)
1240 std::find(aod.begin(), aod.end(), ab) == aod.end());
1241
1242 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id()));
1243 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1244 BEAST_EXPECT(
1245 // NOLINTNEXTLINE(modernize-use-ranges)
1246 std::find(bod.begin(), bod.end(), ab) == bod.end());
1247 BEAST_EXPECT(
1248 // NOLINTNEXTLINE(modernize-use-ranges)
1249 std::find(bod.begin(), bod.end(), bc) != bod.end());
1250
1251 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1252 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1253 }
1254
1255 env.close(5s);
1256 env(escrow::cancel(bruce, bruce, bseq));
1257 {
1258 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq)));
1259 BEAST_EXPECT(!env.le(keylet::escrow(bruce.id(), bseq)));
1260
1261 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1262 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0);
1263 BEAST_EXPECT(
1264 // NOLINTNEXTLINE(modernize-use-ranges)
1265 std::find(aod.begin(), aod.end(), ab) == aod.end());
1266
1267 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bruce.id()));
1268 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 0);
1269 BEAST_EXPECT(
1270 // NOLINTNEXTLINE(modernize-use-ranges)
1271 std::find(bod.begin(), bod.end(), ab) == bod.end());
1272 BEAST_EXPECT(
1273 // NOLINTNEXTLINE(modernize-use-ranges)
1274 std::find(bod.begin(), bod.end(), bc) == bod.end());
1275
1276 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1277 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 0);
1278 }
1279 }
1280 }
1281
1282 void
1284 {
1285 testcase("Consequences");
1286
1287 using namespace jtx;
1288 using namespace std::chrono;
1289 Env env(*this, features);
1290 auto const baseFee = env.current()->fees().base;
1291
1292 env.memoize("alice");
1293 env.memoize("bob");
1294 env.memoize("carol");
1295
1296 {
1297 auto const jtx = env.jt(
1298 escrow::create("alice", "carol", XRP(1000)),
1299 escrow::kFinishTime(env.now() + 1s),
1300 Seq(1),
1301 Fee(baseFee));
1302 auto const pf =
1303 preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
1304 BEAST_EXPECT(isTesSuccess(pf.ter));
1305 BEAST_EXPECT(!pf.consequences.isBlocker());
1306 BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
1307 BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(1000));
1308 }
1309
1310 {
1311 auto const jtx = env.jt(escrow::cancel("bob", "alice", 3), Seq(1), Fee(baseFee));
1312 auto const pf =
1313 preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
1314 BEAST_EXPECT(isTesSuccess(pf.ter));
1315 BEAST_EXPECT(!pf.consequences.isBlocker());
1316 BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
1317 BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0));
1318 }
1319
1320 {
1321 auto const jtx = env.jt(escrow::finish("bob", "alice", 3), Seq(1), Fee(baseFee));
1322 auto const pf =
1323 preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
1324 BEAST_EXPECT(isTesSuccess(pf.ter));
1325 BEAST_EXPECT(!pf.consequences.isBlocker());
1326 BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
1327 BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0));
1328 }
1329 }
1330
1331 void
1333 {
1334 testcase("Escrow with tickets");
1335
1336 using namespace jtx;
1337 using namespace std::chrono;
1338 Account const alice{"alice"};
1339 Account const bob{"bob"};
1340
1341 {
1342 // Create escrow and finish using tickets.
1343 Env env(*this, features);
1344 auto const baseFee = env.current()->fees().base;
1345 env.fund(XRP(5000), alice, bob);
1346 env.close();
1347
1348 // alice creates a ticket.
1349 std::uint32_t const aliceTicket{env.seq(alice) + 1};
1350 env(ticket::create(alice, 1));
1351
1352 // bob creates a bunch of tickets because he will be burning
1353 // through them with tec transactions. Just because we can
1354 // we'll use them up starting from largest and going smaller.
1355 static constexpr std::uint32_t kBobTicketCount{20};
1356 env(ticket::create(bob, kBobTicketCount));
1357 env.close();
1358 std::uint32_t bobTicket{env.seq(bob)};
1359 env.require(tickets(alice, 1));
1360 env.require(tickets(bob, kBobTicketCount));
1361
1362 // Note that from here on all transactions use tickets. No account
1363 // root sequences should change.
1364 std::uint32_t const aliceRootSeq{env.seq(alice)};
1365 std::uint32_t const bobRootSeq{env.seq(bob)};
1366
1367 // alice creates an escrow that can be finished in the future
1368 auto const ts = env.now() + 97s;
1369
1370 std::uint32_t const escrowSeq = aliceTicket;
1371 env(escrow::create(alice, bob, XRP(1000)),
1373 ticket::Use(aliceTicket));
1374 BEAST_EXPECT(env.seq(alice) == aliceRootSeq);
1375 env.require(tickets(alice, 0));
1376 env.require(tickets(bob, kBobTicketCount));
1377
1378 // Advance the ledger, verifying that the finish won't complete
1379 // prematurely. Note that each tec consumes one of bob's tickets.
1380 for (; env.now() < ts; env.close())
1381 {
1382 env(escrow::finish(bob, alice, escrowSeq),
1383 Fee(150 * baseFee),
1384 ticket::Use(--bobTicket),
1386 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1387 }
1388
1389 // bob tries to re-use a ticket, which is rejected.
1390 env(escrow::finish(bob, alice, escrowSeq),
1391 Fee(150 * baseFee),
1392 ticket::Use(bobTicket),
1393 Ter(tefNO_TICKET));
1394
1395 // bob uses one of his remaining tickets. Success!
1396 env(escrow::finish(bob, alice, escrowSeq),
1397 Fee(150 * baseFee),
1398 ticket::Use(--bobTicket));
1399 env.close();
1400 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1401 }
1402 {
1403 // Create escrow and cancel using tickets.
1404 Env env(*this, features);
1405 auto const baseFee = env.current()->fees().base;
1406 env.fund(XRP(5000), alice, bob);
1407 env.close();
1408
1409 // alice creates a ticket.
1410 std::uint32_t const aliceTicket{env.seq(alice) + 1};
1411 env(ticket::create(alice, 1));
1412
1413 // bob creates a bunch of tickets because he will be burning
1414 // through them with tec transactions.
1415 static constexpr std::uint32_t kBobTicketCount{20};
1416 std::uint32_t bobTicket{env.seq(bob) + 1};
1417 env(ticket::create(bob, kBobTicketCount));
1418 env.close();
1419 env.require(tickets(alice, 1));
1420 env.require(tickets(bob, kBobTicketCount));
1421
1422 // Note that from here on all transactions use tickets. No account
1423 // root sequences should change.
1424 std::uint32_t const aliceRootSeq{env.seq(alice)};
1425 std::uint32_t const bobRootSeq{env.seq(bob)};
1426
1427 // alice creates an escrow that can be finished in the future.
1428 auto const ts = env.now() + 117s;
1429
1430 std::uint32_t const escrowSeq = aliceTicket;
1431 env(escrow::create(alice, bob, XRP(1000)),
1434 ticket::Use(aliceTicket));
1435 BEAST_EXPECT(env.seq(alice) == aliceRootSeq);
1436 env.require(tickets(alice, 0));
1437 env.require(tickets(bob, kBobTicketCount));
1438
1439 // Advance the ledger, verifying that the cancel won't complete
1440 // prematurely.
1441 for (; env.now() < ts; env.close())
1442 {
1443 env(escrow::cancel(bob, alice, escrowSeq),
1444 Fee(150 * baseFee),
1445 ticket::Use(bobTicket++),
1447 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1448 }
1449
1450 // Verify that a finish won't work anymore.
1451 env(escrow::finish(bob, alice, escrowSeq),
1454 Fee(150 * baseFee),
1455 ticket::Use(bobTicket++),
1457 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1458
1459 // Verify that the cancel succeeds.
1460 env(escrow::cancel(bob, alice, escrowSeq),
1461 Fee(150 * baseFee),
1462 ticket::Use(bobTicket++));
1463 env.close();
1464 BEAST_EXPECT(env.seq(bob) == bobRootSeq);
1465
1466 // Verify that bob actually consumed his tickets.
1467 env.require(tickets(bob, env.seq(bob) - bobTicket));
1468 }
1469 }
1470
1471 void
1473 {
1474 testcase("Test with credentials");
1475
1476 using namespace jtx;
1477 using namespace std::chrono;
1478
1479 Account const alice{"alice"};
1480 Account const bob{"bob"};
1481 Account const carol{"carol"};
1482 Account const dillon{"dillon "};
1483 Account const zelda{"zelda"};
1484
1485 char const credType[] = "abcde";
1486
1487 {
1488 // Credentials amendment not enabled
1489 Env env(*this, features - featureCredentials);
1490 env.fund(XRP(5000), alice, bob);
1491 env.close();
1492
1493 auto const seq = env.seq(alice);
1494 env(escrow::create(alice, bob, XRP(1000)), escrow::kFinishTime(env.now() + 1s));
1495 env.close();
1496
1497 env(fset(bob, asfDepositAuth));
1498 env.close();
1499 env(deposit::auth(bob, alice));
1500 env.close();
1501
1502 std::string const credIdx =
1503 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
1504 "E4";
1505 env(escrow::finish(bob, alice, seq), credentials::Ids({credIdx}), Ter(temDISABLED));
1506 }
1507
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 auto const jv = credentials::ledgerEntry(env, carol, zelda, credType);
1517 std::string const credIdx = jv[jss::result][jss::index].asString();
1518
1519 auto const seq = env.seq(alice);
1520 env(escrow::create(alice, bob, XRP(1000)), escrow::kFinishTime(env.now() + 50s));
1521 env.close();
1522
1523 // Bob require pre-authorization
1524 env(fset(bob, asfDepositAuth));
1525 env.close();
1526
1527 // Fail, credentials not accepted
1528 env(escrow::finish(carol, alice, seq),
1529 credentials::Ids({credIdx}),
1531
1532 env.close();
1533
1534 env(credentials::accept(carol, zelda, credType));
1535 env.close();
1536
1537 // Fail, credentials doesn’t belong to root account
1538 env(escrow::finish(dillon, alice, seq),
1539 credentials::Ids({credIdx}),
1541
1542 // Fail, no depositPreauth
1543 env(escrow::finish(carol, alice, seq),
1544 credentials::Ids({credIdx}),
1546
1547 env(deposit::authCredentials(bob, {{.issuer = zelda, .credType = credType}}));
1548 env.close();
1549
1550 // Success
1551 env.close();
1552 env(escrow::finish(carol, alice, seq), credentials::Ids({credIdx}));
1553 env.close();
1554 }
1555
1556 {
1557 testcase("Escrow with credentials without depositPreauth");
1558 using namespace std::chrono;
1559
1560 Env env(*this, features);
1561
1562 env.fund(XRP(5000), alice, bob, carol, dillon, zelda);
1563 env.close();
1564
1565 env(credentials::create(carol, zelda, credType));
1566 env.close();
1567 env(credentials::accept(carol, zelda, credType));
1568 env.close();
1569 auto const jv = credentials::ledgerEntry(env, carol, zelda, credType);
1570 std::string const credIdx = jv[jss::result][jss::index].asString();
1571
1572 auto const seq = env.seq(alice);
1573 env(escrow::create(alice, bob, XRP(1000)), escrow::kFinishTime(env.now() + 50s));
1574 // time advance
1575 env.close();
1576 env.close();
1577 env.close();
1578 env.close();
1579 env.close();
1580 env.close();
1581
1582 // Succeed, Bob doesn't require pre-authorization
1583 env(escrow::finish(carol, alice, seq), credentials::Ids({credIdx}));
1584 env.close();
1585
1586 {
1587 char const credType2[] = "random";
1588
1589 env(credentials::create(bob, zelda, credType2));
1590 env.close();
1591 env(credentials::accept(bob, zelda, credType2));
1592 env.close();
1593 auto const credIdxBob =
1594 credentials::ledgerEntry(env, bob, zelda, credType2)[jss::result][jss::index]
1595 .asString();
1596
1597 auto const seq = env.seq(alice);
1598 env(escrow::create(alice, bob, XRP(1000)), escrow::kFinishTime(env.now() + 1s));
1599 env.close();
1600
1601 // Bob require pre-authorization
1602 env(fset(bob, asfDepositAuth));
1603 env.close();
1604 env(deposit::authCredentials(bob, {{.issuer = zelda, .credType = credType}}));
1605 env.close();
1606
1607 // Use any valid credentials if account == dst
1608 env(escrow::finish(bob, alice, seq), credentials::Ids({credIdxBob}));
1609 env.close();
1610 }
1611 }
1612 }
1613
1614 void
1616 {
1617 testEnablement(features);
1618 testTiming(features);
1619 testTags(features);
1620 testDisallowXRP(features);
1622 testFails(features);
1623 testLockup(features);
1624 testEscrowConditions(features);
1625 testMetaAndOwnership(features);
1626 testConsequences(features);
1627 testEscrowWithTickets(features);
1628 testCredentials(features);
1629 }
1630
1631public:
1632 void
1633 run() override
1634 {
1635 using namespace test::jtx;
1636 FeatureBitset const all{testableAmendments()};
1637 testWithFeats(all);
1638 testWithFeats(all - featureTokenEscrow);
1639 testTags(all - fixIncludeKeyletFields);
1640 }
1641};
1642
1644
1645} // namespace xrpl::test
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
std::string asString() const
Returns the unquoted string value.
A class that simplifies iterating ledger directory pages.
Definition Dir.h:21
ConstIterator begin() const
Definition Dir.cpp:26
ConstIterator end() const
Definition Dir.cpp:44
An immutable linear range of bytes.
Definition Slice.h:26
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
A transaction testing environment.
Definition Env.h:143
Application & app()
Definition Env.h:280
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
SLE::const_pointer le(Account const &account) const
Return an account root.
Definition Env.cpp:284
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:275
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
Definition Env.h:566
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:201
std::shared_ptr< STObject const > meta()
Return metadata for the last JTx.
Definition Env.cpp:511
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:174
beast::Journal const journal
Definition Env.h:184
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:605
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
NetClock::time_point now()
Returns the current network time.
Definition Env.h:305
Set the fee on a JTx.
Definition fee.h:15
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:13
Set the flags on a JTx.
Definition txflags.h:9
Set a ticket sequence on a JTx.
Definition ticket.h:26
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:372
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:357
json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:29
json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:16
json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:56
json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:38
json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:16
json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount)
Definition escrow.cpp:21
json::Value cancel(AccountID const &account, Account const &from, std::uint32_t seq)
Definition escrow.cpp:45
auto const kCondition
Definition escrow.h:74
auto const kCancelTime
Set the "CancelAfter" time tag on a JTx.
Definition escrow.h:72
auto const kFinishTime
Set the "FinishAfter" time tag on a JTx.
Definition escrow.h:69
std::array< std::uint8_t, 8 > const kFb3
Definition escrow.h:61
std::array< std::uint8_t, 7 > const kFb2
Definition escrow.h:53
std::array< std::uint8_t, 39 > const kCb2
Definition escrow.h:55
auto const kFulfillment
Definition escrow.h:76
json::Value finish(AccountID const &account, AccountID const &from, std::uint32_t seq)
Definition escrow.cpp:33
std::array< std::uint8_t, 4 > const kFb1
Definition escrow.h:45
std::array< std::uint8_t, 39 > const kCb1
Definition escrow.h:47
std::array< std::uint8_t, 39 > const kCb3
Definition escrow.h:63
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:16
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
FeatureBitset testableAmendments()
Definition Env.h:76
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
OwnerCount< ltTICKET > tickets
Match the number of tickets on the account.
Definition ticket.h:42
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:15
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
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:175
@ TapNone
Definition ApplyView.h:13
@ temBAD_EXPIRATION
Definition TER.h:77
@ temINVALID_FLAG
Definition TER.h:97
@ temMALFORMED
Definition TER.h:73
@ temDISABLED
Definition TER.h:100
@ temBAD_AMOUNT
Definition TER.h:75
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
@ tecNO_TARGET
Definition TER.h:302
@ tecBAD_CREDENTIALS
Definition TER.h:357
@ tecCRYPTOCONDITION_ERROR
Definition TER.h:310
@ tecINSUFFICIENT_RESERVE
Definition TER.h:305
@ tecNO_PERMISSION
Definition TER.h:303
@ tecDST_TAG_NEEDED
Definition TER.h:307
@ tecNO_DST
Definition TER.h:288
@ tecUNFUNDED
Definition TER.h:293
@ tesSUCCESS
Definition TER.h:240
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:9
Set the sequence number on a JTx.
Definition seq.h:12
Set the source tag on a JTx.
Definition tag.h:24