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