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() == 2u);
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() == 2u);
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  // access via the ledger_hash field
1319  Json::Value jvParams;
1320  jvParams[jss::ledger_hash] =
1321  "2E81FC6EC0DD943197E0C7E3FBE9AE30"
1322  "7F2775F2F7485BB37307984C3C0F2340";
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 will be ignored
1332  jvParams[jss::ledger_hash] =
1333  "DEADBEEF"
1334  "2E81FC6EC0DD943197E0C7E3FBE9AE30"
1335  "7F2775F2F7485BB37307984C3C0F2340";
1336  jrr = env.rpc(
1337  "json",
1338  "ledger",
1339  boost::lexical_cast<std::string>(jvParams))[jss::result];
1340  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1341  BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
1342 
1343  // request with non-string ledger_hash
1344  jvParams[jss::ledger_hash] = 2;
1345  jrr = env.rpc(
1346  "json",
1347  "ledger",
1348  boost::lexical_cast<std::string>(jvParams))[jss::result];
1349  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1350  BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashNotString");
1351 
1352  // malformed (non hex) hash
1353  jvParams[jss::ledger_hash] =
1354  "2E81FC6EC0DD943197EGC7E3FBE9AE30"
1355  "7F2775F2F7485BB37307984C3C0F2340";
1356  jrr = env.rpc(
1357  "json",
1358  "ledger",
1359  boost::lexical_cast<std::string>(jvParams))[jss::result];
1360  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1361  BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
1362 
1363  // properly formed, but just doesn't exist
1364  jvParams[jss::ledger_hash] =
1365  "8C3EEDB3124D92E49E75D81A8826A2E6"
1366  "5A75FD71FC3FD6F36FEB803C5F1D812D";
1367  jrr = env.rpc(
1368  "json",
1369  "ledger",
1370  boost::lexical_cast<std::string>(jvParams))[jss::result];
1371  BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
1372  BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
1373  }
1374 
1375  {
1376  // access via the ledger_index field, keyword index values
1377  Json::Value jvParams;
1378  jvParams[jss::ledger_index] = "closed";
1379  auto jrr = env.rpc(
1380  "json",
1381  "ledger",
1382  boost::lexical_cast<std::string>(jvParams))[jss::result];
1383  BEAST_EXPECT(jrr.isMember(jss::ledger));
1384  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1385  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1386  BEAST_EXPECT(jrr.isMember(jss::ledger_index));
1387 
1388  jvParams[jss::ledger_index] = "validated";
1389  jrr = env.rpc(
1390  "json",
1391  "ledger",
1392  boost::lexical_cast<std::string>(jvParams))[jss::result];
1393  BEAST_EXPECT(jrr.isMember(jss::ledger));
1394  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1395  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
1396 
1397  jvParams[jss::ledger_index] = "current";
1398  jrr = env.rpc(
1399  "json",
1400  "ledger",
1401  boost::lexical_cast<std::string>(jvParams))[jss::result];
1402  BEAST_EXPECT(jrr.isMember(jss::ledger));
1403  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
1404  BEAST_EXPECT(jrr.isMember(jss::ledger_current_index));
1405 
1406  // ask for a bad ledger keyword
1407  jvParams[jss::ledger_index] = "invalid";
1408  jrr = env.rpc(
1409  "json",
1410  "ledger",
1411  boost::lexical_cast<std::string>(jvParams))[jss::result];
1412  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1413  BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
1414 
1415  // numeric index
1416  for (auto i : {1, 2, 3, 4, 5, 6})
1417  {
1418  jvParams[jss::ledger_index] = i;
1419  jrr = env.rpc(
1420  "json",
1421  "ledger",
1422  boost::lexical_cast<std::string>(jvParams))[jss::result];
1423  BEAST_EXPECT(jrr.isMember(jss::ledger));
1424  if (i < 6)
1425  BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
1426  BEAST_EXPECT(
1427  jrr[jss::ledger][jss::ledger_index] == std::to_string(i));
1428  }
1429 
1430  // numeric index - out of range
1431  jvParams[jss::ledger_index] = 7;
1432  jrr = env.rpc(
1433  "json",
1434  "ledger",
1435  boost::lexical_cast<std::string>(jvParams))[jss::result];
1436  BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
1437  BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
1438  }
1439  }
1440 
1441  void
1443  {
1444  testcase("Ledger with queueing disabled");
1445  using namespace test::jtx;
1446  Env env{*this};
1447 
1448  Json::Value jv;
1449  jv[jss::ledger_index] = "current";
1450  jv[jss::queue] = true;
1451  jv[jss::expand] = true;
1452 
1453  auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1454  BEAST_EXPECT(!jrr.isMember(jss::queue_data));
1455  }
1456 
1457  void
1459  {
1460  testcase("Ledger with Queued Transactions");
1461  using namespace test::jtx;
1462  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
1463  auto& section = cfg->section("transaction_queue");
1464  section.set("minimum_txn_in_ledger_standalone", "3");
1465  section.set("normal_consensus_increase_percent", "0");
1466  return cfg;
1467  })};
1468 
1469  Json::Value jv;
1470  jv[jss::ledger_index] = "current";
1471  jv[jss::queue] = true;
1472  jv[jss::expand] = true;
1473 
1474  Account const alice{"alice"};
1475  Account const bob{"bob"};
1476  Account const charlie{"charlie"};
1477  Account const daria{"daria"};
1478  env.fund(XRP(10000), alice);
1479  env.fund(XRP(10000), bob);
1480  env.close();
1481  env.fund(XRP(10000), charlie);
1482  env.fund(XRP(10000), daria);
1483  env.close();
1484 
1485  auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1486  BEAST_EXPECT(!jrr.isMember(jss::queue_data));
1487 
1488  // Fill the open ledger
1489  for (;;)
1490  {
1491  auto metrics = env.app().getTxQ().getMetrics(*env.current());
1492  if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
1493  break;
1494  env(noop(alice));
1495  }
1496 
1497  BEAST_EXPECT(env.current()->info().seq == 5);
1498  // Put some txs in the queue
1499  // Alice
1500  auto aliceSeq = env.seq(alice);
1501  env(pay(alice, "george", XRP(1000)),
1502  json(R"({"LastLedgerSequence":7})"),
1503  ter(terQUEUED));
1504  env(offer(alice, XRP(50000), alice["USD"](5000)),
1505  seq(aliceSeq + 1),
1506  ter(terQUEUED));
1507  env(noop(alice), seq(aliceSeq + 2), ter(terQUEUED));
1508  // Bob
1509  auto batch = [&env](Account a) {
1510  auto aSeq = env.seq(a);
1511  // Enough fee to get in front of alice in the queue
1512  for (int i = 0; i < 10; ++i)
1513  {
1514  env(noop(a), fee(1000 + i), seq(aSeq + i), ter(terQUEUED));
1515  }
1516  };
1517  batch(bob);
1518  // Charlie
1519  batch(charlie);
1520  // Daria
1521  batch(daria);
1522 
1523  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1524  BEAST_EXPECT(jrr[jss::queue_data].size() == 33);
1525 
1526  // Close enough ledgers so that alice's first tx expires.
1527  env.close();
1528  env.close();
1529  env.close();
1530  BEAST_EXPECT(env.current()->info().seq == 8);
1531 
1532  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1533  BEAST_EXPECT(jrr[jss::queue_data].size() == 11);
1534 
1535  env.close();
1536 
1537  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1538  const std::string txid1 = [&]() {
1539  auto const& parentHash = env.current()->info().parentHash;
1540  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
1541  {
1542  const std::string txid1 = [&]() {
1543  auto const& txj = jrr[jss::queue_data][1u];
1544  BEAST_EXPECT(txj[jss::account] == alice.human());
1545  BEAST_EXPECT(txj[jss::fee_level] == "256");
1546  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1547  BEAST_EXPECT(txj["retries_remaining"] == 10);
1548  BEAST_EXPECT(txj.isMember(jss::tx));
1549  auto const& tx = txj[jss::tx];
1550  BEAST_EXPECT(tx[jss::Account] == alice.human());
1551  BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
1552  return tx[jss::hash].asString();
1553  }();
1554 
1555  auto const& txj = jrr[jss::queue_data][0u];
1556  BEAST_EXPECT(txj[jss::account] == alice.human());
1557  BEAST_EXPECT(txj[jss::fee_level] == "256");
1558  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1559  BEAST_EXPECT(txj["retries_remaining"] == 10);
1560  BEAST_EXPECT(txj.isMember(jss::tx));
1561  auto const& tx = txj[jss::tx];
1562  BEAST_EXPECT(tx[jss::Account] == alice.human());
1563  BEAST_EXPECT(tx[jss::TransactionType] == jss::OfferCreate);
1564  const auto txid0 = tx[jss::hash].asString();
1565  uint256 tx0, tx1;
1566  BEAST_EXPECT(tx0.parseHex(txid0));
1567  BEAST_EXPECT(tx1.parseHex(txid1));
1568  BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
1569  return txid0;
1570  }
1571  return std::string{};
1572  }();
1573 
1574  env.close();
1575 
1576  jv[jss::expand] = false;
1577 
1578  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1579  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
1580  {
1581  auto const& parentHash = env.current()->info().parentHash;
1582  auto const txid0 = [&]() {
1583  auto const& txj = jrr[jss::queue_data][0u];
1584  BEAST_EXPECT(txj[jss::account] == alice.human());
1585  BEAST_EXPECT(txj[jss::fee_level] == "256");
1586  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1587  BEAST_EXPECT(txj.isMember(jss::tx));
1588  return txj[jss::tx].asString();
1589  }();
1590  auto const& txj = jrr[jss::queue_data][1u];
1591  BEAST_EXPECT(txj[jss::account] == alice.human());
1592  BEAST_EXPECT(txj[jss::fee_level] == "256");
1593  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1594  BEAST_EXPECT(txj["retries_remaining"] == 9);
1595  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1596  BEAST_EXPECT(txj.isMember(jss::tx));
1597  BEAST_EXPECT(txj[jss::tx] == txid1);
1598  uint256 tx0, tx1;
1599  BEAST_EXPECT(tx0.parseHex(txid0));
1600  BEAST_EXPECT(tx1.parseHex(txid1));
1601  BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
1602  }
1603 
1604  env.close();
1605 
1606  jv[jss::expand] = true;
1607  jv[jss::binary] = true;
1608 
1609  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1610  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
1611  {
1612  auto const& txj = jrr[jss::queue_data][1u];
1613  BEAST_EXPECT(txj[jss::account] == alice.human());
1614  BEAST_EXPECT(txj[jss::fee_level] == "256");
1615  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1616  BEAST_EXPECT(txj["retries_remaining"] == 8);
1617  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1618  BEAST_EXPECT(txj.isMember(jss::tx));
1619  BEAST_EXPECT(txj[jss::tx].isMember(jss::tx_blob));
1620 
1621  auto const& txj2 = jrr[jss::queue_data][0u];
1622  BEAST_EXPECT(txj2[jss::account] == alice.human());
1623  BEAST_EXPECT(txj2[jss::fee_level] == "256");
1624  BEAST_EXPECT(txj2["preflight_result"] == "tesSUCCESS");
1625  BEAST_EXPECT(txj2["retries_remaining"] == 10);
1626  BEAST_EXPECT(!txj2.isMember("last_result"));
1627  BEAST_EXPECT(txj2.isMember(jss::tx));
1628  BEAST_EXPECT(txj2[jss::tx].isMember(jss::tx_blob));
1629  }
1630 
1631  for (int i = 0; i != 9; ++i)
1632  {
1633  env.close();
1634  }
1635 
1636  jv[jss::expand] = false;
1637  jv[jss::binary] = false;
1638 
1639  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1640  const std::string txid2 = [&]() {
1641  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
1642  {
1643  auto const& txj = jrr[jss::queue_data][0u];
1644  BEAST_EXPECT(txj[jss::account] == alice.human());
1645  BEAST_EXPECT(txj[jss::fee_level] == "256");
1646  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1647  BEAST_EXPECT(txj["retries_remaining"] == 1);
1648  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1649  BEAST_EXPECT(txj.isMember(jss::tx));
1650  BEAST_EXPECT(txj[jss::tx] != txid1);
1651  return txj[jss::tx].asString();
1652  }
1653  return std::string{};
1654  }();
1655 
1656  jv[jss::full] = true;
1657 
1658  jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
1659  if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
1660  {
1661  auto const& txj = jrr[jss::queue_data][0u];
1662  BEAST_EXPECT(txj[jss::account] == alice.human());
1663  BEAST_EXPECT(txj[jss::fee_level] == "256");
1664  BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
1665  BEAST_EXPECT(txj["retries_remaining"] == 1);
1666  BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
1667  BEAST_EXPECT(txj.isMember(jss::tx));
1668  auto const& tx = txj[jss::tx];
1669  BEAST_EXPECT(tx[jss::Account] == alice.human());
1670  BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
1671  BEAST_EXPECT(tx[jss::hash] == txid2);
1672  }
1673  }
1674 
1675  void
1677  {
1678  testcase("Ledger Request, Accounts Option");
1679  using namespace test::jtx;
1680 
1681  Env env{*this};
1682 
1683  env.close();
1684 
1685  std::string index;
1686  {
1687  Json::Value jvParams;
1688  jvParams[jss::ledger_index] = 3u;
1689  jvParams[jss::accounts] = true;
1690  jvParams[jss::expand] = true;
1691  jvParams[jss::type] = "hashes";
1692  auto const jrr =
1693  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
1694  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
1695  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
1696  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
1697  BEAST_EXPECT(
1698  jrr[jss::ledger][jss::accountState][0u]["LedgerEntryType"] ==
1699  jss::LedgerHashes);
1700  index = jrr[jss::ledger][jss::accountState][0u]["index"].asString();
1701  }
1702  {
1703  Json::Value jvParams;
1704  jvParams[jss::ledger_index] = 3u;
1705  jvParams[jss::accounts] = true;
1706  jvParams[jss::expand] = false;
1707  jvParams[jss::type] = "hashes";
1708  auto const jrr =
1709  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
1710  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
1711  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
1712  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
1713  BEAST_EXPECT(jrr[jss::ledger][jss::accountState][0u] == index);
1714  }
1715  }
1716 
1717 public:
1718  void
1719  run() override
1720  {
1722  testBadInput();
1725  testLedgerFull();
1738  testLookupLedger();
1739  testNoQueue();
1740  testQueue();
1742  }
1743 };
1744 
1745 BEAST_DEFINE_TESTSUITE(LedgerRPC, app, ripple);
1746 
1747 } // 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:1442
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:203
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:1676
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:204
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:1458
ripple::LedgerRPC_test::testLedgerEntryAccountRoot
void testLedgerEntryAccountRoot()
Definition: LedgerRPC_test.cpp:283
std::to_string
T to_string(T... args)
ripple::STAmount
Definition: STAmount.h:44
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:1719
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:45
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:495
ripple::tfUniversal
constexpr std::uint32_t tfUniversal
Definition: TxFlags.h:57
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