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