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