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