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