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