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