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