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