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