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