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