rippled
Loading...
Searching...
No Matches
PayChan_test.cpp
1#include <test/jtx.h>
2
3#include <xrpl/basics/chrono.h>
4#include <xrpl/ledger/Dir.h>
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/Indexes.h>
7#include <xrpl/protocol/PayChan.h>
8#include <xrpl/protocol/TxFlags.h>
9#include <xrpl/protocol/jss.h>
10
11namespace ripple {
12namespace test {
13using namespace jtx::paychan;
14
16{
19 ReadView const& view,
20 jtx::Account const& account,
21 jtx::Account const& dst)
22 {
23 auto const sle = view.read(keylet::account(account));
24 if (!sle)
25 return {};
26 auto const k = keylet::payChan(account, dst, (*sle)[sfSequence] - 1);
27 return {k.key, view.read(k)};
28 }
29
30 static Buffer
32 PublicKey const& pk,
33 SecretKey const& sk,
34 uint256 const& channel,
35 STAmount const& authAmt)
36 {
37 Serializer msg;
39 return sign(pk, sk, msg.slice());
40 }
41
42 static STAmount
43 channelAmount(ReadView const& view, uint256 const& chan)
44 {
45 auto const slep = view.read({ltPAYCHAN, chan});
46 if (!slep)
47 return XRPAmount{-1};
48 return (*slep)[sfAmount];
49 }
50
52 channelExpiration(ReadView const& view, uint256 const& chan)
53 {
54 auto const slep = view.read({ltPAYCHAN, chan});
55 if (!slep)
56 return std::nullopt;
57 if (auto const r = (*slep)[~sfExpiration])
58 return r.value();
59 return std::nullopt;
60 }
61
62 void
64 {
65 testcase("simple");
66 using namespace jtx;
67 using namespace std::literals::chrono_literals;
68 Env env{*this, features};
69 auto const alice = Account("alice");
70 auto const bob = Account("bob");
71 auto USDA = alice["USD"];
72 env.fund(XRP(10000), alice, bob);
73 auto const pk = alice.pk();
74 auto const settleDelay = 100s;
75 auto const chan = channel(alice, bob, env.seq(alice));
76 env(create(alice, bob, XRP(1000), settleDelay, pk));
77 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
78 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
79
80 {
81 auto const preAlice = env.balance(alice);
82 env(fund(alice, chan, XRP(1000)));
83 auto const feeDrops = env.current()->fees().base;
84 BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
85 }
86
87 auto chanBal = channelBalance(*env.current(), chan);
88 auto chanAmt = channelAmount(*env.current(), chan);
89 BEAST_EXPECT(chanBal == XRP(0));
90 BEAST_EXPECT(chanAmt == XRP(2000));
91
92 {
93 // bad amounts (non-xrp, negative amounts)
94 env(create(alice, bob, USDA(1000), settleDelay, pk),
96 env(fund(alice, chan, USDA(1000)), ter(temBAD_AMOUNT));
97 env(create(alice, bob, XRP(-1000), settleDelay, pk),
99 env(fund(alice, chan, XRP(-1000)), ter(temBAD_AMOUNT));
100 }
101
102 // invalid account
103 env(create(alice, "noAccount", XRP(1000), settleDelay, pk),
104 ter(tecNO_DST));
105 // can't create channel to the same account
106 env(create(alice, alice, XRP(1000), settleDelay, pk),
108 // invalid channel
109
110 env(fund(
111 alice,
112 channel(alice, "noAccount", env.seq(alice) - 1),
113 XRP(1000)),
115 // not enough funds
116 env(create(alice, bob, XRP(10000), settleDelay, pk), ter(tecUNFUNDED));
117
118 {
119 // No signature claim with bad amounts (negative and non-xrp)
120 auto const iou = USDA(100).value();
121 auto const negXRP = XRP(-100).value();
122 auto const posXRP = XRP(100).value();
123 env(claim(alice, chan, iou, iou), ter(temBAD_AMOUNT));
124 env(claim(alice, chan, posXRP, iou), ter(temBAD_AMOUNT));
125 env(claim(alice, chan, iou, posXRP), ter(temBAD_AMOUNT));
126 env(claim(alice, chan, negXRP, negXRP), ter(temBAD_AMOUNT));
127 env(claim(alice, chan, posXRP, negXRP), ter(temBAD_AMOUNT));
128 env(claim(alice, chan, negXRP, posXRP), ter(temBAD_AMOUNT));
129 }
130 {
131 // No signature claim more than authorized
132 auto const delta = XRP(500);
133 auto const reqBal = chanBal + delta;
134 auto const authAmt = reqBal + XRP(-100);
135 assert(reqBal <= chanAmt);
136 env(claim(alice, chan, reqBal, authAmt), ter(temBAD_AMOUNT));
137 }
138 {
139 // No signature needed since the owner is claiming
140 auto const preBob = env.balance(bob);
141 auto const delta = XRP(500);
142 auto const reqBal = chanBal + delta;
143 auto const authAmt = reqBal + XRP(100);
144 assert(reqBal <= chanAmt);
145 env(claim(alice, chan, reqBal, authAmt));
146 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
147 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
148 BEAST_EXPECT(env.balance(bob) == preBob + delta);
149 chanBal = reqBal;
150 }
151 {
152 // Claim with signature
153 auto preBob = env.balance(bob);
154 auto const delta = XRP(500);
155 auto const reqBal = chanBal + delta;
156 auto const authAmt = reqBal + XRP(100);
157 assert(reqBal <= chanAmt);
158 auto const sig =
159 signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
160 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
161 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
162 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
163 auto const feeDrops = env.current()->fees().base;
164 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
165 chanBal = reqBal;
166
167 // claim again
168 preBob = env.balance(bob);
169 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()),
171 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
172 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
173 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
174 }
175 {
176 // Try to claim more than authorized
177 auto const preBob = env.balance(bob);
178 STAmount const authAmt = chanBal + XRP(500);
179 STAmount const reqAmt = authAmt + STAmount{1};
180 assert(reqAmt <= chanAmt);
181 auto const sig =
182 signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
183 env(claim(bob, chan, reqAmt, authAmt, Slice(sig), alice.pk()),
185 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
186 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
187 BEAST_EXPECT(env.balance(bob) == preBob);
188 }
189
190 // Dst tries to fund the channel
191 env(fund(bob, chan, XRP(1000)), ter(tecNO_PERMISSION));
192 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
193 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
194
195 {
196 // Wrong signing key
197 auto const sig = signClaimAuth(bob.pk(), bob.sk(), chan, XRP(1500));
198 env(claim(
199 bob,
200 chan,
201 XRP(1500).value(),
202 XRP(1500).value(),
203 Slice(sig),
204 bob.pk()),
206 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
207 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
208 }
209 {
210 // Bad signature
211 auto const sig = signClaimAuth(bob.pk(), bob.sk(), chan, XRP(1500));
212 env(claim(
213 bob,
214 chan,
215 XRP(1500).value(),
216 XRP(1500).value(),
217 Slice(sig),
218 alice.pk()),
220 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
221 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
222 }
223 {
224 // Dst closes channel
225 auto const preAlice = env.balance(alice);
226 auto const preBob = env.balance(bob);
227 env(claim(bob, chan), txflags(tfClose));
228 BEAST_EXPECT(!channelExists(*env.current(), chan));
229 auto const feeDrops = env.current()->fees().base;
230 auto const delta = chanAmt - chanBal;
231 assert(delta > beast::zero);
232 BEAST_EXPECT(env.balance(alice) == preAlice + delta);
233 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
234 }
235 }
236
237 void
239 {
240 testcase("Disallow Incoming Flag");
241 using namespace jtx;
242
243 using namespace std::literals::chrono_literals;
244 Env env{*this, features};
245 auto const alice = Account("alice");
246 auto const bob = Account("bob");
247 auto const cho = Account("cho");
248 env.fund(XRP(10000), alice, bob, cho);
249 auto const pk = alice.pk();
250 auto const settleDelay = 100s;
251
252 // set flag on bob only
254 env.close();
255
256 // channel creation from alice to bob is disallowed
257 {
258 auto const chan = channel(alice, bob, env.seq(alice));
259 env(create(alice, bob, XRP(1000), settleDelay, pk),
261 BEAST_EXPECT(!channelExists(*env.current(), chan));
262 }
263
264 // set flag on alice also
265 env(fset(alice, asfDisallowIncomingPayChan));
266 env.close();
267
268 // channel creation from bob to alice is now disallowed
269 {
270 auto const chan = channel(bob, alice, env.seq(bob));
271 env(create(bob, alice, XRP(1000), settleDelay, pk),
273 BEAST_EXPECT(!channelExists(*env.current(), chan));
274 }
275
276 // remove flag from bob
278 env.close();
279
280 // now the channel between alice and bob can exist
281 {
282 auto const chan = channel(alice, bob, env.seq(alice));
283 env(create(alice, bob, XRP(1000), settleDelay, pk),
284 ter(tesSUCCESS));
285 BEAST_EXPECT(channelExists(*env.current(), chan));
286 }
287
288 // a channel from cho to alice isn't allowed
289 {
290 auto const chan = channel(cho, alice, env.seq(cho));
291 env(create(cho, alice, XRP(1000), settleDelay, pk),
293 BEAST_EXPECT(!channelExists(*env.current(), chan));
294 }
295
296 // remove flag from alice
298 env.close();
299
300 // now a channel from cho to alice is allowed
301 {
302 auto const chan = channel(cho, alice, env.seq(cho));
303 env(create(cho, alice, XRP(1000), settleDelay, pk),
304 ter(tesSUCCESS));
305 BEAST_EXPECT(channelExists(*env.current(), chan));
306 }
307 }
308
309 void
311 {
312 testcase("cancel after");
313 using namespace jtx;
314 using namespace std::literals::chrono_literals;
315 auto const alice = Account("alice");
316 auto const bob = Account("bob");
317 auto const carol = Account("carol");
318 {
319 // If dst claims after cancel after, channel closes
320 Env env{*this, features};
321 env.fund(XRP(10000), alice, bob);
322 auto const pk = alice.pk();
323 auto const settleDelay = 100s;
324 NetClock::time_point const cancelAfter =
325 env.current()->info().parentCloseTime + 3600s;
326 auto const channelFunds = XRP(1000);
327 auto const chan = channel(alice, bob, env.seq(alice));
328 env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
329 BEAST_EXPECT(channelExists(*env.current(), chan));
330 env.close(cancelAfter);
331 {
332 // dst cannot claim after cancelAfter
333 auto const chanBal = channelBalance(*env.current(), chan);
334 auto const chanAmt = channelAmount(*env.current(), chan);
335 auto preAlice = env.balance(alice);
336 auto preBob = env.balance(bob);
337 auto const delta = XRP(500);
338 auto const reqBal = chanBal + delta;
339 auto const authAmt = reqBal + XRP(100);
340 assert(reqBal <= chanAmt);
341 auto const sig =
342 signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
343 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
344 auto const feeDrops = env.current()->fees().base;
345 BEAST_EXPECT(!channelExists(*env.current(), chan));
346 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
347 BEAST_EXPECT(env.balance(alice) == preAlice + channelFunds);
348 }
349 }
350 {
351 // Third party can close after cancel after
352 Env env{*this, features};
353 env.fund(XRP(10000), alice, bob, carol);
354 auto const pk = alice.pk();
355 auto const settleDelay = 100s;
356 NetClock::time_point const cancelAfter =
357 env.current()->info().parentCloseTime + 3600s;
358 auto const channelFunds = XRP(1000);
359 auto const chan = channel(alice, bob, env.seq(alice));
360 env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
361 BEAST_EXPECT(channelExists(*env.current(), chan));
362 // third party close before cancelAfter
363 env(claim(carol, chan), txflags(tfClose), ter(tecNO_PERMISSION));
364 BEAST_EXPECT(channelExists(*env.current(), chan));
365 env.close(cancelAfter);
366 // third party close after cancelAfter
367 auto const preAlice = env.balance(alice);
368 env(claim(carol, chan), txflags(tfClose));
369 BEAST_EXPECT(!channelExists(*env.current(), chan));
370 BEAST_EXPECT(env.balance(alice) == preAlice + channelFunds);
371 }
372 // fixPayChanCancelAfter
373 // CancelAfter should be greater than close time
374 {
375 for (bool const withFixPayChan : {true, false})
376 {
377 auto const amend = withFixPayChan
378 ? features
379 : features - fixPayChanCancelAfter;
380 Env env{*this, amend};
381 env.fund(XRP(10000), alice, bob);
382 env.close();
383
384 auto const pk = alice.pk();
385 auto const settleDelay = 100s;
386 auto const channelFunds = XRP(1000);
387 NetClock::time_point const cancelAfter =
388 env.current()->info().parentCloseTime - 1s;
389 auto const txResult =
390 withFixPayChan ? ter(tecEXPIRED) : ter(tesSUCCESS);
391 env(create(
392 alice, bob, channelFunds, settleDelay, pk, cancelAfter),
393 txResult);
394 }
395 }
396 // fixPayChanCancelAfter
397 // CancelAfter can be equal to the close time
398 {
399 for (bool const withFixPayChan : {true, false})
400 {
401 auto const amend = withFixPayChan
402 ? features
403 : features - fixPayChanCancelAfter;
404 Env env{*this, amend};
405 env.fund(XRP(10000), alice, bob);
406 env.close();
407
408 auto const pk = alice.pk();
409 auto const settleDelay = 100s;
410 auto const channelFunds = XRP(1000);
411 NetClock::time_point const cancelAfter =
412 env.current()->info().parentCloseTime;
413 env(create(
414 alice, bob, channelFunds, settleDelay, pk, cancelAfter),
415 ter(tesSUCCESS));
416 }
417 }
418 }
419
420 void
422 {
423 testcase("expiration");
424 using namespace jtx;
425 using namespace std::literals::chrono_literals;
426 Env env{*this, features};
427 auto const alice = Account("alice");
428 auto const bob = Account("bob");
429 auto const carol = Account("carol");
430 env.fund(XRP(10000), alice, bob, carol);
431 auto const pk = alice.pk();
432 auto const settleDelay = 3600s;
433 auto const closeTime = env.current()->info().parentCloseTime;
434 auto const minExpiration = closeTime + settleDelay;
435 NetClock::time_point const cancelAfter = closeTime + 7200s;
436 auto const channelFunds = XRP(1000);
437 auto const chan = channel(alice, bob, env.seq(alice));
438 env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
439 BEAST_EXPECT(channelExists(*env.current(), chan));
440 BEAST_EXPECT(!channelExpiration(*env.current(), chan));
441 // Owner closes, will close after settleDelay
442 env(claim(alice, chan), txflags(tfClose));
443 auto counts = [](auto const& t) {
444 return t.time_since_epoch().count();
445 };
446 BEAST_EXPECT(
447 *channelExpiration(*env.current(), chan) == counts(minExpiration));
448 // increase the expiration time
449 env(fund(
450 alice, chan, XRP(1), NetClock::time_point{minExpiration + 100s}));
451 BEAST_EXPECT(
452 *channelExpiration(*env.current(), chan) ==
453 counts(minExpiration) + 100);
454 // decrease the expiration, but still above minExpiration
455 env(fund(
456 alice, chan, XRP(1), NetClock::time_point{minExpiration + 50s}));
457 BEAST_EXPECT(
458 *channelExpiration(*env.current(), chan) ==
459 counts(minExpiration) + 50);
460 // decrease the expiration below minExpiration
461 env(fund(
462 alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}),
464 BEAST_EXPECT(
465 *channelExpiration(*env.current(), chan) ==
466 counts(minExpiration) + 50);
467 env(claim(bob, chan), txflags(tfRenew), ter(tecNO_PERMISSION));
468 BEAST_EXPECT(
469 *channelExpiration(*env.current(), chan) ==
470 counts(minExpiration) + 50);
471 env(claim(alice, chan), txflags(tfRenew));
472 BEAST_EXPECT(!channelExpiration(*env.current(), chan));
473 // decrease the expiration below minExpiration
474 env(fund(
475 alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}),
477 BEAST_EXPECT(!channelExpiration(*env.current(), chan));
478 env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration}));
479 env.close(minExpiration);
480 // Try to extend the expiration after the expiration has already passed
481 env(fund(
482 alice, chan, XRP(1), NetClock::time_point{minExpiration + 1000s}));
483 BEAST_EXPECT(!channelExists(*env.current(), chan));
484 }
485
486 void
488 {
489 testcase("settle delay");
490 using namespace jtx;
491 using namespace std::literals::chrono_literals;
492 Env env{*this, features};
493 auto const alice = Account("alice");
494 auto const bob = Account("bob");
495 env.fund(XRP(10000), alice, bob);
496 auto const pk = alice.pk();
497 auto const settleDelay = 3600s;
498 NetClock::time_point const settleTimepoint =
499 env.current()->info().parentCloseTime + settleDelay;
500 auto const channelFunds = XRP(1000);
501 auto const chan = channel(alice, bob, env.seq(alice));
502 env(create(alice, bob, channelFunds, settleDelay, pk));
503 BEAST_EXPECT(channelExists(*env.current(), chan));
504 // Owner closes, will close after settleDelay
505 env(claim(alice, chan), txflags(tfClose));
506 BEAST_EXPECT(channelExists(*env.current(), chan));
507 env.close(settleTimepoint - settleDelay / 2);
508 {
509 // receiver can still claim
510 auto const chanBal = channelBalance(*env.current(), chan);
511 auto const chanAmt = channelAmount(*env.current(), chan);
512 auto preBob = env.balance(bob);
513 auto const delta = XRP(500);
514 auto const reqBal = chanBal + delta;
515 auto const authAmt = reqBal + XRP(100);
516 assert(reqBal <= chanAmt);
517 auto const sig =
518 signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
519 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
520 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
521 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
522 auto const feeDrops = env.current()->fees().base;
523 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
524 }
525 env.close(settleTimepoint);
526 {
527 // past settleTime, channel will close
528 auto const chanBal = channelBalance(*env.current(), chan);
529 auto const chanAmt = channelAmount(*env.current(), chan);
530 auto const preAlice = env.balance(alice);
531 auto preBob = env.balance(bob);
532 auto const delta = XRP(500);
533 auto const reqBal = chanBal + delta;
534 auto const authAmt = reqBal + XRP(100);
535 assert(reqBal <= chanAmt);
536 auto const sig =
537 signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
538 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
539 BEAST_EXPECT(!channelExists(*env.current(), chan));
540 auto const feeDrops = env.current()->fees().base;
541 BEAST_EXPECT(env.balance(alice) == preAlice + chanAmt - chanBal);
542 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
543 }
544 }
545
546 void
548 {
549 testcase("close dry");
550 using namespace jtx;
551 using namespace std::literals::chrono_literals;
552 Env env{*this, features};
553 auto const alice = Account("alice");
554 auto const bob = Account("bob");
555 env.fund(XRP(10000), alice, bob);
556 auto const pk = alice.pk();
557 auto const settleDelay = 3600s;
558 auto const channelFunds = XRP(1000);
559 auto const chan = channel(alice, bob, env.seq(alice));
560 env(create(alice, bob, channelFunds, settleDelay, pk));
561 BEAST_EXPECT(channelExists(*env.current(), chan));
562 // Owner tries to close channel, but it will remain open (settle delay)
563 env(claim(alice, chan), txflags(tfClose));
564 BEAST_EXPECT(channelExists(*env.current(), chan));
565 {
566 // claim the entire amount
567 auto const preBob = env.balance(bob);
568 env(claim(alice, chan, channelFunds.value(), channelFunds.value()));
569 BEAST_EXPECT(channelBalance(*env.current(), chan) == channelFunds);
570 BEAST_EXPECT(env.balance(bob) == preBob + channelFunds);
571 }
572 auto const preAlice = env.balance(alice);
573 // Channel is now dry, can close before expiration date
574 env(claim(alice, chan), txflags(tfClose));
575 BEAST_EXPECT(!channelExists(*env.current(), chan));
576 auto const feeDrops = env.current()->fees().base;
577 BEAST_EXPECT(env.balance(alice) == preAlice - feeDrops);
578 }
579
580 void
582 {
583 // auth amount defaults to balance if not present
584 testcase("default amount");
585 using namespace jtx;
586 using namespace std::literals::chrono_literals;
587 Env env{*this, features};
588 auto const alice = Account("alice");
589 auto const bob = Account("bob");
590 env.fund(XRP(10000), alice, bob);
591 auto const pk = alice.pk();
592 auto const settleDelay = 3600s;
593 auto const channelFunds = XRP(1000);
594 auto const chan = channel(alice, bob, env.seq(alice));
595 env(create(alice, bob, channelFunds, settleDelay, pk));
596 BEAST_EXPECT(channelExists(*env.current(), chan));
597 // Owner tries to close channel, but it will remain open (settle delay)
598 env(claim(alice, chan), txflags(tfClose));
599 BEAST_EXPECT(channelExists(*env.current(), chan));
600 {
601 auto chanBal = channelBalance(*env.current(), chan);
602 auto chanAmt = channelAmount(*env.current(), chan);
603 auto const preBob = env.balance(bob);
604
605 auto const delta = XRP(500);
606 auto const reqBal = chanBal + delta;
607 assert(reqBal <= chanAmt);
608 auto const sig =
609 signClaimAuth(alice.pk(), alice.sk(), chan, reqBal);
610 env(claim(bob, chan, reqBal, std::nullopt, Slice(sig), alice.pk()));
611 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
612 auto const feeDrops = env.current()->fees().base;
613 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
614 chanBal = reqBal;
615 }
616 {
617 // Claim again
618 auto chanBal = channelBalance(*env.current(), chan);
619 auto chanAmt = channelAmount(*env.current(), chan);
620 auto const preBob = env.balance(bob);
621
622 auto const delta = XRP(500);
623 auto const reqBal = chanBal + delta;
624 assert(reqBal <= chanAmt);
625 auto const sig =
626 signClaimAuth(alice.pk(), alice.sk(), chan, reqBal);
627 env(claim(bob, chan, reqBal, std::nullopt, Slice(sig), alice.pk()));
628 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
629 auto const feeDrops = env.current()->fees().base;
630 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
631 chanBal = reqBal;
632 }
633 }
634
635 void
637 {
638 // auth amount defaults to balance if not present
639 testcase("Disallow XRP");
640 using namespace jtx;
641 using namespace std::literals::chrono_literals;
642
643 auto const alice = Account("alice");
644 auto const bob = Account("bob");
645 {
646 // Create a channel where dst disallows XRP. Ignore that flag,
647 // since it's just advisory.
648 Env env{*this, features};
649 env.fund(XRP(10000), alice, bob);
650 env(fset(bob, asfDisallowXRP));
651 auto const chan = channel(alice, bob, env.seq(alice));
652 env(create(alice, bob, XRP(1000), 3600s, alice.pk()));
653 BEAST_EXPECT(channelExists(*env.current(), chan));
654 }
655
656 {
657 // Claim to a channel where dst disallows XRP (channel is
658 // created before disallow xrp is set). Ignore that flag
659 // since it is just advisory.
660 Env env{*this, features};
661 env.fund(XRP(10000), alice, bob);
662 auto const chan = channel(alice, bob, env.seq(alice));
663 env(create(alice, bob, XRP(1000), 3600s, alice.pk()));
664 BEAST_EXPECT(channelExists(*env.current(), chan));
665
666 env(fset(bob, asfDisallowXRP));
667 auto const reqBal = XRP(500).value();
668 env(claim(alice, chan, reqBal, reqBal));
669 }
670 }
671
672 void
674 {
675 // auth amount defaults to balance if not present
676 testcase("Dst Tag");
677 using namespace jtx;
678 using namespace std::literals::chrono_literals;
679 // Create a channel where dst disallows XRP
680 Env env{*this, features};
681 auto const alice = Account("alice");
682 auto const bob = Account("bob");
683 env.fund(XRP(10000), alice, bob);
684 env(fset(bob, asfRequireDest));
685 auto const pk = alice.pk();
686 auto const settleDelay = 3600s;
687 auto const channelFunds = XRP(1000);
688 {
689 auto const chan = channel(alice, bob, env.seq(alice));
690 env(create(alice, bob, channelFunds, settleDelay, pk),
692 BEAST_EXPECT(!channelExists(*env.current(), chan));
693 }
694 {
695 auto const chan = channel(alice, bob, env.seq(alice));
696 env(create(
697 alice, bob, channelFunds, settleDelay, pk, std::nullopt, 1));
698 BEAST_EXPECT(channelExists(*env.current(), chan));
699 }
700 }
701
702 void
704 {
705 testcase("Deposit Authorization");
706 using namespace jtx;
707 using namespace std::literals::chrono_literals;
708
709 auto const alice = Account("alice");
710 auto const bob = Account("bob");
711 auto const carol = Account("carol");
712 auto USDA = alice["USD"];
713 {
714 Env env{*this, features};
715 env.fund(XRP(10000), alice, bob, carol);
716
717 env(fset(bob, asfDepositAuth));
718 env.close();
719
720 auto const pk = alice.pk();
721 auto const settleDelay = 100s;
722 auto const chan = channel(alice, bob, env.seq(alice));
723 env(create(alice, bob, XRP(1000), settleDelay, pk));
724 env.close();
725
726 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
727 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
728
729 // alice can add more funds to the channel even though bob has
730 // asfDepositAuth set.
731 env(fund(alice, chan, XRP(1000)));
732 env.close();
733
734 // alice claims. Fails because bob's lsfDepositAuth flag is set.
735 env(claim(alice, chan, XRP(500).value(), XRP(500).value()),
737 env.close();
738
739 // Claim with signature
740 auto const baseFee = env.current()->fees().base;
741 auto const preBob = env.balance(bob);
742 {
743 auto const delta = XRP(500).value();
744 auto const sig = signClaimAuth(pk, alice.sk(), chan, delta);
745
746 // alice claims with signature. Fails since bob has
747 // lsfDepositAuth flag set.
748 env(claim(alice, chan, delta, delta, Slice(sig), pk),
750 env.close();
751 BEAST_EXPECT(env.balance(bob) == preBob);
752
753 // bob claims but omits the signature. Fails because only
754 // alice can claim without a signature.
755 env(claim(bob, chan, delta, delta), ter(temBAD_SIGNATURE));
756 env.close();
757
758 // bob claims with signature. Succeeds even though bob's
759 // lsfDepositAuth flag is set since bob submitted the
760 // transaction.
761 env(claim(bob, chan, delta, delta, Slice(sig), pk));
762 env.close();
763 BEAST_EXPECT(env.balance(bob) == preBob + delta - baseFee);
764 }
765 {
766 // Explore the limits of deposit preauthorization.
767 auto const delta = XRP(600).value();
768 auto const sig = signClaimAuth(pk, alice.sk(), chan, delta);
769
770 // carol claims and fails. Only channel participants (bob or
771 // alice) may claim.
772 env(claim(carol, chan, delta, delta, Slice(sig), pk),
774 env.close();
775
776 // bob preauthorizes carol for deposit. But after that carol
777 // still can't claim since only channel participants may claim.
778 env(deposit::auth(bob, carol));
779 env.close();
780
781 env(claim(carol, chan, delta, delta, Slice(sig), pk),
783
784 // Since alice is not preauthorized she also may not claim
785 // for bob.
786 env(claim(alice, chan, delta, delta, Slice(sig), pk),
788 env.close();
789
790 // However if bob preauthorizes alice for deposit then she can
791 // successfully submit a claim.
792 env(deposit::auth(bob, alice));
793 env.close();
794
795 env(claim(alice, chan, delta, delta, Slice(sig), pk));
796 env.close();
797
798 BEAST_EXPECT(
799 env.balance(bob) == preBob + delta - (3 * baseFee));
800 }
801 {
802 // bob removes preauthorization of alice. Once again she
803 // cannot submit a claim.
804 auto const delta = XRP(800).value();
805
806 env(deposit::unauth(bob, alice));
807 env.close();
808
809 // alice claims and fails since she is no longer preauthorized.
810 env(claim(alice, chan, delta, delta), ter(tecNO_PERMISSION));
811 env.close();
812
813 // bob clears lsfDepositAuth. Now alice can claim.
814 env(fclear(bob, asfDepositAuth));
815 env.close();
816
817 // alice claims successfully.
818 env(claim(alice, chan, delta, delta));
819 env.close();
820 BEAST_EXPECT(
821 env.balance(bob) == preBob + XRP(800) - (5 * baseFee));
822 }
823 }
824 }
825
826 void
828 {
829 testcase("Deposit Authorization with Credentials");
830 using namespace jtx;
831 using namespace std::literals::chrono_literals;
832
833 char const credType[] = "abcde";
834
835 Account const alice("alice");
836 Account const bob("bob");
837 Account const carol("carol");
838 Account const dillon("dillon");
839 Account const zelda("zelda");
840
841 {
842 Env env{*this};
843 env.fund(XRP(10000), alice, bob, carol, dillon, zelda);
844
845 auto const pk = alice.pk();
846 auto const settleDelay = 100s;
847 auto const chan = channel(alice, bob, env.seq(alice));
848 env(create(alice, bob, XRP(1000), settleDelay, pk));
849 env.close();
850
851 // alice add funds to the channel
852 env(fund(alice, chan, XRP(1000)));
853 env.close();
854
855 std::string const credBadIdx =
856 "D007AE4B6E1274B4AF872588267B810C2F82716726351D1C7D38D3E5499FC6"
857 "E1";
858
859 auto const delta = XRP(500).value();
860
861 { // create credentials
862 auto jv = credentials::create(alice, carol, credType);
863 uint32_t const t = env.current()
864 ->info()
865 .parentCloseTime.time_since_epoch()
866 .count() +
867 100;
868 jv[sfExpiration.jsonName] = t;
869 env(jv);
870 env.close();
871 }
872
873 auto const jv =
874 credentials::ledgerEntry(env, alice, carol, credType);
875 std::string const credIdx = jv[jss::result][jss::index].asString();
876
877 // Bob require preauthorization
878 env(fset(bob, asfDepositAuth));
879 env.close();
880
881 // Fail, credentials not accepted
882 env(claim(alice, chan, delta, delta),
883 credentials::ids({credIdx}),
885 env.close();
886
887 env(credentials::accept(alice, carol, credType));
888 env.close();
889
890 // Fail, no depositPreauth object
891 env(claim(alice, chan, delta, delta),
892 credentials::ids({credIdx}),
894 env.close();
895
896 // Setup deposit authorization
897 env(deposit::authCredentials(bob, {{carol, credType}}));
898 env.close();
899
900 // Fail, credentials doesn’t belong to root account
901 env(claim(dillon, chan, delta, delta),
902 credentials::ids({credIdx}),
904
905 // Fails because bob's lsfDepositAuth flag is set.
906 env(claim(alice, chan, delta, delta), ter(tecNO_PERMISSION));
907
908 // Fail, bad credentials index.
909 env(claim(alice, chan, delta, delta),
910 credentials::ids({credBadIdx}),
912
913 // Fail, empty credentials
914 env(claim(alice, chan, delta, delta),
917
918 {
919 // claim fails cause of expired credentials
920
921 // Every cycle +10sec.
922 for (int i = 0; i < 10; ++i)
923 env.close();
924
925 env(claim(alice, chan, delta, delta),
926 credentials::ids({credIdx}),
927 ter(tecEXPIRED));
928 env.close();
929 }
930
931 { // create credentials once more
932 env(credentials::create(alice, carol, credType));
933 env.close();
934 env(credentials::accept(alice, carol, credType));
935 env.close();
936
937 auto const jv =
938 credentials::ledgerEntry(env, alice, carol, credType);
939 std::string const credIdx =
940 jv[jss::result][jss::index].asString();
941
942 // Success
943 env(claim(alice, chan, delta, delta),
944 credentials::ids({credIdx}));
945 }
946 }
947
948 {
949 Env env{*this};
950 env.fund(XRP(10000), alice, bob, carol, dillon, zelda);
951
952 auto const pk = alice.pk();
953 auto const settleDelay = 100s;
954 auto const chan = channel(alice, bob, env.seq(alice));
955 env(create(alice, bob, XRP(1000), settleDelay, pk));
956 env.close();
957
958 // alice add funds to the channel
959 env(fund(alice, chan, XRP(1000)));
960 env.close();
961
962 auto const delta = XRP(500).value();
963
964 { // create credentials
965 env(credentials::create(alice, carol, credType));
966 env.close();
967 env(credentials::accept(alice, carol, credType));
968 env.close();
969 }
970
971 auto const jv =
972 credentials::ledgerEntry(env, alice, carol, credType);
973 std::string const credIdx = jv[jss::result][jss::index].asString();
974
975 // Succeed, lsfDepositAuth is not set
976 env(claim(alice, chan, delta, delta), credentials::ids({credIdx}));
977 env.close();
978 }
979
980 {
981 // Credentials amendment not enabled
982 Env env(*this, testable_amendments() - featureCredentials);
983 env.fund(XRP(5000), "alice", "bob");
984 env.close();
985
986 auto const pk = alice.pk();
987 auto const settleDelay = 100s;
988 auto const chan = channel(alice, bob, env.seq(alice));
989 env(create(alice, bob, XRP(1000), settleDelay, pk));
990 env.close();
991
992 env(fund(alice, chan, XRP(1000)));
993 env.close();
994 std::string const credIdx =
995 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
996 "E4";
997
998 // can't claim with old DepositPreauth because rule is not enabled.
999 env(fset(bob, asfDepositAuth));
1000 env.close();
1001 env(deposit::auth(bob, alice));
1002 env.close();
1003
1004 env(claim(alice, chan, XRP(500).value(), XRP(500).value()),
1005 credentials::ids({credIdx}),
1006 ter(temDISABLED));
1007 }
1008 }
1009
1010 void
1012 {
1013 // auth amount defaults to balance if not present
1014 testcase("Multiple channels to the same account");
1015 using namespace jtx;
1016 using namespace std::literals::chrono_literals;
1017 Env env{*this, features};
1018 auto const alice = Account("alice");
1019 auto const bob = Account("bob");
1020 env.fund(XRP(10000), alice, bob);
1021 auto const pk = alice.pk();
1022 auto const settleDelay = 3600s;
1023 auto const channelFunds = XRP(1000);
1024 auto const chan1 = channel(alice, bob, env.seq(alice));
1025 env(create(alice, bob, channelFunds, settleDelay, pk));
1026 BEAST_EXPECT(channelExists(*env.current(), chan1));
1027 auto const chan2 = channel(alice, bob, env.seq(alice));
1028 env(create(alice, bob, channelFunds, settleDelay, pk));
1029 BEAST_EXPECT(channelExists(*env.current(), chan2));
1030 BEAST_EXPECT(chan1 != chan2);
1031 }
1032
1033 void
1035 {
1036 testcase("AccountChannels RPC");
1037
1038 using namespace jtx;
1039 using namespace std::literals::chrono_literals;
1040 Env env{*this, features};
1041 auto const alice = Account("alice");
1042 auto const bob = Account("bob");
1043 auto const charlie = Account("charlie", KeyType::ed25519);
1044 env.fund(XRP(10000), alice, bob, charlie);
1045 auto const pk = alice.pk();
1046 auto const settleDelay = 3600s;
1047 auto const channelFunds = XRP(1000);
1048 auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
1049 env(create(alice, bob, channelFunds, settleDelay, pk));
1050 env.close();
1051 {
1052 // test account non-string
1053 auto testInvalidAccountParam = [&](auto const& param) {
1054 Json::Value params;
1055 params[jss::account] = param;
1056 auto jrr = env.rpc(
1057 "json", "account_channels", to_string(params))[jss::result];
1058 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1059 BEAST_EXPECT(
1060 jrr[jss::error_message] == "Invalid field 'account'.");
1061 };
1062
1063 testInvalidAccountParam(1);
1064 testInvalidAccountParam(1.1);
1065 testInvalidAccountParam(true);
1066 testInvalidAccountParam(Json::Value(Json::nullValue));
1067 testInvalidAccountParam(Json::Value(Json::objectValue));
1068 testInvalidAccountParam(Json::Value(Json::arrayValue));
1069 }
1070 {
1071 auto const r =
1072 env.rpc("account_channels", alice.human(), bob.human());
1073 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1074 BEAST_EXPECT(
1075 r[jss::result][jss::channels][0u][jss::channel_id] == chan1Str);
1076 BEAST_EXPECT(r[jss::result][jss::validated]);
1077 }
1078 {
1079 auto const r = env.rpc("account_channels", alice.human());
1080 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1081 BEAST_EXPECT(
1082 r[jss::result][jss::channels][0u][jss::channel_id] == chan1Str);
1083 BEAST_EXPECT(r[jss::result][jss::validated]);
1084 }
1085 {
1086 auto const r =
1087 env.rpc("account_channels", bob.human(), alice.human());
1088 BEAST_EXPECT(r[jss::result][jss::channels].size() == 0);
1089 BEAST_EXPECT(r[jss::result][jss::validated]);
1090 }
1091 auto const chan2Str = to_string(channel(alice, bob, env.seq(alice)));
1092 env(create(alice, bob, channelFunds, settleDelay, pk));
1093 env.close();
1094 {
1095 auto const r =
1096 env.rpc("account_channels", alice.human(), bob.human());
1097 BEAST_EXPECT(r[jss::result][jss::channels].size() == 2);
1098 BEAST_EXPECT(r[jss::result][jss::validated]);
1099 BEAST_EXPECT(chan1Str != chan2Str);
1100 for (auto const& c : {chan1Str, chan2Str})
1101 BEAST_EXPECT(
1102 r[jss::result][jss::channels][0u][jss::channel_id] == c ||
1103 r[jss::result][jss::channels][1u][jss::channel_id] == c);
1104 }
1105 }
1106
1107 void
1109 {
1110 testcase("Account channels RPC markers");
1111
1112 using namespace test::jtx;
1113 using namespace std::literals;
1114
1115 auto const alice = Account("alice");
1116 auto const bobs = []() -> std::vector<Account> {
1117 int const n = 10;
1119 r.reserve(n);
1120 for (int i = 0; i < n; ++i)
1121 {
1122 r.emplace_back("bob"s + std::to_string(i));
1123 }
1124 return r;
1125 }();
1126
1127 Env env{*this, features};
1128 env.fund(XRP(10000), alice);
1129 for (auto const& a : bobs)
1130 {
1131 env.fund(XRP(10000), a);
1132 env.close();
1133 }
1134
1135 {
1136 // create a channel from alice to every bob account
1137 auto const settleDelay = 3600s;
1138 auto const channelFunds = XRP(1);
1139 for (auto const& b : bobs)
1140 {
1141 env(create(alice, b, channelFunds, settleDelay, alice.pk()));
1142 }
1143 }
1144
1145 auto testLimit = [](test::jtx::Env& env,
1146 test::jtx::Account const& src,
1148 Json::Value const& marker = Json::nullValue,
1150 std::nullopt) {
1151 Json::Value jvc;
1152 jvc[jss::account] = src.human();
1153 if (dst)
1154 jvc[jss::destination_account] = dst->human();
1155 if (limit)
1156 jvc[jss::limit] = *limit;
1157 if (marker)
1158 jvc[jss::marker] = marker;
1159
1160 return env.rpc(
1161 "json", "account_channels", to_string(jvc))[jss::result];
1162 };
1163
1164 {
1165 // No marker
1166 auto const r = testLimit(env, alice);
1167 BEAST_EXPECT(r.isMember(jss::channels));
1168 BEAST_EXPECT(r[jss::channels].size() == bobs.size());
1169 }
1170
1171 auto const bobsB58 = [&bobs]() -> std::set<std::string> {
1173 for (auto const& a : bobs)
1174 r.insert(a.human());
1175 return r;
1176 }();
1177
1178 for (int limit = 1; limit < bobs.size() + 1; ++limit)
1179 {
1180 auto leftToFind = bobsB58;
1181 auto const numFull = bobs.size() / limit;
1182 auto const numNonFull = bobs.size() % limit ? 1 : 0;
1183
1185
1186 auto const testIt = [&](bool expectMarker, int expectedBatchSize) {
1187 auto const r = testLimit(env, alice, limit, marker);
1188 BEAST_EXPECT(!expectMarker || r.isMember(jss::marker));
1189 if (r.isMember(jss::marker))
1190 marker = r[jss::marker];
1191 BEAST_EXPECT(r[jss::channels].size() == expectedBatchSize);
1192 auto const c = r[jss::channels];
1193 auto const s = r[jss::channels].size();
1194 for (int j = 0; j < s; ++j)
1195 {
1196 auto const dstAcc =
1197 c[j][jss::destination_account].asString();
1198 BEAST_EXPECT(leftToFind.count(dstAcc));
1199 leftToFind.erase(dstAcc);
1200 }
1201 };
1202
1203 for (int i = 0; i < numFull; ++i)
1204 {
1205 bool const expectMarker = (numNonFull != 0 || i < numFull - 1);
1206 testIt(expectMarker, limit);
1207 }
1208
1209 if (numNonFull)
1210 {
1211 testIt(false, bobs.size() % limit);
1212 }
1213 BEAST_EXPECT(leftToFind.empty());
1214 }
1215
1216 {
1217 // degenerate case
1218 auto const r = testLimit(env, alice, 0);
1219 BEAST_EXPECT(r.isMember(jss::error_message));
1220 }
1221 }
1222
1223 void
1225 {
1226 // Check that the account_channels command only returns channels owned
1227 // by the account
1228 testcase("Account channels RPC owner only");
1229
1230 using namespace test::jtx;
1231 using namespace std::literals;
1232
1233 auto const alice = Account("alice");
1234 auto const bob = Account("bob");
1235 Env env{*this, features};
1236 env.fund(XRP(10000), alice, bob);
1237
1238 // Create a channel from alice to bob and from bob to alice
1239 // When retrieving alice's channels, it should only retrieve the
1240 // channels where alice is the source, not the destination
1241 auto const settleDelay = 3600s;
1242 auto const channelFunds = XRP(1000);
1243 env(create(alice, bob, channelFunds, settleDelay, alice.pk()));
1244 env(create(bob, alice, channelFunds, settleDelay, bob.pk()));
1245
1246 auto const r = [&] {
1247 Json::Value jvc;
1248 jvc[jss::account] = alice.human();
1249
1250 return env.rpc(
1251 "json", "account_channels", to_string(jvc))[jss::result];
1252 }();
1253 BEAST_EXPECT(r.isMember(jss::channels));
1254 BEAST_EXPECT(r[jss::channels].size() == 1);
1255 BEAST_EXPECT(
1256 r[jss::channels][0u][jss::destination_account].asString() ==
1257 bob.human());
1258 }
1259
1260 void
1262 {
1263 using namespace jtx;
1264 using namespace std::literals::chrono_literals;
1265
1266 Env env{*this, features};
1267 auto const alice = Account("alice");
1268 auto const bob = Account("bob");
1269 auto const charlie = Account("charlie", KeyType::ed25519);
1270 env.fund(XRP(10000), alice, bob, charlie);
1271 auto const pk = alice.pk();
1272 auto const settleDelay = 3600s;
1273 auto const channelFunds = XRP(1000);
1274 auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
1275 env(create(alice, bob, channelFunds, settleDelay, pk));
1276 env.close();
1277
1279 args[jss::channel_id] = chan1Str;
1280 args[jss::key_type] = "ed255191";
1281 args[jss::seed] = "snHq1rzQoN2qiUkC3XF5RyxBzUtN";
1282 args[jss::amount] = 51110000;
1283
1284 // test for all api versions
1285 forAllApiVersions([&, this](unsigned apiVersion) {
1286 testcase(
1287 "PayChan Channel_Auth RPC Api " + std::to_string(apiVersion));
1288 args[jss::api_version] = apiVersion;
1289 auto const rs = env.rpc(
1290 "json",
1291 "channel_authorize",
1292 args.toStyledString())[jss::result];
1293 auto const error = apiVersion < 2u ? "invalidParams" : "badKeyType";
1294 BEAST_EXPECT(rs[jss::error] == error);
1295 });
1296 }
1297
1298 void
1300 {
1301 testcase("PayChan Auth/Verify RPC");
1302 using namespace jtx;
1303 using namespace std::literals::chrono_literals;
1304 Env env{*this, features};
1305 auto const alice = Account("alice");
1306 auto const bob = Account("bob");
1307 auto const charlie = Account("charlie", KeyType::ed25519);
1308 env.fund(XRP(10000), alice, bob, charlie);
1309 auto const pk = alice.pk();
1310 auto const settleDelay = 3600s;
1311 auto const channelFunds = XRP(1000);
1312 auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
1313 env(create(alice, bob, channelFunds, settleDelay, pk));
1314 env.close();
1315 std::string chan1PkStr;
1316 {
1317 auto const r =
1318 env.rpc("account_channels", alice.human(), bob.human());
1319 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1320 BEAST_EXPECT(
1321 r[jss::result][jss::channels][0u][jss::channel_id] == chan1Str);
1322 BEAST_EXPECT(r[jss::result][jss::validated]);
1323 chan1PkStr =
1324 r[jss::result][jss::channels][0u][jss::public_key].asString();
1325 }
1326 {
1327 auto const r = env.rpc("account_channels", alice.human());
1328 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1329 BEAST_EXPECT(
1330 r[jss::result][jss::channels][0u][jss::channel_id] == chan1Str);
1331 BEAST_EXPECT(r[jss::result][jss::validated]);
1332 chan1PkStr =
1333 r[jss::result][jss::channels][0u][jss::public_key].asString();
1334 }
1335 {
1336 auto const r =
1337 env.rpc("account_channels", bob.human(), alice.human());
1338 BEAST_EXPECT(r[jss::result][jss::channels].size() == 0);
1339 BEAST_EXPECT(r[jss::result][jss::validated]);
1340 }
1341 auto const chan2Str = to_string(channel(alice, bob, env.seq(alice)));
1342 env(create(alice, bob, channelFunds, settleDelay, pk));
1343 env.close();
1344 {
1345 auto const r =
1346 env.rpc("account_channels", alice.human(), bob.human());
1347 BEAST_EXPECT(r[jss::result][jss::channels].size() == 2);
1348 BEAST_EXPECT(r[jss::result][jss::validated]);
1349 BEAST_EXPECT(chan1Str != chan2Str);
1350 for (auto const& c : {chan1Str, chan2Str})
1351 BEAST_EXPECT(
1352 r[jss::result][jss::channels][0u][jss::channel_id] == c ||
1353 r[jss::result][jss::channels][1u][jss::channel_id] == c);
1354 }
1355
1356 auto sliceToHex = [](Slice const& slice) {
1357 std::string s;
1358 s.reserve(2 * slice.size());
1359 for (int i = 0; i < slice.size(); ++i)
1360 {
1361 s += "0123456789ABCDEF"[((slice[i] & 0xf0) >> 4)];
1362 s += "0123456789ABCDEF"[((slice[i] & 0x0f) >> 0)];
1363 }
1364 return s;
1365 };
1366
1367 {
1368 // Verify chan1 auth
1369 auto const rs =
1370 env.rpc("channel_authorize", "alice", chan1Str, "1000");
1371 auto const sig = rs[jss::result][jss::signature].asString();
1372 BEAST_EXPECT(!sig.empty());
1373 {
1374 auto const rv = env.rpc(
1375 "channel_verify", chan1PkStr, chan1Str, "1000", sig);
1376 BEAST_EXPECT(rv[jss::result][jss::signature_verified].asBool());
1377 }
1378
1379 {
1380 // use pk hex to verify
1381 auto const pkAsHex = sliceToHex(pk.slice());
1382 auto const rv =
1383 env.rpc("channel_verify", pkAsHex, chan1Str, "1000", sig);
1384 BEAST_EXPECT(rv[jss::result][jss::signature_verified].asBool());
1385 }
1386 {
1387 // malformed amount
1388 auto const pkAsHex = sliceToHex(pk.slice());
1389 auto rv =
1390 env.rpc("channel_verify", pkAsHex, chan1Str, "1000x", sig);
1391 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1392 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000 ", sig);
1393 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1394 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "x1000", sig);
1395 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1396 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "x", sig);
1397 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1398 rv = env.rpc("channel_verify", pkAsHex, chan1Str, " ", sig);
1399 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1400 rv = env.rpc(
1401 "channel_verify", pkAsHex, chan1Str, "1000 1000", sig);
1402 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1403 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1,000", sig);
1404 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1405 rv = env.rpc("channel_verify", pkAsHex, chan1Str, " 1000", sig);
1406 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1407 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "", sig);
1408 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1409 }
1410 {
1411 // malformed channel
1412 auto const pkAsHex = sliceToHex(pk.slice());
1413 auto chan1StrBad = chan1Str;
1414 chan1StrBad.pop_back();
1415 auto rv = env.rpc(
1416 "channel_verify", pkAsHex, chan1StrBad, "1000", sig);
1417 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1418 rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
1419 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1420
1421 chan1StrBad = chan1Str;
1422 chan1StrBad.push_back('0');
1423 rv = env.rpc(
1424 "channel_verify", pkAsHex, chan1StrBad, "1000", sig);
1425 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1426 rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
1427 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1428
1429 chan1StrBad = chan1Str;
1430 chan1StrBad.back() = 'x';
1431 rv = env.rpc(
1432 "channel_verify", pkAsHex, chan1StrBad, "1000", sig);
1433 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1434 rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
1435 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1436 }
1437 {
1438 // give an ill formed base 58 public key
1439 auto illFormedPk = chan1PkStr.substr(0, chan1PkStr.size() - 1);
1440 auto const rv = env.rpc(
1441 "channel_verify", illFormedPk, chan1Str, "1000", sig);
1442 BEAST_EXPECT(
1443 !rv[jss::result][jss::signature_verified].asBool());
1444 }
1445 {
1446 // give an ill formed hex public key
1447 auto const pkAsHex = sliceToHex(pk.slice());
1448 auto illFormedPk = pkAsHex.substr(0, chan1PkStr.size() - 1);
1449 auto const rv = env.rpc(
1450 "channel_verify", illFormedPk, chan1Str, "1000", sig);
1451 BEAST_EXPECT(
1452 !rv[jss::result][jss::signature_verified].asBool());
1453 }
1454 }
1455 {
1456 // Try to verify chan2 auth with chan1 key
1457 auto const rs =
1458 env.rpc("channel_authorize", "alice", chan2Str, "1000");
1459 auto const sig = rs[jss::result][jss::signature].asString();
1460 BEAST_EXPECT(!sig.empty());
1461 {
1462 auto const rv = env.rpc(
1463 "channel_verify", chan1PkStr, chan1Str, "1000", sig);
1464 BEAST_EXPECT(
1465 !rv[jss::result][jss::signature_verified].asBool());
1466 }
1467 {
1468 // use pk hex to verify
1469 auto const pkAsHex = sliceToHex(pk.slice());
1470 auto const rv =
1471 env.rpc("channel_verify", pkAsHex, chan1Str, "1000", sig);
1472 BEAST_EXPECT(
1473 !rv[jss::result][jss::signature_verified].asBool());
1474 }
1475 }
1476 {
1477 // Try to explicitly specify secp256k1 and Ed25519 keys:
1478 auto const chan =
1479 to_string(channel(charlie, alice, env.seq(charlie)));
1480 env(create(
1481 charlie, alice, channelFunds, settleDelay, charlie.pk()));
1482 env.close();
1483
1484 std::string cpk;
1485 {
1486 auto const r =
1487 env.rpc("account_channels", charlie.human(), alice.human());
1488 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1489 BEAST_EXPECT(
1490 r[jss::result][jss::channels][0u][jss::channel_id] == chan);
1491 BEAST_EXPECT(r[jss::result][jss::validated]);
1492 cpk = r[jss::result][jss::channels][0u][jss::public_key]
1493 .asString();
1494 }
1495
1496 // Try to authorize without specifying a key type, expect an error:
1497 auto const rs =
1498 env.rpc("channel_authorize", "charlie", chan, "1000");
1499 auto const sig = rs[jss::result][jss::signature].asString();
1500 BEAST_EXPECT(!sig.empty());
1501 {
1502 auto const rv =
1503 env.rpc("channel_verify", cpk, chan, "1000", sig);
1504 BEAST_EXPECT(
1505 !rv[jss::result][jss::signature_verified].asBool());
1506 }
1507
1508 // Try to authorize using an unknown key type, except an error:
1509 auto const rs1 =
1510 env.rpc("channel_authorize", "charlie", "nyx", chan, "1000");
1511 BEAST_EXPECT(rs1[jss::error] == "badKeyType");
1512
1513 // Try to authorize using secp256k1; the authorization _should_
1514 // succeed but the verification should fail:
1515 auto const rs2 = env.rpc(
1516 "channel_authorize", "charlie", "secp256k1", chan, "1000");
1517 auto const sig2 = rs2[jss::result][jss::signature].asString();
1518 BEAST_EXPECT(!sig2.empty());
1519 {
1520 auto const rv =
1521 env.rpc("channel_verify", cpk, chan, "1000", sig2);
1522 BEAST_EXPECT(
1523 !rv[jss::result][jss::signature_verified].asBool());
1524 }
1525
1526 // Try to authorize using Ed25519; expect success:
1527 auto const rs3 = env.rpc(
1528 "channel_authorize", "charlie", "ed25519", chan, "1000");
1529 auto const sig3 = rs3[jss::result][jss::signature].asString();
1530 BEAST_EXPECT(!sig3.empty());
1531 {
1532 auto const rv =
1533 env.rpc("channel_verify", cpk, chan, "1000", sig3);
1534 BEAST_EXPECT(rv[jss::result][jss::signature_verified].asBool());
1535 }
1536 }
1537
1538 {
1539 // send malformed amounts rpc requests
1540 auto rs = env.rpc("channel_authorize", "alice", chan1Str, "1000x");
1541 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1542 rs = env.rpc("channel_authorize", "alice", chan1Str, "x1000");
1543 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1544 rs = env.rpc("channel_authorize", "alice", chan1Str, "x");
1545 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1546 {
1547 // Missing channel_id
1549 args[jss::amount] = "2000";
1550 args[jss::key_type] = "secp256k1";
1551 args[jss::passphrase] = "passphrase_can_be_anything";
1552 rs = env.rpc(
1553 "json",
1554 "channel_authorize",
1555 args.toStyledString())[jss::result];
1556 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1557 }
1558 {
1559 // Missing amount
1561 args[jss::channel_id] = chan1Str;
1562 args[jss::key_type] = "secp256k1";
1563 args[jss::passphrase] = "passphrase_can_be_anything";
1564 rs = env.rpc(
1565 "json",
1566 "channel_authorize",
1567 args.toStyledString())[jss::result];
1568 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1569 }
1570 {
1571 // Missing key_type and no secret.
1573 args[jss::amount] = "2000";
1574 args[jss::channel_id] = chan1Str;
1575 args[jss::passphrase] = "passphrase_can_be_anything";
1576 rs = env.rpc(
1577 "json",
1578 "channel_authorize",
1579 args.toStyledString())[jss::result];
1580 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1581 }
1582 {
1583 // Both passphrase and seed specified.
1585 args[jss::amount] = "2000";
1586 args[jss::channel_id] = chan1Str;
1587 args[jss::key_type] = "secp256k1";
1588 args[jss::passphrase] = "passphrase_can_be_anything";
1589 args[jss::seed] = "seed can be anything";
1590 rs = env.rpc(
1591 "json",
1592 "channel_authorize",
1593 args.toStyledString())[jss::result];
1594 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1595 }
1596 {
1597 // channel_id is not exact hex.
1599 args[jss::amount] = "2000";
1600 args[jss::channel_id] = chan1Str + "1";
1601 args[jss::key_type] = "secp256k1";
1602 args[jss::passphrase] = "passphrase_can_be_anything";
1603 rs = env.rpc(
1604 "json",
1605 "channel_authorize",
1606 args.toStyledString())[jss::result];
1607 BEAST_EXPECT(rs[jss::error] == "channelMalformed");
1608 }
1609 {
1610 // amount is not a string
1612 args[jss::amount] = 2000;
1613 args[jss::channel_id] = chan1Str;
1614 args[jss::key_type] = "secp256k1";
1615 args[jss::passphrase] = "passphrase_can_be_anything";
1616 rs = env.rpc(
1617 "json",
1618 "channel_authorize",
1619 args.toStyledString())[jss::result];
1620 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1621 }
1622 {
1623 // Amount is not a decimal string.
1625 args[jss::amount] = "TwoThousand";
1626 args[jss::channel_id] = chan1Str;
1627 args[jss::key_type] = "secp256k1";
1628 args[jss::passphrase] = "passphrase_can_be_anything";
1629 rs = env.rpc(
1630 "json",
1631 "channel_authorize",
1632 args.toStyledString())[jss::result];
1633 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1634 }
1635 }
1636 }
1637
1638 void
1640 {
1641 testcase("Optional Fields");
1642 using namespace jtx;
1643 using namespace std::literals::chrono_literals;
1644 Env env{*this, features};
1645 auto const alice = Account("alice");
1646 auto const bob = Account("bob");
1647 auto const carol = Account("carol");
1648 auto const dan = Account("dan");
1649 env.fund(XRP(10000), alice, bob, carol, dan);
1650 auto const pk = alice.pk();
1651 auto const settleDelay = 3600s;
1652 auto const channelFunds = XRP(1000);
1653
1655
1656 {
1657 auto const chan = to_string(channel(alice, bob, env.seq(alice)));
1658 env(create(alice, bob, channelFunds, settleDelay, pk));
1659 auto const r =
1660 env.rpc("account_channels", alice.human(), bob.human());
1661 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1662 BEAST_EXPECT(
1663 r[jss::result][jss::channels][0u][jss::channel_id] == chan);
1664 BEAST_EXPECT(!r[jss::result][jss::channels][0u].isMember(
1665 jss::destination_tag));
1666 }
1667 {
1668 std::uint32_t dstTag = 42;
1669 auto const chan = to_string(channel(alice, carol, env.seq(alice)));
1670 env(create(
1671 alice,
1672 carol,
1673 channelFunds,
1674 settleDelay,
1675 pk,
1676 cancelAfter,
1677 dstTag));
1678 auto const r =
1679 env.rpc("account_channels", alice.human(), carol.human());
1680 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1681 BEAST_EXPECT(
1682 r[jss::result][jss::channels][0u][jss::channel_id] == chan);
1683 BEAST_EXPECT(
1684 r[jss::result][jss::channels][0u][jss::destination_tag] ==
1685 dstTag);
1686 }
1687 }
1688
1689 void
1691 {
1692 testcase("malformed pk");
1693 using namespace jtx;
1694 using namespace std::literals::chrono_literals;
1695 Env env{*this, features};
1696 auto const alice = Account("alice");
1697 auto const bob = Account("bob");
1698 auto USDA = alice["USD"];
1699 env.fund(XRP(10000), alice, bob);
1700 auto const pk = alice.pk();
1701 auto const settleDelay = 100s;
1702
1703 auto const chan = channel(alice, bob, env.seq(alice));
1704 auto jv = create(alice, bob, XRP(1000), settleDelay, pk);
1705 auto const pkHex = strHex(pk.slice());
1706 jv["PublicKey"] = pkHex.substr(2, pkHex.size() - 2);
1707 env(jv, ter(temMALFORMED));
1708 jv["PublicKey"] = pkHex.substr(0, pkHex.size() - 2);
1709 env(jv, ter(temMALFORMED));
1710 auto badPrefix = pkHex;
1711 badPrefix[0] = 'f';
1712 badPrefix[1] = 'f';
1713 jv["PublicKey"] = badPrefix;
1714 env(jv, ter(temMALFORMED));
1715
1716 jv["PublicKey"] = pkHex;
1717 env(jv);
1718
1719 auto const authAmt = XRP(100);
1720 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
1721 jv = claim(
1722 bob,
1723 chan,
1724 authAmt.value(),
1725 authAmt.value(),
1726 Slice(sig),
1727 alice.pk());
1728 jv["PublicKey"] = pkHex.substr(2, pkHex.size() - 2);
1729 env(jv, ter(temMALFORMED));
1730 jv["PublicKey"] = pkHex.substr(0, pkHex.size() - 2);
1731 env(jv, ter(temMALFORMED));
1732 badPrefix = pkHex;
1733 badPrefix[0] = 'f';
1734 badPrefix[1] = 'f';
1735 jv["PublicKey"] = badPrefix;
1736 env(jv, ter(temMALFORMED));
1737
1738 // missing public key
1739 jv.removeMember("PublicKey");
1740 env(jv, ter(temMALFORMED));
1741
1742 {
1743 auto const txn = R"*(
1744 {
1745
1746 "channel_id":"5DB01B7FFED6B67E6B0414DED11E051D2EE2B7619CE0EAA6286D67A3A4D5BDB3",
1747 "signature":
1748 "304402204EF0AFB78AC23ED1C472E74F4299C0C21F1B21D07EFC0A3838A420F76D783A400220154FB11B6F54320666E4C36CA7F686C16A3A0456800BBC43746F34AF50290064",
1749 "public_key":
1750 "aKijDDiC2q2gXjMpM7i4BUS6cmixgsEe18e7CjsUxwihKfuoFgS5",
1751 "amount": "1000000"
1752 }
1753 )*";
1754 auto const r = env.rpc("json", "channel_verify", txn);
1755 BEAST_EXPECT(r["result"]["error"] == "publicMalformed");
1756 }
1757 }
1758
1759 void
1761 {
1762 testcase("Metadata & Ownership");
1763
1764 using namespace jtx;
1765 using namespace std::literals::chrono_literals;
1766
1767 auto const alice = Account("alice");
1768 auto const bob = Account("bob");
1769 auto const settleDelay = 100s;
1770 auto const pk = alice.pk();
1771
1772 auto inOwnerDir = [](ReadView const& view,
1773 Account const& acc,
1774 std::shared_ptr<SLE const> const& chan) -> bool {
1775 ripple::Dir const ownerDir(view, keylet::ownerDir(acc.id()));
1776 return std::find(ownerDir.begin(), ownerDir.end(), chan) !=
1777 ownerDir.end();
1778 };
1779
1780 auto ownerDirCount = [](ReadView const& view,
1781 Account const& acc) -> std::size_t {
1782 ripple::Dir const ownerDir(view, keylet::ownerDir(acc.id()));
1783 return std::distance(ownerDir.begin(), ownerDir.end());
1784 };
1785
1786 {
1787 // Test with adding the paychan to the recipient's owner directory
1788 Env env{*this, features};
1789 env.fund(XRP(10000), alice, bob);
1790 env(create(alice, bob, XRP(1000), settleDelay, pk));
1791 env.close();
1792 auto const [chan, chanSle] =
1793 channelKeyAndSle(*env.current(), alice, bob);
1794 BEAST_EXPECT(inOwnerDir(*env.current(), alice, chanSle));
1795 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1);
1796 BEAST_EXPECT(inOwnerDir(*env.current(), bob, chanSle));
1797 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1);
1798 // close the channel
1799 env(claim(bob, chan), txflags(tfClose));
1800 BEAST_EXPECT(!channelExists(*env.current(), chan));
1801 BEAST_EXPECT(!inOwnerDir(*env.current(), alice, chanSle));
1802 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0);
1803 BEAST_EXPECT(!inOwnerDir(*env.current(), bob, chanSle));
1804 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0);
1805 }
1806
1807 {
1808 // Test removing paychans created before adding to the recipient's
1809 // owner directory
1810 Env env(*this, features);
1811 env.fund(XRP(10000), alice, bob);
1812 // create the channel before the amendment activates
1813 env(create(alice, bob, XRP(1000), settleDelay, pk));
1814 env.close();
1815 auto const [chan, chanSle] =
1816 channelKeyAndSle(*env.current(), alice, bob);
1817 BEAST_EXPECT(inOwnerDir(*env.current(), alice, chanSle));
1818 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1);
1819 BEAST_EXPECT(inOwnerDir(*env.current(), bob, chanSle));
1820 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1);
1821
1822 env(claim(bob, chan), txflags(tfClose));
1823 BEAST_EXPECT(!channelExists(*env.current(), chan));
1824 BEAST_EXPECT(!inOwnerDir(*env.current(), alice, chanSle));
1825 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0);
1826 BEAST_EXPECT(!inOwnerDir(*env.current(), bob, chanSle));
1827 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0);
1828 }
1829 }
1830
1831 void
1833 {
1834 testcase("Account Delete");
1835 using namespace test::jtx;
1836 using namespace std::literals::chrono_literals;
1837 auto rmAccount = [this](
1838 Env& env,
1839 Account const& toRm,
1840 Account const& dst,
1841 TER expectedTer = tesSUCCESS) {
1842 // only allow an account to be deleted if the account's sequence
1843 // number is at least 256 less than the current ledger sequence
1844 for (auto minRmSeq = env.seq(toRm) + 257;
1845 env.current()->seq() < minRmSeq;
1846 env.close())
1847 {
1848 }
1849
1850 env(acctdelete(toRm, dst),
1851 fee(drops(env.current()->fees().increment)),
1852 ter(expectedTer));
1853 env.close();
1854 this->BEAST_EXPECT(
1855 isTesSuccess(expectedTer) ==
1856 !env.closed()->exists(keylet::account(toRm.id())));
1857 };
1858
1859 auto const alice = Account("alice");
1860 auto const bob = Account("bob");
1861 auto const carol = Account("carol");
1862
1863 {
1864 Env env{*this, features};
1865 env.fund(XRP(10000), alice, bob, carol);
1866 env.close();
1867
1868 // Create a channel from alice to bob
1869 auto const pk = alice.pk();
1870 auto const settleDelay = 100s;
1871 auto const chan = channel(alice, bob, env.seq(alice));
1872 env(create(alice, bob, XRP(1000), settleDelay, pk));
1873 env.close();
1874 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
1875 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
1876
1877 rmAccount(env, alice, carol, tecHAS_OBLIGATIONS);
1878 rmAccount(env, bob, carol, TER(tecHAS_OBLIGATIONS));
1879
1880 auto const feeDrops = env.current()->fees().base;
1881 auto chanBal = channelBalance(*env.current(), chan);
1882 auto chanAmt = channelAmount(*env.current(), chan);
1883 BEAST_EXPECT(chanBal == XRP(0));
1884 BEAST_EXPECT(chanAmt == XRP(1000));
1885
1886 auto preBob = env.balance(bob);
1887 auto const delta = XRP(50);
1888 auto reqBal = chanBal + delta;
1889 auto authAmt = reqBal + XRP(100);
1890 assert(reqBal <= chanAmt);
1891
1892 env(claim(alice, chan, reqBal, authAmt));
1893 env.close();
1894 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
1895 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
1896 BEAST_EXPECT(env.balance(bob) == preBob + delta);
1897 chanBal = reqBal;
1898
1899 auto const preAlice = env.balance(alice);
1900 env(fund(alice, chan, XRP(1000)));
1901 env.close();
1902 BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
1903 BEAST_EXPECT(
1904 channelAmount(*env.current(), chan) == chanAmt + XRP(1000));
1905 chanAmt = chanAmt + XRP(1000);
1906
1907 {
1908 // Owner closes, will close after settleDelay
1909 env(claim(alice, chan), txflags(tfClose));
1910 env.close();
1911 // settle delay hasn't elapsed. Channels should exist.
1912 BEAST_EXPECT(channelExists(*env.current(), chan));
1913 auto const closeTime = env.current()->info().parentCloseTime;
1914 auto const minExpiration = closeTime + settleDelay;
1915 env.close(minExpiration);
1916 env(claim(alice, chan), txflags(tfClose));
1917 BEAST_EXPECT(!channelExists(*env.current(), chan));
1918 }
1919 }
1920 }
1921
1922 void
1924 {
1925 testcase("using tickets");
1926 using namespace jtx;
1927 using namespace std::literals::chrono_literals;
1928 Env env{*this, features};
1929 auto const alice = Account("alice");
1930 auto const bob = Account("bob");
1931 auto USDA = alice["USD"];
1932 env.fund(XRP(10000), alice, bob);
1933
1934 // alice and bob grab enough tickets for all of the following
1935 // transactions. Note that once the tickets are acquired alice's
1936 // and bob's account sequence numbers should not advance.
1937 std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
1938 env(ticket::create(alice, 10));
1939 std::uint32_t const aliceSeq{env.seq(alice)};
1940
1941 std::uint32_t bobTicketSeq{env.seq(bob) + 1};
1942 env(ticket::create(bob, 10));
1943 std::uint32_t const bobSeq{env.seq(bob)};
1944
1945 auto const pk = alice.pk();
1946 auto const settleDelay = 100s;
1947 auto const chan = channel(alice, bob, aliceTicketSeq);
1948
1949 env(create(alice, bob, XRP(1000), settleDelay, pk),
1950 ticket::use(aliceTicketSeq++));
1951
1952 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1953 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1954
1955 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
1956 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
1957
1958 {
1959 auto const preAlice = env.balance(alice);
1960 env(fund(alice, chan, XRP(1000)), ticket::use(aliceTicketSeq++));
1961
1962 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1963 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1964
1965 auto const feeDrops = env.current()->fees().base;
1966 BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
1967 }
1968
1969 auto chanBal = channelBalance(*env.current(), chan);
1970 auto chanAmt = channelAmount(*env.current(), chan);
1971 BEAST_EXPECT(chanBal == XRP(0));
1972 BEAST_EXPECT(chanAmt == XRP(2000));
1973
1974 {
1975 // No signature needed since the owner is claiming
1976 auto const preBob = env.balance(bob);
1977 auto const delta = XRP(500);
1978 auto const reqBal = chanBal + delta;
1979 auto const authAmt = reqBal + XRP(100);
1980 assert(reqBal <= chanAmt);
1981 env(claim(alice, chan, reqBal, authAmt),
1982 ticket::use(aliceTicketSeq++));
1983
1984 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1985 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1986
1987 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
1988 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
1989 BEAST_EXPECT(env.balance(bob) == preBob + delta);
1990 chanBal = reqBal;
1991 }
1992 {
1993 // Claim with signature
1994 auto preBob = env.balance(bob);
1995 auto const delta = XRP(500);
1996 auto const reqBal = chanBal + delta;
1997 auto const authAmt = reqBal + XRP(100);
1998 assert(reqBal <= chanAmt);
1999 auto const sig =
2000 signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
2001 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()),
2002 ticket::use(bobTicketSeq++));
2003
2004 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2005 BEAST_EXPECT(env.seq(bob) == bobSeq);
2006
2007 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
2008 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2009 auto const feeDrops = env.current()->fees().base;
2010 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
2011 chanBal = reqBal;
2012
2013 // claim again
2014 preBob = env.balance(bob);
2015 // A transaction that generates a tec still consumes its ticket.
2016 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()),
2017 ticket::use(bobTicketSeq++),
2019
2020 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2021 BEAST_EXPECT(env.seq(bob) == bobSeq);
2022
2023 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
2024 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2025 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
2026 }
2027 {
2028 // Try to claim more than authorized
2029 auto const preBob = env.balance(bob);
2030 STAmount const authAmt = chanBal + XRP(500);
2031 STAmount const reqAmt = authAmt + drops(1);
2032 assert(reqAmt <= chanAmt);
2033 // Note that since claim() returns a tem (neither tec nor tes),
2034 // the ticket is not consumed. So we don't increment bobTicket.
2035 auto const sig =
2036 signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
2037 env(claim(bob, chan, reqAmt, authAmt, Slice(sig), alice.pk()),
2038 ticket::use(bobTicketSeq),
2040
2041 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2042 BEAST_EXPECT(env.seq(bob) == bobSeq);
2043
2044 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
2045 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2046 BEAST_EXPECT(env.balance(bob) == preBob);
2047 }
2048
2049 // Dst tries to fund the channel
2050 env(fund(bob, chan, XRP(1000)),
2051 ticket::use(bobTicketSeq++),
2053
2054 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2055 BEAST_EXPECT(env.seq(bob) == bobSeq);
2056
2057 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
2058 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2059
2060 {
2061 // Dst closes channel
2062 auto const preAlice = env.balance(alice);
2063 auto const preBob = env.balance(bob);
2064 env(claim(bob, chan),
2066 ticket::use(bobTicketSeq++));
2067
2068 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2069 BEAST_EXPECT(env.seq(bob) == bobSeq);
2070
2071 BEAST_EXPECT(!channelExists(*env.current(), chan));
2072 auto const feeDrops = env.current()->fees().base;
2073 auto const delta = chanAmt - chanBal;
2074 assert(delta > beast::zero);
2075 BEAST_EXPECT(env.balance(alice) == preAlice + delta);
2076 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
2077 }
2078 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
2079 BEAST_EXPECT(env.seq(alice) == aliceSeq);
2080 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2081 BEAST_EXPECT(env.seq(bob) == bobSeq);
2082 }
2083
2084 void
2086 {
2087 testSimple(features);
2088 testDisallowIncoming(features);
2089 testCancelAfter(features);
2090 testSettleDelay(features);
2091 testExpiration(features);
2092 testCloseDry(features);
2093 testDefaultAmount(features);
2094 testDisallowXRP(features);
2095 testDstTag(features);
2096 testDepositAuth(features);
2097 testMultiple(features);
2098 testAccountChannelsRPC(features);
2102 testAuthVerifyRPC(features);
2103 testOptionalFields(features);
2104 testMalformedPK(features);
2105 testMetaAndOwnership(features);
2106 testAccountDelete(features);
2107 testUsingTickets(features);
2108 }
2109
2110public:
2111 void
2112 run() override
2113 {
2114 using namespace test::jtx;
2118 testMetaAndOwnership(all - fixIncludeKeyletFields);
2119 }
2120};
2121
2122BEAST_DEFINE_TESTSUITE(PayChan, app, ripple);
2123} // namespace test
2124} // namespace ripple
Represents a JSON value.
Definition json_value.h:131
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
Like std::vector<char> but better.
Definition Buffer.h:17
A class that simplifies iterating ledger directory pages.
Definition Dir.h:22
A public key.
Definition PublicKey.h:43
A view into a ledger.
Definition ReadView.h:32
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
XRPAmount xrp() const
Definition STAmount.cpp:264
A secret key.
Definition SecretKey.h:19
Slice slice() const noexcept
Definition Serializer.h:47
An immutable linear range of bytes.
Definition Slice.h:27
Immutable cryptographic account descriptor.
Definition Account.h:20
PublicKey const & pk() const
Return the public key.
Definition Account.h:75
A transaction testing environment.
Definition Env.h:102
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition Env.cpp:97
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:250
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:312
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:103
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:772
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:165
Set the fee on a JTx.
Definition fee.h:18
Set the regular signature on a JTx.
Definition sig.h:16
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:16
Set a ticket sequence on a JTx.
Definition ticket.h:29
Set the flags on a JTx.
Definition txflags.h:12
T distance(T... args)
T emplace_back(T... args)
T find(T... args)
T insert(T... args)
T is_same_v
@ nullValue
'null' value
Definition json_value.h:20
@ arrayValue
array value (ordered list)
Definition json_value.h:26
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:355
Keylet payChan(AccountID const &src, AccountID const &dst, std::uint32_t seq) noexcept
A PaymentChannel.
Definition Indexes.cpp:376
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:13
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:29
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:59
Json::Value unauth(Account const &account, Account const &unauth)
Remove preauthorization for deposit.
Definition deposit.cpp:24
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:13
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:35
bool channelExists(ReadView const &view, uint256 const &chan)
Json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount, NetClock::duration const &settleDelay, PublicKey const &pk, std::optional< NetClock::time_point > const &cancelAfter, std::optional< std::uint32_t > const &dstTag)
uint256 channel(AccountID const &account, AccountID const &dst, std::uint32_t seqProxyValue)
Json::Value claim(AccountID const &account, uint256 const &channel, std::optional< STAmount > const &balance, std::optional< STAmount > const &amount, std::optional< Slice > const &signature, std::optional< PublicKey > const &pk)
STAmount channelBalance(ReadView const &view, uint256 const &chan)
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:102
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:10
void fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition AMMTest.cpp:18
FeatureBitset testable_amendments()
Definition Env.h:55
void sign(Json::Value &jv, Account const &account, Json::Value &sigObject)
Sign automatically into a specific Json field of the jv object.
Definition utility.cpp:28
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
owner_count< ltTICKET > tickets
Match the number of tickets on the account.
Definition ticket.h:45
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:66
constexpr std::uint32_t asfRequireDest
Definition TxFlags.h:58
constexpr std::uint32_t tfRenew
Definition TxFlags.h:115
static std::string sliceToHex(Slice const &slice)
Definition PublicKey.cpp:76
void serializePayChanAuthorization(Serializer &msg, uint256 const &key, XRPAmount const &amt)
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
void forAllApiVersions(Fn const &fn, Args &&... args)
Definition ApiVersion.h:158
@ tecNO_ENTRY
Definition TER.h:288
@ tecNO_DST
Definition TER.h:272
@ tecUNFUNDED
Definition TER.h:277
@ tecBAD_CREDENTIALS
Definition TER.h:341
@ tecNO_PERMISSION
Definition TER.h:287
@ tecDST_TAG_NEEDED
Definition TER.h:291
@ tecHAS_OBLIGATIONS
Definition TER.h:299
@ tecUNFUNDED_PAYMENT
Definition TER.h:267
@ tecEXPIRED
Definition TER.h:296
@ tesSUCCESS
Definition TER.h:226
bool isTesSuccess(TER x) noexcept
Definition TER.h:659
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
constexpr std::uint32_t asfDisallowIncomingPayChan
Definition TxFlags.h:73
constexpr std::uint32_t tfClose
Definition TxFlags.h:116
TERSubset< CanCvtToTER > TER
Definition TER.h:630
constexpr std::uint32_t asfDisallowXRP
Definition TxFlags.h:60
@ temBAD_AMOUNT
Definition TER.h:70
@ temBAD_SIGNER
Definition TER.h:96
@ temMALFORMED
Definition TER.h:68
@ temBAD_EXPIRATION
Definition TER.h:72
@ temDISABLED
Definition TER.h:95
@ temDST_IS_SRC
Definition TER.h:89
@ temBAD_SIGNATURE
Definition TER.h:86
T reserve(T... args)
T size(T... args)
static std::pair< uint256, std::shared_ptr< SLE const > > channelKeyAndSle(ReadView const &view, jtx::Account const &account, jtx::Account const &dst)
void testSimple(FeatureBitset features)
void testMultiple(FeatureBitset features)
void testAccountChannelsRPC(FeatureBitset features)
void testUsingTickets(FeatureBitset features)
void run() override
Runs the suite.
void testSettleDelay(FeatureBitset features)
void testDisallowXRP(FeatureBitset features)
void testDepositAuth(FeatureBitset features)
static std::optional< std::int64_t > channelExpiration(ReadView const &view, uint256 const &chan)
void testAccountDelete(FeatureBitset features)
void testCloseDry(FeatureBitset features)
void testExpiration(FeatureBitset features)
void testMetaAndOwnership(FeatureBitset features)
void testDefaultAmount(FeatureBitset features)
void testAuthVerifyRPC(FeatureBitset features)
void testMalformedPK(FeatureBitset features)
void testCancelAfter(FeatureBitset features)
void testWithFeats(FeatureBitset features)
void testDisallowIncoming(FeatureBitset features)
void testAccountChannelsRPCMarkers(FeatureBitset features)
void testAccountChannelAuthorize(FeatureBitset features)
void testAccountChannelsRPCSenderOnly(FeatureBitset features)
void testDstTag(FeatureBitset features)
static STAmount channelAmount(ReadView const &view, uint256 const &chan)
static Buffer signClaimAuth(PublicKey const &pk, SecretKey const &sk, uint256 const &channel, STAmount const &authAmt)
void testOptionalFields(FeatureBitset features)
T substr(T... args)
T to_string(T... args)