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