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