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