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 <ripple/rpc/impl/RPCHelpers.h>
27 #include <test/jtx.h>
28 
29 namespace ripple {
30 
31 class LedgerRPC_test : public beast::unit_test::suite
32 {
33  void
35  Json::Value const& jv,
36  std::string const& err,
37  std::string const& msg)
38  {
39  if (BEAST_EXPECT(jv.isMember(jss::status)))
40  BEAST_EXPECT(jv[jss::status] == "error");
41  if (BEAST_EXPECT(jv.isMember(jss::error)))
42  BEAST_EXPECT(jv[jss::error] == err);
43  if (msg.empty())
44  {
45  BEAST_EXPECT(
46  jv[jss::error_message] == Json::nullValue ||
47  jv[jss::error_message] == "");
48  }
49  else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
50  BEAST_EXPECT(jv[jss::error_message] == msg);
51  }
52 
53  // Corrupt a valid address by replacing the 10th character with '!'.
54  // '!' is not part of the ripple alphabet.
57  {
58  std::string ret = std::move(good);
59  ret.replace(10, 1, 1, '!');
60  return ret;
61  }
62 
63  void
65  {
66  testcase("Basic Request");
67  using namespace test::jtx;
68 
69  Env env{*this};
70 
71  env.close();
72  BEAST_EXPECT(env.current()->info().seq == 4);
73 
74  {
75  Json::Value jvParams;
76  // can be either numeric or quoted numeric
77  jvParams[jss::ledger_index] = 1;
78  auto const jrr =
79  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
80  BEAST_EXPECT(jrr[jss::ledger][jss::closed] == true);
81  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "1");
82  }
83 
84  {
85  Json::Value jvParams;
86  jvParams[jss::ledger_index] = "1";
87  auto const jrr =
88  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
89  BEAST_EXPECT(jrr[jss::ledger][jss::closed] == true);
90  BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "1");
91  }
92 
93  {
94  // using current identifier
95  auto const jrr = env.rpc("ledger", "current")[jss::result];
96  BEAST_EXPECT(jrr[jss::ledger][jss::closed] == false);
97  BEAST_EXPECT(
98  jrr[jss::ledger][jss::ledger_index] ==
99  std::to_string(env.current()->info().seq));
100  BEAST_EXPECT(
101  jrr[jss::ledger_current_index] == env.current()->info().seq);
102  }
103  }
104 
105  void
107  {
108  testcase("Bad Input");
109  using namespace test::jtx;
110  Env env{*this};
111  Account const gw{"gateway"};
112  auto const USD = gw["USD"];
113  Account const bob{"bob"};
114 
115  env.fund(XRP(10000), gw, bob);
116  env.close();
117  env.trust(USD(1000), bob);
118  env.close();
119 
120  {
121  // ask for an arbitrary string - index
122  Json::Value jvParams;
123  jvParams[jss::ledger_index] = "potato";
124  auto const jrr =
125  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
126  checkErrorValue(jrr, "invalidParams", "ledgerIndexMalformed");
127  }
128 
129  {
130  // ask for a negative index
131  Json::Value jvParams;
132  jvParams[jss::ledger_index] = -1;
133  auto const jrr =
134  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
135  checkErrorValue(jrr, "invalidParams", "ledgerIndexMalformed");
136  }
137 
138  {
139  // ask for a bad ledger index
140  Json::Value jvParams;
141  jvParams[jss::ledger_index] = 10u;
142  auto const jrr =
143  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
144  checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
145  }
146 
147  {
148  // unrecognized string arg -- error
149  auto const jrr = env.rpc("ledger", "arbitrary_text")[jss::result];
150  checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
151  }
152 
153  {
154  // Request queue for closed ledger
155  Json::Value jvParams;
156  jvParams[jss::ledger_index] = "validated";
157  jvParams[jss::queue] = true;
158  auto const jrr =
159  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
160  checkErrorValue(jrr, "invalidParams", "Invalid parameters.");
161  }
162 
163  {
164  // Request a ledger with a very large (double) sequence.
165  auto const ret =
166  env.rpc("json", "ledger", "{ \"ledger_index\" : 2e15 }");
167  BEAST_EXPECT(RPC::contains_error(ret));
168  BEAST_EXPECT(ret[jss::error_message] == "Invalid parameters.");
169  }
170 
171  {
172  // Request a ledger with very large (integer) sequence.
173  auto const ret = env.rpc(
174  "json", "ledger", "{ \"ledger_index\" : 1000000000000000 }");
175  checkErrorValue(ret, "invalidParams", "Invalid parameters.");
176  }
177  }
178 
179  void
181  {
182  testcase("ledger_current Request");
183  using namespace test::jtx;
184 
185  Env env{*this};
186 
187  env.close();
188  BEAST_EXPECT(env.current()->info().seq == 4);
189 
190  {
191  auto const jrr = env.rpc("ledger_current")[jss::result];
192  BEAST_EXPECT(
193  jrr[jss::ledger_current_index] == env.current()->info().seq);
194  }
195  }
196 
197  void
199  {
200  testcase("Missing ledger_entry ledger_hash");
201  using namespace test::jtx;
202  Env env{*this};
203  Account const alice{"alice"};
204  env.fund(XRP(10000), alice);
205  env.close();
206 
207  Json::Value jvParams;
208  jvParams[jss::account_root] = alice.human();
209  jvParams[jss::ledger_hash] =
210  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
211  auto const jrr =
212  env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
213  checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
214  }
215 
216  void
218  {
219  testcase("Ledger Request, Full Option");
220  using namespace test::jtx;
221 
222  Env env{*this};
223 
224  env.close();
225 
226  Json::Value jvParams;
227  jvParams[jss::ledger_index] = 3u;
228  jvParams[jss::full] = true;
229  auto const jrr =
230  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
231  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
232  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
233  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 3u);
234  }
235 
236  void
238  {
239  testcase("Ledger Request, Full Option Without Admin");
240  using namespace test::jtx;
241 
242  Env env{*this, envconfig(no_admin)};
243 
244  // env.close();
245 
246  Json::Value jvParams;
247  jvParams[jss::ledger_index] = 1u;
248  jvParams[jss::full] = true;
249  auto const jrr =
250  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
252  jrr, "noPermission", "You don't have permission for this command.");
253  }
254 
255  void
257  {
258  testcase("Ledger Request, Accounts Option");
259  using namespace test::jtx;
260 
261  Env env{*this};
262 
263  env.close();
264 
265  Json::Value jvParams;
266  jvParams[jss::ledger_index] = 3u;
267  jvParams[jss::accounts] = true;
268  auto const jrr =
269  env.rpc("json", "ledger", to_string(jvParams))[jss::result];
270  BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
271  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
272  BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 3u);
273  }
274 
275  void
277  {
278  testcase("ledger_entry Request AccountRoot");
279  using namespace test::jtx;
280  Env env{*this};
281  Account const alice{"alice"};
282  env.fund(XRP(10000), alice);
283  env.close();
284 
285  std::string const ledgerHash{to_string(env.closed()->info().hash)};
286  {
287  // Exercise ledger_closed along the way.
288  Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
289  BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
290  BEAST_EXPECT(jrr[jss::ledger_index] == 3);
291  }
292 
293  std::string accountRootIndex;
294  {
295  // Request alice's account root.
296  Json::Value jvParams;
297  jvParams[jss::account_root] = alice.human();
298  jvParams[jss::ledger_hash] = ledgerHash;
299  Json::Value const jrr = env.rpc(
300  "json", "ledger_entry", to_string(jvParams))[jss::result];
301  BEAST_EXPECT(jrr.isMember(jss::node));
302  BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
303  BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
304  accountRootIndex = jrr[jss::index].asString();
305  }
306  {
307  constexpr char alicesAcctRootBinary[]{
308  "1100612200800000240000000425000000032D00000000559CE54C3B934E4"
309  "73A995B477E92EC229F99CED5B62BF4D2ACE4DC42719103AE2F6240000002"
310  "540BE4008114AE123A8556F3CF91154711376AFB0F894F832B3D"};
311 
312  // Request alice's account root, but with binary == true;
313  Json::Value jvParams;
314  jvParams[jss::account_root] = alice.human();
315  jvParams[jss::binary] = 1;
316  jvParams[jss::ledger_hash] = ledgerHash;
317  Json::Value const jrr = env.rpc(
318  "json", "ledger_entry", to_string(jvParams))[jss::result];
319  BEAST_EXPECT(jrr.isMember(jss::node_binary));
320  BEAST_EXPECT(jrr[jss::node_binary] == alicesAcctRootBinary);
321  }
322  {
323  // Request alice's account root using the index.
324  Json::Value jvParams;
325  jvParams[jss::index] = accountRootIndex;
326  Json::Value const jrr = env.rpc(
327  "json", "ledger_entry", to_string(jvParams))[jss::result];
328  BEAST_EXPECT(!jrr.isMember(jss::node_binary));
329  BEAST_EXPECT(jrr.isMember(jss::node));
330  BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
331  BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
332  }
333  {
334  // Request alice's account root by index, but with binary == false.
335  Json::Value jvParams;
336  jvParams[jss::index] = accountRootIndex;
337  jvParams[jss::binary] = 0;
338  Json::Value const jrr = env.rpc(
339  "json", "ledger_entry", to_string(jvParams))[jss::result];
340  BEAST_EXPECT(jrr.isMember(jss::node));
341  BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
342  BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
343  }
344  {
345  // Request using a corrupted AccountID.
346  Json::Value jvParams;
347  jvParams[jss::account_root] = makeBadAddress(alice.human());
348  jvParams[jss::ledger_hash] = ledgerHash;
349  Json::Value const jrr = env.rpc(
350  "json", "ledger_entry", to_string(jvParams))[jss::result];
351  checkErrorValue(jrr, "malformedAddress", "");
352  }
353  {
354  // Request an account that is not in the ledger.
355  Json::Value jvParams;
356  jvParams[jss::account_root] = Account("bob").human();
357  jvParams[jss::ledger_hash] = ledgerHash;
358  Json::Value const jrr = env.rpc(
359  "json", "ledger_entry", to_string(jvParams))[jss::result];
360  checkErrorValue(jrr, "entryNotFound", "");
361  }
362  }
363 
364  void
366  {
367  testcase("ledger_entry Request Check");
368  using namespace test::jtx;
369  Env env{*this};
370  Account const alice{"alice"};
371  env.fund(XRP(10000), alice);
372  env.close();
373 
374  auto const checkId = keylet::check(env.master, env.seq(env.master));
375 
376  env(check::create(env.master, alice, XRP(100)));
377  env.close();
378 
379  std::string const ledgerHash{to_string(env.closed()->info().hash)};
380  {
381  // Request a check.
382  Json::Value jvParams;
383  jvParams[jss::check] = to_string(checkId.key);
384  jvParams[jss::ledger_hash] = ledgerHash;
385  Json::Value const jrr = env.rpc(
386  "json", "ledger_entry", to_string(jvParams))[jss::result];
387  BEAST_EXPECT(
388  jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
389  BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
390  }
391  {
392  // Request an index that is not a check. We'll use alice's
393  // account root index.
394  std::string accountRootIndex;
395  {
396  Json::Value jvParams;
397  jvParams[jss::account_root] = alice.human();
398  Json::Value const jrr = env.rpc(
399  "json", "ledger_entry", to_string(jvParams))[jss::result];
400  accountRootIndex = jrr[jss::index].asString();
401  }
402  Json::Value jvParams;
403  jvParams[jss::check] = accountRootIndex;
404  jvParams[jss::ledger_hash] = ledgerHash;
405  Json::Value const jrr = env.rpc(
406  "json", "ledger_entry", to_string(jvParams))[jss::result];
407  checkErrorValue(jrr, "unexpectedLedgerType", "");
408  }
409  }
410 
411  void
413  {
414  testcase("ledger_entry Deposit Preauth");
415 
416  using namespace test::jtx;
417 
418  Env env{*this};
419  Account const alice{"alice"};
420  Account const becky{"becky"};
421 
422  env.fund(XRP(10000), alice, becky);
423  env.close();
424 
425  env(deposit::auth(alice, becky));
426  env.close();
427 
428  std::string const ledgerHash{to_string(env.closed()->info().hash)};
429  std::string depositPreauthIndex;
430  {
431  // Request a depositPreauth by owner and authorized.
432  Json::Value jvParams;
433  jvParams[jss::deposit_preauth][jss::owner] = alice.human();
434  jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
435  jvParams[jss::ledger_hash] = ledgerHash;
436  Json::Value const jrr = env.rpc(
437  "json", "ledger_entry", to_string(jvParams))[jss::result];
438 
439  BEAST_EXPECT(
440  jrr[jss::node][sfLedgerEntryType.jsonName] ==
441  jss::DepositPreauth);
442  BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
443  BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
444  depositPreauthIndex = jrr[jss::node][jss::index].asString();
445  }
446  {
447  // Request a depositPreauth by index.
448  Json::Value jvParams;
449  jvParams[jss::deposit_preauth] = depositPreauthIndex;
450  jvParams[jss::ledger_hash] = ledgerHash;
451  Json::Value const jrr = env.rpc(
452  "json", "ledger_entry", to_string(jvParams))[jss::result];
453 
454  BEAST_EXPECT(
455  jrr[jss::node][sfLedgerEntryType.jsonName] ==
456  jss::DepositPreauth);
457  BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
458  BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
459  }
460  {
461  // Malformed request: deposit_preauth neither object nor string.
462  Json::Value jvParams;
463  jvParams[jss::deposit_preauth] = -5;
464  jvParams[jss::ledger_hash] = ledgerHash;
465  Json::Value const jrr = env.rpc(
466  "json", "ledger_entry", to_string(jvParams))[jss::result];
467  checkErrorValue(jrr, "malformedRequest", "");
468  }
469  {
470  // Malformed request: deposit_preauth not hex string.
471  Json::Value jvParams;
472  jvParams[jss::deposit_preauth] = "0123456789ABCDEFG";
473  jvParams[jss::ledger_hash] = ledgerHash;
474  Json::Value const jrr = env.rpc(
475  "json", "ledger_entry", to_string(jvParams))[jss::result];
476  checkErrorValue(jrr, "malformedRequest", "");
477  }
478  {
479  // Malformed request: missing [jss::deposit_preauth][jss::owner]
480  Json::Value jvParams;
481  jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
482  jvParams[jss::ledger_hash] = ledgerHash;
483  Json::Value const jrr = env.rpc(
484  "json", "ledger_entry", to_string(jvParams))[jss::result];
485  checkErrorValue(jrr, "malformedRequest", "");
486  }
487  {
488  // Malformed request: [jss::deposit_preauth][jss::owner] not string.
489  Json::Value jvParams;
490  jvParams[jss::deposit_preauth][jss::owner] = 7;
491  jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
492  jvParams[jss::ledger_hash] = ledgerHash;
493  Json::Value const jrr = env.rpc(
494  "json", "ledger_entry", to_string(jvParams))[jss::result];
495  checkErrorValue(jrr, "malformedRequest", "");
496  }
497  {
498  // Malformed: missing [jss::deposit_preauth][jss::authorized]
499  Json::Value jvParams;
500  jvParams[jss::deposit_preauth][jss::owner] = alice.human();
501  jvParams[jss::ledger_hash] = ledgerHash;
502  Json::Value const jrr = env.rpc(
503  "json", "ledger_entry", to_string(jvParams))[jss::result];
504  checkErrorValue(jrr, "malformedRequest", "");
505  }
506  {
507  // Malformed: [jss::deposit_preauth][jss::authorized] not string.
508  Json::Value jvParams;
509  jvParams[jss::deposit_preauth][jss::owner] = alice.human();
510  jvParams[jss::deposit_preauth][jss::authorized] = 47;
511  jvParams[jss::ledger_hash] = ledgerHash;
512  Json::Value const jrr = env.rpc(
513  "json", "ledger_entry", to_string(jvParams))[jss::result];
514  checkErrorValue(jrr, "malformedRequest", "");
515  }
516  {
517  // Malformed: [jss::deposit_preauth][jss::owner] is malformed.
518  Json::Value jvParams;
519  jvParams[jss::deposit_preauth][jss::owner] =
520  "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
521 
522  jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
523  jvParams[jss::ledger_hash] = ledgerHash;
524  Json::Value const jrr = env.rpc(
525  "json", "ledger_entry", to_string(jvParams))[jss::result];
526  checkErrorValue(jrr, "malformedOwner", "");
527  }
528  {
529  // Malformed: [jss::deposit_preauth][jss::authorized] is malformed.
530  Json::Value jvParams;
531  jvParams[jss::deposit_preauth][jss::owner] = alice.human();
532  jvParams[jss::deposit_preauth][jss::authorized] =
533  "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
534 
535  jvParams[jss::ledger_hash] = ledgerHash;
536  Json::Value const jrr = env.rpc(
537  "json", "ledger_entry", to_string(jvParams))[jss::result];
538  checkErrorValue(jrr, "malformedAuthorized", "");
539  }
540  }
541 
542  void
544  {
545  testcase("ledger_entry Request Directory");
546  using namespace test::jtx;
547  Env env{*this};
548  Account const alice{"alice"};
549  Account const gw{"gateway"};
550  auto const USD = gw["USD"];
551  env.fund(XRP(10000), alice, gw);
552  env.close();
553 
554  env.trust(USD(1000), alice);
555  env.close();
556 
557  // Run up the number of directory entries so alice has two
558  // directory nodes.
559  for (int d = 1'000'032; d >= 1'000'000; --d)
560  {
561  env(offer(alice, USD(1), drops(d)));
562  }
563  env.close();
564 
565  std::string const ledgerHash{to_string(env.closed()->info().hash)};
566  {
567  // Exercise ledger_closed along the way.
568  Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
569  BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
570  BEAST_EXPECT(jrr[jss::ledger_index] == 5);
571  }
572 
573  std::string const dirRootIndex =
574  "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D";
575  {
576  // Locate directory by index.
577  Json::Value jvParams;
578  jvParams[jss::directory] = dirRootIndex;
579  jvParams[jss::ledger_hash] = ledgerHash;
580  Json::Value const jrr = env.rpc(
581  "json", "ledger_entry", to_string(jvParams))[jss::result];
582  BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32);
583  }
584  {
585  // Locate directory by directory root.
586  Json::Value jvParams;
587  jvParams[jss::directory] = Json::objectValue;
588  jvParams[jss::directory][jss::dir_root] = dirRootIndex;
589  Json::Value const jrr = env.rpc(
590  "json", "ledger_entry", to_string(jvParams))[jss::result];
591  BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
592  }
593  {
594  // Locate directory by owner.
595  Json::Value jvParams;
596  jvParams[jss::directory] = Json::objectValue;
597  jvParams[jss::directory][jss::owner] = alice.human();
598  jvParams[jss::ledger_hash] = ledgerHash;
599  Json::Value const jrr = env.rpc(
600  "json", "ledger_entry", to_string(jvParams))[jss::result];
601  BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
602  }
603  {
604  // Locate directory by directory root and sub_index.
605  Json::Value jvParams;
606  jvParams[jss::directory] = Json::objectValue;
607  jvParams[jss::directory][jss::dir_root] = dirRootIndex;
608  jvParams[jss::directory][jss::sub_index] = 1;
609  Json::Value const jrr = env.rpc(
610  "json", "ledger_entry", to_string(jvParams))[jss::result];
611  BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
612  BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
613  }
614  {
615  // Locate directory by owner and sub_index.
616  Json::Value jvParams;
617  jvParams[jss::directory] = Json::objectValue;
618  jvParams[jss::directory][jss::owner] = alice.human();
619  jvParams[jss::directory][jss::sub_index] = 1;
620  jvParams[jss::ledger_hash] = ledgerHash;
621  Json::Value const jrr = env.rpc(
622  "json", "ledger_entry", to_string(jvParams))[jss::result];
623  BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
624  BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
625  }
626  {
627  // Null directory argument.
628  Json::Value jvParams;
629  jvParams[jss::directory] = Json::nullValue;
630  jvParams[jss::ledger_hash] = ledgerHash;
631  Json::Value const jrr = env.rpc(
632  "json", "ledger_entry", to_string(jvParams))[jss::result];
633  checkErrorValue(jrr, "malformedRequest", "");
634  }
635  {
636  // Non-integer sub_index.
637  Json::Value jvParams;
638  jvParams[jss::directory] = Json::objectValue;
639  jvParams[jss::directory][jss::dir_root] = dirRootIndex;
640  jvParams[jss::directory][jss::sub_index] = 1.5;
641  jvParams[jss::ledger_hash] = ledgerHash;
642  Json::Value const jrr = env.rpc(
643  "json", "ledger_entry", to_string(jvParams))[jss::result];
644  checkErrorValue(jrr, "malformedRequest", "");
645  }
646  {
647  // Malformed owner entry.
648  Json::Value jvParams;
649  jvParams[jss::directory] = Json::objectValue;
650 
651  std::string const badAddress = makeBadAddress(alice.human());
652  jvParams[jss::directory][jss::owner] = badAddress;
653  jvParams[jss::ledger_hash] = ledgerHash;
654  Json::Value const jrr = env.rpc(
655  "json", "ledger_entry", to_string(jvParams))[jss::result];
656  checkErrorValue(jrr, "malformedAddress", "");
657  }
658  {
659  // Malformed directory object. Specify both dir_root and owner.
660  Json::Value jvParams;
661  jvParams[jss::directory] = Json::objectValue;
662  jvParams[jss::directory][jss::owner] = alice.human();
663  jvParams[jss::directory][jss::dir_root] = dirRootIndex;
664  jvParams[jss::ledger_hash] = ledgerHash;
665  Json::Value const jrr = env.rpc(
666  "json", "ledger_entry", to_string(jvParams))[jss::result];
667  checkErrorValue(jrr, "malformedRequest", "");
668  }
669  {
670  // Incomplete directory object. Missing both dir_root and owner.
671  Json::Value jvParams;
672  jvParams[jss::directory] = Json::objectValue;
673  jvParams[jss::directory][jss::sub_index] = 1;
674  jvParams[jss::ledger_hash] = ledgerHash;
675  Json::Value const jrr = env.rpc(
676  "json", "ledger_entry", to_string(jvParams))[jss::result];
677  checkErrorValue(jrr, "malformedRequest", "");
678  }
679  }
680 
681  void
683  {
684  testcase("ledger_entry Request Escrow");
685  using namespace test::jtx;
686  Env env{*this};
687  Account const alice{"alice"};
688  env.fund(XRP(10000), alice);
689  env.close();
690 
691  // Lambda to create an escrow.
692  auto escrowCreate = [](test::jtx::Account const& account,
693  test::jtx::Account const& to,
694  STAmount const& amount,
695  NetClock::time_point const& cancelAfter) {
696  Json::Value jv;
697  jv[jss::TransactionType] = jss::EscrowCreate;
698  jv[jss::Flags] = tfUniversal;
699  jv[jss::Account] = account.human();
700  jv[jss::Destination] = to.human();
701  jv[jss::Amount] = amount.getJson(JsonOptions::none);
702  jv[sfFinishAfter.jsonName] =
703  cancelAfter.time_since_epoch().count() + 2;
704  return jv;
705  };
706 
707  using namespace std::chrono_literals;
708  env(escrowCreate(alice, alice, XRP(333), env.now() + 2s));
709  env.close();
710 
711  std::string const ledgerHash{to_string(env.closed()->info().hash)};
712  std::string escrowIndex;
713  {
714  // Request the escrow using owner and sequence.
715  Json::Value jvParams;
716  jvParams[jss::escrow] = Json::objectValue;
717  jvParams[jss::escrow][jss::owner] = alice.human();
718  jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
719  Json::Value const jrr = env.rpc(
720  "json", "ledger_entry", to_string(jvParams))[jss::result];
721  BEAST_EXPECT(
722  jrr[jss::node][jss::Amount] == XRP(333).value().getText());
723  escrowIndex = jrr[jss::index].asString();
724  }
725  {
726  // Request the escrow by index.
727  Json::Value jvParams;
728  jvParams[jss::escrow] = escrowIndex;
729  jvParams[jss::ledger_hash] = ledgerHash;
730  Json::Value const jrr = env.rpc(
731  "json", "ledger_entry", to_string(jvParams))[jss::result];
732  BEAST_EXPECT(
733  jrr[jss::node][jss::Amount] == XRP(333).value().getText());
734  }
735  {
736  // Malformed owner entry.
737  Json::Value jvParams;
738  jvParams[jss::escrow] = Json::objectValue;
739 
740  std::string const badAddress = makeBadAddress(alice.human());
741  jvParams[jss::escrow][jss::owner] = badAddress;
742  jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
743  jvParams[jss::ledger_hash] = ledgerHash;
744  Json::Value const jrr = env.rpc(
745  "json", "ledger_entry", to_string(jvParams))[jss::result];
746  checkErrorValue(jrr, "malformedOwner", "");
747  }
748  {
749  // Missing owner.
750  Json::Value jvParams;
751  jvParams[jss::escrow] = Json::objectValue;
752  jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
753  jvParams[jss::ledger_hash] = ledgerHash;
754  Json::Value const jrr = env.rpc(
755  "json", "ledger_entry", to_string(jvParams))[jss::result];
756  checkErrorValue(jrr, "malformedRequest", "");
757  }
758  {
759  // Missing sequence.
760  Json::Value jvParams;
761  jvParams[jss::escrow] = Json::objectValue;
762  jvParams[jss::escrow][jss::owner] = alice.human();
763  jvParams[jss::ledger_hash] = ledgerHash;
764  Json::Value const jrr = env.rpc(
765  "json", "ledger_entry", to_string(jvParams))[jss::result];
766  checkErrorValue(jrr, "malformedRequest", "");
767  }
768  {
769  // Non-integer sequence.
770  Json::Value jvParams;
771  jvParams[jss::escrow] = Json::objectValue;
772  jvParams[jss::escrow][jss::owner] = alice.human();
773  jvParams[jss::escrow][jss::seq] =
774  std::to_string(env.seq(alice) - 1);
775  jvParams[jss::ledger_hash] = ledgerHash;
776  Json::Value const jrr = env.rpc(
777  "json", "ledger_entry", to_string(jvParams))[jss::result];
778  checkErrorValue(jrr, "malformedRequest", "");
779  }
780  }
781 
782  void
784  {
785  testcase("ledger_entry Request Offer");
786  using namespace test::jtx;
787  Env env{*this};
788  Account const alice{"alice"};
789  Account const gw{"gateway"};
790  auto const USD = gw["USD"];
791  env.fund(XRP(10000), alice, gw);
792  env.close();
793 
794  env(offer(alice, USD(321), XRP(322)));
795  env.close();
796 
797  std::string const ledgerHash{to_string(env.closed()->info().hash)};
798  std::string offerIndex;
799  {
800  // Request the offer using owner and sequence.
801  Json::Value jvParams;
802  jvParams[jss::offer] = Json::objectValue;
803  jvParams[jss::offer][jss::account] = alice.human();
804  jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
805  jvParams[jss::ledger_hash] = ledgerHash;
806  Json::Value const jrr = env.rpc(
807  "json", "ledger_entry", to_string(jvParams))[jss::result];
808  BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
809  offerIndex = jrr[jss::index].asString();
810  }
811  {
812  // Request the offer using its index.
813  Json::Value jvParams;
814  jvParams[jss::offer] = offerIndex;
815  Json::Value const jrr = env.rpc(
816  "json", "ledger_entry", to_string(jvParams))[jss::result];
817  BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
818  }
819  {
820  // Malformed account entry.
821  Json::Value jvParams;
822  jvParams[jss::offer] = Json::objectValue;
823 
824  std::string const badAddress = makeBadAddress(alice.human());
825  jvParams[jss::offer][jss::account] = badAddress;
826  jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
827  jvParams[jss::ledger_hash] = ledgerHash;
828  Json::Value const jrr = env.rpc(
829  "json", "ledger_entry", to_string(jvParams))[jss::result];
830  checkErrorValue(jrr, "malformedAddress", "");
831  }
832  {
833  // Malformed offer object. Missing account member.
834  Json::Value jvParams;
835  jvParams[jss::offer] = Json::objectValue;
836  jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
837  jvParams[jss::ledger_hash] = ledgerHash;
838  Json::Value const jrr = env.rpc(
839  "json", "ledger_entry", to_string(jvParams))[jss::result];
840  checkErrorValue(jrr, "malformedRequest", "");
841  }
842  {
843  // Malformed offer object. Missing seq member.
844  Json::Value jvParams;
845  jvParams[jss::offer] = Json::objectValue;
846  jvParams[jss::offer][jss::account] = alice.human();
847  jvParams[jss::ledger_hash] = ledgerHash;
848  Json::Value const jrr = env.rpc(
849  "json", "ledger_entry", to_string(jvParams))[jss::result];
850  checkErrorValue(jrr, "malformedRequest", "");
851  }
852  {
853  // Malformed offer object. Non-integral seq member.
854  Json::Value jvParams;
855  jvParams[jss::offer] = Json::objectValue;
856  jvParams[jss::offer][jss::account] = alice.human();
857  jvParams[jss::offer][jss::seq] = std::to_string(env.seq(alice) - 1);
858  jvParams[jss::ledger_hash] = ledgerHash;
859  Json::Value const jrr = env.rpc(
860  "json", "ledger_entry", to_string(jvParams))[jss::result];
861  checkErrorValue(jrr, "malformedRequest", "");
862  }
863  }
864 
865  void
867  {
868  testcase("ledger_entry Request Pay Chan");
869  using namespace test::jtx;
870  using namespace std::literals::chrono_literals;
871  Env env{*this};
872  Account const alice{"alice"};
873 
874  env.fund(XRP(10000), alice);
875  env.close();
876 
877  // Lambda to create a PayChan.
878  auto payChanCreate = [](test::jtx::Account const& account,
879  test::jtx::Account const& to,
880  STAmount const& amount,
881  NetClock::duration const& settleDelay,
882  PublicKey const& pk) {
883  Json::Value jv;
884  jv[jss::TransactionType] = jss::PaymentChannelCreate;
885  jv[jss::Account] = account.human();
886  jv[jss::Destination] = to.human();
887  jv[jss::Amount] = amount.getJson(JsonOptions::none);
888  jv[sfSettleDelay.jsonName] = settleDelay.count();
889  jv[sfPublicKey.jsonName] = strHex(pk.slice());
890  return jv;
891  };
892 
893  env(payChanCreate(alice, env.master, XRP(57), 18s, alice.pk()));
894  env.close();
895 
896  std::string const ledgerHash{to_string(env.closed()->info().hash)};
897 
898  uint256 const payChanIndex{
899  keylet::payChan(alice, env.master, env.seq(alice) - 1).key};
900  {
901  // Request the payment channel using its index.
902  Json::Value jvParams;
903  jvParams[jss::payment_channel] = to_string(payChanIndex);
904  jvParams[jss::ledger_hash] = ledgerHash;
905  Json::Value const jrr = env.rpc(
906  "json", "ledger_entry", to_string(jvParams))[jss::result];
907  BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
908  BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
909  BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
910  }
911  {
912  // Request an index that is not a payment channel.
913  Json::Value jvParams;
914  jvParams[jss::payment_channel] = ledgerHash;
915  jvParams[jss::ledger_hash] = ledgerHash;
916  Json::Value const jrr = env.rpc(
917  "json", "ledger_entry", to_string(jvParams))[jss::result];
918  checkErrorValue(jrr, "entryNotFound", "");
919  }
920  }
921 
922  void
924  {
925  testcase("ledger_entry Request RippleState");
926  using namespace test::jtx;
927  Env env{*this};
928  Account const alice{"alice"};
929  Account const gw{"gateway"};
930  auto const USD = gw["USD"];
931  env.fund(XRP(10000), alice, gw);
932  env.close();
933 
934  env.trust(USD(999), alice);
935  env.close();
936 
937  env(pay(gw, alice, USD(97)));
938  env.close();
939 
940  std::string const ledgerHash{to_string(env.closed()->info().hash)};
941  {
942  // Request the trust line using the accounts and currency.
943  Json::Value jvParams;
944  jvParams[jss::ripple_state] = Json::objectValue;
945  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
946  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
947  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
948  jvParams[jss::ripple_state][jss::currency] = "USD";
949  jvParams[jss::ledger_hash] = ledgerHash;
950  Json::Value const jrr = env.rpc(
951  "json", "ledger_entry", to_string(jvParams))[jss::result];
952  BEAST_EXPECT(
953  jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
954  BEAST_EXPECT(
955  jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
956  }
957  {
958  // ripple_state is not an object.
959  Json::Value jvParams;
960  jvParams[jss::ripple_state] = "ripple_state";
961  jvParams[jss::ledger_hash] = ledgerHash;
962  Json::Value const jrr = env.rpc(
963  "json", "ledger_entry", to_string(jvParams))[jss::result];
964  checkErrorValue(jrr, "malformedRequest", "");
965  }
966  {
967  // ripple_state.currency is missing.
968  Json::Value jvParams;
969  jvParams[jss::ripple_state] = Json::objectValue;
970  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
971  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
972  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
973  jvParams[jss::ledger_hash] = ledgerHash;
974  Json::Value const jrr = env.rpc(
975  "json", "ledger_entry", to_string(jvParams))[jss::result];
976  checkErrorValue(jrr, "malformedRequest", "");
977  }
978  {
979  // ripple_state accounts is not an array.
980  Json::Value jvParams;
981  jvParams[jss::ripple_state] = Json::objectValue;
982  jvParams[jss::ripple_state][jss::accounts] = 2;
983  jvParams[jss::ripple_state][jss::currency] = "USD";
984  jvParams[jss::ledger_hash] = ledgerHash;
985  Json::Value const jrr = env.rpc(
986  "json", "ledger_entry", to_string(jvParams))[jss::result];
987  checkErrorValue(jrr, "malformedRequest", "");
988  }
989  {
990  // ripple_state one of the accounts is missing.
991  Json::Value jvParams;
992  jvParams[jss::ripple_state] = Json::objectValue;
993  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
994  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
995  jvParams[jss::ripple_state][jss::currency] = "USD";
996  jvParams[jss::ledger_hash] = ledgerHash;
997  Json::Value const jrr = env.rpc(
998  "json", "ledger_entry", to_string(jvParams))[jss::result];
999  checkErrorValue(jrr, "malformedRequest", "");
1000  }
1001  {
1002  // ripple_state more than 2 accounts.
1003  Json::Value jvParams;
1004  jvParams[jss::ripple_state] = Json::objectValue;
1005  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1006  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
1007  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
1008  jvParams[jss::ripple_state][jss::accounts][2u] = alice.human();
1009  jvParams[jss::ripple_state][jss::currency] = "USD";
1010  jvParams[jss::ledger_hash] = ledgerHash;
1011  Json::Value const jrr = env.rpc(
1012  "json", "ledger_entry", to_string(jvParams))[jss::result];
1013  checkErrorValue(jrr, "malformedRequest", "");
1014  }
1015  {
1016  // ripple_state account[0] is not a string.
1017  Json::Value jvParams;
1018  jvParams[jss::ripple_state] = Json::objectValue;
1019  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1020  jvParams[jss::ripple_state][jss::accounts][0u] = 44;
1021  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
1022  jvParams[jss::ripple_state][jss::currency] = "USD";
1023  jvParams[jss::ledger_hash] = ledgerHash;
1024  Json::Value const jrr = env.rpc(
1025  "json", "ledger_entry", to_string(jvParams))[jss::result];
1026  checkErrorValue(jrr, "malformedRequest", "");
1027  }
1028  {
1029  // ripple_state account[1] is not a string.
1030  Json::Value jvParams;
1031  jvParams[jss::ripple_state] = Json::objectValue;
1032  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1033  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
1034  jvParams[jss::ripple_state][jss::accounts][1u] = 21;
1035  jvParams[jss::ripple_state][jss::currency] = "USD";
1036  jvParams[jss::ledger_hash] = ledgerHash;
1037  Json::Value const jrr = env.rpc(
1038  "json", "ledger_entry", to_string(jvParams))[jss::result];
1039  checkErrorValue(jrr, "malformedRequest", "");
1040  }
1041  {
1042  // ripple_state account[0] == account[1].
1043  Json::Value jvParams;
1044  jvParams[jss::ripple_state] = Json::objectValue;
1045  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1046  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
1047  jvParams[jss::ripple_state][jss::accounts][1u] = alice.human();
1048  jvParams[jss::ripple_state][jss::currency] = "USD";
1049  jvParams[jss::ledger_hash] = ledgerHash;
1050  Json::Value const jrr = env.rpc(
1051  "json", "ledger_entry", to_string(jvParams))[jss::result];
1052  checkErrorValue(jrr, "malformedRequest", "");
1053  }
1054  {
1055  // ripple_state malformed account[0].
1056  Json::Value jvParams;
1057  jvParams[jss::ripple_state] = Json::objectValue;
1058  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1059  jvParams[jss::ripple_state][jss::accounts][0u] =
1060  makeBadAddress(alice.human());
1061  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
1062  jvParams[jss::ripple_state][jss::currency] = "USD";
1063  jvParams[jss::ledger_hash] = ledgerHash;
1064  Json::Value const jrr = env.rpc(
1065  "json", "ledger_entry", to_string(jvParams))[jss::result];
1066  checkErrorValue(jrr, "malformedAddress", "");
1067  }
1068  {
1069  // ripple_state malformed account[1].
1070  Json::Value jvParams;
1071  jvParams[jss::ripple_state] = Json::objectValue;
1072  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1073  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
1074  jvParams[jss::ripple_state][jss::accounts][1u] =
1075  makeBadAddress(gw.human());
1076  jvParams[jss::ripple_state][jss::currency] = "USD";
1077  jvParams[jss::ledger_hash] = ledgerHash;
1078  Json::Value const jrr = env.rpc(
1079  "json", "ledger_entry", to_string(jvParams))[jss::result];
1080  checkErrorValue(jrr, "malformedAddress", "");
1081  }
1082  {
1083  // ripple_state malformed currency.
1084  Json::Value jvParams;
1085  jvParams[jss::ripple_state] = Json::objectValue;
1086  jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
1087  jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
1088  jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
1089  jvParams[jss::ripple_state][jss::currency] = "USDollars";
1090  jvParams[jss::ledger_hash] = ledgerHash;
1091  Json::Value const jrr = env.rpc(
1092  "json", "ledger_entry", to_string(jvParams))[jss::result];
1093  checkErrorValue(jrr, "malformedCurrency", "");
1094  }
1095  }
1096 
1097  void
1099  {
1100  testcase("ledger_entry Request Ticket");
1101  using namespace test::jtx;
1102  Env env{*this};
1103  env.close();
1104 
1105  // Create two tickets.
1106  std::uint32_t const tkt1{env.seq(env.master) + 1};
1107  env(ticket::create(env.master, 2));
1108  env.close();
1109 
1110  std::string const ledgerHash{to_string(env.closed()->info().hash)};
1111  // Request four tickets: one before the first one we created, the
1112  // two created tickets, and the ticket that would come after the
1113  // last created ticket.
1114  {
1115  // Not a valid ticket requested by index.
1116  Json::Value jvParams;
1117  jvParams[jss::ticket] =
1118  to_string(getTicketIndex(env.master, tkt1 - 1));
1119  jvParams[jss::ledger_hash] = ledgerHash;
1120  Json::Value const jrr = env.rpc(
1121  "json", "ledger_entry", to_string(jvParams))[jss::result];
1122  checkErrorValue(jrr, "entryNotFound", "");
1123  }
1124  {
1125  // First real ticket requested by index.
1126  Json::Value jvParams;
1127  jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1));
1128  jvParams[jss::ledger_hash] = ledgerHash;
1129  Json::Value const jrr = env.rpc(
1130  "json", "ledger_entry", to_string(jvParams))[jss::result];
1131  BEAST_EXPECT(
1132  jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Ticket);
1133  BEAST_EXPECT(jrr[jss::node][sfTicketSequence.jsonName] == tkt1);
1134  }
1135  {
1136  // Second real ticket requested by account and sequence.
1137  Json::Value jvParams;
1138  jvParams[jss::ticket] = Json::objectValue;
1139  jvParams[jss::ticket][jss::account] = env.master.human();
1140  jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 1;
1141  jvParams[jss::ledger_hash] = ledgerHash;
1142  Json::Value const jrr = env.rpc(
1143  "json", "ledger_entry", to_string(jvParams))[jss::result];
1144  BEAST_EXPECT(
1145  jrr[jss::node][jss::index] ==
1146  to_string(getTicketIndex(env.master, tkt1 + 1)));
1147  }
1148  {
1149  // Not a valid ticket requested by account and sequence.
1150  Json::Value jvParams;
1151  jvParams[jss::ticket] = Json::objectValue;
1152  jvParams[jss::ticket][jss::account] = env.master.human();
1153  jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 2;
1154  jvParams[jss::ledger_hash] = ledgerHash;
1155  Json::Value const jrr = env.rpc(
1156  "json", "ledger_entry", to_string(jvParams))[jss::result];
1157  checkErrorValue(jrr, "entryNotFound", "");
1158  }
1159  {
1160  // Request a ticket using an account root entry.
1161  Json::Value jvParams;
1162  jvParams[jss::ticket] = to_string(keylet::account(env.master).key);
1163  jvParams[jss::ledger_hash] = ledgerHash;
1164  Json::Value const jrr = env.rpc(
1165  "json", "ledger_entry", to_string(jvParams))[jss::result];
1166  checkErrorValue(jrr, "unexpectedLedgerType", "");
1167  }
1168  {
1169  // Malformed account entry.
1170  Json::Value jvParams;
1171  jvParams[jss::ticket] = Json::objectValue;
1172 
1173  std::string const badAddress = makeBadAddress(env.master.human());
1174  jvParams[jss::ticket][jss::account] = badAddress;
1175  jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
1176  jvParams[jss::ledger_hash] = ledgerHash;
1177  Json::Value const jrr = env.rpc(
1178  "json", "ledger_entry", to_string(jvParams))[jss::result];
1179  checkErrorValue(jrr, "malformedAddress", "");
1180  }
1181  {
1182  // Malformed ticket object. Missing account member.
1183  Json::Value jvParams;
1184  jvParams[jss::ticket] = Json::objectValue;
1185  jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
1186  jvParams[jss::ledger_hash] = ledgerHash;
1187  Json::Value const jrr = env.rpc(
1188  "json", "ledger_entry", to_string(jvParams))[jss::result];
1189  checkErrorValue(jrr, "malformedRequest", "");
1190  }
1191  {
1192  // Malformed ticket object. Missing seq member.
1193  Json::Value jvParams;
1194  jvParams[jss::ticket] = Json::objectValue;
1195  jvParams[jss::ticket][jss::account] = env.master.human();
1196  jvParams[jss::ledger_hash] = ledgerHash;
1197  Json::Value const jrr = env.rpc(
1198  "json", "ledger_entry", to_string(jvParams))[jss::result];
1199  checkErrorValue(jrr, "malformedRequest", "");
1200  }
1201  {
1202  // Malformed ticket object. Non-integral seq member.
1203  Json::Value jvParams;
1204  jvParams[jss::ticket] = Json::objectValue;
1205  jvParams[jss::ticket][jss::account] = env.master.human();
1206  jvParams[jss::ticket][jss::ticket_seq] =
1207  std::to_string(env.seq(env.master) - 1);
1208  jvParams[jss::ledger_hash] = ledgerHash;
1209  Json::Value const jrr = env.rpc(
1210  "json", "ledger_entry", to_string(jvParams))[jss::result];
1211  checkErrorValue(jrr, "malformedRequest", "");
1212  }
1213  }
1214 
1215  void
1216  testLedgerEntryInvalidParams(unsigned int apiVersion)
1217  {
1218  testcase(
1219  "ledger_entry Request With Invalid Parameters v" +
1220  std::to_string(apiVersion));
1221  using namespace test::jtx;
1222  Env env{*this};
1223 
1224  std::string const ledgerHash{to_string(env.closed()->info().hash)};
1225 
1226  // "features" is not an option supported by ledger_entry.
1227  Json::Value jvParams;
1228  jvParams[jss::api_version] = apiVersion;
1229  jvParams[jss::features] = ledgerHash;
1230  jvParams[jss::ledger_hash] = ledgerHash;
1231  Json::Value const jrr =
1232  env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
1233 
1234  if (apiVersion < 2u)
1235  checkErrorValue(jrr, "unknownOption", "");
1236  else
1237  checkErrorValue(jrr, "invalidParams", "");
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();
1735  testLookupLedger();
1736  testNoQueue();
1737  testQueue();
1739 
1740  // version specific tests
1741  for (auto testVersion = RPC::apiMinimumSupportedVersion;
1742  testVersion <= RPC::apiBetaVersion;
1743  ++testVersion)
1744  {
1745  testLedgerEntryInvalidParams(testVersion);
1746  }
1747  }
1748 };
1749 
1750 BEAST_DEFINE_TESTSUITE(LedgerRPC, app, ripple);
1751 
1752 } // namespace ripple
ripple::LedgerRPC_test::testLedgerEntryRippleState
void testLedgerEntryRippleState()
Definition: LedgerRPC_test.cpp:923
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:1098
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:783
std::chrono::duration
ripple::LedgerRPC_test::testNoQueue
void testNoQueue()
Definition: LedgerRPC_test.cpp:1440
ripple::LedgerRPC_test::testLedgerEntryInvalidParams
void testLedgerEntryInvalidParams(unsigned int apiVersion)
Definition: LedgerRPC_test.cpp:1216
ripple::sfTicketSequence
const SF_UINT32 sfTicketSequence
ripple::SField::jsonName
const Json::StaticString jsonName
Definition: SField.h:139
ripple::RPC::apiBetaVersion
constexpr unsigned int apiBetaVersion
Definition: RPCHelpers.h:245
ripple::getTicketIndex
uint256 getTicketIndex(AccountID const &account, std::uint32_t ticketSeq)
Definition: Indexes.cpp:116
ripple::LedgerRPC_test
Definition: LedgerRPC_test.cpp:31
std::string::replace
T replace(T... args)
ripple::terQUEUED
@ terQUEUED
Definition: TER.h:208
ripple::sfIndexes
const SF_VECTOR256 sfIndexes
ripple::Keylet::key
uint256 key
Definition: Keylet.h:40
ripple::base_uint< 256 >
ripple::LedgerRPC_test::testLedgerAccountsOption
void testLedgerAccountsOption()
Definition: LedgerRPC_test.cpp:1674
ripple::LedgerRPC_test::testLedgerFullNonAdmin
void testLedgerFullNonAdmin()
Definition: LedgerRPC_test.cpp:237
ripple::sfSettleDelay
const SF_UINT32 sfSettleDelay
ripple::LedgerRPC_test::testLedgerFull
void testLedgerFull()
Definition: LedgerRPC_test.cpp:217
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::RPC::contains_error
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Definition: ErrorCodes.cpp:195
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:134
ripple::LedgerRPC_test::makeBadAddress
std::string makeBadAddress(std::string good)
Definition: LedgerRPC_test.cpp:56
ripple::LedgerRPC_test::testBadInput
void testBadInput()
Definition: LedgerRPC_test.cpp:106
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:276
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:682
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:543
ripple::LedgerRPC_test::testMissingLedgerEntryLedgerHash
void testMissingLedgerEntryLedgerHash()
Definition: LedgerRPC_test.cpp:198
ripple::LedgerRPC_test::testLedgerEntryPayChan
void testLedgerEntryPayChan()
Definition: LedgerRPC_test.cpp:866
ripple::RPC::apiMinimumSupportedVersion
constexpr unsigned int apiMinimumSupportedVersion
Definition: RPCHelpers.h:243
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::sfLedgerEntryType
const SF_UINT16 sfLedgerEntryType
ripple::keylet::payChan
Keylet payChan(AccountID const &src, AccountID const &dst, std::uint32_t seq) noexcept
A PaymentChannel.
Definition: Indexes.cpp:325
ripple::LedgerRPC_test::testLedgerAccounts
void testLedgerAccounts()
Definition: LedgerRPC_test.cpp:256
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:365
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:412
ripple::base_uint::parseHex
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:496
ripple::tfUniversal
constexpr std::uint32_t tfUniversal
Definition: TxFlags.h:59
ripple::keylet::check
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition: Indexes.cpp:282
std::unique_ptr
STL class.
ripple::sfPublicKey
const SF_VL sfPublicKey
ripple::LedgerRPC_test::testLedgerCurrent
void testLedgerCurrent()
Definition: LedgerRPC_test.cpp:180
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::LedgerRPC_test::checkErrorValue
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
Definition: LedgerRPC_test.cpp:34
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469
ripple::LedgerRPC_test::testLedgerRequest
void testLedgerRequest()
Definition: LedgerRPC_test.cpp:64