rippled
LedgerData_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 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/basics/StringUtilities.h>
21 #include <ripple/protocol/jss.h>
22 #include <test/jtx.h>
23 
24 namespace ripple {
25 
26 class LedgerData_test : public beast::unit_test::suite
27 {
28 public:
29  // test helper
30  static bool
32  {
33  return val.isMember(jss::marker) && val[jss::marker].isString() &&
34  val[jss::marker].asString().size() > 0;
35  }
36 
37  void
39  {
40  using namespace test::jtx;
41  Env env{*this, asAdmin ? envconfig() : envconfig(no_admin)};
42  Account const gw{"gateway"};
43  auto const USD = gw["USD"];
44  env.fund(XRP(100000), gw);
45 
46  int const max_limit = 256; // would be 2048 for binary requests, no
47  // need to test that here
48 
49  for (auto i = 0; i < max_limit + 10; i++)
50  {
51  Account const bob{std::string("bob") + std::to_string(i)};
52  env.fund(XRP(1000), bob);
53  }
54  // Note that calls to env.close() fail without admin permission.
55  if (asAdmin)
56  env.close();
57 
58  // with no limit specified, we get the max_limit if the total number of
59  // accounts is greater than max, which it is here
60  Json::Value jvParams;
61  jvParams[jss::ledger_index] = "current";
62  jvParams[jss::binary] = false;
63  {
64  auto const jrr = env.rpc(
65  "json",
66  "ledger_data",
67  boost::lexical_cast<std::string>(jvParams))[jss::result];
68  BEAST_EXPECT(
69  jrr[jss::ledger_current_index].isIntegral() &&
70  jrr[jss::ledger_current_index].asInt() > 0);
71  BEAST_EXPECT(checkMarker(jrr));
72  BEAST_EXPECT(checkArraySize(jrr[jss::state], max_limit));
73  }
74 
75  // check limits values around the max_limit (+/- 1)
76  for (auto delta = -1; delta <= 1; delta++)
77  {
78  jvParams[jss::limit] = max_limit + delta;
79  auto const jrr = env.rpc(
80  "json",
81  "ledger_data",
82  boost::lexical_cast<std::string>(jvParams))[jss::result];
83  BEAST_EXPECT(checkArraySize(
84  jrr[jss::state],
85  (delta > 0 && !asAdmin) ? max_limit : max_limit + delta));
86  }
87  }
88 
89  void
91  {
92  using namespace test::jtx;
93  Env env{*this, envconfig(no_admin)};
94  Account const gw{"gateway"};
95  auto const USD = gw["USD"];
96  env.fund(XRP(100000), gw);
97 
98  int const num_accounts = 10;
99 
100  for (auto i = 0; i < num_accounts; i++)
101  {
102  Account const bob{std::string("bob") + std::to_string(i)};
103  env.fund(XRP(1000), bob);
104  }
105 
106  // with no limit specified, we should get all of our fund entries
107  // plus three more related to the gateway setup
108  Json::Value jvParams;
109  jvParams[jss::ledger_index] = "current";
110  jvParams[jss::binary] = true;
111  auto const jrr = env.rpc(
112  "json",
113  "ledger_data",
114  boost::lexical_cast<std::string>(jvParams))[jss::result];
115  BEAST_EXPECT(
116  jrr[jss::ledger_current_index].isIntegral() &&
117  jrr[jss::ledger_current_index].asInt() > 0);
118  BEAST_EXPECT(!jrr.isMember(jss::marker));
119  BEAST_EXPECT(checkArraySize(jrr[jss::state], num_accounts + 4));
120  }
121 
122  void
124  {
125  using namespace test::jtx;
126  Env env{*this};
127  Account const gw{"gateway"};
128  auto const USD = gw["USD"];
129  Account const bob{"bob"};
130 
131  env.fund(XRP(10000), gw, bob);
132  env.trust(USD(1000), bob);
133 
134  {
135  // bad limit
136  Json::Value jvParams;
137  jvParams[jss::limit] = "0"; // NOT an integer
138  auto const jrr = env.rpc(
139  "json",
140  "ledger_data",
141  boost::lexical_cast<std::string>(jvParams))[jss::result];
142  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
143  BEAST_EXPECT(jrr[jss::status] == "error");
144  BEAST_EXPECT(
145  jrr[jss::error_message] ==
146  "Invalid field 'limit', not integer.");
147  }
148 
149  {
150  // invalid marker
151  Json::Value jvParams;
152  jvParams[jss::marker] = "NOT_A_MARKER";
153  auto const jrr = env.rpc(
154  "json",
155  "ledger_data",
156  boost::lexical_cast<std::string>(jvParams))[jss::result];
157  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
158  BEAST_EXPECT(jrr[jss::status] == "error");
159  BEAST_EXPECT(
160  jrr[jss::error_message] ==
161  "Invalid field 'marker', not valid.");
162  }
163 
164  {
165  // invalid marker - not a string
166  Json::Value jvParams;
167  jvParams[jss::marker] = 1;
168  auto const jrr = env.rpc(
169  "json",
170  "ledger_data",
171  boost::lexical_cast<std::string>(jvParams))[jss::result];
172  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
173  BEAST_EXPECT(jrr[jss::status] == "error");
174  BEAST_EXPECT(
175  jrr[jss::error_message] ==
176  "Invalid field 'marker', not valid.");
177  }
178 
179  {
180  // ask for a bad ledger index
181  Json::Value jvParams;
182  jvParams[jss::ledger_index] = 10u;
183  auto const jrr = env.rpc(
184  "json",
185  "ledger_data",
186  boost::lexical_cast<std::string>(jvParams))[jss::result];
187  BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
188  BEAST_EXPECT(jrr[jss::status] == "error");
189  BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
190  }
191  }
192 
193  void
195  {
196  using namespace test::jtx;
197  Env env{*this, envconfig(no_admin)};
198  Account const gw{"gateway"};
199  auto const USD = gw["USD"];
200  env.fund(XRP(100000), gw);
201 
202  int const num_accounts = 20;
203 
204  for (auto i = 0; i < num_accounts; i++)
205  {
206  Account const bob{std::string("bob") + std::to_string(i)};
207  env.fund(XRP(1000), bob);
208  }
209 
210  // with no limit specified, we should get all of our fund entries
211  // plus three more related to the gateway setup
212  Json::Value jvParams;
213  jvParams[jss::ledger_index] = "current";
214  jvParams[jss::binary] = false;
215  auto jrr = env.rpc(
216  "json",
217  "ledger_data",
218  boost::lexical_cast<std::string>(jvParams))[jss::result];
219  auto const total_count = jrr[jss::state].size();
220 
221  // now make request with a limit and loop until we get all
222  jvParams[jss::limit] = 5;
223  jrr = env.rpc(
224  "json",
225  "ledger_data",
226  boost::lexical_cast<std::string>(jvParams))[jss::result];
227  BEAST_EXPECT(checkMarker(jrr));
228  auto running_total = jrr[jss::state].size();
229  while (jrr.isMember(jss::marker))
230  {
231  jvParams[jss::marker] = jrr[jss::marker];
232  jrr = env.rpc(
233  "json",
234  "ledger_data",
235  boost::lexical_cast<std::string>(jvParams))[jss::result];
236  running_total += jrr[jss::state].size();
237  }
238  BEAST_EXPECT(running_total == total_count);
239  }
240 
241  void
243  {
244  using namespace test::jtx;
245  Env env{*this};
246  env.fund(XRP(100000), "alice");
247  env.close();
248 
249  // Ledger header should be present in the first query
250  {
251  // Closed ledger with non binary form
252  Json::Value jvParams;
253  jvParams[jss::ledger_index] = "closed";
254  auto jrr = env.rpc(
255  "json",
256  "ledger_data",
257  boost::lexical_cast<std::string>(jvParams))[jss::result];
258  if (BEAST_EXPECT(jrr.isMember(jss::ledger)))
259  BEAST_EXPECT(
260  jrr[jss::ledger][jss::ledger_hash] ==
261  to_string(env.closed()->info().hash));
262  }
263  {
264  // Closed ledger with binary form
265  Json::Value jvParams;
266  jvParams[jss::ledger_index] = "closed";
267  jvParams[jss::binary] = true;
268  auto jrr = env.rpc(
269  "json",
270  "ledger_data",
271  boost::lexical_cast<std::string>(jvParams))[jss::result];
272  if (BEAST_EXPECT(jrr.isMember(jss::ledger)))
273  {
274  auto data =
275  strUnHex(jrr[jss::ledger][jss::ledger_data].asString());
276  if (BEAST_EXPECT(data))
277  {
278  Serializer s(data->data(), data->size());
279  std::uint32_t seq = 0;
280  BEAST_EXPECT(s.getInteger<std::uint32_t>(seq, 0));
281  BEAST_EXPECT(seq == 3);
282  }
283  }
284  }
285  {
286  // Current ledger with binary form
287  Json::Value jvParams;
288  jvParams[jss::binary] = true;
289  auto jrr = env.rpc(
290  "json",
291  "ledger_data",
292  boost::lexical_cast<std::string>(jvParams))[jss::result];
293  BEAST_EXPECT(jrr.isMember(jss::ledger));
294  BEAST_EXPECT(!jrr[jss::ledger].isMember(jss::ledger_data));
295  }
296  }
297 
298  void
300  {
301  // Put a bunch of different LedgerEntryTypes into a ledger
302  using namespace test::jtx;
303  using namespace std::chrono;
304  Env env{*this, envconfig(validator, "")};
305 
306  Account const gw{"gateway"};
307  auto const USD = gw["USD"];
308  env.fund(XRP(100000), gw);
309 
310  auto makeRequest = [&env](Json::StaticString const& type) {
311  Json::Value jvParams;
312  jvParams[jss::ledger_index] = "current";
313  jvParams[jss::type] = type;
314  return env.rpc(
315  "json",
316  "ledger_data",
317  boost::lexical_cast<std::string>(jvParams))[jss::result];
318  };
319 
320  // Assert that state is an empty array.
321  for (auto const& type :
322  {jss::amendments,
323  jss::check,
324  jss::directory,
325  jss::offer,
326  jss::signer_list,
327  jss::state,
328  jss::ticket,
329  jss::escrow,
330  jss::payment_channel,
331  jss::deposit_preauth})
332  {
333  auto const jrr = makeRequest(type);
334  BEAST_EXPECT(checkArraySize(jrr[jss::state], 0));
335  }
336 
337  int const num_accounts = 10;
338 
339  for (auto i = 0; i < num_accounts; i++)
340  {
341  Account const bob{std::string("bob") + std::to_string(i)};
342  env.fund(XRP(1000), bob);
343  }
344  env(offer(Account{"bob0"}, USD(100), XRP(100)));
345  env.trust(Account{"bob2"}["USD"](100), Account{"bob3"});
346 
347  auto majorities = getMajorityAmendments(*env.closed());
348  for (int i = 0; i <= 256; ++i)
349  {
350  env.close();
351  majorities = getMajorityAmendments(*env.closed());
352  if (!majorities.empty())
353  break;
354  }
355  env(signers(
356  Account{"bob0"}, 1, {{Account{"bob1"}, 1}, {Account{"bob2"}, 1}}));
357  env(ticket::create(env.master, 1));
358 
359  {
360  Json::Value jv;
361  jv[jss::TransactionType] = jss::EscrowCreate;
362  jv[jss::Flags] = tfUniversal;
363  jv[jss::Account] = Account{"bob5"}.human();
364  jv[jss::Destination] = Account{"bob6"}.human();
365  jv[jss::Amount] = XRP(50).value().getJson(JsonOptions::none);
366  jv[sfFinishAfter.fieldName] = NetClock::time_point{env.now() + 10s}
368  .count();
369  env(jv);
370  }
371 
372  {
373  Json::Value jv;
374  jv[jss::TransactionType] = jss::PaymentChannelCreate;
375  jv[jss::Flags] = tfUniversal;
376  jv[jss::Account] = Account{"bob6"}.human();
377  jv[jss::Destination] = Account{"bob7"}.human();
378  jv[jss::Amount] = XRP(100).value().getJson(JsonOptions::none);
379  jv[jss::SettleDelay] = NetClock::duration{10s}.count();
380  jv[sfPublicKey.fieldName] = strHex(Account{"bob6"}.pk().slice());
381  jv[sfCancelAfter.fieldName] = NetClock::time_point{env.now() + 300s}
383  .count();
384  env(jv);
385  }
386 
387  env(check::create("bob6", "bob7", XRP(100)));
388 
389  // bob9 DepositPreauths bob4 and bob8.
390  env(deposit::auth(Account{"bob9"}, Account{"bob4"}));
391  env(deposit::auth(Account{"bob9"}, Account{"bob8"}));
392  env.close();
393 
394  // Now fetch each type
395 
396  { // jvParams[jss::type] = "account";
397  auto const jrr = makeRequest(jss::account);
398  BEAST_EXPECT(checkArraySize(jrr[jss::state], 12));
399  for (auto const& j : jrr[jss::state])
400  BEAST_EXPECT(j["LedgerEntryType"] == jss::AccountRoot);
401  }
402 
403  { // jvParams[jss::type] = "amendments";
404  auto const jrr = makeRequest(jss::amendments);
405  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
406  for (auto const& j : jrr[jss::state])
407  BEAST_EXPECT(j["LedgerEntryType"] == jss::Amendments);
408  }
409 
410  { // jvParams[jss::type] = "check";
411  auto const jrr = makeRequest(jss::check);
412  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
413  for (auto const& j : jrr[jss::state])
414  BEAST_EXPECT(j["LedgerEntryType"] == jss::Check);
415  }
416 
417  { // jvParams[jss::type] = "directory";
418  auto const jrr = makeRequest(jss::directory);
419  BEAST_EXPECT(checkArraySize(jrr[jss::state], 9));
420  for (auto const& j : jrr[jss::state])
421  BEAST_EXPECT(j["LedgerEntryType"] == jss::DirectoryNode);
422  }
423 
424  { // jvParams[jss::type] = "fee";
425  auto const jrr = makeRequest(jss::fee);
426  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
427  for (auto const& j : jrr[jss::state])
428  BEAST_EXPECT(j["LedgerEntryType"] == jss::FeeSettings);
429  }
430 
431  { // jvParams[jss::type] = "hashes";
432  auto const jrr = makeRequest(jss::hashes);
433  BEAST_EXPECT(checkArraySize(jrr[jss::state], 2));
434  for (auto const& j : jrr[jss::state])
435  BEAST_EXPECT(j["LedgerEntryType"] == jss::LedgerHashes);
436  }
437 
438  { // jvParams[jss::type] = "offer";
439  auto const jrr = makeRequest(jss::offer);
440  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
441  for (auto const& j : jrr[jss::state])
442  BEAST_EXPECT(j["LedgerEntryType"] == jss::Offer);
443  }
444 
445  { // jvParams[jss::type] = "signer_list";
446  auto const jrr = makeRequest(jss::signer_list);
447  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
448  for (auto const& j : jrr[jss::state])
449  BEAST_EXPECT(j["LedgerEntryType"] == jss::SignerList);
450  }
451 
452  { // jvParams[jss::type] = "state";
453  auto const jrr = makeRequest(jss::state);
454  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
455  for (auto const& j : jrr[jss::state])
456  BEAST_EXPECT(j["LedgerEntryType"] == jss::RippleState);
457  }
458 
459  { // jvParams[jss::type] = "ticket";
460  auto const jrr = makeRequest(jss::ticket);
461  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
462  for (auto const& j : jrr[jss::state])
463  BEAST_EXPECT(j["LedgerEntryType"] == jss::Ticket);
464  }
465 
466  { // jvParams[jss::type] = "escrow";
467  auto const jrr = makeRequest(jss::escrow);
468  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
469  for (auto const& j : jrr[jss::state])
470  BEAST_EXPECT(j["LedgerEntryType"] == jss::Escrow);
471  }
472 
473  { // jvParams[jss::type] = "payment_channel";
474  auto const jrr = makeRequest(jss::payment_channel);
475  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
476  for (auto const& j : jrr[jss::state])
477  BEAST_EXPECT(j["LedgerEntryType"] == jss::PayChannel);
478  }
479 
480  { // jvParams[jss::type] = "deposit_preauth";
481  auto const jrr = makeRequest(jss::deposit_preauth);
482  BEAST_EXPECT(checkArraySize(jrr[jss::state], 2));
483  for (auto const& j : jrr[jss::state])
484  BEAST_EXPECT(j["LedgerEntryType"] == jss::DepositPreauth);
485  }
486 
487  { // jvParams[jss::type] = "misspelling";
488  Json::Value jvParams;
489  jvParams[jss::ledger_index] = "current";
490  jvParams[jss::type] = "misspelling";
491  auto const jrr = env.rpc(
492  "json",
493  "ledger_data",
494  boost::lexical_cast<std::string>(jvParams))[jss::result];
495  BEAST_EXPECT(jrr.isMember("error"));
496  BEAST_EXPECT(jrr["error"] == "invalidParams");
497  BEAST_EXPECT(jrr["error_message"] == "Invalid field 'type'.");
498  }
499  }
500 
501  void
502  run() override
503  {
507  testBadInput();
510  testLedgerType();
511  }
512 };
513 
514 BEAST_DEFINE_TESTSUITE_PRIO(LedgerData, app, ripple, 1);
515 
516 } // namespace ripple
ripple::getMajorityAmendments
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition: View.cpp:638
std::string
STL class.
Json::Value::isString
bool isString() const
Definition: json_value.cpp:1009
ripple::LedgerData_test::testLedgerHeader
void testLedgerHeader()
Definition: LedgerData_test.cpp:242
std::string::size
T size(T... args)
ripple::SField::fieldName
const std::string fieldName
Definition: SField.h:159
std::chrono::duration
ripple::Serializer::getInteger
bool getInteger(Integer &number, int offset)
Definition: Serializer.h:128
ripple::LedgerData_test
Definition: LedgerData_test.cpp:26
std::chrono::time_point::time_since_epoch
T time_since_epoch(T... args)
ripple::LedgerData_test::checkMarker
static bool checkMarker(Json::Value const &val)
Definition: LedgerData_test.cpp:31
ripple::JsonOptions::none
@ none
ripple::BEAST_DEFINE_TESTSUITE_PRIO
BEAST_DEFINE_TESTSUITE_PRIO(NFToken0, tx, ripple, 2)
std::to_string
T to_string(T... args)
std::chrono::time_point
Json::Value::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
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::LedgerData_test::testCurrentLedgerBinary
void testCurrentLedgerBinary()
Definition: LedgerData_test.cpp:90
std::uint32_t
ripple::Serializer
Definition: Serializer.h:40
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
Json::StaticString
Lightweight wrapper to tag static string.
Definition: json_value.h:60
ripple::LedgerData_test::run
void run() override
Definition: LedgerData_test.cpp:502
ripple::LedgerData_test::testBadInput
void testBadInput()
Definition: LedgerData_test.cpp:123
std::chrono::duration::count
T count(T... args)
ripple::sfCancelAfter
const SF_UINT32 sfCancelAfter
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::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
ripple::LedgerData_test::testLedgerType
void testLedgerType()
Definition: LedgerData_test.cpp:299
ripple::LedgerData_test::testMarkerFollow
void testMarkerFollow()
Definition: LedgerData_test.cpp:194
ripple::tfUniversal
constexpr std::uint32_t tfUniversal
Definition: TxFlags.h:59
ripple::makeRequest
auto makeRequest(bool crawlPublic, bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled) -> request_type
Make outbound http request.
Definition: Handshake.cpp:367
ripple::strUnHex
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
Definition: StringUtilities.h:52
ripple::LedgerData_test::testCurrentLedgerToLimits
void testCurrentLedgerToLimits(bool asAdmin)
Definition: LedgerData_test.cpp:38
ripple::sfPublicKey
const SF_VL sfPublicKey
Json::Value
Represents a JSON value.
Definition: json_value.h:145
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469
std::chrono