rippled
LedgerRPC_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2016 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/app/misc/Manifest.h>
21 #include <ripple/app/misc/TxQ.h>
22 #include <ripple/basics/StringUtilities.h>
23 #include <ripple/beast/unit_test.h>
24 #include <ripple/protocol/ErrorCodes.h>
25 #include <ripple/protocol/Feature.h>
26 #include <ripple/protocol/jss.h>
27 #include <test/jtx.h>
28 
29 namespace ripple {
30 
31 class LedgerRPC_test : public beast::unit_test::suite
32 {
33  void
35  Json::Value const& jv,
36  std::string const& err,
37  std::string const& msg)
38  {
39  if (BEAST_EXPECT(jv.isMember(jss::status)))
40  BEAST_EXPECT(jv[jss::status] == "error");
41  if (BEAST_EXPECT(jv.isMember(jss::error)))
42  BEAST_EXPECT(jv[jss::error] == err);
43  if (msg.empty())
44  {
45  BEAST_EXPECT(
46  jv[jss::error_message] == Json::nullValue ||
47  jv[jss::error_message] == "");
48  }
49  else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
50  BEAST_EXPECT(jv[jss::error_message] == msg);
51  }
52 
53  // Corrupt a valid address by replacing the 10th character with '!'.
54  // '!' is not part of the ripple alphabet.
57  {
58  std::string ret = std::move(good);
59  ret.replace(10, 1, 1, '!');
60  return ret;
61  }
62 
63  void
65  {
66  testcase("Basic Request");
67  using namespace test::jtx;
68 
69  Env env{*this};
70 
71  env.close();
72  BEAST_EXPECT(env.current()->info().seq == 4);
73 
74  {
75  // in this case, numeric string converted to number
76  auto const jrr = env.rpc("ledger", "1")[jss::result];
77  BEAST_EXPECT(jrr[jss::ledger][jss::closed] == true);
78  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "1");
79  BEAST_EXPECT(jrr[jss::ledger][jss::accepted] == true);
80  BEAST_EXPECT(
81  jrr[jss::ledger][jss::totalCoins] ==
82  env.balance(env.master).value().getText());
83  }
84 
85  {
86  // using current identifier
87  auto const jrr = env.rpc("ledger", "current")[jss::result];
88  BEAST_EXPECT(jrr[jss::ledger][jss::closed] == false);
89  BEAST_EXPECT(
90  jrr[jss::ledger][jss::ledger_index] ==
91  std::to_string(env.current()->info().seq));
92  BEAST_EXPECT(
93  jrr[jss::ledger_current_index] == env.current()->info().seq);
94  }
95  }
96 
97  void
99  {
100  testcase("Bad Input");
101  using namespace test::jtx;
102  Env env{*this};
103  Account const gw{"gateway"};
104  auto const USD = gw["USD"];
105  Account const bob{"bob"};
106 
107  env.fund(XRP(10000), gw, bob);
108  env.close();
109  env.trust(USD(1000), bob);
110  env.close();
111 
112  {
113  Json::Value jvParams;
114  jvParams[jss::ledger_index] = "0"; // NOT an integer
115  auto const jrr =
116  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
117  checkErrorValue(jrr, "invalidParams", "ledgerIndexMalformed");
118  }
119 
120  {
121  // ask for a bad ledger index
122  Json::Value jvParams;
123  jvParams[jss::ledger_index] = 10u;
124  auto const jrr =
125  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
126  checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
127  }
128 
129  {
130  // unrecognized string arg -- error
131  auto const jrr = env.rpc("ledger", "arbitrary_text")[jss::result];
132  checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
133  }
134 
135  {
136  // Request queue for closed ledger
137  Json::Value jvParams;
138  jvParams[jss::ledger_index] = "validated";
139  jvParams[jss::queue] = true;
140  auto const jrr =
141  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
142  checkErrorValue(jrr, "invalidParams", "Invalid parameters.");
143  }
144 
145  {
146  // Request a ledger with a very large (double) sequence.
147  auto const ret =
148  env.rpc("json", "ledger", "{ \"ledger_index\" : 2e15 }");
149  BEAST_EXPECT(RPC::contains_error(ret));
150  BEAST_EXPECT(ret[jss::error_message] == "Invalid parameters.");
151  }
152 
153  {
154  // Request a ledger with very large (integer) sequence.
155  auto const ret = env.rpc(
156  "json", "ledger", "{ \"ledger_index\" : 1000000000000000 }");
157  checkErrorValue(ret, "invalidParams", "Invalid parameters.");
158  }
159  }
160 
161  void
163  {
164  testcase("ledger_current Request");
165  using namespace test::jtx;
166 
167  Env env{*this};
168 
169  env.close();
170  BEAST_EXPECT(env.current()->info().seq == 4);
171 
172  {
173  auto const jrr = env.rpc("ledger_current")[jss::result];
174  BEAST_EXPECT(
175  jrr[jss::ledger_current_index] == env.current()->info().seq);
176  }
177  }
178 
179  void
181  {
182  testcase("Missing ledger_entry ledger_hash");
183  using namespace test::jtx;
184  Env env{*this};
185  Account const alice{"alice"};
186  env.fund(XRP(10000), alice);
187  env.close();
188 
189  Json::Value jvParams;
190  jvParams[jss::account_root] = alice.human();
191  jvParams[jss::ledger_hash] =
192  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
193  auto const jrr =
194  env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
195  checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
196  }
197 
198  void
200  {
201  testcase("Ledger Request, Full Option");
202  using namespace test::jtx;
203 
204  Env env{*this};
205 
206  env.close();
207 
208  Json::Value jvParams;
209  jvParams[jss::ledger_index] = 3u;
210  jvParams[jss::full] = true;
211  auto const jrr =
212  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
213  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
214  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
215  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 2u);
216  }
217 
218  void
220  {
221  testcase("Ledger Request, Full Option Without Admin");
222  using namespace test::jtx;
223 
224  Env env{*this, envconfig(no_admin)};
225 
226  env.close();
227 
228  Json::Value jvParams;
229  jvParams[jss::ledger_index] = 3u;
230  jvParams[jss::full] = true;
231  auto const jrr =
232  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
234  jrr, "noPermission", "You don't have permission for this command.");
235  }
236 
237  void
239  {
240  testcase("Ledger Request, Accounts Option");
241  using namespace test::jtx;
242 
243  Env env{*this};
244 
245  env.close();
246 
247  Json::Value jvParams;
248  jvParams[jss::ledger_index] = 3u;
249  jvParams[jss::accounts] = true;
250  auto const jrr =
251  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
252  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
253  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
254  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 2u);
255  }
256 
257  void
259  {
260  testcase("ledger_entry Request AccountRoot");
261  using namespace test::jtx;
262  Env env{*this};
263  Account const alice{"alice"};
264  env.fund(XRP(10000), alice);
265  env.close();
266 
267  std::string const ledgerHash{to_string(env.closed()->info().hash)};
268  {
269  // Exercise ledger_closed along the way.
270  Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
271  BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
272  BEAST_EXPECT(jrr[jss::ledger_index] == 3);
273  }
274 
275  std::string accountRootIndex;
276  {
277  // Request alice's account root.
278  Json::Value jvParams;
279  jvParams[jss::account_root] = alice.human();
280  jvParams[jss::ledger_hash] = ledgerHash;
281  Json::Value const jrr = env.rpc(
282  "json", "ledger_entry", to_string(jvParams))[jss::result];
283  BEAST_EXPECT(jrr.isMember(jss::node));
284  BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
285  BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
286  accountRootIndex = jrr[jss::index].asString();
287  }
288  {
289  constexpr char alicesAcctRootBinary[]{
290  "1100612200800000240000000425000000032D00000000559CE54C3B934E4"
291  "73A995B477E92EC229F99CED5B62BF4D2ACE4DC42719103AE2F6240000002"
292  "540BE4008114AE123A8556F3CF91154711376AFB0F894F832B3D"};
293 
294  // Request alice's account root, but with binary == true;
295  Json::Value jvParams;
296  jvParams[jss::account_root] = alice.human();
297  jvParams[jss::binary] = 1;
298  jvParams[jss::ledger_hash] = ledgerHash;
299  Json::Value const jrr = env.rpc(
300  "json", "ledger_entry", to_string(jvParams))[jss::result];
301  BEAST_EXPECT(jrr.isMember(jss::node_binary));
302  BEAST_EXPECT(jrr[jss::node_binary] == alicesAcctRootBinary);
303  }
304  {
305  // Request alice's account root using the index.
306  Json::Value jvParams;
307  jvParams[jss::index] = accountRootIndex;
308  Json::Value const jrr = env.rpc(
309  "json", "ledger_entry", to_string(jvParams))[jss::result];
310  BEAST_EXPECT(!jrr.isMember(jss::node_binary));
311  BEAST_EXPECT(jrr.isMember(jss::node));
312  BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
313  BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
314  }
315  {
316  // Request alice's account root by index, but with binary == false.
317  Json::Value jvParams;
318  jvParams[jss::index] = accountRootIndex;
319  jvParams[jss::binary] = 0;
320  Json::Value const jrr = env.rpc(
321  "json", "ledger_entry", to_string(jvParams))[jss::result];
322  BEAST_EXPECT(jrr.isMember(jss::node));
323  BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
324  BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
325  }
326  {
327  // Request using a corrupted AccountID.
328  Json::Value jvParams;
329  jvParams[jss::account_root] = makeBadAddress(alice.human());
330  jvParams[jss::ledger_hash] = ledgerHash;
331  Json::Value const jrr = env.rpc(
332  "json", "ledger_entry", to_string(jvParams))[jss::result];
333  checkErrorValue(jrr, "malformedAddress", "");
334  }
335  {
336  // Request an account that is not in the ledger.
337  Json::Value jvParams;
338  jvParams[jss::account_root] = Account("bob").human();
339  jvParams[jss::ledger_hash] = ledgerHash;
340  Json::Value const jrr = env.rpc(
341  "json", "ledger_entry", to_string(jvParams))[jss::result];
342  checkErrorValue(jrr, "entryNotFound", "");
343  }
344  }
345 
346  void
348  {
349  testcase("ledger_entry Request Check");
350  using namespace test::jtx;
351  Env env{*this};
352  Account const alice{"alice"};
353  env.fund(XRP(10000), alice);
354  env.close();
355 
356  uint256 const checkId{getCheckIndex(env.master, env.seq(env.master))};
357 
358  env(check::create(env.master, alice, XRP(100)));
359  env.close();
360 
361  std::string const ledgerHash{to_string(env.closed()->info().hash)};
362  {
363  // Request a check.
364  Json::Value jvParams;
365  jvParams[jss::check] = to_string(checkId);
366  jvParams[jss::ledger_hash] = ledgerHash;
367  Json::Value const jrr = env.rpc(
368  "json", "ledger_entry", to_string(jvParams))[jss::result];
369  BEAST_EXPECT(
370  jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
371  BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
372  }
373  {
374  // Request an index that is not a check. We'll use alice's
375  // account root index.
376  std::string accountRootIndex;
377  {
378  Json::Value jvParams;
379  jvParams[jss::account_root] = alice.human();
380  Json::Value const jrr = env.rpc(
381  "json", "ledger_entry", to_string(jvParams))[jss::result];
382  accountRootIndex = jrr[jss::index].asString();
383  }
384  Json::Value jvParams;
385  jvParams[jss::check] = accountRootIndex;
386  jvParams[jss::ledger_hash] = ledgerHash;
387  Json::Value const jrr = env.rpc(
388  "json", "ledger_entry", to_string(jvParams))[jss::result];
389  checkErrorValue(jrr, "malformedRequest", "");
390  }
391  }
392 
393  void
395  {
396  testcase("ledger_entry Request Directory");
397  using namespace test::jtx;
398  Env env{*this};
399  Account const alice{"alice"};
400  Account const becky{"becky"};
401 
402  env.fund(XRP(10000), alice, becky);
403  env.close();
404 
405  env(deposit::auth(alice, becky));
406  env.close();
407 
408  std::string const ledgerHash{to_string(env.closed()->info().hash)};
409  std::string depositPreauthIndex;
410  {
411  // Request a depositPreauth by owner and authorized.
412  Json::Value jvParams;
413  jvParams[jss::deposit_preauth][jss::owner] = alice.human();
414  jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
415  jvParams[jss::ledger_hash] = ledgerHash;
416  Json::Value const jrr = env.rpc(
417  "json", "ledger_entry", to_string(jvParams))[jss::result];
418 
419  BEAST_EXPECT(
420  jrr[jss::node][sfLedgerEntryType.jsonName] ==
421  jss::DepositPreauth);
422  BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
423  BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
424  depositPreauthIndex = jrr[jss::node][jss::index].asString();
425  }
426  {
427  // Request a depositPreauth by index.
428  Json::Value jvParams;
429  jvParams[jss::deposit_preauth] = depositPreauthIndex;
430  jvParams[jss::ledger_hash] = ledgerHash;
431  Json::Value const jrr = env.rpc(
432  "json", "ledger_entry", to_string(jvParams))[jss::result];
433 
434  BEAST_EXPECT(
435  jrr[jss::node][sfLedgerEntryType.jsonName] ==
436  jss::DepositPreauth);
437  BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
438  BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
439  }
440  {
441  // Malformed request: deposit_preauth neither object nor string.
442  Json::Value jvParams;
443  jvParams[jss::deposit_preauth] = -5;
444  jvParams[jss::ledger_hash] = ledgerHash;
445  Json::Value const jrr = env.rpc(
446  "json", "ledger_entry", to_string(jvParams))[jss::result];
447  checkErrorValue(jrr, "malformedRequest", "");
448  }
449  {
450  // Malformed request: deposit_preauth not hex string.
451  Json::Value jvParams;
452  jvParams[jss::deposit_preauth] = "0123456789ABCDEFG";
453  jvParams[jss::ledger_hash] = ledgerHash;
454  Json::Value const jrr = env.rpc(
455  "json", "ledger_entry", to_string(jvParams))[jss::result];
456  checkErrorValue(jrr, "malformedRequest", "");
457  }
458  {
459  // Malformed request: missing [jss::deposit_preauth][jss::owner]
460  Json::Value jvParams;
461  jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
462  jvParams[jss::ledger_hash] = ledgerHash;
463  Json::Value const jrr = env.rpc(
464  "json", "ledger_entry", to_string(jvParams))[jss::result];
465  checkErrorValue(jrr, "malformedRequest", "");
466  }
467  {
468  // Malformed request: [jss::deposit_preauth][jss::owner] not string.
469  Json::Value jvParams;
470  jvParams[jss::deposit_preauth][jss::owner] = 7;
471  jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
472  jvParams[jss::ledger_hash] = ledgerHash;
473  Json::Value const jrr = env.rpc(
474  "json", "ledger_entry", to_string(jvParams))[jss::result];
475  checkErrorValue(jrr, "malformedRequest", "");
476  }
477  {
478  // Malformed: missing [jss::deposit_preauth][jss::authorized]
479  Json::Value jvParams;
480  jvParams[jss::deposit_preauth][jss::owner] = alice.human();
481  jvParams[jss::ledger_hash] = ledgerHash;
482  Json::Value const jrr = env.rpc(
483  "json", "ledger_entry", to_string(jvParams))[jss::result];
484  checkErrorValue(jrr, "malformedRequest", "");
485  }
486  {
487  // Malformed: [jss::deposit_preauth][jss::authorized] not string.
488  Json::Value jvParams;
489  jvParams[jss::deposit_preauth][jss::owner] = alice.human();
490  jvParams[jss::deposit_preauth][jss::authorized] = 47;
491  jvParams[jss::ledger_hash] = ledgerHash;
492  Json::Value const jrr = env.rpc(
493  "json", "ledger_entry", to_string(jvParams))[jss::result];
494  checkErrorValue(jrr, "malformedRequest", "");
495  }
496  {
497  // Malformed: [jss::deposit_preauth][jss::owner] is malformed.
498  Json::Value jvParams;
499  jvParams[jss::deposit_preauth][jss::owner] =
500  "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
501 
502  jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
503  jvParams[jss::ledger_hash] = ledgerHash;
504  Json::Value const jrr = env.rpc(
505  "json", "ledger_entry", to_string(jvParams))[jss::result];
506  checkErrorValue(jrr, "malformedOwner", "");
507  }
508  {
509  // Malformed: [jss::deposit_preauth][jss::authorized] is malformed.
510  Json::Value jvParams;
511  jvParams[jss::deposit_preauth][jss::owner] = alice.human();
512  jvParams[jss::deposit_preauth][jss::authorized] =
513  "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
514 
515  jvParams[jss::ledger_hash] = ledgerHash;
516  Json::Value const jrr = env.rpc(
517  "json", "ledger_entry", to_string(jvParams))[jss::result];
518  checkErrorValue(jrr, "malformedAuthorized", "");
519  }
520  }
521 
522  void
524  {
525  testcase("ledger_entry Request Directory");
526  using namespace test::jtx;
527  Env env{*this};
528  Account const alice{"alice"};
529  Account const gw{"gateway"};
530  auto const USD = gw["USD"];
531  env.fund(XRP(10000), alice, gw);
532  env.close();
533 
534  env.trust(USD(1000), alice);
535  env.close();
536 
537  // Run up the number of directory entries so alice has two
538  // directory nodes.
539  for (int d = 1'000'032; d >= 1'000'000; --d)
540  {
541  env(offer(alice, USD(1), drops(d)));
542  }
543  env.close();
544 
545  std::string const ledgerHash{to_string(env.closed()->info().hash)};
546  {
547  // Exercise ledger_closed along the way.
548  Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
549  BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
550  BEAST_EXPECT(jrr[jss::ledger_index] == 5);
551  }
552 
553  std::string const dirRootIndex =
554  "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D";
555  {
556  // Locate directory by index.
557  Json::Value jvParams;
558  jvParams[jss::directory] = dirRootIndex;
559  jvParams[jss::ledger_hash] = ledgerHash;
560  Json::Value const jrr = env.rpc(
561  "json", "ledger_entry", to_string(jvParams))[jss::result];
562  BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32);
563  }
564  {
565  // Locate directory by directory root.
566  Json::Value jvParams;
567  jvParams[jss::directory] = Json::objectValue;
568  jvParams[jss::directory][jss::dir_root] = dirRootIndex;
569  Json::Value const jrr = env.rpc(
570  "json", "ledger_entry", to_string(jvParams))[jss::result];
571  BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
572  }
573  {
574  // Locate directory by owner.
575  Json::Value jvParams;
576  jvParams[jss::directory] = Json::objectValue;
577  jvParams[jss::directory][jss::owner] = alice.human();
578  jvParams[jss::ledger_hash] = ledgerHash;
579  Json::Value const jrr = env.rpc(
580  "json", "ledger_entry", to_string(jvParams))[jss::result];
581  BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
582  }
583  {
584  // Locate directory by directory root and sub_index.
585  Json::Value jvParams;
586  jvParams[jss::directory] = Json::objectValue;
587  jvParams[jss::directory][jss::dir_root] = dirRootIndex;
588  jvParams[jss::directory][jss::sub_index] = 1;
589  Json::Value const jrr = env.rpc(
590  "json", "ledger_entry", to_string(jvParams))[jss::result];
591  BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
592  BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
593  }
594  {
595  // Locate directory by owner and sub_index.
596  Json::Value jvParams;
597  jvParams[jss::directory] = Json::objectValue;
598  jvParams[jss::directory][jss::owner] = alice.human();
599  jvParams[jss::directory][jss::sub_index] = 1;
600  jvParams[jss::ledger_hash] = ledgerHash;
601  Json::Value const jrr = env.rpc(
602  "json", "ledger_entry", to_string(jvParams))[jss::result];
603  BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
604  BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
605  }
606  {
607  // Null directory argument.
608  Json::Value jvParams;
609  jvParams[jss::directory] = Json::nullValue;
610  jvParams[jss::ledger_hash] = ledgerHash;
611  Json::Value const jrr = env.rpc(
612  "json", "ledger_entry", to_string(jvParams))[jss::result];
613  checkErrorValue(jrr, "malformedRequest", "");
614  }
615  {
616  // Non-integer sub_index.
617  Json::Value jvParams;
618  jvParams[jss::directory] = Json::objectValue;
619  jvParams[jss::directory][jss::dir_root] = dirRootIndex;
620  jvParams[jss::directory][jss::sub_index] = 1.5;
621  jvParams[jss::ledger_hash] = ledgerHash;
622  Json::Value const jrr = env.rpc(
623  "json", "ledger_entry", to_string(jvParams))[jss::result];
624  checkErrorValue(jrr, "malformedRequest", "");
625  }
626  {
627  // Malformed owner entry.
628  Json::Value jvParams;
629  jvParams[jss::directory] = Json::objectValue;
630 
631  std::string const badAddress = makeBadAddress(alice.human());
632  jvParams[jss::directory][jss::owner] = badAddress;
633  jvParams[jss::ledger_hash] = ledgerHash;
634  Json::Value const jrr = env.rpc(
635  "json", "ledger_entry", to_string(jvParams))[jss::result];
636  checkErrorValue(jrr, "malformedAddress", "");
637  }
638  {
639  // Malformed directory object. Specify both dir_root and owner.
640  Json::Value jvParams;
641  jvParams[jss::directory] = Json::objectValue;
642  jvParams[jss::directory][jss::owner] = alice.human();
643  jvParams[jss::directory][jss::dir_root] = dirRootIndex;
644  jvParams[jss::ledger_hash] = ledgerHash;
645  Json::Value const jrr = env.rpc(
646  "json", "ledger_entry", to_string(jvParams))[jss::result];
647  checkErrorValue(jrr, "malformedRequest", "");
648  }
649  {
650  // Incomplete directory object. Missing both dir_root and owner.
651  Json::Value jvParams;
652  jvParams[jss::directory] = Json::objectValue;
653  jvParams[jss::directory][jss::sub_index] = 1;
654  jvParams[jss::ledger_hash] = ledgerHash;
655  Json::Value const jrr = env.rpc(
656  "json", "ledger_entry", to_string(jvParams))[jss::result];
657  checkErrorValue(jrr, "malformedRequest", "");
658  }
659  }
660 
661  void
663  {
664  testcase("ledger_entry Request Escrow");
665  using namespace test::jtx;
666  Env env{*this};
667  Account const alice{"alice"};
668  env.fund(XRP(10000), alice);
669  env.close();
670 
671  // Lambda to create an escrow.
672  auto escrowCreate = [](test::jtx::Account const& account,
673  test::jtx::Account const& to,
674  STAmount const& amount,
675  NetClock::time_point const& cancelAfter) {
676  Json::Value jv;
677  jv[jss::TransactionType] = jss::EscrowCreate;
678  jv[jss::Flags] = tfUniversal;
679  jv[jss::Account] = account.human();
680  jv[jss::Destination] = to.human();
681  jv[jss::Amount] = amount.getJson(JsonOptions::none);
682  jv[sfFinishAfter.jsonName] =
683  cancelAfter.time_since_epoch().count() + 2;
684  return jv;
685  };
686 
687  using namespace std::chrono_literals;
688  env(escrowCreate(alice, alice, XRP(333), env.now() + 2s));
689  env.close();
690 
691  std::string const ledgerHash{to_string(env.closed()->info().hash)};
692  std::string escrowIndex;
693  {
694  // Request the escrow using owner and sequence.
695  Json::Value jvParams;
696  jvParams[jss::escrow] = Json::objectValue;
697  jvParams[jss::escrow][jss::owner] = alice.human();
698  jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
699  Json::Value const jrr = env.rpc(
700  "json", "ledger_entry", to_string(jvParams))[jss::result];
701  BEAST_EXPECT(
702  jrr[jss::node][jss::Amount] == XRP(333).value().getText());
703  escrowIndex = jrr[jss::index].asString();
704  }
705  {
706  // Request the escrow by index.
707  Json::Value jvParams;
708  jvParams[jss::escrow] = escrowIndex;
709  jvParams[jss::ledger_hash] = ledgerHash;
710  Json::Value const jrr = env.rpc(
711  "json", "ledger_entry", to_string(jvParams))[jss::result];
712  BEAST_EXPECT(
713  jrr[jss::node][jss::Amount] == XRP(333).value().getText());
714  }
715  {
716  // Malformed owner entry.
717  Json::Value jvParams;
718  jvParams[jss::escrow] = Json::objectValue;
719 
720  std::string const badAddress = makeBadAddress(alice.human());
721  jvParams[jss::escrow][jss::owner] = badAddress;
722  jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
723  jvParams[jss::ledger_hash] = ledgerHash;
724  Json::Value const jrr = env.rpc(
725  "json", "ledger_entry", to_string(jvParams))[jss::result];
726  checkErrorValue(jrr, "malformedOwner", "");
727  }
728  {
729  // Missing owner.
730  Json::Value jvParams;
731  jvParams[jss::escrow] = Json::objectValue;
732  jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
733  jvParams[jss::ledger_hash] = ledgerHash;
734  Json::Value const jrr = env.rpc(
735  "json", "ledger_entry", to_string(jvParams))[jss::result];
736  checkErrorValue(jrr, "malformedRequest", "");
737  }
738  {
739  // Missing sequence.
740  Json::Value jvParams;
741  jvParams[jss::escrow] = Json::objectValue;
742  jvParams[jss::escrow][jss::owner] = alice.human();
743  jvParams[jss::ledger_hash] = ledgerHash;
744  Json::Value const jrr = env.rpc(
745  "json", "ledger_entry", to_string(jvParams))[jss::result];
746  checkErrorValue(jrr, "malformedRequest", "");
747  }
748  {
749  // Non-integer sequence.
750  Json::Value jvParams;
751  jvParams[jss::escrow] = Json::objectValue;
752  jvParams[jss::escrow][jss::owner] = alice.human();
753  jvParams[jss::escrow][jss::seq] =
754  std::to_string(env.seq(alice) - 1);
755  jvParams[jss::ledger_hash] = ledgerHash;
756  Json::Value const jrr = env.rpc(
757  "json", "ledger_entry", to_string(jvParams))[jss::result];
758  checkErrorValue(jrr, "malformedRequest", "");
759  }
760  }
761 
762  void
764  {
765  testcase("ledger_entry Request Offer");
766  using namespace test::jtx;
767  Env env{*this};
768  Account const alice{"alice"};
769  Account const gw{"gateway"};
770  auto const USD = gw["USD"];
771  env.fund(XRP(10000), alice, gw);
772  env.close();
773 
774  env(offer(alice, USD(321), XRP(322)));
775  env.close();
776 
777  std::string const ledgerHash{to_string(env.closed()->info().hash)};
778  std::string offerIndex;
779  {
780  // Request the offer using owner and sequence.
781  Json::Value jvParams;
782  jvParams[jss::offer] = Json::objectValue;
783  jvParams[jss::offer][jss::account] = alice.human();
784  jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
785  jvParams[jss::ledger_hash] = ledgerHash;
786  Json::Value const jrr = env.rpc(
787  "json", "ledger_entry", to_string(jvParams))[jss::result];
788  BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
789  offerIndex = jrr[jss::index].asString();
790  }
791  {
792  // Request the offer using its index.
793  Json::Value jvParams;
794  jvParams[jss::offer] = offerIndex;
795  Json::Value const jrr = env.rpc(
796  "json", "ledger_entry", to_string(jvParams))[jss::result];
797  BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
798  }
799  {
800  // Malformed account entry.
801  Json::Value jvParams;
802  jvParams[jss::offer] = Json::objectValue;
803 
804  std::string const badAddress = makeBadAddress(alice.human());
805  jvParams[jss::offer][jss::account] = badAddress;
806  jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
807  jvParams[jss::ledger_hash] = ledgerHash;
808  Json::Value const jrr = env.rpc(
809  "json", "ledger_entry", to_string(jvParams))[jss::result];
810  checkErrorValue(jrr, "malformedAddress", "");
811  }
812  {
813  // Malformed offer object. Missing account member.
814  Json::Value jvParams;
815  jvParams[jss::offer] = Json::objectValue;
816  jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
817  jvParams[jss::ledger_hash] = ledgerHash;
818  Json::Value const jrr = env.rpc(
819  "json", "ledger_entry", to_string(jvParams))[jss::result];
820  checkErrorValue(jrr, "malformedRequest", "");
821  }
822  {
823  // Malformed offer object. Missing seq member.
824  Json::Value jvParams;
825  jvParams[jss::offer] = Json::objectValue;
826  jvParams[jss::offer][jss::account] = alice.human();
827  jvParams[jss::ledger_hash] = ledgerHash;
828  Json::Value const jrr = env.rpc(
829  "json", "ledger_entry", to_string(jvParams))[jss::result];
830  checkErrorValue(jrr, "malformedRequest", "");
831  }
832  {
833  // Malformed offer object. Non-integral seq member.
834  Json::Value jvParams;
835  jvParams[jss::offer] = Json::objectValue;
836  jvParams[jss::offer][jss::account] = alice.human();
837  jvParams[jss::offer][jss::seq] = std::to_string(env.seq(alice) - 1);
838  jvParams[jss::ledger_hash] = ledgerHash;
839  Json::Value const jrr = env.rpc(
840  "json", "ledger_entry", to_string(jvParams))[jss::result];
841  checkErrorValue(jrr, "malformedRequest", "");
842  }
843  }
844 
845  void
847  {
848  testcase("ledger_entry Request Pay Chan");
849  using namespace test::jtx;
850  using namespace std::literals::chrono_literals;
851  Env env{*this};
852  Account const alice{"alice"};
853 
854  env.fund(XRP(10000), alice);
855  env.close();
856 
857  // Lambda to create a PayChan.
858  auto payChanCreate = [](test::jtx::Account const& account,
859  test::jtx::Account const& to,
860  STAmount const& amount,
861  NetClock::duration const& settleDelay,
862  PublicKey const& pk) {
863  Json::Value jv;
864  jv[jss::TransactionType] = jss::PaymentChannelCreate;
865  jv[jss::Account] = account.human();
866  jv[jss::Destination] = to.human();
867  jv[jss::Amount] = amount.getJson(JsonOptions::none);
868  jv[sfSettleDelay.jsonName] = settleDelay.count();
869  jv[sfPublicKey.jsonName] = strHex(pk.slice());
870  return jv;
871  };
872 
873  env(payChanCreate(alice, env.master, XRP(57), 18s, alice.pk()));
874  env.close();
875 
876  std::string const ledgerHash{to_string(env.closed()->info().hash)};
877 
878  uint256 const payChanIndex{
879  keylet::payChan(alice, env.master, env.seq(alice) - 1).key};
880  {
881  // Request the payment channel using its index.
882  Json::Value jvParams;
883  jvParams[jss::payment_channel] = to_string(payChanIndex);
884  jvParams[jss::ledger_hash] = ledgerHash;
885  Json::Value const jrr = env.rpc(
886  "json", "ledger_entry", to_string(jvParams))[jss::result];
887  BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
888  BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
889  BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
890  }
891  {
892  // Request an index that is not a payment channel.
893  Json::Value jvParams;
894  jvParams[jss::payment_channel] = ledgerHash;
895  jvParams[jss::ledger_hash] = ledgerHash;
896  Json::Value const jrr = env.rpc(
897  "json", "ledger_entry", to_string(jvParams))[jss::result];
898  checkErrorValue(jrr, "entryNotFound", "");
899  }
900  }
901 
902  void
904  {
905  testcase("ledger_entry Request RippleState");
906  using namespace test::jtx;
907  Env env{*this};
908  Account const alice{"alice"};
909  Account const gw{"gateway"};
910  auto const USD = gw["USD"];
911  env.fund(XRP(10000), alice, gw);
912  env.close();
913 
914  env.trust(USD(999), alice);
915  env.close();
916 
917  env(pay(gw, alice, USD(97)));
918  env.close();
919 
920  std::string const ledgerHash{to_string(env.closed()->info().hash)};
921  {
922  // Request the trust line using the accounts and currency.
923  Json::Value jvParams;
924  jvParams[jss::ripple_state] = Json::objectValue;
925  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
926  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
927  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
928  jvParams[jss::ripple_state][jss::currency] = "USD";
929  jvParams[jss::ledger_hash] = ledgerHash;
930  Json::Value const jrr = env.rpc(
931  "json", "ledger_entry", to_string(jvParams))[jss::result];
932  BEAST_EXPECT(
933  jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
934  BEAST_EXPECT(
935  jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
936  }
937  {
938  // ripple_state is not an object.
939  Json::Value jvParams;
940  jvParams[jss::ripple_state] = "ripple_state";
941  jvParams[jss::ledger_hash] = ledgerHash;
942  Json::Value const jrr = env.rpc(
943  "json", "ledger_entry", to_string(jvParams))[jss::result];
944  checkErrorValue(jrr, "malformedRequest", "");
945  }
946  {
947  // ripple_state.currency is missing.
948  Json::Value jvParams;
949  jvParams[jss::ripple_state] = Json::objectValue;
950  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
951  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
952  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
953  jvParams[jss::ledger_hash] = ledgerHash;
954  Json::Value const jrr = env.rpc(
955  "json", "ledger_entry", to_string(jvParams))[jss::result];
956  checkErrorValue(jrr, "malformedRequest", "");
957  }
958  {
959  // ripple_state accounts is not an array.
960  Json::Value jvParams;
961  jvParams[jss::ripple_state] = Json::objectValue;
962  jvParams[jss::ripple_state][jss::accounts] = 2;
963  jvParams[jss::ripple_state][jss::currency] = "USD";
964  jvParams[jss::ledger_hash] = ledgerHash;
965  Json::Value const jrr = env.rpc(
966  "json", "ledger_entry", to_string(jvParams))[jss::result];
967  checkErrorValue(jrr, "malformedRequest", "");
968  }
969  {
970  // ripple_state one of the accounts is missing.
971  Json::Value jvParams;
972  jvParams[jss::ripple_state] = Json::objectValue;
973  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
974  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
975  jvParams[jss::ripple_state][jss::currency] = "USD";
976  jvParams[jss::ledger_hash] = ledgerHash;
977  Json::Value const jrr = env.rpc(
978  "json", "ledger_entry", to_string(jvParams))[jss::result];
979  checkErrorValue(jrr, "malformedRequest", "");
980  }
981  {
982  // ripple_state more than 2 accounts.
983  Json::Value jvParams;
984  jvParams[jss::ripple_state] = Json::objectValue;
985  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
986  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
987  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
988  jvParams[jss::ripple_state][jss::accounts][2u] = alice.human();
989  jvParams[jss::ripple_state][jss::currency] = "USD";
990  jvParams[jss::ledger_hash] = ledgerHash;
991  Json::Value const jrr = env.rpc(
992  "json", "ledger_entry", to_string(jvParams))[jss::result];
993  checkErrorValue(jrr, "malformedRequest", "");
994  }
995  {
996  // ripple_state account[0] is not a string.
997  Json::Value jvParams;
998  jvParams[jss::ripple_state] = Json::objectValue;
999  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1000  jvParams[jss::ripple_state][jss::accounts][0u] = 44;
1001  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
1002  jvParams[jss::ripple_state][jss::currency] = "USD";
1003  jvParams[jss::ledger_hash] = ledgerHash;
1004  Json::Value const jrr = env.rpc(
1005  "json", "ledger_entry", to_string(jvParams))[jss::result];
1006  checkErrorValue(jrr, "malformedRequest", "");
1007  }
1008  {
1009  // ripple_state account[1] is not a string.
1010  Json::Value jvParams;
1011  jvParams[jss::ripple_state] = Json::objectValue;
1012  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1013  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
1014  jvParams[jss::ripple_state][jss::accounts][1u] = 21;
1015  jvParams[jss::ripple_state][jss::currency] = "USD";
1016  jvParams[jss::ledger_hash] = ledgerHash;
1017  Json::Value const jrr = env.rpc(
1018  "json", "ledger_entry", to_string(jvParams))[jss::result];
1019  checkErrorValue(jrr, "malformedRequest", "");
1020  }
1021  {
1022  // ripple_state account[0] == account[1].
1023  Json::Value jvParams;
1024  jvParams[jss::ripple_state] = Json::objectValue;
1025  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1026  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
1027  jvParams[jss::ripple_state][jss::accounts][1u] = alice.human();
1028  jvParams[jss::ripple_state][jss::currency] = "USD";
1029  jvParams[jss::ledger_hash] = ledgerHash;
1030  Json::Value const jrr = env.rpc(
1031  "json", "ledger_entry", to_string(jvParams))[jss::result];
1032  checkErrorValue(jrr, "malformedRequest", "");
1033  }
1034  {
1035  // ripple_state malformed account[0].
1036  Json::Value jvParams;
1037  jvParams[jss::ripple_state] = Json::objectValue;
1038  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1039  jvParams[jss::ripple_state][jss::accounts][0u] =
1040  makeBadAddress(alice.human());
1041  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
1042  jvParams[jss::ripple_state][jss::currency] = "USD";
1043  jvParams[jss::ledger_hash] = ledgerHash;
1044  Json::Value const jrr = env.rpc(
1045  "json", "ledger_entry", to_string(jvParams))[jss::result];
1046  checkErrorValue(jrr, "malformedAddress", "");
1047  }
1048  {
1049  // ripple_state malformed account[1].
1050  Json::Value jvParams;
1051  jvParams[jss::ripple_state] = Json::objectValue;
1052  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1053  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
1054  jvParams[jss::ripple_state][jss::accounts][1u] =
1055  makeBadAddress(gw.human());
1056  jvParams[jss::ripple_state][jss::currency] = "USD";
1057  jvParams[jss::ledger_hash] = ledgerHash;
1058  Json::Value const jrr = env.rpc(
1059  "json", "ledger_entry", to_string(jvParams))[jss::result];
1060  checkErrorValue(jrr, "malformedAddress", "");
1061  }
1062  {
1063  // ripple_state malformed currency.
1064  Json::Value jvParams;
1065  jvParams[jss::ripple_state] = Json::objectValue;
1066  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1067  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
1068  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
1069  jvParams[jss::ripple_state][jss::currency] = "USDollars";
1070  jvParams[jss::ledger_hash] = ledgerHash;
1071  Json::Value const jrr = env.rpc(
1072  "json", "ledger_entry", to_string(jvParams))[jss::result];
1073  checkErrorValue(jrr, "malformedCurrency", "");
1074  }
1075  }
1076 
1077  void
1079  {
1080  testcase("ledger_entry Request Unknown Option");
1081  using namespace test::jtx;
1082  Env env{*this};
1083 
1084  std::string const ledgerHash{to_string(env.closed()->info().hash)};
1085 
1086  // "features" is not an option supported by ledger_entry.
1087  Json::Value jvParams;
1088  jvParams[jss::features] = ledgerHash;
1089  jvParams[jss::ledger_hash] = ledgerHash;
1090  Json::Value const jrr =
1091  env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
1092  checkErrorValue(jrr, "unknownOption", "");
1093  }
1094 
1099  void
1101  {
1102  testcase("Lookup ledger");
1103  using namespace test::jtx;
1104  Env env{*this, FeatureBitset{}}; // hashes requested below assume
1105  // no amendments
1106  env.fund(XRP(10000), "alice");
1107  env.close();
1108  env.fund(XRP(10000), "bob");
1109  env.close();
1110  env.fund(XRP(10000), "jim");
1111  env.close();
1112  env.fund(XRP(10000), "jill");
1113 
1114  // closed ledger hashes are:
1115  // 1 - AB868A6CFEEC779C2FF845C0AF00A642259986AF40C01976A7F842B6918936C7
1116  // 2 - 8AEDBB96643962F1D40F01E25632ABB3C56C9F04B0231EE4B18248B90173D189
1117  // 3 - 7C3EEDB3124D92E49E75D81A8826A2E65A75FD71FC3FD6F36FEB803C5F1D812D
1118  // 4 - 9F9E6A4ECAA84A08FF94713FA41C3151177D6222EA47DD2F0020CA49913EE2E6
1119  // 5 - C516522DE274EB52CE69A3D22F66DD73A53E16597E06F7A86F66DF7DD4309173
1120  //
1121  {
1122  // access via the legacy ledger field, keyword index values
1123  Json::Value jvParams;
1124  jvParams[jss::ledger] = "closed";
1125  auto jrr = env.rpc(
1126  "json",
1127  "ledger",
1128  boost::lexical_cast<std::string>(jvParams))[jss::result];
1129  BEAST_EXPECT(jrr.isMember(jss::ledger));
1130  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1131  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1132 
1133  jvParams[jss::ledger] = "validated";
1134  jrr = env.rpc(
1135  "json",
1136  "ledger",
1137  boost::lexical_cast<std::string>(jvParams))[jss::result];
1138  BEAST_EXPECT(jrr.isMember(jss::ledger));
1139  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1140  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1141 
1142  jvParams[jss::ledger] = "current";
1143  jrr = env.rpc(
1144  "json",
1145  "ledger",
1146  boost::lexical_cast<std::string>(jvParams))[jss::result];
1147  BEAST_EXPECT(jrr.isMember(jss::ledger));
1148  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
1149 
1150  // ask for a bad ledger keyword
1151  jvParams[jss::ledger] = "invalid";
1152  jrr = env.rpc(
1153  "json",
1154  "ledger",
1155  boost::lexical_cast<std::string>(jvParams))[jss::result];
1156  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1157  BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
1158 
1159  // numeric index
1160  jvParams[jss::ledger] = 4;
1161  jrr = env.rpc(
1162  "json",
1163  "ledger",
1164  boost::lexical_cast<std::string>(jvParams))[jss::result];
1165  BEAST_EXPECT(jrr.isMember(jss::ledger));
1166  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1167  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "4");
1168 
1169  // numeric index - out of range
1170  jvParams[jss::ledger] = 20;
1171  jrr = env.rpc(
1172  "json",
1173  "ledger",
1174  boost::lexical_cast<std::string>(jvParams))[jss::result];
1175  BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
1176  BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
1177  }
1178 
1179  {
1180  // access via the ledger_hash field
1181  Json::Value jvParams;
1182  jvParams[jss::ledger_hash] =
1183  "7C3EEDB3124D92E49E75D81A8826A2E6"
1184  "5A75FD71FC3FD6F36FEB803C5F1D812D";
1185  auto jrr = env.rpc(
1186  "json",
1187  "ledger",
1188  boost::lexical_cast<std::string>(jvParams))[jss::result];
1189  BEAST_EXPECT(jrr.isMember(jss::ledger));
1190  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1191  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "3");
1192 
1193  // extra leading hex chars in hash will be ignored
1194  jvParams[jss::ledger_hash] =
1195  "DEADBEEF"
1196  "7C3EEDB3124D92E49E75D81A8826A2E6"
1197  "5A75FD71FC3FD6F36FEB803C5F1D812D";
1198  jrr = env.rpc(
1199  "json",
1200  "ledger",
1201  boost::lexical_cast<std::string>(jvParams))[jss::result];
1202  BEAST_EXPECT(jrr.isMember(jss::ledger));
1203  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1204  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "3");
1205 
1206  // request with non-string ledger_hash
1207  jvParams[jss::ledger_hash] = 2;
1208  jrr = env.rpc(
1209  "json",
1210  "ledger",
1211  boost::lexical_cast<std::string>(jvParams))[jss::result];
1212  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1213  BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashNotString");
1214 
1215  // malformed (non hex) hash
1216  jvParams[jss::ledger_hash] =
1217  "ZZZZZZZZZZZD92E49E75D81A8826A2E6"
1218  "5A75FD71FC3FD6F36FEB803C5F1D812D";
1219  jrr = env.rpc(
1220  "json",
1221  "ledger",
1222  boost::lexical_cast<std::string>(jvParams))[jss::result];
1223  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1224  BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
1225 
1226  // properly formed, but just doesn't exist
1227  jvParams[jss::ledger_hash] =
1228  "8C3EEDB3124D92E49E75D81A8826A2E6"
1229  "5A75FD71FC3FD6F36FEB803C5F1D812D";
1230  jrr = env.rpc(
1231  "json",
1232  "ledger",
1233  boost::lexical_cast<std::string>(jvParams))[jss::result];
1234  BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
1235  BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
1236  }
1237 
1238  {
1239  // access via the ledger_index field, keyword index values
1240  Json::Value jvParams;
1241  jvParams[jss::ledger_index] = "closed";
1242  auto jrr = env.rpc(
1243  "json",
1244  "ledger",
1245  boost::lexical_cast<std::string>(jvParams))[jss::result];
1246  BEAST_EXPECT(jrr.isMember(jss::ledger));
1247  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1248  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1249  BEAST_EXPECT(jrr.isMember(jss::ledger_index));
1250 
1251  jvParams[jss::ledger_index] = "validated";
1252  jrr = env.rpc(
1253  "json",
1254  "ledger",
1255  boost::lexical_cast<std::string>(jvParams))[jss::result];
1256  BEAST_EXPECT(jrr.isMember(jss::ledger));
1257  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1258  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1259 
1260  jvParams[jss::ledger_index] = "current";
1261  jrr = env.rpc(
1262  "json",
1263  "ledger",
1264  boost::lexical_cast<std::string>(jvParams))[jss::result];
1265  BEAST_EXPECT(jrr.isMember(jss::ledger));
1266  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
1267  BEAST_EXPECT(jrr.isMember(jss::ledger_current_index));
1268 
1269  // ask for a bad ledger keyword
1270  jvParams[jss::ledger_index] = "invalid";
1271  jrr = env.rpc(
1272  "json",
1273  "ledger",
1274  boost::lexical_cast<std::string>(jvParams))[jss::result];
1275  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1276  BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
1277 
1278  // numeric index
1279  for (auto i : {1, 2, 3, 4, 5, 6})
1280  {
1281  jvParams[jss::ledger_index] = i;
1282  jrr = env.rpc(
1283  "json",
1284  "ledger",
1285  boost::lexical_cast<std::string>(jvParams))[jss::result];
1286  BEAST_EXPECT(jrr.isMember(jss::ledger));
1287  if (i < 6)
1288  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1289  BEAST_EXPECT(
1290  jrr[jss::ledger][jss::ledger_index] == std::to_string(i));
1291  }
1292 
1293  // numeric index - out of range
1294  jvParams[jss::ledger_index] = 7;
1295  jrr = env.rpc(
1296  "json",
1297  "ledger",
1298  boost::lexical_cast<std::string>(jvParams))[jss::result];
1299  BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
1300  BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
1301  }
1302  }
1303 
1304  void
1306  {
1307  testcase("Ledger with queueing disabled");
1308  using namespace test::jtx;
1309  Env env{*this};
1310 
1311  Json::Value jv;
1312  jv[jss::ledger_index] = "current";
1313  jv[jss::queue] = true;
1314  jv[jss::expand] = true;
1315 
1316  auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1317  BEAST_EXPECT(!jrr.isMember(jss::queue_data));
1318  }
1319 
1320  void
1322  {
1323  testcase("Ledger with Queued Transactions");
1324  using namespace test::jtx;
1325  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
1326  auto& section = cfg->section("transaction_queue");
1327  section.set("minimum_txn_in_ledger_standalone", "3");
1328  section.set("normal_consensus_increase_percent", "0");
1329  return cfg;
1330  })};
1331 
1332  Json::Value jv;
1333  jv[jss::ledger_index] = "current";
1334  jv[jss::queue] = true;
1335  jv[jss::expand] = true;
1336 
1337  Account const alice{"alice"};
1338  Account const bob{"bob"};
1339  Account const charlie{"charlie"};
1340  Account const daria{"daria"};
1341  env.fund(XRP(10000), alice);
1342  env.fund(XRP(10000), bob);
1343  env.close();
1344  env.fund(XRP(10000), charlie);
1345  env.fund(XRP(10000), daria);
1346  env.close();
1347 
1348  auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1349  BEAST_EXPECT(!jrr.isMember(jss::queue_data));
1350 
1351  // Fill the open ledger
1352  for (;;)
1353  {
1354  auto metrics = env.app().getTxQ().getMetrics(*env.current());
1355  if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
1356  break;
1357  env(noop(alice));
1358  }
1359 
1360  BEAST_EXPECT(env.current()->info().seq == 5);
1361  // Put some txs in the queue
1362  // Alice
1363  auto aliceSeq = env.seq(alice);
1364  env(pay(alice, "george", XRP(1000)),
1365  json(R"({"LastLedgerSequence":7})"),
1366  ter(terQUEUED));
1367  env(offer(alice, XRP(50000), alice["USD"](5000)),
1368  seq(aliceSeq + 1),
1369  ter(terQUEUED));
1370  env(noop(alice), seq(aliceSeq + 2), ter(terQUEUED));
1371  // Bob
1372  auto batch = [&env](Account a) {
1373  auto aSeq = env.seq(a);
1374  // Enough fee to get in front of alice in the queue
1375  for (int i = 0; i < 10; ++i)
1376  {
1377  env(noop(a), fee(1000 + i), seq(aSeq + i), ter(terQUEUED));
1378  }
1379  };
1380  batch(bob);
1381  // Charlie
1382  batch(charlie);
1383  // Daria
1384  batch(daria);
1385 
1386  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1387  BEAST_EXPECT(jrr[jss::queue_data].size() == 33);
1388 
1389  // Close enough ledgers so that alice's first tx expires.
1390  env.close();
1391  env.close();
1392  env.close();
1393  BEAST_EXPECT(env.current()->info().seq == 8);
1394 
1395  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1396  BEAST_EXPECT(jrr[jss::queue_data].size() == 11);
1397 
1398  env.close();
1399 
1400  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1401  std::string txid1;
1402  std::string txid2;
1403  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
1404  {
1405  auto const& txj = jrr[jss::queue_data][0u];
1406  BEAST_EXPECT(txj[jss::account] == alice.human());
1407  BEAST_EXPECT(txj[jss::fee_level] == "256");
1408  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1409  BEAST_EXPECT(txj["retries_remaining"] == 10);
1410  BEAST_EXPECT(txj.isMember(jss::tx));
1411  auto const& tx = txj[jss::tx];
1412  BEAST_EXPECT(tx[jss::Account] == alice.human());
1413  BEAST_EXPECT(tx[jss::TransactionType] == jss::OfferCreate);
1414  txid1 = tx[jss::hash].asString();
1415  }
1416 
1417  env.close();
1418 
1419  jv[jss::expand] = false;
1420 
1421  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1422  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
1423  {
1424  auto const& txj = jrr[jss::queue_data][0u];
1425  BEAST_EXPECT(txj[jss::account] == alice.human());
1426  BEAST_EXPECT(txj[jss::fee_level] == "256");
1427  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1428  BEAST_EXPECT(txj["retries_remaining"] == 9);
1429  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1430  BEAST_EXPECT(txj.isMember(jss::tx));
1431  BEAST_EXPECT(txj[jss::tx] == txid1);
1432  }
1433 
1434  env.close();
1435 
1436  jv[jss::expand] = true;
1437  jv[jss::binary] = true;
1438 
1439  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1440  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
1441  {
1442  auto const& txj = jrr[jss::queue_data][0u];
1443  BEAST_EXPECT(txj[jss::account] == alice.human());
1444  BEAST_EXPECT(txj[jss::fee_level] == "256");
1445  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1446  BEAST_EXPECT(txj["retries_remaining"] == 8);
1447  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1448  BEAST_EXPECT(txj.isMember(jss::tx));
1449  BEAST_EXPECT(txj[jss::tx].isMember(jss::tx_blob));
1450 
1451  auto const& txj2 = jrr[jss::queue_data][1u];
1452  BEAST_EXPECT(txj2[jss::account] == alice.human());
1453  BEAST_EXPECT(txj2[jss::fee_level] == "256");
1454  BEAST_EXPECT(txj2["preflight_result"] == "tesSUCCESS");
1455  BEAST_EXPECT(txj2["retries_remaining"] == 10);
1456  BEAST_EXPECT(!txj2.isMember("last_result"));
1457  BEAST_EXPECT(txj2.isMember(jss::tx));
1458  BEAST_EXPECT(txj2[jss::tx].isMember(jss::tx_blob));
1459  }
1460 
1461  for (int i = 0; i != 9; ++i)
1462  {
1463  env.close();
1464  }
1465 
1466  jv[jss::expand] = false;
1467  jv[jss::binary] = false;
1468 
1469  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1470  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
1471  {
1472  auto const& txj = jrr[jss::queue_data][0u];
1473  BEAST_EXPECT(txj[jss::account] == alice.human());
1474  BEAST_EXPECT(txj[jss::fee_level] == "256");
1475  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1476  BEAST_EXPECT(txj["retries_remaining"] == 1);
1477  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1478  BEAST_EXPECT(txj.isMember(jss::tx));
1479  BEAST_EXPECT(txj[jss::tx] != txid1);
1480  txid2 = txj[jss::tx].asString();
1481  }
1482 
1483  jv[jss::full] = true;
1484 
1485  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1486  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
1487  {
1488  auto const& txj = jrr[jss::queue_data][0u];
1489  BEAST_EXPECT(txj[jss::account] == alice.human());
1490  BEAST_EXPECT(txj[jss::fee_level] == "256");
1491  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1492  BEAST_EXPECT(txj["retries_remaining"] == 1);
1493  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1494  BEAST_EXPECT(txj.isMember(jss::tx));
1495  auto const& tx = txj[jss::tx];
1496  BEAST_EXPECT(tx[jss::Account] == alice.human());
1497  BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
1498  BEAST_EXPECT(tx[jss::hash] == txid2);
1499  }
1500  }
1501 
1502  void
1504  {
1505  testcase("Ledger Request, Accounts Option");
1506  using namespace test::jtx;
1507 
1508  Env env{*this};
1509 
1510  env.close();
1511 
1512  std::string index;
1513  {
1514  Json::Value jvParams;
1515  jvParams[jss::ledger_index] = 3u;
1516  jvParams[jss::accounts] = true;
1517  jvParams[jss::expand] = true;
1518  jvParams[jss::type] = "hashes";
1519  auto const jrr =
1520  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
1521  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
1522  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
1523  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
1524  BEAST_EXPECT(
1525  jrr[jss::ledger][jss::accountState][0u]["LedgerEntryType"] ==
1526  jss::LedgerHashes);
1527  index = jrr[jss::ledger][jss::accountState][0u]["index"].asString();
1528  }
1529  {
1530  Json::Value jvParams;
1531  jvParams[jss::ledger_index] = 3u;
1532  jvParams[jss::accounts] = true;
1533  jvParams[jss::expand] = false;
1534  jvParams[jss::type] = "hashes";
1535  auto const jrr =
1536  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
1537  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
1538  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
1539  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
1540  BEAST_EXPECT(jrr[jss::ledger][jss::accountState][0u] == index);
1541  }
1542  }
1543 
1544 public:
1545  void
1546  run() override
1547  {
1549  testBadInput();
1552  testLedgerFull();
1564  testLookupLedger();
1565  testNoQueue();
1566  testQueue();
1568  }
1569 };
1570 
1571 BEAST_DEFINE_TESTSUITE(LedgerRPC, app, ripple);
1572 
1573 } // namespace ripple
ripple::LedgerRPC_test::testLedgerEntryRippleState
void testLedgerEntryRippleState()
Definition: LedgerRPC_test.cpp:903
ripple::LedgerRPC_test::testLedgerEntryUnknownOption
void testLedgerEntryUnknownOption()
Definition: LedgerRPC_test.cpp:1078
ripple::LedgerRPC_test::testLookupLedger
void testLookupLedger()
ledger RPC requests as a way to drive input options to lookupLedger.
Definition: LedgerRPC_test.cpp:1100
std::string
STL class.
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
ripple::sfLedgerEntryType
const SF_U16 sfLedgerEntryType(access, STI_UINT16, 1, "LedgerEntryType", SField::sMD_Never)
Definition: SField.h:345
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::LedgerRPC_test::testLedgerEntryOffer
void testLedgerEntryOffer()
Definition: LedgerRPC_test.cpp:763
ripple::keylet::payChan
Keylet payChan(AccountID const &source, AccountID const &dst, std::uint32_t seq)
A PaymentChannel.
Definition: Indexes.cpp:326
ripple::sfAccount
const SF_Account sfAccount(access, STI_ACCOUNT, 1, "Account")
Definition: SField.h:474
std::chrono::duration
ripple::LedgerRPC_test::testNoQueue
void testNoQueue()
Definition: LedgerRPC_test.cpp:1305
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
ripple::sfAmount
const SF_Amount sfAmount(access, STI_AMOUNT, 1, "Amount")
Definition: SField.h:437
ripple::SField::jsonName
const Json::StaticString jsonName
Definition: SField.h:133
ripple::sfHighLimit
const SF_Amount sfHighLimit(access, STI_AMOUNT, 7, "HighLimit")
Definition: SField.h:443
ripple::sfFinishAfter
const SF_U32 sfFinishAfter(access, STI_UINT32, 37, "FinishAfter")
Definition: SField.h:389
ripple::LedgerRPC_test
Definition: LedgerRPC_test.cpp:31
std::string::replace
T replace(T... args)
ripple::terQUEUED
@ terQUEUED
Definition: TER.h:198
ripple::Keylet::key
uint256 key
Definition: Keylet.h:41
ripple::base_uint< 256 >
ripple::getCheckIndex
uint256 getCheckIndex(AccountID const &account, std::uint32_t uSequence)
Definition: Indexes.cpp:168
ripple::LedgerRPC_test::testLedgerAccountsOption
void testLedgerAccountsOption()
Definition: LedgerRPC_test.cpp:1503
ripple::LedgerRPC_test::testLedgerFullNonAdmin
void testLedgerFullNonAdmin()
Definition: LedgerRPC_test.cpp:219
ripple::LedgerRPC_test::testLedgerFull
void testLedgerFull()
Definition: LedgerRPC_test.cpp:199
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::RPC::contains_error
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Definition: ErrorCodes.cpp:218
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::sfSendMax
const SF_Amount sfSendMax(access, STI_AMOUNT, 9, "SendMax")
Definition: SField.h:445
ripple::LedgerRPC_test::makeBadAddress
std::string makeBadAddress(std::string good)
Definition: LedgerRPC_test.cpp:56
ripple::LedgerRPC_test::testBadInput
void testBadInput()
Definition: LedgerRPC_test.cpp:98
ripple::JsonOptions::none
@ none
ripple::LedgerRPC_test::testQueue
void testQueue()
Definition: LedgerRPC_test.cpp:1321
ripple::LedgerRPC_test::testLedgerEntryAccountRoot
void testLedgerEntryAccountRoot()
Definition: LedgerRPC_test.cpp:258
std::to_string
T to_string(T... args)
ripple::STAmount
Definition: STAmount.h:42
std::chrono::time_point
Json::Value::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
ripple::LedgerRPC_test::testLedgerEntryEscrow
void testLedgerEntryEscrow()
Definition: LedgerRPC_test.cpp:662
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
ripple::LedgerRPC_test::run
void run() override
Definition: LedgerRPC_test.cpp:1546
ripple::sfIndexes
const SF_Vec256 sfIndexes(access, STI_VECTOR256, 1, "Indexes", SField::sMD_Never)
Definition: SField.h:487
ripple::tfUniversal
const std::uint32_t tfUniversal
Definition: TxFlags.h:49
ripple::LedgerRPC_test::testLedgerEntryDirectory
void testLedgerEntryDirectory()
Definition: LedgerRPC_test.cpp:523
ripple::LedgerRPC_test::testMissingLedgerEntryLedgerHash
void testMissingLedgerEntryLedgerHash()
Definition: LedgerRPC_test.cpp:180
ripple::sfPublicKey
const SF_Blob sfPublicKey(access, STI_VL, 1, "PublicKey")
Definition: SField.h:454
ripple::LedgerRPC_test::testLedgerEntryPayChan
void testLedgerEntryPayChan()
Definition: LedgerRPC_test.cpp:846
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::sfBalance
const SF_Amount sfBalance(access, STI_AMOUNT, 2, "Balance")
Definition: SField.h:438
ripple::LedgerRPC_test::testLedgerAccounts
void testLedgerAccounts()
Definition: LedgerRPC_test.cpp:238
Json::nullValue
@ nullValue
'null' value
Definition: json_value.h:36
ripple::FeatureBitset
Definition: Feature.h:154
ripple::sfSettleDelay
const SF_U32 sfSettleDelay(access, STI_UINT32, 39, "SettleDelay")
Definition: SField.h:391
std::string::empty
T empty(T... args)
ripple::LedgerRPC_test::testLedgerEntryCheck
void testLedgerEntryCheck()
Definition: LedgerRPC_test.cpp:347
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:67
ripple::LedgerRPC_test::testLedgerEntryDepositPreauth
void testLedgerEntryDepositPreauth()
Definition: LedgerRPC_test.cpp:394
ripple::sfAuthorize
const SF_Account sfAuthorize(access, STI_ACCOUNT, 5, "Authorize")
Definition: SField.h:478
std::unique_ptr
STL class.
ripple::LedgerRPC_test::testLedgerCurrent
void testLedgerCurrent()
Definition: LedgerRPC_test.cpp:162
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::LedgerRPC_test::checkErrorValue
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
Definition: LedgerRPC_test.cpp:34
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469
ripple::LedgerRPC_test::testLedgerRequest
void testLedgerRequest()
Definition: LedgerRPC_test.cpp:64