rippled
Loading...
Searching...
No Matches
LedgerRPC_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-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 <test/jtx/Oracle.h>
22#include <test/jtx/attester.h>
23#include <test/jtx/multisign.h>
24#include <test/jtx/xchain_bridge.h>
25
26#include <xrpld/app/misc/TxQ.h>
27
28#include <xrpl/beast/unit_test.h>
29#include <xrpl/json/json_value.h>
30#include <xrpl/protocol/ErrorCodes.h>
31#include <xrpl/protocol/jss.h>
32
33namespace ripple {
34
35namespace test {
36
38{
39 void
41 Json::Value const& jv,
42 std::string const& err,
43 std::string const& msg)
44 {
45 if (BEAST_EXPECT(jv.isMember(jss::status)))
46 BEAST_EXPECT(jv[jss::status] == "error");
47 if (BEAST_EXPECT(jv.isMember(jss::error)))
48 BEAST_EXPECT(jv[jss::error] == err);
49 if (msg.empty())
50 {
51 BEAST_EXPECT(
52 jv[jss::error_message] == Json::nullValue ||
53 jv[jss::error_message] == "");
54 }
55 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
56 BEAST_EXPECT(jv[jss::error_message] == msg);
57 }
58
59 // Corrupt a valid address by replacing the 10th character with '!'.
60 // '!' is not part of the ripple alphabet.
63 {
64 std::string ret = std::move(good);
65 ret.replace(10, 1, 1, '!');
66 return ret;
67 }
68
69 void
71 {
72 testcase("Basic Request");
73 using namespace test::jtx;
74
75 Env env{*this};
76
77 env.close();
78 BEAST_EXPECT(env.current()->info().seq == 4);
79
80 {
81 Json::Value jvParams;
82 // can be either numeric or quoted numeric
83 jvParams[jss::ledger_index] = 1;
84 auto const jrr =
85 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
86 BEAST_EXPECT(jrr[jss::ledger][jss::closed] == true);
87 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "1");
88 }
89
90 {
91 Json::Value jvParams;
92 jvParams[jss::ledger_index] = "1";
93 auto const jrr =
94 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
95 BEAST_EXPECT(jrr[jss::ledger][jss::closed] == true);
96 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "1");
97 }
98
99 {
100 // using current identifier
101 auto const jrr = env.rpc("ledger", "current")[jss::result];
102 BEAST_EXPECT(jrr[jss::ledger][jss::closed] == false);
103 BEAST_EXPECT(
104 jrr[jss::ledger][jss::ledger_index] ==
105 std::to_string(env.current()->info().seq));
106 BEAST_EXPECT(
107 jrr[jss::ledger_current_index] == env.current()->info().seq);
108 }
109 }
110
111 void
113 {
114 testcase("Bad Input");
115 using namespace test::jtx;
116 Env env{*this};
117 Account const gw{"gateway"};
118 auto const USD = gw["USD"];
119 Account const bob{"bob"};
120
121 env.fund(XRP(10000), gw, bob);
122 env.close();
123 env.trust(USD(1000), bob);
124 env.close();
125
126 {
127 // ask for an arbitrary string - index
128 Json::Value jvParams;
129 jvParams[jss::ledger_index] = "potato";
130 auto const jrr =
131 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
132 checkErrorValue(jrr, "invalidParams", "ledgerIndexMalformed");
133 }
134
135 {
136 // ask for a negative index
137 Json::Value jvParams;
138 jvParams[jss::ledger_index] = -1;
139 auto const jrr =
140 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
141 checkErrorValue(jrr, "invalidParams", "ledgerIndexMalformed");
142 }
143
144 {
145 // ask for a bad ledger index
146 Json::Value jvParams;
147 jvParams[jss::ledger_index] = 10u;
148 auto const jrr =
149 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
150 checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
151 }
152
153 {
154 // unrecognized string arg -- error
155 auto const jrr = env.rpc("ledger", "arbitrary_text")[jss::result];
156 checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
157 }
158
159 {
160 // Request queue for closed ledger
161 Json::Value jvParams;
162 jvParams[jss::ledger_index] = "validated";
163 jvParams[jss::queue] = true;
164 auto const jrr =
165 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
166 checkErrorValue(jrr, "invalidParams", "Invalid parameters.");
167 }
168
169 {
170 // Request a ledger with a very large (double) sequence.
171 auto const ret =
172 env.rpc("json", "ledger", "{ \"ledger_index\" : 2e15 }");
173 BEAST_EXPECT(RPC::contains_error(ret));
174 BEAST_EXPECT(ret[jss::error_message] == "Invalid parameters.");
175 }
176
177 {
178 // Request a ledger with very large (integer) sequence.
179 auto const ret = env.rpc(
180 "json", "ledger", "{ \"ledger_index\" : 1000000000000000 }");
181 checkErrorValue(ret, "invalidParams", "Invalid parameters.");
182 }
183 }
184
185 void
187 {
188 testcase("ledger_current Request");
189 using namespace test::jtx;
190
191 Env env{*this};
192
193 env.close();
194 BEAST_EXPECT(env.current()->info().seq == 4);
195
196 {
197 auto const jrr = env.rpc("ledger_current")[jss::result];
198 BEAST_EXPECT(
199 jrr[jss::ledger_current_index] == env.current()->info().seq);
200 }
201 }
202
203 void
205 {
206 testcase("Ledger Request, Full Option");
207 using namespace test::jtx;
208
209 Env env{*this};
210
211 env.close();
212
213 Json::Value jvParams;
214 jvParams[jss::ledger_index] = 3u;
215 jvParams[jss::full] = true;
216 auto const jrr =
217 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
218 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
219 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
220 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 3u);
221 }
222
223 void
225 {
226 testcase("Ledger Request, Full Option Without Admin");
227 using namespace test::jtx;
228
229 Env env{*this, envconfig(no_admin)};
230
231 // env.close();
232
233 Json::Value jvParams;
234 jvParams[jss::ledger_index] = 1u;
235 jvParams[jss::full] = true;
236 auto const jrr =
237 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
239 jrr, "noPermission", "You don't have permission for this command.");
240 }
241
242 void
244 {
245 testcase("Ledger Request, Accounts Option");
246 using namespace test::jtx;
247
248 Env env{*this};
249
250 env.close();
251
252 Json::Value jvParams;
253 jvParams[jss::ledger_index] = 3u;
254 jvParams[jss::accounts] = true;
255 auto const jrr =
256 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
257 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
258 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
259 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 3u);
260 }
261
266 void
268 {
269 testcase("Lookup ledger");
270 using namespace test::jtx;
271
272 auto cfg = envconfig();
273 cfg->FEES.reference_fee = 10;
274 Env env{
275 *this, std::move(cfg), FeatureBitset{}}; // hashes requested below
276 // assume no amendments
277 env.fund(XRP(10000), "alice");
278 env.close();
279 env.fund(XRP(10000), "bob");
280 env.close();
281 env.fund(XRP(10000), "jim");
282 env.close();
283 env.fund(XRP(10000), "jill");
284
285 {
286 // access via the legacy ledger field, keyword index values
287 Json::Value jvParams;
288 jvParams[jss::ledger] = "closed";
289 auto jrr = env.rpc(
290 "json",
291 "ledger",
292 boost::lexical_cast<std::string>(jvParams))[jss::result];
293 BEAST_EXPECT(jrr.isMember(jss::ledger));
294 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
295 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
296
297 jvParams[jss::ledger] = "validated";
298 jrr = env.rpc(
299 "json",
300 "ledger",
301 boost::lexical_cast<std::string>(jvParams))[jss::result];
302 BEAST_EXPECT(jrr.isMember(jss::ledger));
303 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
304 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
305
306 jvParams[jss::ledger] = "current";
307 jrr = env.rpc(
308 "json",
309 "ledger",
310 boost::lexical_cast<std::string>(jvParams))[jss::result];
311 BEAST_EXPECT(jrr.isMember(jss::ledger));
312 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
313
314 // ask for a bad ledger keyword
315 jvParams[jss::ledger] = "invalid";
316 jrr = env.rpc(
317 "json",
318 "ledger",
319 boost::lexical_cast<std::string>(jvParams))[jss::result];
320 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
321 BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
322
323 // numeric index
324 jvParams[jss::ledger] = 4;
325 jrr = env.rpc(
326 "json",
327 "ledger",
328 boost::lexical_cast<std::string>(jvParams))[jss::result];
329 BEAST_EXPECT(jrr.isMember(jss::ledger));
330 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
331 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "4");
332
333 // numeric index - out of range
334 jvParams[jss::ledger] = 20;
335 jrr = env.rpc(
336 "json",
337 "ledger",
338 boost::lexical_cast<std::string>(jvParams))[jss::result];
339 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
340 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
341 }
342
343 {
344 std::string const hash3{
345 "E86DE7F3D7A4D9CE17EF7C8BA08A8F4D"
346 "8F643B9552F0D895A31CDA78F541DE4E"};
347 // access via the ledger_hash field
348 Json::Value jvParams;
349 jvParams[jss::ledger_hash] = hash3;
350 auto jrr = env.rpc(
351 "json",
352 "ledger",
353 boost::lexical_cast<std::string>(jvParams))[jss::result];
354 BEAST_EXPECT(jrr.isMember(jss::ledger));
355 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
356 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "3");
357
358 // extra leading hex chars in hash are not allowed
359 jvParams[jss::ledger_hash] = "DEADBEEF" + hash3;
360 jrr = env.rpc(
361 "json",
362 "ledger",
363 boost::lexical_cast<std::string>(jvParams))[jss::result];
364 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
365 BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
366
367 // request with non-string ledger_hash
368 jvParams[jss::ledger_hash] = 2;
369 jrr = env.rpc(
370 "json",
371 "ledger",
372 boost::lexical_cast<std::string>(jvParams))[jss::result];
373 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
374 BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashNotString");
375
376 // malformed (non hex) hash
377 jvParams[jss::ledger_hash] =
378 "2E81FC6EC0DD943197EGC7E3FBE9AE30"
379 "7F2775F2F7485BB37307984C3C0F2340";
380 jrr = env.rpc(
381 "json",
382 "ledger",
383 boost::lexical_cast<std::string>(jvParams))[jss::result];
384 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
385 BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
386
387 // properly formed, but just doesn't exist
388 jvParams[jss::ledger_hash] =
389 "8C3EEDB3124D92E49E75D81A8826A2E6"
390 "5A75FD71FC3FD6F36FEB803C5F1D812D";
391 jrr = env.rpc(
392 "json",
393 "ledger",
394 boost::lexical_cast<std::string>(jvParams))[jss::result];
395 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
396 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
397 }
398
399 {
400 // access via the ledger_index field, keyword index values
401 Json::Value jvParams;
402 jvParams[jss::ledger_index] = "closed";
403 auto jrr = env.rpc(
404 "json",
405 "ledger",
406 boost::lexical_cast<std::string>(jvParams))[jss::result];
407 BEAST_EXPECT(jrr.isMember(jss::ledger));
408 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
409 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
410 BEAST_EXPECT(jrr.isMember(jss::ledger_index));
411
412 jvParams[jss::ledger_index] = "validated";
413 jrr = env.rpc(
414 "json",
415 "ledger",
416 boost::lexical_cast<std::string>(jvParams))[jss::result];
417 BEAST_EXPECT(jrr.isMember(jss::ledger));
418 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
419 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
420
421 jvParams[jss::ledger_index] = "current";
422 jrr = env.rpc(
423 "json",
424 "ledger",
425 boost::lexical_cast<std::string>(jvParams))[jss::result];
426 BEAST_EXPECT(jrr.isMember(jss::ledger));
427 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
428 BEAST_EXPECT(jrr.isMember(jss::ledger_current_index));
429
430 // ask for a bad ledger keyword
431 jvParams[jss::ledger_index] = "invalid";
432 jrr = env.rpc(
433 "json",
434 "ledger",
435 boost::lexical_cast<std::string>(jvParams))[jss::result];
436 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
437 BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
438
439 // numeric index
440 for (auto i : {1, 2, 3, 4, 5, 6})
441 {
442 jvParams[jss::ledger_index] = i;
443 jrr = env.rpc(
444 "json",
445 "ledger",
446 boost::lexical_cast<std::string>(jvParams))[jss::result];
447 BEAST_EXPECT(jrr.isMember(jss::ledger));
448 if (i < 6)
449 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
450 BEAST_EXPECT(
451 jrr[jss::ledger][jss::ledger_index] == std::to_string(i));
452 }
453
454 // numeric index - out of range
455 jvParams[jss::ledger_index] = 7;
456 jrr = env.rpc(
457 "json",
458 "ledger",
459 boost::lexical_cast<std::string>(jvParams))[jss::result];
460 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
461 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
462 }
463 }
464
465 void
467 {
468 testcase("Ledger with queueing disabled");
469 using namespace test::jtx;
470 Env env{*this};
471
472 Json::Value jv;
473 jv[jss::ledger_index] = "current";
474 jv[jss::queue] = true;
475 jv[jss::expand] = true;
476
477 auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
478 BEAST_EXPECT(!jrr.isMember(jss::queue_data));
479 }
480
481 void
483 {
484 testcase("Ledger with Queued Transactions");
485 using namespace test::jtx;
486 auto cfg = envconfig([](std::unique_ptr<Config> cfg) {
487 auto& section = cfg->section("transaction_queue");
488 section.set("minimum_txn_in_ledger_standalone", "3");
489 section.set("normal_consensus_increase_percent", "0");
490 return cfg;
491 });
492
493 cfg->FEES.reference_fee = 10;
494 Env env(*this, std::move(cfg));
495
496 Json::Value jv;
497 jv[jss::ledger_index] = "current";
498 jv[jss::queue] = true;
499 jv[jss::expand] = true;
500
501 Account const alice{"alice"};
502 Account const bob{"bob"};
503 Account const charlie{"charlie"};
504 Account const daria{"daria"};
505 env.fund(XRP(10000), alice);
506 env.fund(XRP(10000), bob);
507 env.close();
508 env.fund(XRP(10000), charlie);
509 env.fund(XRP(10000), daria);
510 env.close();
511
512 auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
513 BEAST_EXPECT(!jrr.isMember(jss::queue_data));
514
515 // Fill the open ledger
516 for (;;)
517 {
518 auto metrics = env.app().getTxQ().getMetrics(*env.current());
519 if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
520 break;
521 env(noop(alice));
522 }
523
524 BEAST_EXPECT(env.current()->info().seq == 5);
525 // Put some txs in the queue
526 // Alice
527 auto aliceSeq = env.seq(alice);
528 env(pay(alice, "george", XRP(1000)),
529 json(R"({"LastLedgerSequence":7})"),
530 ter(terQUEUED));
531 env(offer(alice, XRP(50000), alice["USD"](5000)),
532 seq(aliceSeq + 1),
533 ter(terQUEUED));
534 env(noop(alice), seq(aliceSeq + 2), ter(terQUEUED));
535 // Bob
536 auto batch = [&env](Account a) {
537 auto aSeq = env.seq(a);
538 // Enough fee to get in front of alice in the queue
539 for (int i = 0; i < 10; ++i)
540 {
541 env(noop(a), fee(1000 + i), seq(aSeq + i), ter(terQUEUED));
542 }
543 };
544 batch(bob);
545 // Charlie
546 batch(charlie);
547 // Daria
548 batch(daria);
549
550 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
551 BEAST_EXPECT(jrr[jss::queue_data].size() == 33);
552
553 // Close enough ledgers so that alice's first tx expires.
554 env.close();
555 env.close();
556 env.close();
557 BEAST_EXPECT(env.current()->info().seq == 8);
558
559 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
560 BEAST_EXPECT(jrr[jss::queue_data].size() == 11);
561
562 env.close();
563
564 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
565 const std::string txid0 = [&]() {
566 auto const& parentHash = env.current()->info().parentHash;
567 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
568 {
569 const std::string txid1 = [&]() {
570 auto const& txj = jrr[jss::queue_data][1u];
571 BEAST_EXPECT(txj[jss::account] == alice.human());
572 BEAST_EXPECT(txj[jss::fee_level] == "256");
573 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
574 BEAST_EXPECT(txj["retries_remaining"] == 10);
575 BEAST_EXPECT(txj.isMember(jss::tx));
576 auto const& tx = txj[jss::tx];
577 BEAST_EXPECT(tx[jss::Account] == alice.human());
578 BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
579 return tx[jss::hash].asString();
580 }();
581
582 auto const& txj = jrr[jss::queue_data][0u];
583 BEAST_EXPECT(txj[jss::account] == alice.human());
584 BEAST_EXPECT(txj[jss::fee_level] == "256");
585 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
586 BEAST_EXPECT(txj["retries_remaining"] == 10);
587 BEAST_EXPECT(txj.isMember(jss::tx));
588 auto const& tx = txj[jss::tx];
589 BEAST_EXPECT(tx[jss::Account] == alice.human());
590 BEAST_EXPECT(tx[jss::TransactionType] == jss::OfferCreate);
591 const auto txid0 = tx[jss::hash].asString();
592 uint256 tx0, tx1;
593 BEAST_EXPECT(tx0.parseHex(txid0));
594 BEAST_EXPECT(tx1.parseHex(txid1));
595 BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
596 return txid0;
597 }
598 return std::string{};
599 }();
600
601 env.close();
602
603 jv[jss::expand] = false;
604
605 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
606 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
607 {
608 auto const& parentHash = env.current()->info().parentHash;
609 auto const txid1 = [&]() {
610 auto const& txj = jrr[jss::queue_data][1u];
611 BEAST_EXPECT(txj[jss::account] == alice.human());
612 BEAST_EXPECT(txj[jss::fee_level] == "256");
613 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
614 BEAST_EXPECT(txj.isMember(jss::tx));
615 return txj[jss::tx].asString();
616 }();
617 auto const& txj = jrr[jss::queue_data][0u];
618 BEAST_EXPECT(txj[jss::account] == alice.human());
619 BEAST_EXPECT(txj[jss::fee_level] == "256");
620 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
621 BEAST_EXPECT(txj["retries_remaining"] == 9);
622 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
623 BEAST_EXPECT(txj.isMember(jss::tx));
624 BEAST_EXPECT(txj[jss::tx] == txid0);
625 uint256 tx0, tx1;
626 BEAST_EXPECT(tx0.parseHex(txid0));
627 BEAST_EXPECT(tx1.parseHex(txid1));
628 BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
629 }
630
631 env.close();
632
633 jv[jss::expand] = true;
634 jv[jss::binary] = true;
635
636 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
637 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
638 {
639 auto const& txj = jrr[jss::queue_data][1u];
640 BEAST_EXPECT(txj[jss::account] == alice.human());
641 BEAST_EXPECT(txj[jss::fee_level] == "256");
642 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
643 BEAST_EXPECT(txj["retries_remaining"] == 8);
644 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
645 BEAST_EXPECT(txj.isMember(jss::tx));
646 BEAST_EXPECT(txj[jss::tx].isMember(jss::tx_blob));
647
648 auto const& txj2 = jrr[jss::queue_data][0u];
649 BEAST_EXPECT(txj2[jss::account] == alice.human());
650 BEAST_EXPECT(txj2[jss::fee_level] == "256");
651 BEAST_EXPECT(txj2["preflight_result"] == "tesSUCCESS");
652 BEAST_EXPECT(txj2["retries_remaining"] == 10);
653 BEAST_EXPECT(!txj2.isMember("last_result"));
654 BEAST_EXPECT(txj2.isMember(jss::tx));
655 BEAST_EXPECT(txj2[jss::tx].isMember(jss::tx_blob));
656 }
657
658 for (int i = 0; i != 9; ++i)
659 {
660 env.close();
661 }
662
663 jv[jss::expand] = false;
664 jv[jss::binary] = false;
665
666 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
667 const std::string txid2 = [&]() {
668 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
669 {
670 auto const& txj = jrr[jss::queue_data][0u];
671 BEAST_EXPECT(txj[jss::account] == alice.human());
672 BEAST_EXPECT(txj[jss::fee_level] == "256");
673 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
674 BEAST_EXPECT(txj["retries_remaining"] == 1);
675 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
676 BEAST_EXPECT(txj.isMember(jss::tx));
677 BEAST_EXPECT(txj[jss::tx] != txid0);
678 return txj[jss::tx].asString();
679 }
680 return std::string{};
681 }();
682
683 jv[jss::full] = true;
684
685 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
686 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
687 {
688 auto const& txj = jrr[jss::queue_data][0u];
689 BEAST_EXPECT(txj[jss::account] == alice.human());
690 BEAST_EXPECT(txj[jss::fee_level] == "256");
691 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
692 BEAST_EXPECT(txj["retries_remaining"] == 1);
693 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
694 BEAST_EXPECT(txj.isMember(jss::tx));
695 auto const& tx = txj[jss::tx];
696 BEAST_EXPECT(tx[jss::Account] == alice.human());
697 BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
698 BEAST_EXPECT(tx[jss::hash] == txid2);
699 }
700 }
701
702 void
704 {
705 testcase("Ledger Request, Accounts Hashes");
706 using namespace test::jtx;
707
708 Env env{*this};
709
710 env.close();
711
712 std::string index;
713 {
714 Json::Value jvParams;
715 jvParams[jss::ledger_index] = 3u;
716 jvParams[jss::accounts] = true;
717 jvParams[jss::expand] = true;
718 jvParams[jss::type] = "hashes";
719 auto const jrr =
720 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
721 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
722 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
723 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
724 BEAST_EXPECT(
725 jrr[jss::ledger][jss::accountState][0u]["LedgerEntryType"] ==
726 jss::LedgerHashes);
727 index = jrr[jss::ledger][jss::accountState][0u]["index"].asString();
728 }
729 {
730 Json::Value jvParams;
731 jvParams[jss::ledger_index] = 3u;
732 jvParams[jss::accounts] = true;
733 jvParams[jss::expand] = false;
734 jvParams[jss::type] = "hashes";
735 auto const jrr =
736 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
737 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
738 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
739 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
740 BEAST_EXPECT(jrr[jss::ledger][jss::accountState][0u] == index);
741 }
742 }
743
744public:
745 void
746 run() override
747 {
749 testBadInput();
755 testNoQueue();
756 testQueue();
758 }
759};
760
761BEAST_DEFINE_TESTSUITE(LedgerRPC, app, ripple);
762
763} // namespace test
764} // namespace ripple
Represents a JSON value.
Definition: json_value.h:148
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
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
virtual TxQ & getTxQ()=0
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
Definition: TxQ.cpp:1778
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:503
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
void run() override
Runs the suite.
void testLookupLedger()
ledger RPC requests as a way to drive input options to lookupLedger.
std::string makeBadAddress(std::string good)
Immutable cryptographic account descriptor.
Definition: Account.h:39
A transaction testing environment.
Definition: Env.h:120
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition: Env.cpp:212
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:330
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:117
Application & app()
Definition: Env.h:260
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:769
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:233
Set the fee on a JTx.
Definition: fee.h:37
Inject raw JSON.
Definition: jtx_json.h:33
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
T empty(T... args)
@ nullValue
'null' value
Definition: json_value.h:37
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Definition: ErrorCodes.cpp:202
std::unique_ptr< Config > no_admin(std::unique_ptr< Config >)
adjust config so no admin ports are enabled
Definition: envconfig.cpp:76
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:30
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition: offer.cpp:29
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
@ terQUEUED
Definition: TER.h:225
T replace(T... args)
Set the sequence number on a JTx.
Definition: seq.h:34
T to_string(T... args)