rippled
Loading...
Searching...
No Matches
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 <test/jtx.h>
21
22#include <xrpl/basics/StringUtilities.h>
23#include <xrpl/protocol/jss.h>
24
25namespace ripple {
26
28{
29public:
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
305 // Make sure fixInnerObjTemplate2 doesn't break amendments.
306 for (FeatureBitset const& features :
307 {supported_amendments() - fixInnerObjTemplate2,
308 supported_amendments() | fixInnerObjTemplate2})
309 {
310 using namespace std::chrono;
311 Env env{*this, envconfig(validator, ""), features};
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
363 env(signers(
364 Account{"bob0"},
365 1,
366 {{Account{"bob1"}, 1}, {Account{"bob2"}, 1}}));
367 env(ticket::create(env.master, 1));
368
369 {
370 Json::Value jv;
371 jv[jss::TransactionType] = jss::EscrowCreate;
372 jv[jss::Flags] = tfUniversal;
373 jv[jss::Account] = Account{"bob5"}.human();
374 jv[jss::Destination] = Account{"bob6"}.human();
375 jv[jss::Amount] = XRP(50).value().getJson(JsonOptions::none);
376 jv[sfFinishAfter.fieldName] =
377 NetClock::time_point{env.now() + 10s}
379 .count();
380 env(jv);
381 }
382
383 {
384 Json::Value jv;
385 jv[jss::TransactionType] = jss::PaymentChannelCreate;
386 jv[jss::Flags] = tfUniversal;
387 jv[jss::Account] = Account{"bob6"}.human();
388 jv[jss::Destination] = Account{"bob7"}.human();
389 jv[jss::Amount] = XRP(100).value().getJson(JsonOptions::none);
390 jv[jss::SettleDelay] = NetClock::duration{10s}.count();
391 jv[sfPublicKey.fieldName] =
392 strHex(Account{"bob6"}.pk().slice());
393 jv[sfCancelAfter.fieldName] =
394 NetClock::time_point{env.now() + 300s}
396 .count();
397 env(jv);
398 }
399
400 env(check::create("bob6", "bob7", XRP(100)));
401
402 // bob9 DepositPreauths bob4 and bob8.
403 env(deposit::auth(Account{"bob9"}, Account{"bob4"}));
404 env(deposit::auth(Account{"bob9"}, Account{"bob8"}));
405 env.close();
406
407 // Now fetch each type
408
409 { // jvParams[jss::type] = "account";
410 auto const jrr = makeRequest(jss::account);
411 BEAST_EXPECT(checkArraySize(jrr[jss::state], 12));
412 for (auto const& j : jrr[jss::state])
413 BEAST_EXPECT(j["LedgerEntryType"] == jss::AccountRoot);
414 }
415
416 { // jvParams[jss::type] = "amendments";
417 auto const jrr = makeRequest(jss::amendments);
418 BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
419 for (auto const& j : jrr[jss::state])
420 BEAST_EXPECT(j["LedgerEntryType"] == jss::Amendments);
421 }
422
423 { // jvParams[jss::type] = "check";
424 auto const jrr = makeRequest(jss::check);
425 BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
426 for (auto const& j : jrr[jss::state])
427 BEAST_EXPECT(j["LedgerEntryType"] == jss::Check);
428 }
429
430 { // jvParams[jss::type] = "directory";
431 auto const jrr = makeRequest(jss::directory);
432 BEAST_EXPECT(checkArraySize(jrr[jss::state], 9));
433 for (auto const& j : jrr[jss::state])
434 BEAST_EXPECT(j["LedgerEntryType"] == jss::DirectoryNode);
435 }
436
437 { // jvParams[jss::type] = "fee";
438 auto const jrr = makeRequest(jss::fee);
439 BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
440 for (auto const& j : jrr[jss::state])
441 BEAST_EXPECT(j["LedgerEntryType"] == jss::FeeSettings);
442 }
443
444 { // jvParams[jss::type] = "hashes";
445 auto const jrr = makeRequest(jss::hashes);
446 BEAST_EXPECT(checkArraySize(jrr[jss::state], 2));
447 for (auto const& j : jrr[jss::state])
448 BEAST_EXPECT(j["LedgerEntryType"] == jss::LedgerHashes);
449 }
450
451 { // jvParams[jss::type] = "offer";
452 auto const jrr = makeRequest(jss::offer);
453 BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
454 for (auto const& j : jrr[jss::state])
455 BEAST_EXPECT(j["LedgerEntryType"] == jss::Offer);
456 }
457
458 { // jvParams[jss::type] = "signer_list";
459 auto const jrr = makeRequest(jss::signer_list);
460 BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
461 for (auto const& j : jrr[jss::state])
462 BEAST_EXPECT(j["LedgerEntryType"] == jss::SignerList);
463 }
464
465 { // jvParams[jss::type] = "state";
466 auto const jrr = makeRequest(jss::state);
467 BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
468 for (auto const& j : jrr[jss::state])
469 BEAST_EXPECT(j["LedgerEntryType"] == jss::RippleState);
470 }
471
472 { // jvParams[jss::type] = "ticket";
473 auto const jrr = makeRequest(jss::ticket);
474 BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
475 for (auto const& j : jrr[jss::state])
476 BEAST_EXPECT(j["LedgerEntryType"] == jss::Ticket);
477 }
478
479 { // jvParams[jss::type] = "escrow";
480 auto const jrr = makeRequest(jss::escrow);
481 BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
482 for (auto const& j : jrr[jss::state])
483 BEAST_EXPECT(j["LedgerEntryType"] == jss::Escrow);
484 }
485
486 { // jvParams[jss::type] = "payment_channel";
487 auto const jrr = makeRequest(jss::payment_channel);
488 BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
489 for (auto const& j : jrr[jss::state])
490 BEAST_EXPECT(j["LedgerEntryType"] == jss::PayChannel);
491 }
492
493 { // jvParams[jss::type] = "deposit_preauth";
494 auto const jrr = makeRequest(jss::deposit_preauth);
495 BEAST_EXPECT(checkArraySize(jrr[jss::state], 2));
496 for (auto const& j : jrr[jss::state])
497 BEAST_EXPECT(j["LedgerEntryType"] == jss::DepositPreauth);
498 }
499
500 { // jvParams[jss::type] = "misspelling";
501 Json::Value jvParams;
502 jvParams[jss::ledger_index] = "current";
503 jvParams[jss::type] = "misspelling";
504 auto const jrr = env.rpc(
505 "json",
506 "ledger_data",
507 boost::lexical_cast<std::string>(jvParams))[jss::result];
508 BEAST_EXPECT(jrr.isMember("error"));
509 BEAST_EXPECT(jrr["error"] == "invalidParams");
510 BEAST_EXPECT(jrr["error_message"] == "Invalid field 'type'.");
511 }
512 }
513 }
514
515 void
516 run() override
517 {
521 testBadInput();
525 }
526};
527
528BEAST_DEFINE_TESTSUITE_PRIO(LedgerData, app, ripple, 1);
529
530} // namespace ripple
Lightweight wrapper to tag static string.
Definition: json_value.h:62
Represents a JSON value.
Definition: json_value.h:148
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:712
bool isString() const
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:475
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:949
A testsuite class.
Definition: suite.h:55
void testCurrentLedgerToLimits(bool asAdmin)
static bool checkMarker(Json::Value const &val)
void run() override
Runs the suite.
bool getInteger(Integer &number, int offset)
Definition: Serializer.h:162
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
constexpr std::uint32_t tfUniversal
Definition: TxFlags.h:61
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition: View.cpp:813
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
auto makeRequest(bool crawlPublic, bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled) -> request_type
Make outbound http request.
Definition: Handshake.cpp:365
T size(T... args)
T time_since_epoch(T... args)
T to_string(T... args)