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
1215  testLedgerEntryInvalidParams(unsigned int apiVersion)
1216  {
1217  testcase(
1218  "ledger_entry Request With Invalid Parameters v" +
1219  std::to_string(apiVersion));
1220  using namespace test::jtx;
1221  Env env{*this};
1222 
1223  std::string const ledgerHash{to_string(env.closed()->info().hash)};
1224 
1225  // "features" is not an option supported by ledger_entry.
1226  Json::Value jvParams;
1227  jvParams[jss::api_version] = apiVersion;
1228  jvParams[jss::features] = ledgerHash;
1229  jvParams[jss::ledger_hash] = ledgerHash;
1230  Json::Value const jrr =
1231  env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
1232 
1233  if (apiVersion < 2u)
1234  checkErrorValue(jrr, "unknownOption", "");
1235  else
1236  checkErrorValue(jrr, "invalidParams", "");
1237  }
1238 
1243  void
1245  {
1246  testcase("Lookup ledger");
1247  using namespace test::jtx;
1248  Env env{*this, FeatureBitset{}}; // hashes requested below assume
1249  // no amendments
1250  env.fund(XRP(10000), "alice");
1251  env.close();
1252  env.fund(XRP(10000), "bob");
1253  env.close();
1254  env.fund(XRP(10000), "jim");
1255  env.close();
1256  env.fund(XRP(10000), "jill");
1257 
1258  {
1259  // access via the legacy ledger field, keyword index values
1260  Json::Value jvParams;
1261  jvParams[jss::ledger] = "closed";
1262  auto jrr = env.rpc(
1263  "json",
1264  "ledger",
1265  boost::lexical_cast<std::string>(jvParams))[jss::result];
1266  BEAST_EXPECT(jrr.isMember(jss::ledger));
1267  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1268  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1269 
1270  jvParams[jss::ledger] = "validated";
1271  jrr = env.rpc(
1272  "json",
1273  "ledger",
1274  boost::lexical_cast<std::string>(jvParams))[jss::result];
1275  BEAST_EXPECT(jrr.isMember(jss::ledger));
1276  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1277  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1278 
1279  jvParams[jss::ledger] = "current";
1280  jrr = env.rpc(
1281  "json",
1282  "ledger",
1283  boost::lexical_cast<std::string>(jvParams))[jss::result];
1284  BEAST_EXPECT(jrr.isMember(jss::ledger));
1285  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
1286 
1287  // ask for a bad ledger keyword
1288  jvParams[jss::ledger] = "invalid";
1289  jrr = env.rpc(
1290  "json",
1291  "ledger",
1292  boost::lexical_cast<std::string>(jvParams))[jss::result];
1293  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1294  BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
1295 
1296  // numeric index
1297  jvParams[jss::ledger] = 4;
1298  jrr = env.rpc(
1299  "json",
1300  "ledger",
1301  boost::lexical_cast<std::string>(jvParams))[jss::result];
1302  BEAST_EXPECT(jrr.isMember(jss::ledger));
1303  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1304  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "4");
1305 
1306  // numeric index - out of range
1307  jvParams[jss::ledger] = 20;
1308  jrr = env.rpc(
1309  "json",
1310  "ledger",
1311  boost::lexical_cast<std::string>(jvParams))[jss::result];
1312  BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
1313  BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
1314  }
1315 
1316  {
1317  std::string const hash3{
1318  "E86DE7F3D7A4D9CE17EF7C8BA08A8F4D"
1319  "8F643B9552F0D895A31CDA78F541DE4E"};
1320  // access via the ledger_hash field
1321  Json::Value jvParams;
1322  jvParams[jss::ledger_hash] = hash3;
1323  auto jrr = env.rpc(
1324  "json",
1325  "ledger",
1326  boost::lexical_cast<std::string>(jvParams))[jss::result];
1327  BEAST_EXPECT(jrr.isMember(jss::ledger));
1328  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1329  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "3");
1330 
1331  // extra leading hex chars in hash are not allowed
1332  jvParams[jss::ledger_hash] = "DEADBEEF" + hash3;
1333  jrr = env.rpc(
1334  "json",
1335  "ledger",
1336  boost::lexical_cast<std::string>(jvParams))[jss::result];
1337  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1338  BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
1339 
1340  // request with non-string ledger_hash
1341  jvParams[jss::ledger_hash] = 2;
1342  jrr = env.rpc(
1343  "json",
1344  "ledger",
1345  boost::lexical_cast<std::string>(jvParams))[jss::result];
1346  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1347  BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashNotString");
1348 
1349  // malformed (non hex) hash
1350  jvParams[jss::ledger_hash] =
1351  "2E81FC6EC0DD943197EGC7E3FBE9AE30"
1352  "7F2775F2F7485BB37307984C3C0F2340";
1353  jrr = env.rpc(
1354  "json",
1355  "ledger",
1356  boost::lexical_cast<std::string>(jvParams))[jss::result];
1357  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1358  BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
1359 
1360  // properly formed, but just doesn't exist
1361  jvParams[jss::ledger_hash] =
1362  "8C3EEDB3124D92E49E75D81A8826A2E6"
1363  "5A75FD71FC3FD6F36FEB803C5F1D812D";
1364  jrr = env.rpc(
1365  "json",
1366  "ledger",
1367  boost::lexical_cast<std::string>(jvParams))[jss::result];
1368  BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
1369  BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
1370  }
1371 
1372  {
1373  // access via the ledger_index field, keyword index values
1374  Json::Value jvParams;
1375  jvParams[jss::ledger_index] = "closed";
1376  auto jrr = env.rpc(
1377  "json",
1378  "ledger",
1379  boost::lexical_cast<std::string>(jvParams))[jss::result];
1380  BEAST_EXPECT(jrr.isMember(jss::ledger));
1381  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1382  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1383  BEAST_EXPECT(jrr.isMember(jss::ledger_index));
1384 
1385  jvParams[jss::ledger_index] = "validated";
1386  jrr = env.rpc(
1387  "json",
1388  "ledger",
1389  boost::lexical_cast<std::string>(jvParams))[jss::result];
1390  BEAST_EXPECT(jrr.isMember(jss::ledger));
1391  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1392  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1393 
1394  jvParams[jss::ledger_index] = "current";
1395  jrr = env.rpc(
1396  "json",
1397  "ledger",
1398  boost::lexical_cast<std::string>(jvParams))[jss::result];
1399  BEAST_EXPECT(jrr.isMember(jss::ledger));
1400  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
1401  BEAST_EXPECT(jrr.isMember(jss::ledger_current_index));
1402 
1403  // ask for a bad ledger keyword
1404  jvParams[jss::ledger_index] = "invalid";
1405  jrr = env.rpc(
1406  "json",
1407  "ledger",
1408  boost::lexical_cast<std::string>(jvParams))[jss::result];
1409  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1410  BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
1411 
1412  // numeric index
1413  for (auto i : {1, 2, 3, 4, 5, 6})
1414  {
1415  jvParams[jss::ledger_index] = i;
1416  jrr = env.rpc(
1417  "json",
1418  "ledger",
1419  boost::lexical_cast<std::string>(jvParams))[jss::result];
1420  BEAST_EXPECT(jrr.isMember(jss::ledger));
1421  if (i < 6)
1422  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1423  BEAST_EXPECT(
1424  jrr[jss::ledger][jss::ledger_index] == std::to_string(i));
1425  }
1426 
1427  // numeric index - out of range
1428  jvParams[jss::ledger_index] = 7;
1429  jrr = env.rpc(
1430  "json",
1431  "ledger",
1432  boost::lexical_cast<std::string>(jvParams))[jss::result];
1433  BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
1434  BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
1435  }
1436  }
1437 
1438  void
1440  {
1441  testcase("Ledger with queueing disabled");
1442  using namespace test::jtx;
1443  Env env{*this};
1444 
1445  Json::Value jv;
1446  jv[jss::ledger_index] = "current";
1447  jv[jss::queue] = true;
1448  jv[jss::expand] = true;
1449 
1450  auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1451  BEAST_EXPECT(!jrr.isMember(jss::queue_data));
1452  }
1453 
1454  void
1456  {
1457  testcase("Ledger with Queued Transactions");
1458  using namespace test::jtx;
1459  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
1460  auto& section = cfg->section("transaction_queue");
1461  section.set("minimum_txn_in_ledger_standalone", "3");
1462  section.set("normal_consensus_increase_percent", "0");
1463  return cfg;
1464  })};
1465 
1466  Json::Value jv;
1467  jv[jss::ledger_index] = "current";
1468  jv[jss::queue] = true;
1469  jv[jss::expand] = true;
1470 
1471  Account const alice{"alice"};
1472  Account const bob{"bob"};
1473  Account const charlie{"charlie"};
1474  Account const daria{"daria"};
1475  env.fund(XRP(10000), alice);
1476  env.fund(XRP(10000), bob);
1477  env.close();
1478  env.fund(XRP(10000), charlie);
1479  env.fund(XRP(10000), daria);
1480  env.close();
1481 
1482  auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1483  BEAST_EXPECT(!jrr.isMember(jss::queue_data));
1484 
1485  // Fill the open ledger
1486  for (;;)
1487  {
1488  auto metrics = env.app().getTxQ().getMetrics(*env.current());
1489  if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
1490  break;
1491  env(noop(alice));
1492  }
1493 
1494  BEAST_EXPECT(env.current()->info().seq == 5);
1495  // Put some txs in the queue
1496  // Alice
1497  auto aliceSeq = env.seq(alice);
1498  env(pay(alice, "george", XRP(1000)),
1499  json(R"({"LastLedgerSequence":7})"),
1500  ter(terQUEUED));
1501  env(offer(alice, XRP(50000), alice["USD"](5000)),
1502  seq(aliceSeq + 1),
1503  ter(terQUEUED));
1504  env(noop(alice), seq(aliceSeq + 2), ter(terQUEUED));
1505  // Bob
1506  auto batch = [&env](Account a) {
1507  auto aSeq = env.seq(a);
1508  // Enough fee to get in front of alice in the queue
1509  for (int i = 0; i < 10; ++i)
1510  {
1511  env(noop(a), fee(1000 + i), seq(aSeq + i), ter(terQUEUED));
1512  }
1513  };
1514  batch(bob);
1515  // Charlie
1516  batch(charlie);
1517  // Daria
1518  batch(daria);
1519 
1520  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1521  BEAST_EXPECT(jrr[jss::queue_data].size() == 33);
1522 
1523  // Close enough ledgers so that alice's first tx expires.
1524  env.close();
1525  env.close();
1526  env.close();
1527  BEAST_EXPECT(env.current()->info().seq == 8);
1528 
1529  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1530  BEAST_EXPECT(jrr[jss::queue_data].size() == 11);
1531 
1532  env.close();
1533 
1534  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1535  const std::string txid0 = [&]() {
1536  auto const& parentHash = env.current()->info().parentHash;
1537  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
1538  {
1539  const std::string txid1 = [&]() {
1540  auto const& txj = jrr[jss::queue_data][1u];
1541  BEAST_EXPECT(txj[jss::account] == alice.human());
1542  BEAST_EXPECT(txj[jss::fee_level] == "256");
1543  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1544  BEAST_EXPECT(txj["retries_remaining"] == 10);
1545  BEAST_EXPECT(txj.isMember(jss::tx));
1546  auto const& tx = txj[jss::tx];
1547  BEAST_EXPECT(tx[jss::Account] == alice.human());
1548  BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
1549  return tx[jss::hash].asString();
1550  }();
1551 
1552  auto const& txj = jrr[jss::queue_data][0u];
1553  BEAST_EXPECT(txj[jss::account] == alice.human());
1554  BEAST_EXPECT(txj[jss::fee_level] == "256");
1555  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1556  BEAST_EXPECT(txj["retries_remaining"] == 10);
1557  BEAST_EXPECT(txj.isMember(jss::tx));
1558  auto const& tx = txj[jss::tx];
1559  BEAST_EXPECT(tx[jss::Account] == alice.human());
1560  BEAST_EXPECT(tx[jss::TransactionType] == jss::OfferCreate);
1561  const auto txid0 = tx[jss::hash].asString();
1562  uint256 tx0, tx1;
1563  BEAST_EXPECT(tx0.parseHex(txid0));
1564  BEAST_EXPECT(tx1.parseHex(txid1));
1565  BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
1566  return txid0;
1567  }
1568  return std::string{};
1569  }();
1570 
1571  env.close();
1572 
1573  jv[jss::expand] = false;
1574 
1575  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1576  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
1577  {
1578  auto const& parentHash = env.current()->info().parentHash;
1579  auto const txid1 = [&]() {
1580  auto const& txj = jrr[jss::queue_data][1u];
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.isMember(jss::tx));
1585  return txj[jss::tx].asString();
1586  }();
1587  auto const& txj = jrr[jss::queue_data][0u];
1588  BEAST_EXPECT(txj[jss::account] == alice.human());
1589  BEAST_EXPECT(txj[jss::fee_level] == "256");
1590  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1591  BEAST_EXPECT(txj["retries_remaining"] == 9);
1592  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1593  BEAST_EXPECT(txj.isMember(jss::tx));
1594  BEAST_EXPECT(txj[jss::tx] == txid0);
1595  uint256 tx0, tx1;
1596  BEAST_EXPECT(tx0.parseHex(txid0));
1597  BEAST_EXPECT(tx1.parseHex(txid1));
1598  BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
1599  }
1600 
1601  env.close();
1602 
1603  jv[jss::expand] = true;
1604  jv[jss::binary] = true;
1605 
1606  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1607  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
1608  {
1609  auto const& txj = jrr[jss::queue_data][1u];
1610  BEAST_EXPECT(txj[jss::account] == alice.human());
1611  BEAST_EXPECT(txj[jss::fee_level] == "256");
1612  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1613  BEAST_EXPECT(txj["retries_remaining"] == 8);
1614  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1615  BEAST_EXPECT(txj.isMember(jss::tx));
1616  BEAST_EXPECT(txj[jss::tx].isMember(jss::tx_blob));
1617 
1618  auto const& txj2 = jrr[jss::queue_data][0u];
1619  BEAST_EXPECT(txj2[jss::account] == alice.human());
1620  BEAST_EXPECT(txj2[jss::fee_level] == "256");
1621  BEAST_EXPECT(txj2["preflight_result"] == "tesSUCCESS");
1622  BEAST_EXPECT(txj2["retries_remaining"] == 10);
1623  BEAST_EXPECT(!txj2.isMember("last_result"));
1624  BEAST_EXPECT(txj2.isMember(jss::tx));
1625  BEAST_EXPECT(txj2[jss::tx].isMember(jss::tx_blob));
1626  }
1627 
1628  for (int i = 0; i != 9; ++i)
1629  {
1630  env.close();
1631  }
1632 
1633  jv[jss::expand] = false;
1634  jv[jss::binary] = false;
1635 
1636  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1637  const std::string txid2 = [&]() {
1638  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
1639  {
1640  auto const& txj = jrr[jss::queue_data][0u];
1641  BEAST_EXPECT(txj[jss::account] == alice.human());
1642  BEAST_EXPECT(txj[jss::fee_level] == "256");
1643  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1644  BEAST_EXPECT(txj["retries_remaining"] == 1);
1645  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1646  BEAST_EXPECT(txj.isMember(jss::tx));
1647  BEAST_EXPECT(txj[jss::tx] != txid0);
1648  return txj[jss::tx].asString();
1649  }
1650  return std::string{};
1651  }();
1652 
1653  jv[jss::full] = true;
1654 
1655  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1656  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
1657  {
1658  auto const& txj = jrr[jss::queue_data][0u];
1659  BEAST_EXPECT(txj[jss::account] == alice.human());
1660  BEAST_EXPECT(txj[jss::fee_level] == "256");
1661  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1662  BEAST_EXPECT(txj["retries_remaining"] == 1);
1663  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1664  BEAST_EXPECT(txj.isMember(jss::tx));
1665  auto const& tx = txj[jss::tx];
1666  BEAST_EXPECT(tx[jss::Account] == alice.human());
1667  BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
1668  BEAST_EXPECT(tx[jss::hash] == txid2);
1669  }
1670  }
1671 
1672  void
1674  {
1675  testcase("Ledger Request, Accounts Hashes");
1676  using namespace test::jtx;
1677 
1678  Env env{*this};
1679 
1680  env.close();
1681 
1682  std::string index;
1683  {
1684  Json::Value jvParams;
1685  jvParams[jss::ledger_index] = 3u;
1686  jvParams[jss::accounts] = true;
1687  jvParams[jss::expand] = true;
1688  jvParams[jss::type] = "hashes";
1689  auto const jrr =
1690  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
1691  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
1692  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
1693  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
1694  BEAST_EXPECT(
1695  jrr[jss::ledger][jss::accountState][0u]["LedgerEntryType"] ==
1696  jss::LedgerHashes);
1697  index = jrr[jss::ledger][jss::accountState][0u]["index"].asString();
1698  }
1699  {
1700  Json::Value jvParams;
1701  jvParams[jss::ledger_index] = 3u;
1702  jvParams[jss::accounts] = true;
1703  jvParams[jss::expand] = false;
1704  jvParams[jss::type] = "hashes";
1705  auto const jrr =
1706  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
1707  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
1708  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
1709  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
1710  BEAST_EXPECT(jrr[jss::ledger][jss::accountState][0u] == index);
1711  }
1712  }
1713 
1714 public:
1715  void
1716  run() override
1717  {
1719  testBadInput();
1722  testLedgerFull();
1734  testLookupLedger();
1735  testNoQueue();
1736  testQueue();
1738 
1741  }
1742 };
1743 
1744 BEAST_DEFINE_TESTSUITE(LedgerRPC, app, ripple);
1745 
1746 } // namespace ripple
ripple::LedgerRPC_test::testLedgerEntryRippleState
void testLedgerEntryRippleState()
Definition: LedgerRPC_test.cpp:922
ripple::LedgerRPC_test::testLookupLedger
void testLookupLedger()
ledger RPC requests as a way to drive input options to lookupLedger.
Definition: LedgerRPC_test.cpp:1244
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:1439
ripple::LedgerRPC_test::testLedgerEntryInvalidParams
void testLedgerEntryInvalidParams(unsigned int apiVersion)
Definition: LedgerRPC_test.cpp:1215
ripple::sfTicketSequence
const SF_UINT32 sfTicketSequence
ripple::test::jtx::forAllApiVersions
void forAllApiVersions(VersionedTestCallable auto... testCallable)
Definition: Env.h:743
std::bind_front
T bind_front(T... args)
ripple::SField::jsonName
const Json::StaticString jsonName
Definition: SField.h:139
ripple::getTicketIndex
uint256 getTicketIndex(AccountID const &account, std::uint32_t ticketSeq)
Definition: Indexes.cpp:116
ripple::LedgerRPC_test
Definition: LedgerRPC_test.cpp:30
std::string::replace
T replace(T... args)
ripple::terQUEUED
@ terQUEUED
Definition: TER.h:208
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:1673
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:195
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:134
ripple::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:1455
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:1716
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:325
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:282
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