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
265 void
267 {
268 testcase("Lookup ledger");
269 using namespace test::jtx;
270
271 auto cfg = envconfig();
272 cfg->fees.referenceFee = 10;
273 Env env{*this, std::move(cfg), FeatureBitset{}}; // hashes requested below
274 // assume no amendments
275 env.fund(XRP(10000), "alice");
276 env.close();
277 env.fund(XRP(10000), "bob");
278 env.close();
279 env.fund(XRP(10000), "jim");
280 env.close();
281 env.fund(XRP(10000), "jill");
282
283 {
284 // access via the legacy ledger field, keyword index values
285 json::Value jvParams;
286 jvParams[jss::ledger] = "closed";
287 auto 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] = "validated";
293 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
294 BEAST_EXPECT(jrr.isMember(jss::ledger));
295 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
296 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
297
298 jvParams[jss::ledger] = "current";
299 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
300 BEAST_EXPECT(jrr.isMember(jss::ledger));
301 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
302
303 // ask for a bad ledger keyword
304 jvParams[jss::ledger] = "invalid";
305 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
306 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
307 BEAST_EXPECT(
308 jrr[jss::error_message] == "Invalid field 'ledger', not string or number.");
309
310 // numeric index
311 jvParams[jss::ledger] = 4;
312 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
313 BEAST_EXPECT(jrr.isMember(jss::ledger));
314 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
315 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "4");
316
317 // numeric index - out of range
318 jvParams[jss::ledger] = 20;
319 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
320 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
321 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
322 }
323
324 {
325 std::string const hash3{
326 "0F1A9E0C109ADEF6DA2BDE19217C12BBEC57174CBDBD212B0EBDC1CEDB8531"
327 "85"};
328 // access via the ledger_hash field
329 json::Value jvParams;
330 jvParams[jss::ledger_hash] = hash3;
331 auto jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
332 BEAST_EXPECT(jrr.isMember(jss::ledger));
333 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
334 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "3");
335
336 // extra leading hex chars in hash are not allowed
337 jvParams[jss::ledger_hash] = "DEADBEEF" + hash3;
338 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
339 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
340 BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'ledger_hash', not hex string.");
341
342 // request with non-string ledger_hash
343 jvParams[jss::ledger_hash] = 2;
344 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
345 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
346 BEAST_EXPECT(jrr[jss::error_message] == "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(jrr[jss::error_message] == "Invalid field 'ledger_hash', not hex string.");
355
356 // properly formed, but just doesn't exist
357 jvParams[jss::ledger_hash] =
358 "8C3EEDB3124D92E49E75D81A8826A2E6"
359 "5A75FD71FC3FD6F36FEB803C5F1D812D";
360 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
361 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
362 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
363 }
364
365 {
366 // access via the ledger_index field, keyword index values
367 json::Value jvParams;
368 jvParams[jss::ledger_index] = "closed";
369 auto jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
370 BEAST_EXPECT(jrr.isMember(jss::ledger));
371 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
372 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
373 BEAST_EXPECT(jrr.isMember(jss::ledger_index));
374
375 jvParams[jss::ledger_index] = "validated";
376 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
377 BEAST_EXPECT(jrr.isMember(jss::ledger));
378 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
379 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
380
381 jvParams[jss::ledger_index] = "current";
382 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
383 BEAST_EXPECT(jrr.isMember(jss::ledger));
384 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
385 BEAST_EXPECT(jrr.isMember(jss::ledger_current_index));
386
387 // ask for a bad ledger keyword
388 jvParams[jss::ledger_index] = "invalid";
389 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
390 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
391 BEAST_EXPECT(
392 jrr[jss::error_message] == "Invalid field 'ledger_index', not string or number.");
393
394 // numeric index
395 for (auto i : {1, 2, 3, 4, 5, 6})
396 {
397 jvParams[jss::ledger_index] = i;
398 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
399 BEAST_EXPECT(jrr.isMember(jss::ledger));
400 if (i < 6)
401 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
402 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == std::to_string(i));
403 }
404
405 // numeric index - out of range
406 jvParams[jss::ledger_index] = 7;
407 jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
408 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
409 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
410 }
411 }
412
413 void
415 {
416 testcase("Ledger with queueing disabled");
417 using namespace test::jtx;
418 Env env{*this};
419
420 json::Value jv;
421 jv[jss::ledger_index] = "current";
422 jv[jss::queue] = true;
423 jv[jss::expand] = true;
424
425 auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
426 BEAST_EXPECT(!jrr.isMember(jss::queue_data));
427 }
428
429 void
431 {
432 testcase("Ledger with Queued Transactions");
433 using namespace test::jtx;
434 auto cfg = envconfig([](std::unique_ptr<Config> cfg) {
435 auto& section = cfg->section(Sections::kTransactionQueue);
436 section.set(Keys::kMinimumTxnInLedgerStandalone, "3");
438 return cfg;
439 });
440
441 cfg->fees.referenceFee = 10;
442 Env env(*this, std::move(cfg));
443
444 json::Value jv;
445 jv[jss::ledger_index] = "current";
446 jv[jss::queue] = true;
447 jv[jss::expand] = true;
448
449 Account const alice{"alice"};
450 Account const bob{"bob"};
451 Account const charlie{"charlie"};
452 Account const daria{"daria"};
453 env.fund(XRP(10000), alice);
454 env.fund(XRP(10000), bob);
455 env.close();
456 env.fund(XRP(10000), charlie);
457 env.fund(XRP(10000), daria);
458 env.close();
459
460 auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
461 BEAST_EXPECT(!jrr.isMember(jss::queue_data));
462
463 // Fill the open ledger
464 for (;;)
465 {
466 auto metrics = env.app().getTxQ().getMetrics(*env.current());
467 if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
468 break;
469 env(noop(alice));
470 }
471
472 BEAST_EXPECT(env.current()->header().seq == 5);
473 // Put some txs in the queue
474 // Alice
475 auto aliceSeq = env.seq(alice);
476 env(pay(alice, "george", XRP(1000)), LastLedgerSeq(7), Ter(terQUEUED));
477 env(offer(alice, XRP(50000), alice["USD"](5000)), Seq(aliceSeq + 1), Ter(terQUEUED));
478 env(noop(alice), Seq(aliceSeq + 2), Ter(terQUEUED));
479 // Bob
480 auto batch = [&env](Account a) {
481 auto aSeq = env.seq(a);
482 // Enough fee to get in front of alice in the queue
483 for (int i = 0; i < 10; ++i)
484 {
485 env(noop(a), Fee(1000 + i), Seq(aSeq + i), Ter(terQUEUED));
486 }
487 };
488 batch(bob);
489 // Charlie
490 batch(charlie);
491 // Daria
492 batch(daria);
493
494 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
495 BEAST_EXPECT(jrr[jss::queue_data].size() == 33);
496
497 // Close enough ledgers so that alice's first tx expires.
498 env.close();
499 env.close();
500 env.close();
501 BEAST_EXPECT(env.current()->header().seq == 8);
502
503 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
504 BEAST_EXPECT(jrr[jss::queue_data].size() == 11);
505
506 env.close();
507
508 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
509 std::string const txid0 = [&]() {
510 auto const& parentHash = env.current()->header().parentHash;
511 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
512 {
513 std::string const txid1 = [&]() {
514 auto const& txj = jrr[jss::queue_data][1u];
515 BEAST_EXPECT(txj[jss::account] == alice.human());
516 BEAST_EXPECT(txj[jss::fee_level] == "256");
517 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
518 BEAST_EXPECT(txj["retries_remaining"] == 10);
519 BEAST_EXPECT(txj.isMember(jss::tx));
520 auto const& tx = txj[jss::tx];
521 BEAST_EXPECT(tx[jss::Account] == alice.human());
522 BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
523 return tx[jss::hash].asString();
524 }();
525
526 auto const& txj = jrr[jss::queue_data][0u];
527 BEAST_EXPECT(txj[jss::account] == alice.human());
528 BEAST_EXPECT(txj[jss::fee_level] == "256");
529 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
530 BEAST_EXPECT(txj["retries_remaining"] == 10);
531 BEAST_EXPECT(txj.isMember(jss::tx));
532 auto const& tx = txj[jss::tx];
533 BEAST_EXPECT(tx[jss::Account] == alice.human());
534 BEAST_EXPECT(tx[jss::TransactionType] == jss::OfferCreate);
535 auto const txid0 = tx[jss::hash].asString();
536 uint256 tx0, tx1;
537 BEAST_EXPECT(tx0.parseHex(txid0));
538 BEAST_EXPECT(tx1.parseHex(txid1));
539 BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
540 return txid0;
541 }
542 return std::string{};
543 }();
544
545 env.close();
546
547 jv[jss::expand] = false;
548
549 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
550 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
551 {
552 auto const& parentHash = env.current()->header().parentHash;
553 auto const txid1 = [&]() {
554 auto const& txj = jrr[jss::queue_data][1u];
555 BEAST_EXPECT(txj[jss::account] == alice.human());
556 BEAST_EXPECT(txj[jss::fee_level] == "256");
557 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
558 BEAST_EXPECT(txj.isMember(jss::tx));
559 return txj[jss::tx].asString();
560 }();
561 auto const& txj = jrr[jss::queue_data][0u];
562 BEAST_EXPECT(txj[jss::account] == alice.human());
563 BEAST_EXPECT(txj[jss::fee_level] == "256");
564 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
565 BEAST_EXPECT(txj["retries_remaining"] == 9);
566 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
567 BEAST_EXPECT(txj.isMember(jss::tx));
568 BEAST_EXPECT(txj[jss::tx] == txid0);
569 uint256 tx0, tx1;
570 BEAST_EXPECT(tx0.parseHex(txid0));
571 BEAST_EXPECT(tx1.parseHex(txid1));
572 BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
573 }
574
575 env.close();
576
577 jv[jss::expand] = true;
578 jv[jss::binary] = true;
579
580 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
581 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
582 {
583 auto const& txj = jrr[jss::queue_data][1u];
584 BEAST_EXPECT(txj[jss::account] == alice.human());
585 BEAST_EXPECT(txj[jss::fee_level] == "256");
586 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
587 BEAST_EXPECT(txj["retries_remaining"] == 8);
588 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
589 BEAST_EXPECT(txj.isMember(jss::tx));
590 BEAST_EXPECT(txj[jss::tx].isMember(jss::tx_blob));
591
592 auto const& txj2 = jrr[jss::queue_data][0u];
593 BEAST_EXPECT(txj2[jss::account] == alice.human());
594 BEAST_EXPECT(txj2[jss::fee_level] == "256");
595 BEAST_EXPECT(txj2["preflight_result"] == "tesSUCCESS");
596 BEAST_EXPECT(txj2["retries_remaining"] == 10);
597 BEAST_EXPECT(!txj2.isMember("last_result"));
598 BEAST_EXPECT(txj2.isMember(jss::tx));
599 BEAST_EXPECT(txj2[jss::tx].isMember(jss::tx_blob));
600 }
601
602 for (int i = 0; i != 9; ++i)
603 {
604 env.close();
605 }
606
607 jv[jss::expand] = false;
608 jv[jss::binary] = false;
609
610 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
611 std::string const txid2 = [&]() {
612 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
613 {
614 auto const& txj = jrr[jss::queue_data][0u];
615 BEAST_EXPECT(txj[jss::account] == alice.human());
616 BEAST_EXPECT(txj[jss::fee_level] == "256");
617 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
618 BEAST_EXPECT(txj["retries_remaining"] == 1);
619 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
620 BEAST_EXPECT(txj.isMember(jss::tx));
621 BEAST_EXPECT(txj[jss::tx] != txid0);
622 return txj[jss::tx].asString();
623 }
624 return std::string{};
625 }();
626
627 jv[jss::full] = true;
628
629 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
630 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
631 {
632 auto const& txj = jrr[jss::queue_data][0u];
633 BEAST_EXPECT(txj[jss::account] == alice.human());
634 BEAST_EXPECT(txj[jss::fee_level] == "256");
635 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
636 BEAST_EXPECT(txj["retries_remaining"] == 1);
637 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
638 BEAST_EXPECT(txj.isMember(jss::tx));
639 auto const& tx = txj[jss::tx];
640 BEAST_EXPECT(tx[jss::Account] == alice.human());
641 BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
642 BEAST_EXPECT(tx[jss::hash] == txid2);
643 }
644 }
645
646 void
648 {
649 testcase("Ledger Request, Accounts Hashes");
650 using namespace test::jtx;
651
652 Env env{*this};
653
654 env.close();
655
656 std::string index;
657 int hashesLedgerEntryIndex = -1;
658 {
659 json::Value jvParams;
660 jvParams[jss::ledger_index] = 3u;
661 jvParams[jss::accounts] = true;
662 jvParams[jss::expand] = true;
663 jvParams[jss::type] = "hashes";
664 auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
665 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
666 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
667
668 for (auto i = 0; i < jrr[jss::ledger][jss::accountState].size(); i++)
669 {
670 if (jrr[jss::ledger][jss::accountState][i]["LedgerEntryType"] == jss::LedgerHashes)
671 {
672 index = jrr[jss::ledger][jss::accountState][i]["index"].asString();
673 hashesLedgerEntryIndex = i;
674 }
675 }
676
677 for (auto const& object : jrr[jss::ledger][jss::accountState])
678 {
679 if (object["LedgerEntryType"] == jss::LedgerHashes)
680 index = object["index"].asString();
681 }
682
683 // jss::type is a deprecated field
684 BEAST_EXPECT(
685 jrr.isMember(jss::warnings) && jrr[jss::warnings].isArray() &&
686 jrr[jss::warnings].size() == 1 &&
687 jrr[jss::warnings][0u][jss::id].asInt() == WarnRpcFieldsDeprecated);
688 }
689 {
690 json::Value jvParams;
691 jvParams[jss::ledger_index] = 3u;
692 jvParams[jss::accounts] = true;
693 jvParams[jss::expand] = false;
694 jvParams[jss::type] = "hashes";
695 auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
696 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
697 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
698 BEAST_EXPECT(
699 hashesLedgerEntryIndex > 0 &&
700 jrr[jss::ledger][jss::accountState][hashesLedgerEntryIndex] == index);
701
702 // jss::type is a deprecated field
703 BEAST_EXPECT(
704 jrr.isMember(jss::warnings) && jrr[jss::warnings].isArray() &&
705 jrr[jss::warnings].size() == 1 &&
706 jrr[jss::warnings][0u][jss::id].asInt() == WarnRpcFieldsDeprecated);
707 }
708 }
709
710public:
711 void
725};
726
728
729} // namespace xrpl::test
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Lightweight wrapper to tag static string.
Definition json_value.h:44
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.
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:507
virtual TxQ & getTxQ()=0
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
Definition TxQ.cpp:1739
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:17
std::string const & human() const
Returns the human readable public key.
Definition jtx/Account.h:92
A transaction testing environment.
Definition Env.h:143
Application & app()
Definition Env.h:280
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:296
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:275
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:864
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:327
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
Set the fee on a JTx.
Definition fee.h:15
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:13
T empty(T... args)
@ Null
'null' value
Definition json_value.h:19
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:18
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:9
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:28
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:160
@ terQUEUED
Definition TER.h:217
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
BaseUInt< 256 > uint256
Definition base_uint.h:562
T replace(T... args)
static constexpr auto kNormalConsensusIncreasePercent
Definition Constants.h:131
static constexpr auto kMinimumTxnInLedgerStandalone
Definition Constants.h:130
static constexpr auto kTransactionQueue
Definition Constants.h:65
Set the sequence number on a JTx.
Definition seq.h:12
T to_string(T... args)