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