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