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