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