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