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