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