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 + 3));
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  int const num_accounts = 10;
318 
319  for (auto i = 0; i < num_accounts; i++)
320  {
321  Account const bob{std::string("bob") + std::to_string(i)};
322  env.fund(XRP(1000), bob);
323  }
324  env(offer(Account{"bob0"}, USD(100), XRP(100)));
325  env.trust(Account{"bob2"}["USD"](100), Account{"bob3"});
326 
327  auto majorities = getMajorityAmendments(*env.closed());
328  for (int i = 0; i <= 256; ++i)
329  {
330  env.close();
331  majorities = getMajorityAmendments(*env.closed());
332  if (!majorities.empty())
333  break;
334  }
335  env(signers(
336  Account{"bob0"}, 1, {{Account{"bob1"}, 1}, {Account{"bob2"}, 1}}));
337  env(ticket::create(env.master, 1));
338 
339  {
340  Json::Value jv;
341  jv[jss::TransactionType] = jss::EscrowCreate;
342  jv[jss::Flags] = tfUniversal;
343  jv[jss::Account] = Account{"bob5"}.human();
344  jv[jss::Destination] = Account{"bob6"}.human();
345  jv[jss::Amount] = XRP(50).value().getJson(JsonOptions::none);
346  jv[sfFinishAfter.fieldName] = NetClock::time_point{env.now() + 10s}
348  .count();
349  env(jv);
350  }
351 
352  {
353  Json::Value jv;
354  jv[jss::TransactionType] = jss::PaymentChannelCreate;
355  jv[jss::Flags] = tfUniversal;
356  jv[jss::Account] = Account{"bob6"}.human();
357  jv[jss::Destination] = Account{"bob7"}.human();
358  jv[jss::Amount] = XRP(100).value().getJson(JsonOptions::none);
359  jv[jss::SettleDelay] = NetClock::duration{10s}.count();
360  jv[sfPublicKey.fieldName] = strHex(Account{"bob6"}.pk().slice());
361  jv[sfCancelAfter.fieldName] = NetClock::time_point{env.now() + 300s}
363  .count();
364  env(jv);
365  }
366 
367  env(check::create("bob6", "bob7", XRP(100)));
368 
369  // bob9 DepositPreauths bob4 and bob8.
370  env(deposit::auth(Account{"bob9"}, Account{"bob4"}));
371  env(deposit::auth(Account{"bob9"}, Account{"bob8"}));
372  env.close();
373 
374  // Now fetch each type
375  auto makeRequest = [&env](Json::StaticString t) {
376  Json::Value jvParams;
377  jvParams[jss::ledger_index] = "current";
378  jvParams[jss::type] = t;
379  return env.rpc(
380  "json",
381  "ledger_data",
382  boost::lexical_cast<std::string>(jvParams))[jss::result];
383  };
384 
385  { // jvParams[jss::type] = "account";
386  auto const jrr = makeRequest(jss::account);
387  BEAST_EXPECT(checkArraySize(jrr[jss::state], 12));
388  for (auto const& j : jrr[jss::state])
389  BEAST_EXPECT(j["LedgerEntryType"] == jss::AccountRoot);
390  }
391 
392  { // jvParams[jss::type] = "amendments";
393  auto const jrr = makeRequest(jss::amendments);
394  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
395  for (auto const& j : jrr[jss::state])
396  BEAST_EXPECT(j["LedgerEntryType"] == jss::Amendments);
397  }
398 
399  { // jvParams[jss::type] = "check";
400  auto const jrr = makeRequest(jss::check);
401  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
402  for (auto const& j : jrr[jss::state])
403  BEAST_EXPECT(j["LedgerEntryType"] == jss::Check);
404  }
405 
406  { // jvParams[jss::type] = "directory";
407  auto const jrr = makeRequest(jss::directory);
408  BEAST_EXPECT(checkArraySize(jrr[jss::state], 9));
409  for (auto const& j : jrr[jss::state])
410  BEAST_EXPECT(j["LedgerEntryType"] == jss::DirectoryNode);
411  }
412 
413  { // jvParams[jss::type] = "fee";
414  auto const jrr = makeRequest(jss::fee);
415  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
416  for (auto const& j : jrr[jss::state])
417  BEAST_EXPECT(j["LedgerEntryType"] == jss::FeeSettings);
418  }
419 
420  { // jvParams[jss::type] = "hashes";
421  auto const jrr = makeRequest(jss::hashes);
422  BEAST_EXPECT(checkArraySize(jrr[jss::state], 2));
423  for (auto const& j : jrr[jss::state])
424  BEAST_EXPECT(j["LedgerEntryType"] == jss::LedgerHashes);
425  }
426 
427  { // jvParams[jss::type] = "offer";
428  auto const jrr = makeRequest(jss::offer);
429  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
430  for (auto const& j : jrr[jss::state])
431  BEAST_EXPECT(j["LedgerEntryType"] == jss::Offer);
432  }
433 
434  { // jvParams[jss::type] = "signer_list";
435  auto const jrr = makeRequest(jss::signer_list);
436  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
437  for (auto const& j : jrr[jss::state])
438  BEAST_EXPECT(j["LedgerEntryType"] == jss::SignerList);
439  }
440 
441  { // jvParams[jss::type] = "state";
442  auto const jrr = makeRequest(jss::state);
443  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
444  for (auto const& j : jrr[jss::state])
445  BEAST_EXPECT(j["LedgerEntryType"] == jss::RippleState);
446  }
447 
448  { // jvParams[jss::type] = "ticket";
449  auto const jrr = makeRequest(jss::ticket);
450  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
451  for (auto const& j : jrr[jss::state])
452  BEAST_EXPECT(j["LedgerEntryType"] == jss::Ticket);
453  }
454 
455  { // jvParams[jss::type] = "escrow";
456  auto const jrr = makeRequest(jss::escrow);
457  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
458  for (auto const& j : jrr[jss::state])
459  BEAST_EXPECT(j["LedgerEntryType"] == jss::Escrow);
460  }
461 
462  { // jvParams[jss::type] = "payment_channel";
463  auto const jrr = makeRequest(jss::payment_channel);
464  BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
465  for (auto const& j : jrr[jss::state])
466  BEAST_EXPECT(j["LedgerEntryType"] == jss::PayChannel);
467  }
468 
469  { // jvParams[jss::type] = "deposit_preauth";
470  auto const jrr = makeRequest(jss::deposit_preauth);
471  BEAST_EXPECT(checkArraySize(jrr[jss::state], 2));
472  for (auto const& j : jrr[jss::state])
473  BEAST_EXPECT(j["LedgerEntryType"] == jss::DepositPreauth);
474  }
475 
476  { // jvParams[jss::type] = "misspelling";
477  Json::Value jvParams;
478  jvParams[jss::ledger_index] = "current";
479  jvParams[jss::type] = "misspelling";
480  auto const jrr = env.rpc(
481  "json",
482  "ledger_data",
483  boost::lexical_cast<std::string>(jvParams))[jss::result];
484  BEAST_EXPECT(jrr.isMember("error"));
485  BEAST_EXPECT(jrr["error"] == "invalidParams");
486  BEAST_EXPECT(jrr["error_message"] == "Invalid field 'type'.");
487  }
488  }
489 
490  void
491  run() override
492  {
496  testBadInput();
499  testLedgerType();
500  }
501 };
502 
503 BEAST_DEFINE_TESTSUITE_PRIO(LedgerData, app, ripple, 1);
504 
505 } // 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:491
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