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