rippled
Loading...
Searching...
No Matches
LedgerRPC_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2016 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx.h>
21#include <test/jtx/Oracle.h>
22#include <test/jtx/attester.h>
23#include <test/jtx/multisign.h>
24#include <test/jtx/xchain_bridge.h>
25#include <xrpld/app/misc/TxQ.h>
26#include <xrpl/beast/unit_test.h>
27#include <xrpl/json/json_value.h>
28#include <xrpl/protocol/AccountID.h>
29#include <xrpl/protocol/ErrorCodes.h>
30#include <xrpl/protocol/jss.h>
31
32namespace ripple {
33
36{
37 void
39 Json::Value const& jv,
40 std::string const& err,
41 std::string const& msg)
42 {
43 if (BEAST_EXPECT(jv.isMember(jss::status)))
44 BEAST_EXPECT(jv[jss::status] == "error");
45 if (BEAST_EXPECT(jv.isMember(jss::error)))
46 BEAST_EXPECT(jv[jss::error] == err);
47 if (msg.empty())
48 {
49 BEAST_EXPECT(
50 jv[jss::error_message] == Json::nullValue ||
51 jv[jss::error_message] == "");
52 }
53 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
54 BEAST_EXPECT(jv[jss::error_message] == msg);
55 }
56
57 void
59 {
60 testcase("ledger_entry: bridge");
61 using namespace test::jtx;
62
63 Env mcEnv{*this, features};
64 Env scEnv(*this, envconfig(), features);
65
66 createBridgeObjects(mcEnv, scEnv);
67
68 std::string const ledgerHash{to_string(mcEnv.closed()->info().hash)};
69 std::string bridge_index;
70 Json::Value mcBridge;
71 {
72 // request the bridge via RPC
73 Json::Value jvParams;
74 jvParams[jss::bridge_account] = mcDoor.human();
75 jvParams[jss::bridge] = jvb;
76 Json::Value const jrr = mcEnv.rpc(
77 "json", "ledger_entry", to_string(jvParams))[jss::result];
78
79 BEAST_EXPECT(jrr.isMember(jss::node));
80 auto r = jrr[jss::node];
81 // std::cout << to_string(r) << '\n';
82
83 BEAST_EXPECT(r.isMember(jss::Account));
84 BEAST_EXPECT(r[jss::Account] == mcDoor.human());
85
86 BEAST_EXPECT(r.isMember(jss::Flags));
87
88 BEAST_EXPECT(r.isMember(sfLedgerEntryType.jsonName));
89 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::Bridge);
90
91 // we not created an account yet
92 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
93 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 0);
94
95 // we have not claimed a locking chain tx yet
96 BEAST_EXPECT(r.isMember(sfXChainAccountClaimCount.jsonName));
97 BEAST_EXPECT(r[sfXChainAccountClaimCount.jsonName].asInt() == 0);
98
99 BEAST_EXPECT(r.isMember(jss::index));
100 bridge_index = r[jss::index].asString();
101 mcBridge = r;
102 }
103 {
104 // request the bridge via RPC by index
105 Json::Value jvParams;
106 jvParams[jss::index] = bridge_index;
107 Json::Value const jrr = mcEnv.rpc(
108 "json", "ledger_entry", to_string(jvParams))[jss::result];
109
110 BEAST_EXPECT(jrr.isMember(jss::node));
111 BEAST_EXPECT(jrr[jss::node] == mcBridge);
112 }
113 {
114 // swap door accounts and make sure we get an error value
115 Json::Value jvParams;
116 // Sidechain door account is "master", not scDoor
117 jvParams[jss::bridge_account] = Account::master.human();
118 jvParams[jss::bridge] = jvb;
119 jvParams[jss::ledger_hash] = ledgerHash;
120 Json::Value const jrr = mcEnv.rpc(
121 "json", "ledger_entry", to_string(jvParams))[jss::result];
122
123 checkErrorValue(jrr, "entryNotFound", "");
124 }
125 {
126 // create two claim ids and verify that the bridge counter was
127 // incremented
128 mcEnv(xchain_create_claim_id(mcAlice, jvb, reward, scAlice));
129 mcEnv.close();
130 mcEnv(xchain_create_claim_id(mcBob, jvb, reward, scBob));
131 mcEnv.close();
132
133 // request the bridge via RPC
134 Json::Value jvParams;
135 jvParams[jss::bridge_account] = mcDoor.human();
136 jvParams[jss::bridge] = jvb;
137 // std::cout << to_string(jvParams) << '\n';
138 Json::Value const jrr = mcEnv.rpc(
139 "json", "ledger_entry", to_string(jvParams))[jss::result];
140
141 BEAST_EXPECT(jrr.isMember(jss::node));
142 auto r = jrr[jss::node];
143
144 // we executed two create claim id txs
145 BEAST_EXPECT(r.isMember(sfXChainClaimID.jsonName));
146 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
147 }
148 }
149
150 void
152 {
153 testcase("ledger_entry: xchain_claim_id");
154 using namespace test::jtx;
155
156 Env mcEnv{*this, features};
157 Env scEnv(*this, envconfig(), features);
158
159 createBridgeObjects(mcEnv, scEnv);
160
161 scEnv(xchain_create_claim_id(scAlice, jvb, reward, mcAlice));
162 scEnv.close();
163 scEnv(xchain_create_claim_id(scBob, jvb, reward, mcBob));
164 scEnv.close();
165
166 std::string bridge_index;
167 {
168 // request the xchain_claim_id via RPC
169 Json::Value jvParams;
170 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
171 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
172 1;
173 // std::cout << to_string(jvParams) << '\n';
174 Json::Value const jrr = scEnv.rpc(
175 "json", "ledger_entry", to_string(jvParams))[jss::result];
176
177 BEAST_EXPECT(jrr.isMember(jss::node));
178 auto r = jrr[jss::node];
179 // std::cout << to_string(r) << '\n';
180
181 BEAST_EXPECT(r.isMember(jss::Account));
182 BEAST_EXPECT(r[jss::Account] == scAlice.human());
183 BEAST_EXPECT(
184 r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
185 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 1);
186 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
187 }
188
189 {
190 // request the xchain_claim_id via RPC
191 Json::Value jvParams;
192 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
193 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
194 2;
195 Json::Value const jrr = scEnv.rpc(
196 "json", "ledger_entry", to_string(jvParams))[jss::result];
197
198 BEAST_EXPECT(jrr.isMember(jss::node));
199 auto r = jrr[jss::node];
200 // std::cout << to_string(r) << '\n';
201
202 BEAST_EXPECT(r.isMember(jss::Account));
203 BEAST_EXPECT(r[jss::Account] == scBob.human());
204 BEAST_EXPECT(
205 r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
206 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
207 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
208 }
209 }
210
211 void
213 {
214 testcase("ledger_entry: xchain_create_account_claim_id");
215 using namespace test::jtx;
216
217 Env mcEnv{*this, features};
218 Env scEnv(*this, envconfig(), features);
219
220 // note: signers.size() and quorum are both 5 in createBridgeObjects
221 createBridgeObjects(mcEnv, scEnv);
222
223 auto scCarol =
224 Account("scCarol"); // Don't fund it - it will be created with the
225 // xchain transaction
226 auto const amt = XRP(1000);
227 mcEnv(sidechain_xchain_account_create(
228 mcAlice, jvb, scCarol, amt, reward));
229 mcEnv.close();
230
231 // send less than quorum of attestations (otherwise funds are
232 // immediately transferred and no "claim" object is created)
233 size_t constexpr num_attest = 3;
234 auto attestations = create_account_attestations(
236 jvb,
237 mcAlice,
238 amt,
239 reward,
240 payee,
241 /*wasLockingChainSend*/ true,
242 1,
243 scCarol,
244 signers,
245 UT_XCHAIN_DEFAULT_NUM_SIGNERS);
246 for (size_t i = 0; i < num_attest; ++i)
247 {
248 scEnv(attestations[i]);
249 }
250 scEnv.close();
251
252 {
253 // request the create account claim_id via RPC
254 Json::Value jvParams;
255 jvParams[jss::xchain_owned_create_account_claim_id] =
257 jvParams[jss::xchain_owned_create_account_claim_id]
258 [jss::xchain_owned_create_account_claim_id] = 1;
259 // std::cout << to_string(jvParams) << '\n';
260 Json::Value const jrr = scEnv.rpc(
261 "json", "ledger_entry", to_string(jvParams))[jss::result];
262 // std::cout << to_string(jrr) << '\n';
263
264 BEAST_EXPECT(jrr.isMember(jss::node));
265 auto r = jrr[jss::node];
266
267 BEAST_EXPECT(r.isMember(jss::Account));
268 BEAST_EXPECT(r[jss::Account] == Account::master.human());
269
270 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
271 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 1);
272
273 BEAST_EXPECT(
274 r.isMember(sfXChainCreateAccountAttestations.jsonName));
275 auto attest = r[sfXChainCreateAccountAttestations.jsonName];
276 BEAST_EXPECT(attest.isArray());
277 BEAST_EXPECT(attest.size() == 3);
278 BEAST_EXPECT(attest[Json::Value::UInt(0)].isMember(
279 sfXChainCreateAccountProofSig.jsonName));
280 Json::Value a[num_attest];
281 for (size_t i = 0; i < num_attest; ++i)
282 {
283 a[i] = attest[Json::Value::UInt(0)]
284 [sfXChainCreateAccountProofSig.jsonName];
285 BEAST_EXPECT(
286 a[i].isMember(jss::Amount) &&
287 a[i][jss::Amount].asInt() == 1000 * drop_per_xrp);
288 BEAST_EXPECT(
289 a[i].isMember(jss::Destination) &&
290 a[i][jss::Destination] == scCarol.human());
291 BEAST_EXPECT(
292 a[i].isMember(sfAttestationSignerAccount.jsonName) &&
294 signers.begin(), signers.end(), [&](signer const& s) {
295 return a[i][sfAttestationSignerAccount.jsonName] ==
296 s.account.human();
297 }));
298 BEAST_EXPECT(
299 a[i].isMember(sfAttestationRewardAccount.jsonName) &&
301 payee.begin(),
302 payee.end(),
303 [&](Account const& account) {
304 return a[i][sfAttestationRewardAccount.jsonName] ==
305 account.human();
306 }));
307 BEAST_EXPECT(
308 a[i].isMember(sfWasLockingChainSend.jsonName) &&
309 a[i][sfWasLockingChainSend.jsonName] == 1);
310 BEAST_EXPECT(
311 a[i].isMember(sfSignatureReward.jsonName) &&
312 a[i][sfSignatureReward.jsonName].asInt() ==
313 1 * drop_per_xrp);
314 }
315 }
316
317 // complete attestations quorum - CreateAccountClaimID should not be
318 // present anymore
319 for (size_t i = num_attest; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS; ++i)
320 {
321 scEnv(attestations[i]);
322 }
323 scEnv.close();
324 {
325 // request the create account claim_id via RPC
326 Json::Value jvParams;
327 jvParams[jss::xchain_owned_create_account_claim_id] =
329 jvParams[jss::xchain_owned_create_account_claim_id]
330 [jss::xchain_owned_create_account_claim_id] = 1;
331 // std::cout << to_string(jvParams) << '\n';
332 Json::Value const jrr = scEnv.rpc(
333 "json", "ledger_entry", to_string(jvParams))[jss::result];
334 checkErrorValue(jrr, "entryNotFound", "");
335 }
336 }
337
338public:
339 void
340 run() override
341 {
345 }
346};
347
349{
350 void
352 Json::Value const& jv,
353 std::string const& err,
354 std::string const& msg)
355 {
356 if (BEAST_EXPECT(jv.isMember(jss::status)))
357 BEAST_EXPECT(jv[jss::status] == "error");
358 if (BEAST_EXPECT(jv.isMember(jss::error)))
359 BEAST_EXPECT(jv[jss::error] == err);
360 if (msg.empty())
361 {
362 BEAST_EXPECT(
363 jv[jss::error_message] == Json::nullValue ||
364 jv[jss::error_message] == "");
365 }
366 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
367 BEAST_EXPECT(jv[jss::error_message] == msg);
368 }
369
370 // Corrupt a valid address by replacing the 10th character with '!'.
371 // '!' is not part of the ripple alphabet.
374 {
375 std::string ret = std::move(good);
376 ret.replace(10, 1, 1, '!');
377 return ret;
378 }
379
380 void
382 {
383 testcase("Basic Request");
384 using namespace test::jtx;
385
386 Env env{*this};
387
388 env.close();
389 BEAST_EXPECT(env.current()->info().seq == 4);
390
391 {
392 Json::Value jvParams;
393 // can be either numeric or quoted numeric
394 jvParams[jss::ledger_index] = 1;
395 auto const jrr =
396 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
397 BEAST_EXPECT(jrr[jss::ledger][jss::closed] == true);
398 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "1");
399 }
400
401 {
402 Json::Value jvParams;
403 jvParams[jss::ledger_index] = "1";
404 auto const jrr =
405 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
406 BEAST_EXPECT(jrr[jss::ledger][jss::closed] == true);
407 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "1");
408 }
409
410 {
411 // using current identifier
412 auto const jrr = env.rpc("ledger", "current")[jss::result];
413 BEAST_EXPECT(jrr[jss::ledger][jss::closed] == false);
414 BEAST_EXPECT(
415 jrr[jss::ledger][jss::ledger_index] ==
416 std::to_string(env.current()->info().seq));
417 BEAST_EXPECT(
418 jrr[jss::ledger_current_index] == env.current()->info().seq);
419 }
420 }
421
422 void
424 {
425 testcase("Bad Input");
426 using namespace test::jtx;
427 Env env{*this};
428 Account const gw{"gateway"};
429 auto const USD = gw["USD"];
430 Account const bob{"bob"};
431
432 env.fund(XRP(10000), gw, bob);
433 env.close();
434 env.trust(USD(1000), bob);
435 env.close();
436
437 {
438 // ask for an arbitrary string - index
439 Json::Value jvParams;
440 jvParams[jss::ledger_index] = "potato";
441 auto const jrr =
442 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
443 checkErrorValue(jrr, "invalidParams", "ledgerIndexMalformed");
444 }
445
446 {
447 // ask for a negative index
448 Json::Value jvParams;
449 jvParams[jss::ledger_index] = -1;
450 auto const jrr =
451 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
452 checkErrorValue(jrr, "invalidParams", "ledgerIndexMalformed");
453 }
454
455 {
456 // ask for a bad ledger index
457 Json::Value jvParams;
458 jvParams[jss::ledger_index] = 10u;
459 auto const jrr =
460 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
461 checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
462 }
463
464 {
465 // unrecognized string arg -- error
466 auto const jrr = env.rpc("ledger", "arbitrary_text")[jss::result];
467 checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
468 }
469
470 {
471 // Request queue for closed ledger
472 Json::Value jvParams;
473 jvParams[jss::ledger_index] = "validated";
474 jvParams[jss::queue] = true;
475 auto const jrr =
476 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
477 checkErrorValue(jrr, "invalidParams", "Invalid parameters.");
478 }
479
480 {
481 // Request a ledger with a very large (double) sequence.
482 auto const ret =
483 env.rpc("json", "ledger", "{ \"ledger_index\" : 2e15 }");
484 BEAST_EXPECT(RPC::contains_error(ret));
485 BEAST_EXPECT(ret[jss::error_message] == "Invalid parameters.");
486 }
487
488 {
489 // Request a ledger with very large (integer) sequence.
490 auto const ret = env.rpc(
491 "json", "ledger", "{ \"ledger_index\" : 1000000000000000 }");
492 checkErrorValue(ret, "invalidParams", "Invalid parameters.");
493 }
494
495 {
496 // ask for an zero index
497 Json::Value jvParams;
498 jvParams[jss::ledger_index] = "validated";
499 jvParams[jss::index] =
500 "00000000000000000000000000000000000000000000000000000000000000"
501 "0000";
502 auto const jrr = env.rpc(
503 "json", "ledger_entry", to_string(jvParams))[jss::result];
504 checkErrorValue(jrr, "malformedRequest", "");
505 }
506 }
507
508 void
510 {
511 testcase("ledger_current Request");
512 using namespace test::jtx;
513
514 Env env{*this};
515
516 env.close();
517 BEAST_EXPECT(env.current()->info().seq == 4);
518
519 {
520 auto const jrr = env.rpc("ledger_current")[jss::result];
521 BEAST_EXPECT(
522 jrr[jss::ledger_current_index] == env.current()->info().seq);
523 }
524 }
525
526 void
528 {
529 testcase("Missing ledger_entry ledger_hash");
530 using namespace test::jtx;
531 Env env{*this};
532 Account const alice{"alice"};
533 env.fund(XRP(10000), alice);
534 env.close();
535
536 Json::Value jvParams;
537 jvParams[jss::account_root] = alice.human();
538 jvParams[jss::ledger_hash] =
539 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
540 auto const jrr =
541 env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
542 checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
543 }
544
545 void
547 {
548 testcase("Ledger Request, Full Option");
549 using namespace test::jtx;
550
551 Env env{*this};
552
553 env.close();
554
555 Json::Value jvParams;
556 jvParams[jss::ledger_index] = 3u;
557 jvParams[jss::full] = true;
558 auto const jrr =
559 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
560 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
561 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
562 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 3u);
563 }
564
565 void
567 {
568 testcase("Ledger Request, Full Option Without Admin");
569 using namespace test::jtx;
570
571 Env env{*this, envconfig(no_admin)};
572
573 // env.close();
574
575 Json::Value jvParams;
576 jvParams[jss::ledger_index] = 1u;
577 jvParams[jss::full] = true;
578 auto const jrr =
579 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
581 jrr, "noPermission", "You don't have permission for this command.");
582 }
583
584 void
586 {
587 testcase("Ledger Request, Accounts Option");
588 using namespace test::jtx;
589
590 Env env{*this};
591
592 env.close();
593
594 Json::Value jvParams;
595 jvParams[jss::ledger_index] = 3u;
596 jvParams[jss::accounts] = true;
597 auto const jrr =
598 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
599 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
600 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
601 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 3u);
602 }
603
604 void
606 {
607 testcase("ledger_entry Request AccountRoot");
608 using namespace test::jtx;
609 Env env{*this};
610 Account const alice{"alice"};
611 env.fund(XRP(10000), alice);
612 env.close();
613
614 std::string const ledgerHash{to_string(env.closed()->info().hash)};
615 {
616 // Exercise ledger_closed along the way.
617 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
618 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
619 BEAST_EXPECT(jrr[jss::ledger_index] == 3);
620 }
621
622 std::string accountRootIndex;
623 {
624 // Request alice's account root.
625 Json::Value jvParams;
626 jvParams[jss::account_root] = alice.human();
627 jvParams[jss::ledger_hash] = ledgerHash;
628 Json::Value const jrr = env.rpc(
629 "json", "ledger_entry", to_string(jvParams))[jss::result];
630 BEAST_EXPECT(jrr.isMember(jss::node));
631 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
632 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
633 accountRootIndex = jrr[jss::index].asString();
634 }
635 {
636 constexpr char alicesAcctRootBinary[]{
637 "1100612200800000240000000425000000032D00000000559CE54C3B934E4"
638 "73A995B477E92EC229F99CED5B62BF4D2ACE4DC42719103AE2F6240000002"
639 "540BE4008114AE123A8556F3CF91154711376AFB0F894F832B3D"};
640
641 // Request alice's account root, but with binary == true;
642 Json::Value jvParams;
643 jvParams[jss::account_root] = alice.human();
644 jvParams[jss::binary] = 1;
645 jvParams[jss::ledger_hash] = ledgerHash;
646 Json::Value const jrr = env.rpc(
647 "json", "ledger_entry", to_string(jvParams))[jss::result];
648 BEAST_EXPECT(jrr.isMember(jss::node_binary));
649 BEAST_EXPECT(jrr[jss::node_binary] == alicesAcctRootBinary);
650 }
651 {
652 // Request alice's account root using the index.
653 Json::Value jvParams;
654 jvParams[jss::index] = accountRootIndex;
655 Json::Value const jrr = env.rpc(
656 "json", "ledger_entry", to_string(jvParams))[jss::result];
657 BEAST_EXPECT(!jrr.isMember(jss::node_binary));
658 BEAST_EXPECT(jrr.isMember(jss::node));
659 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
660 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
661 }
662 {
663 // Request alice's account root by index, but with binary == false.
664 Json::Value jvParams;
665 jvParams[jss::index] = accountRootIndex;
666 jvParams[jss::binary] = 0;
667 Json::Value const jrr = env.rpc(
668 "json", "ledger_entry", to_string(jvParams))[jss::result];
669 BEAST_EXPECT(jrr.isMember(jss::node));
670 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
671 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
672 }
673 {
674 // Request using a corrupted AccountID.
675 Json::Value jvParams;
676 jvParams[jss::account_root] = makeBadAddress(alice.human());
677 jvParams[jss::ledger_hash] = ledgerHash;
678 Json::Value const jrr = env.rpc(
679 "json", "ledger_entry", to_string(jvParams))[jss::result];
680 checkErrorValue(jrr, "malformedAddress", "");
681 }
682 {
683 // Request an account that is not in the ledger.
684 Json::Value jvParams;
685 jvParams[jss::account_root] = Account("bob").human();
686 jvParams[jss::ledger_hash] = ledgerHash;
687 Json::Value const jrr = env.rpc(
688 "json", "ledger_entry", to_string(jvParams))[jss::result];
689 checkErrorValue(jrr, "entryNotFound", "");
690 }
691 }
692
693 void
695 {
696 testcase("ledger_entry Request Check");
697 using namespace test::jtx;
698 Env env{*this};
699 Account const alice{"alice"};
700 env.fund(XRP(10000), alice);
701 env.close();
702
703 auto const checkId = keylet::check(env.master, env.seq(env.master));
704
705 env(check::create(env.master, alice, XRP(100)));
706 env.close();
707
708 std::string const ledgerHash{to_string(env.closed()->info().hash)};
709 {
710 // Request a check.
711 Json::Value jvParams;
712 jvParams[jss::check] = to_string(checkId.key);
713 jvParams[jss::ledger_hash] = ledgerHash;
714 Json::Value const jrr = env.rpc(
715 "json", "ledger_entry", to_string(jvParams))[jss::result];
716 BEAST_EXPECT(
717 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
718 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
719 }
720 {
721 // Request an index that is not a check. We'll use alice's
722 // account root index.
723 std::string accountRootIndex;
724 {
725 Json::Value jvParams;
726 jvParams[jss::account_root] = alice.human();
727 Json::Value const jrr = env.rpc(
728 "json", "ledger_entry", to_string(jvParams))[jss::result];
729 accountRootIndex = jrr[jss::index].asString();
730 }
731 Json::Value jvParams;
732 jvParams[jss::check] = accountRootIndex;
733 jvParams[jss::ledger_hash] = ledgerHash;
734 Json::Value const jrr = env.rpc(
735 "json", "ledger_entry", to_string(jvParams))[jss::result];
736 checkErrorValue(jrr, "unexpectedLedgerType", "");
737 }
738 }
739
740 void
742 {
743 testcase("ledger_entry credentials");
744
745 using namespace test::jtx;
746
747 Env env(*this);
748 Account const issuer{"issuer"};
749 Account const alice{"alice"};
750 Account const bob{"bob"};
751 const char credType[] = "abcde";
752
753 env.fund(XRP(5000), issuer, alice, bob);
754 env.close();
755
756 // Setup credentials with DepositAuth object for Alice and Bob
757 env(credentials::create(alice, issuer, credType));
758 env.close();
759
760 {
761 // Succeed
762 auto jv = credentials::ledgerEntry(env, alice, issuer, credType);
763 BEAST_EXPECT(
764 jv.isObject() && jv.isMember(jss::result) &&
765 !jv[jss::result].isMember(jss::error) &&
766 jv[jss::result].isMember(jss::node) &&
767 jv[jss::result][jss::node].isMember(
768 sfLedgerEntryType.jsonName) &&
769 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
770 jss::Credential);
771
772 std::string const credIdx = jv[jss::result][jss::index].asString();
773
774 jv = credentials::ledgerEntry(env, credIdx);
775 BEAST_EXPECT(
776 jv.isObject() && jv.isMember(jss::result) &&
777 !jv[jss::result].isMember(jss::error) &&
778 jv[jss::result].isMember(jss::node) &&
779 jv[jss::result][jss::node].isMember(
780 sfLedgerEntryType.jsonName) &&
781 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
782 jss::Credential);
783 }
784
785 {
786 // Fail, index not a hash
787 auto const jv = credentials::ledgerEntry(env, "");
788 checkErrorValue(jv[jss::result], "malformedRequest", "");
789 }
790
791 {
792 // Fail, credential doesn't exist
793 auto const jv = credentials::ledgerEntry(
794 env,
795 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
796 "E4");
797 checkErrorValue(jv[jss::result], "entryNotFound", "");
798 }
799
800 {
801 // Fail, invalid subject
802 Json::Value jv;
803 jv[jss::ledger_index] = jss::validated;
804 jv[jss::credential][jss::subject] = 42;
805 jv[jss::credential][jss::issuer] = issuer.human();
806 jv[jss::credential][jss::credential_type] =
807 strHex(std::string_view(credType));
808 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
809 checkErrorValue(jrr[jss::result], "malformedRequest", "");
810 }
811
812 {
813 // Fail, invalid issuer
814 Json::Value jv;
815 jv[jss::ledger_index] = jss::validated;
816 jv[jss::credential][jss::subject] = alice.human();
817 jv[jss::credential][jss::issuer] = 42;
818 jv[jss::credential][jss::credential_type] =
819 strHex(std::string_view(credType));
820 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
821 checkErrorValue(jrr[jss::result], "malformedRequest", "");
822 }
823
824 {
825 // Fail, invalid credentials type
826 Json::Value jv;
827 jv[jss::ledger_index] = jss::validated;
828 jv[jss::credential][jss::subject] = alice.human();
829 jv[jss::credential][jss::issuer] = issuer.human();
830 jv[jss::credential][jss::credential_type] = 42;
831 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
832 checkErrorValue(jrr[jss::result], "malformedRequest", "");
833 }
834
835 {
836 // Fail, empty subject
837 Json::Value jv;
838 jv[jss::ledger_index] = jss::validated;
839 jv[jss::credential][jss::subject] = "";
840 jv[jss::credential][jss::issuer] = issuer.human();
841 jv[jss::credential][jss::credential_type] =
842 strHex(std::string_view(credType));
843 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
844 checkErrorValue(jrr[jss::result], "malformedRequest", "");
845 }
846
847 {
848 // Fail, empty issuer
849 Json::Value jv;
850 jv[jss::ledger_index] = jss::validated;
851 jv[jss::credential][jss::subject] = alice.human();
852 jv[jss::credential][jss::issuer] = "";
853 jv[jss::credential][jss::credential_type] =
854 strHex(std::string_view(credType));
855 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
856 checkErrorValue(jrr[jss::result], "malformedRequest", "");
857 }
858
859 {
860 // Fail, empty credentials type
861 Json::Value jv;
862 jv[jss::ledger_index] = jss::validated;
863 jv[jss::credential][jss::subject] = alice.human();
864 jv[jss::credential][jss::issuer] = issuer.human();
865 jv[jss::credential][jss::credential_type] = "";
866 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
867 checkErrorValue(jrr[jss::result], "malformedRequest", "");
868 }
869
870 {
871 // Fail, no subject
872 Json::Value jv;
873 jv[jss::ledger_index] = jss::validated;
874 jv[jss::credential][jss::issuer] = issuer.human();
875 jv[jss::credential][jss::credential_type] =
876 strHex(std::string_view(credType));
877 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
878 checkErrorValue(jrr[jss::result], "malformedRequest", "");
879 }
880
881 {
882 // Fail, no issuer
883 Json::Value jv;
884 jv[jss::ledger_index] = jss::validated;
885 jv[jss::credential][jss::subject] = alice.human();
886 jv[jss::credential][jss::credential_type] =
887 strHex(std::string_view(credType));
888 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
889 checkErrorValue(jrr[jss::result], "malformedRequest", "");
890 }
891
892 {
893 // Fail, no credentials type
894 Json::Value jv;
895 jv[jss::ledger_index] = jss::validated;
896 jv[jss::credential][jss::subject] = alice.human();
897 jv[jss::credential][jss::issuer] = issuer.human();
898 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
899 checkErrorValue(jrr[jss::result], "malformedRequest", "");
900 }
901
902 {
903 // Fail, not AccountID subject
904 Json::Value jv;
905 jv[jss::ledger_index] = jss::validated;
906 jv[jss::credential][jss::subject] = "wehsdbvasbdfvj";
907 jv[jss::credential][jss::issuer] = issuer.human();
908 jv[jss::credential][jss::credential_type] =
909 strHex(std::string_view(credType));
910 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
911 checkErrorValue(jrr[jss::result], "malformedRequest", "");
912 }
913
914 {
915 // Fail, not AccountID issuer
916 Json::Value jv;
917 jv[jss::ledger_index] = jss::validated;
918 jv[jss::credential][jss::subject] = alice.human();
919 jv[jss::credential][jss::issuer] = "c4p93ugndfbsiu";
920 jv[jss::credential][jss::credential_type] =
921 strHex(std::string_view(credType));
922 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
923 checkErrorValue(jrr[jss::result], "malformedRequest", "");
924 }
925
926 {
927 // Fail, credentials type isn't hex encoded
928 Json::Value jv;
929 jv[jss::ledger_index] = jss::validated;
930 jv[jss::credential][jss::subject] = alice.human();
931 jv[jss::credential][jss::issuer] = issuer.human();
932 jv[jss::credential][jss::credential_type] = "12KK";
933 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
934 checkErrorValue(jrr[jss::result], "malformedRequest", "");
935 }
936 }
937
938 void
940 {
941 testcase("ledger_entry Deposit Preauth");
942
943 using namespace test::jtx;
944
945 Env env{*this};
946 Account const alice{"alice"};
947 Account const becky{"becky"};
948
949 env.fund(XRP(10000), alice, becky);
950 env.close();
951
952 env(deposit::auth(alice, becky));
953 env.close();
954
955 std::string const ledgerHash{to_string(env.closed()->info().hash)};
956 std::string depositPreauthIndex;
957 {
958 // Request a depositPreauth by owner and authorized.
959 Json::Value jvParams;
960 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
961 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
962 jvParams[jss::ledger_hash] = ledgerHash;
963 Json::Value const jrr = env.rpc(
964 "json", "ledger_entry", to_string(jvParams))[jss::result];
965
966 BEAST_EXPECT(
967 jrr[jss::node][sfLedgerEntryType.jsonName] ==
968 jss::DepositPreauth);
969 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
970 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
971 depositPreauthIndex = jrr[jss::node][jss::index].asString();
972 }
973 {
974 // Request a depositPreauth by index.
975 Json::Value jvParams;
976 jvParams[jss::deposit_preauth] = depositPreauthIndex;
977 jvParams[jss::ledger_hash] = ledgerHash;
978 Json::Value const jrr = env.rpc(
979 "json", "ledger_entry", to_string(jvParams))[jss::result];
980
981 BEAST_EXPECT(
982 jrr[jss::node][sfLedgerEntryType.jsonName] ==
983 jss::DepositPreauth);
984 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
985 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
986 }
987 {
988 // Malformed request: deposit_preauth neither object nor string.
989 Json::Value jvParams;
990 jvParams[jss::deposit_preauth] = -5;
991 jvParams[jss::ledger_hash] = ledgerHash;
992 Json::Value const jrr = env.rpc(
993 "json", "ledger_entry", to_string(jvParams))[jss::result];
994 checkErrorValue(jrr, "malformedRequest", "");
995 }
996 {
997 // Malformed request: deposit_preauth not hex string.
998 Json::Value jvParams;
999 jvParams[jss::deposit_preauth] = "0123456789ABCDEFG";
1000 jvParams[jss::ledger_hash] = ledgerHash;
1001 Json::Value const jrr = env.rpc(
1002 "json", "ledger_entry", to_string(jvParams))[jss::result];
1003 checkErrorValue(jrr, "malformedRequest", "");
1004 }
1005 {
1006 // Malformed request: missing [jss::deposit_preauth][jss::owner]
1007 Json::Value jvParams;
1008 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
1009 jvParams[jss::ledger_hash] = ledgerHash;
1010 Json::Value const jrr = env.rpc(
1011 "json", "ledger_entry", to_string(jvParams))[jss::result];
1012 checkErrorValue(jrr, "malformedRequest", "");
1013 }
1014 {
1015 // Malformed request: [jss::deposit_preauth][jss::owner] not string.
1016 Json::Value jvParams;
1017 jvParams[jss::deposit_preauth][jss::owner] = 7;
1018 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
1019 jvParams[jss::ledger_hash] = ledgerHash;
1020 Json::Value const jrr = env.rpc(
1021 "json", "ledger_entry", to_string(jvParams))[jss::result];
1022 checkErrorValue(jrr, "malformedRequest", "");
1023 }
1024 {
1025 // Malformed: missing [jss::deposit_preauth][jss::authorized]
1026 Json::Value jvParams;
1027 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
1028 jvParams[jss::ledger_hash] = ledgerHash;
1029 Json::Value const jrr = env.rpc(
1030 "json", "ledger_entry", to_string(jvParams))[jss::result];
1031 checkErrorValue(jrr, "malformedRequest", "");
1032 }
1033 {
1034 // Malformed: [jss::deposit_preauth][jss::authorized] not string.
1035 Json::Value jvParams;
1036 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
1037 jvParams[jss::deposit_preauth][jss::authorized] = 47;
1038 jvParams[jss::ledger_hash] = ledgerHash;
1039 Json::Value const jrr = env.rpc(
1040 "json", "ledger_entry", to_string(jvParams))[jss::result];
1041 checkErrorValue(jrr, "malformedRequest", "");
1042 }
1043 {
1044 // Malformed: [jss::deposit_preauth][jss::owner] is malformed.
1045 Json::Value jvParams;
1046 jvParams[jss::deposit_preauth][jss::owner] =
1047 "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
1048
1049 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
1050 jvParams[jss::ledger_hash] = ledgerHash;
1051 Json::Value const jrr = env.rpc(
1052 "json", "ledger_entry", to_string(jvParams))[jss::result];
1053 checkErrorValue(jrr, "malformedOwner", "");
1054 }
1055 {
1056 // Malformed: [jss::deposit_preauth][jss::authorized] is malformed.
1057 Json::Value jvParams;
1058 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
1059 jvParams[jss::deposit_preauth][jss::authorized] =
1060 "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
1061
1062 jvParams[jss::ledger_hash] = ledgerHash;
1063 Json::Value const jrr = env.rpc(
1064 "json", "ledger_entry", to_string(jvParams))[jss::result];
1065 checkErrorValue(jrr, "malformedAuthorized", "");
1066 }
1067 }
1068
1069 void
1071 {
1072 testcase("ledger_entry Deposit Preauth with credentials");
1073
1074 using namespace test::jtx;
1075
1076 Env env(*this);
1077 Account const issuer{"issuer"};
1078 Account const alice{"alice"};
1079 Account const bob{"bob"};
1080 const char credType[] = "abcde";
1081
1082 env.fund(XRP(5000), issuer, alice, bob);
1083 env.close();
1084
1085 {
1086 // Setup Bob with DepositAuth
1087 env(fset(bob, asfDepositAuth), fee(drops(10)));
1088 env.close();
1089 env(deposit::authCredentials(bob, {{issuer, credType}}));
1090 env.close();
1091 }
1092
1093 {
1094 // Succeed
1095 Json::Value jvParams;
1096 jvParams[jss::ledger_index] = jss::validated;
1097 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1098
1099 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1101 auto& arr(
1102 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1103
1104 Json::Value jo;
1105 jo[jss::issuer] = issuer.human();
1106 jo[jss::credential_type] = strHex(std::string_view(credType));
1107 arr.append(std::move(jo));
1108 auto const jrr =
1109 env.rpc("json", "ledger_entry", to_string(jvParams));
1110
1111 BEAST_EXPECT(
1112 jrr.isObject() && jrr.isMember(jss::result) &&
1113 !jrr[jss::result].isMember(jss::error) &&
1114 jrr[jss::result].isMember(jss::node) &&
1115 jrr[jss::result][jss::node].isMember(
1116 sfLedgerEntryType.jsonName) &&
1117 jrr[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
1118 jss::DepositPreauth);
1119 }
1120
1121 {
1122 // Failed, invalid account
1123 Json::Value jvParams;
1124 jvParams[jss::ledger_index] = jss::validated;
1125 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1126
1127 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1129 auto& arr(
1130 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1131
1132 Json::Value jo;
1133 jo[jss::issuer] = to_string(xrpAccount());
1134 jo[jss::credential_type] = strHex(std::string_view(credType));
1135 arr.append(std::move(jo));
1136 auto const jrr =
1137 env.rpc("json", "ledger_entry", to_string(jvParams));
1139 jrr[jss::result], "malformedAuthorizedCredentials", "");
1140 }
1141
1142 {
1143 // Failed, duplicates in credentials
1144 Json::Value jvParams;
1145 jvParams[jss::ledger_index] = jss::validated;
1146 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1147
1148 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1150 auto& arr(
1151 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1152
1153 Json::Value jo;
1154 jo[jss::issuer] = issuer.human();
1155 jo[jss::credential_type] = strHex(std::string_view(credType));
1156 arr.append(jo);
1157 arr.append(std::move(jo));
1158 auto const jrr =
1159 env.rpc("json", "ledger_entry", to_string(jvParams));
1161 jrr[jss::result], "malformedAuthorizedCredentials", "");
1162 }
1163
1164 {
1165 // Failed, invalid credential_type
1166 Json::Value jvParams;
1167 jvParams[jss::ledger_index] = jss::validated;
1168 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1169
1170 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1172 auto& arr(
1173 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1174
1175 Json::Value jo;
1176 jo[jss::issuer] = issuer.human();
1177 jo[jss::credential_type] = "";
1178 arr.append(std::move(jo));
1179
1180 auto const jrr =
1181 env.rpc("json", "ledger_entry", to_string(jvParams));
1183 jrr[jss::result], "malformedAuthorizedCredentials", "");
1184 }
1185
1186 {
1187 // Failed, authorized and authorized_credentials both present
1188 Json::Value jvParams;
1189 jvParams[jss::ledger_index] = jss::validated;
1190 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1191 jvParams[jss::deposit_preauth][jss::authorized] = alice.human();
1192
1193 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1195 auto& arr(
1196 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1197
1198 Json::Value jo;
1199 jo[jss::issuer] = issuer.human();
1200 jo[jss::credential_type] = strHex(std::string_view(credType));
1201 arr.append(std::move(jo));
1202
1203 auto const jrr =
1204 env.rpc("json", "ledger_entry", to_string(jvParams));
1205 checkErrorValue(jrr[jss::result], "malformedRequest", "");
1206 }
1207
1208 {
1209 // Failed, authorized_credentials is not an array
1210 Json::Value jvParams;
1211 jvParams[jss::ledger_index] = jss::validated;
1212 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1213 jvParams[jss::deposit_preauth][jss::authorized_credentials] = 42;
1214
1215 auto const jrr =
1216 env.rpc("json", "ledger_entry", to_string(jvParams));
1217 checkErrorValue(jrr[jss::result], "malformedRequest", "");
1218 }
1219
1220 {
1221 // Failed, authorized_credentials contains string data
1222 Json::Value jvParams;
1223 jvParams[jss::ledger_index] = jss::validated;
1224 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1225 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1227 auto& arr(
1228 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1229 arr.append("foobar");
1230
1231 auto const jrr =
1232 env.rpc("json", "ledger_entry", to_string(jvParams));
1234 jrr[jss::result], "malformedAuthorizedCredentials", "");
1235 }
1236
1237 {
1238 // Failed, authorized_credentials contains arrays
1239 Json::Value jvParams;
1240 jvParams[jss::ledger_index] = jss::validated;
1241 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1242 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1244 auto& arr(
1245 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1246 Json::Value payload = Json::arrayValue;
1247 payload.append(42);
1248 arr.append(std::move(payload));
1249
1250 auto const jrr =
1251 env.rpc("json", "ledger_entry", to_string(jvParams));
1253 jrr[jss::result], "malformedAuthorizedCredentials", "");
1254 }
1255
1256 {
1257 // Failed, authorized_credentials is empty array
1258 Json::Value jvParams;
1259 jvParams[jss::ledger_index] = jss::validated;
1260 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1261 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1263
1264 auto const jrr =
1265 env.rpc("json", "ledger_entry", to_string(jvParams));
1267 jrr[jss::result], "malformedAuthorizedCredentials", "");
1268 }
1269
1270 {
1271 // Failed, authorized_credentials is too long
1272
1273 static const std::string_view credTypes[] = {
1274 "cred1",
1275 "cred2",
1276 "cred3",
1277 "cred4",
1278 "cred5",
1279 "cred6",
1280 "cred7",
1281 "cred8",
1282 "cred9"};
1283 static_assert(
1284 sizeof(credTypes) / sizeof(credTypes[0]) >
1286
1287 Json::Value jvParams;
1288 jvParams[jss::ledger_index] = jss::validated;
1289 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1290 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1292
1293 auto& arr(
1294 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1295
1296 for (unsigned i = 0; i < sizeof(credTypes) / sizeof(credTypes[0]);
1297 ++i)
1298 {
1299 Json::Value jo;
1300 jo[jss::issuer] = issuer.human();
1301 jo[jss::credential_type] =
1302 strHex(std::string_view(credTypes[i]));
1303 arr.append(std::move(jo));
1304 }
1305
1306 auto const jrr =
1307 env.rpc("json", "ledger_entry", to_string(jvParams));
1309 jrr[jss::result], "malformedAuthorizedCredentials", "");
1310 }
1311
1312 {
1313 // Failed, issuer is not set
1314 Json::Value jvParams;
1315 jvParams[jss::ledger_index] = jss::validated;
1316 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1317
1318 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1320 auto& arr(
1321 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1322
1323 Json::Value jo;
1324 jo[jss::credential_type] = strHex(std::string_view(credType));
1325 arr.append(std::move(jo));
1326
1327 auto const jrr =
1328 env.rpc("json", "ledger_entry", to_string(jvParams));
1330 jrr[jss::result], "malformedAuthorizedCredentials", "");
1331 }
1332
1333 {
1334 // Failed, issuer isn't string
1335 Json::Value jvParams;
1336 jvParams[jss::ledger_index] = jss::validated;
1337 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1338
1339 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1341 auto& arr(
1342 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1343
1344 Json::Value jo;
1345 jo[jss::issuer] = 42;
1346 jo[jss::credential_type] = strHex(std::string_view(credType));
1347 arr.append(std::move(jo));
1348
1349 auto const jrr =
1350 env.rpc("json", "ledger_entry", to_string(jvParams));
1352 jrr[jss::result], "malformedAuthorizedCredentials", "");
1353 }
1354
1355 {
1356 // Failed, issuer is an array
1357 Json::Value jvParams;
1358 jvParams[jss::ledger_index] = jss::validated;
1359 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1360
1361 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1363 auto& arr(
1364 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1365
1366 Json::Value jo;
1367 Json::Value payload = Json::arrayValue;
1368 payload.append(42);
1369 jo[jss::issuer] = std::move(payload);
1370 jo[jss::credential_type] = strHex(std::string_view(credType));
1371 arr.append(std::move(jo));
1372
1373 auto const jrr =
1374 env.rpc("json", "ledger_entry", to_string(jvParams));
1376 jrr[jss::result], "malformedAuthorizedCredentials", "");
1377 }
1378
1379 {
1380 // Failed, issuer isn't valid encoded account
1381 Json::Value jvParams;
1382 jvParams[jss::ledger_index] = jss::validated;
1383 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1384
1385 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1387 auto& arr(
1388 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1389
1390 Json::Value jo;
1391 jo[jss::issuer] = "invalid_account";
1392 jo[jss::credential_type] = strHex(std::string_view(credType));
1393 arr.append(std::move(jo));
1394
1395 auto const jrr =
1396 env.rpc("json", "ledger_entry", to_string(jvParams));
1398 jrr[jss::result], "malformedAuthorizedCredentials", "");
1399 }
1400
1401 {
1402 // Failed, credential_type is not set
1403 Json::Value jvParams;
1404 jvParams[jss::ledger_index] = jss::validated;
1405 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1406
1407 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1409 auto& arr(
1410 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1411
1412 Json::Value jo;
1413 jo[jss::issuer] = issuer.human();
1414 arr.append(std::move(jo));
1415
1416 auto const jrr =
1417 env.rpc("json", "ledger_entry", to_string(jvParams));
1419 jrr[jss::result], "malformedAuthorizedCredentials", "");
1420 }
1421
1422 {
1423 // Failed, credential_type isn't string
1424 Json::Value jvParams;
1425 jvParams[jss::ledger_index] = jss::validated;
1426 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1427
1428 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1430 auto& arr(
1431 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1432
1433 Json::Value jo;
1434 jo[jss::issuer] = issuer.human();
1435 jo[jss::credential_type] = 42;
1436 arr.append(std::move(jo));
1437
1438 auto const jrr =
1439 env.rpc("json", "ledger_entry", to_string(jvParams));
1441 jrr[jss::result], "malformedAuthorizedCredentials", "");
1442 }
1443
1444 {
1445 // Failed, credential_type is an array
1446 Json::Value jvParams;
1447 jvParams[jss::ledger_index] = jss::validated;
1448 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1449
1450 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1452 auto& arr(
1453 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1454
1455 Json::Value jo;
1456 jo[jss::issuer] = issuer.human();
1457 Json::Value payload = Json::arrayValue;
1458 payload.append(42);
1459 jo[jss::credential_type] = std::move(payload);
1460 arr.append(std::move(jo));
1461
1462 auto const jrr =
1463 env.rpc("json", "ledger_entry", to_string(jvParams));
1465 jrr[jss::result], "malformedAuthorizedCredentials", "");
1466 }
1467
1468 {
1469 // Failed, credential_type isn't hex encoded
1470 Json::Value jvParams;
1471 jvParams[jss::ledger_index] = jss::validated;
1472 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1473
1474 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1476 auto& arr(
1477 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1478
1479 Json::Value jo;
1480 jo[jss::issuer] = issuer.human();
1481 jo[jss::credential_type] = "12KK";
1482 arr.append(std::move(jo));
1483
1484 auto const jrr =
1485 env.rpc("json", "ledger_entry", to_string(jvParams));
1487 jrr[jss::result], "malformedAuthorizedCredentials", "");
1488 }
1489 }
1490
1491 void
1493 {
1494 testcase("ledger_entry Request Directory");
1495 using namespace test::jtx;
1496 Env env{*this};
1497 Account const alice{"alice"};
1498 Account const gw{"gateway"};
1499 auto const USD = gw["USD"];
1500 env.fund(XRP(10000), alice, gw);
1501 env.close();
1502
1503 env.trust(USD(1000), alice);
1504 env.close();
1505
1506 // Run up the number of directory entries so alice has two
1507 // directory nodes.
1508 for (int d = 1'000'032; d >= 1'000'000; --d)
1509 {
1510 env(offer(alice, USD(1), drops(d)));
1511 }
1512 env.close();
1513
1514 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1515 {
1516 // Exercise ledger_closed along the way.
1517 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
1518 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
1519 BEAST_EXPECT(jrr[jss::ledger_index] == 5);
1520 }
1521
1522 std::string const dirRootIndex =
1523 "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D";
1524 {
1525 // Locate directory by index.
1526 Json::Value jvParams;
1527 jvParams[jss::directory] = dirRootIndex;
1528 jvParams[jss::ledger_hash] = ledgerHash;
1529 Json::Value const jrr = env.rpc(
1530 "json", "ledger_entry", to_string(jvParams))[jss::result];
1531 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32);
1532 }
1533 {
1534 // Locate directory by directory root.
1535 Json::Value jvParams;
1536 jvParams[jss::directory] = Json::objectValue;
1537 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1538 Json::Value const jrr = env.rpc(
1539 "json", "ledger_entry", to_string(jvParams))[jss::result];
1540 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
1541 }
1542 {
1543 // Locate directory by owner.
1544 Json::Value jvParams;
1545 jvParams[jss::directory] = Json::objectValue;
1546 jvParams[jss::directory][jss::owner] = alice.human();
1547 jvParams[jss::ledger_hash] = ledgerHash;
1548 Json::Value const jrr = env.rpc(
1549 "json", "ledger_entry", to_string(jvParams))[jss::result];
1550 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
1551 }
1552 {
1553 // Locate directory by directory root and sub_index.
1554 Json::Value jvParams;
1555 jvParams[jss::directory] = Json::objectValue;
1556 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1557 jvParams[jss::directory][jss::sub_index] = 1;
1558 Json::Value const jrr = env.rpc(
1559 "json", "ledger_entry", to_string(jvParams))[jss::result];
1560 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
1561 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
1562 }
1563 {
1564 // Locate directory by owner and sub_index.
1565 Json::Value jvParams;
1566 jvParams[jss::directory] = Json::objectValue;
1567 jvParams[jss::directory][jss::owner] = alice.human();
1568 jvParams[jss::directory][jss::sub_index] = 1;
1569 jvParams[jss::ledger_hash] = ledgerHash;
1570 Json::Value const jrr = env.rpc(
1571 "json", "ledger_entry", to_string(jvParams))[jss::result];
1572 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
1573 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
1574 }
1575 {
1576 // Null directory argument.
1577 Json::Value jvParams;
1578 jvParams[jss::directory] = Json::nullValue;
1579 jvParams[jss::ledger_hash] = ledgerHash;
1580 Json::Value const jrr = env.rpc(
1581 "json", "ledger_entry", to_string(jvParams))[jss::result];
1582 checkErrorValue(jrr, "malformedRequest", "");
1583 }
1584 {
1585 // Non-integer sub_index.
1586 Json::Value jvParams;
1587 jvParams[jss::directory] = Json::objectValue;
1588 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1589 jvParams[jss::directory][jss::sub_index] = 1.5;
1590 jvParams[jss::ledger_hash] = ledgerHash;
1591 Json::Value const jrr = env.rpc(
1592 "json", "ledger_entry", to_string(jvParams))[jss::result];
1593 checkErrorValue(jrr, "malformedRequest", "");
1594 }
1595 {
1596 // Malformed owner entry.
1597 Json::Value jvParams;
1598 jvParams[jss::directory] = Json::objectValue;
1599
1600 std::string const badAddress = makeBadAddress(alice.human());
1601 jvParams[jss::directory][jss::owner] = badAddress;
1602 jvParams[jss::ledger_hash] = ledgerHash;
1603 Json::Value const jrr = env.rpc(
1604 "json", "ledger_entry", to_string(jvParams))[jss::result];
1605 checkErrorValue(jrr, "malformedAddress", "");
1606 }
1607 {
1608 // Malformed directory object. Specify both dir_root and owner.
1609 Json::Value jvParams;
1610 jvParams[jss::directory] = Json::objectValue;
1611 jvParams[jss::directory][jss::owner] = alice.human();
1612 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1613 jvParams[jss::ledger_hash] = ledgerHash;
1614 Json::Value const jrr = env.rpc(
1615 "json", "ledger_entry", to_string(jvParams))[jss::result];
1616 checkErrorValue(jrr, "malformedRequest", "");
1617 }
1618 {
1619 // Incomplete directory object. Missing both dir_root and owner.
1620 Json::Value jvParams;
1621 jvParams[jss::directory] = Json::objectValue;
1622 jvParams[jss::directory][jss::sub_index] = 1;
1623 jvParams[jss::ledger_hash] = ledgerHash;
1624 Json::Value const jrr = env.rpc(
1625 "json", "ledger_entry", to_string(jvParams))[jss::result];
1626 checkErrorValue(jrr, "malformedRequest", "");
1627 }
1628 }
1629
1630 void
1632 {
1633 testcase("ledger_entry Request Escrow");
1634 using namespace test::jtx;
1635 Env env{*this};
1636 Account const alice{"alice"};
1637 env.fund(XRP(10000), alice);
1638 env.close();
1639
1640 // Lambda to create an escrow.
1641 auto escrowCreate = [](test::jtx::Account const& account,
1642 test::jtx::Account const& to,
1643 STAmount const& amount,
1644 NetClock::time_point const& cancelAfter) {
1645 Json::Value jv;
1646 jv[jss::TransactionType] = jss::EscrowCreate;
1647 jv[jss::Flags] = tfUniversal;
1648 jv[jss::Account] = account.human();
1649 jv[jss::Destination] = to.human();
1650 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1651 jv[sfFinishAfter.jsonName] =
1652 cancelAfter.time_since_epoch().count() + 2;
1653 return jv;
1654 };
1655
1656 using namespace std::chrono_literals;
1657 env(escrowCreate(alice, alice, XRP(333), env.now() + 2s));
1658 env.close();
1659
1660 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1661 std::string escrowIndex;
1662 {
1663 // Request the escrow using owner and sequence.
1664 Json::Value jvParams;
1665 jvParams[jss::escrow] = Json::objectValue;
1666 jvParams[jss::escrow][jss::owner] = alice.human();
1667 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1668 Json::Value const jrr = env.rpc(
1669 "json", "ledger_entry", to_string(jvParams))[jss::result];
1670 BEAST_EXPECT(
1671 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1672 escrowIndex = jrr[jss::index].asString();
1673 }
1674 {
1675 // Request the escrow by index.
1676 Json::Value jvParams;
1677 jvParams[jss::escrow] = escrowIndex;
1678 jvParams[jss::ledger_hash] = ledgerHash;
1679 Json::Value const jrr = env.rpc(
1680 "json", "ledger_entry", to_string(jvParams))[jss::result];
1681 BEAST_EXPECT(
1682 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1683 }
1684 {
1685 // Malformed owner entry.
1686 Json::Value jvParams;
1687 jvParams[jss::escrow] = Json::objectValue;
1688
1689 std::string const badAddress = makeBadAddress(alice.human());
1690 jvParams[jss::escrow][jss::owner] = badAddress;
1691 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1692 jvParams[jss::ledger_hash] = ledgerHash;
1693 Json::Value const jrr = env.rpc(
1694 "json", "ledger_entry", to_string(jvParams))[jss::result];
1695 checkErrorValue(jrr, "malformedOwner", "");
1696 }
1697 {
1698 // Missing owner.
1699 Json::Value jvParams;
1700 jvParams[jss::escrow] = Json::objectValue;
1701 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1702 jvParams[jss::ledger_hash] = ledgerHash;
1703 Json::Value const jrr = env.rpc(
1704 "json", "ledger_entry", to_string(jvParams))[jss::result];
1705 checkErrorValue(jrr, "malformedRequest", "");
1706 }
1707 {
1708 // Missing sequence.
1709 Json::Value jvParams;
1710 jvParams[jss::escrow] = Json::objectValue;
1711 jvParams[jss::escrow][jss::owner] = alice.human();
1712 jvParams[jss::ledger_hash] = ledgerHash;
1713 Json::Value const jrr = env.rpc(
1714 "json", "ledger_entry", to_string(jvParams))[jss::result];
1715 checkErrorValue(jrr, "malformedRequest", "");
1716 }
1717 {
1718 // Non-integer sequence.
1719 Json::Value jvParams;
1720 jvParams[jss::escrow] = Json::objectValue;
1721 jvParams[jss::escrow][jss::owner] = alice.human();
1722 jvParams[jss::escrow][jss::seq] =
1723 std::to_string(env.seq(alice) - 1);
1724 jvParams[jss::ledger_hash] = ledgerHash;
1725 Json::Value const jrr = env.rpc(
1726 "json", "ledger_entry", to_string(jvParams))[jss::result];
1727 checkErrorValue(jrr, "malformedRequest", "");
1728 }
1729 }
1730
1731 void
1733 {
1734 testcase("ledger_entry Request Offer");
1735 using namespace test::jtx;
1736 Env env{*this};
1737 Account const alice{"alice"};
1738 Account const gw{"gateway"};
1739 auto const USD = gw["USD"];
1740 env.fund(XRP(10000), alice, gw);
1741 env.close();
1742
1743 env(offer(alice, USD(321), XRP(322)));
1744 env.close();
1745
1746 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1747 std::string offerIndex;
1748 {
1749 // Request the offer using owner and sequence.
1750 Json::Value jvParams;
1751 jvParams[jss::offer] = Json::objectValue;
1752 jvParams[jss::offer][jss::account] = alice.human();
1753 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1754 jvParams[jss::ledger_hash] = ledgerHash;
1755 Json::Value const jrr = env.rpc(
1756 "json", "ledger_entry", to_string(jvParams))[jss::result];
1757 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1758 offerIndex = jrr[jss::index].asString();
1759 }
1760 {
1761 // Request the offer using its index.
1762 Json::Value jvParams;
1763 jvParams[jss::offer] = offerIndex;
1764 Json::Value const jrr = env.rpc(
1765 "json", "ledger_entry", to_string(jvParams))[jss::result];
1766 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1767 }
1768 {
1769 // Malformed account entry.
1770 Json::Value jvParams;
1771 jvParams[jss::offer] = Json::objectValue;
1772
1773 std::string const badAddress = makeBadAddress(alice.human());
1774 jvParams[jss::offer][jss::account] = badAddress;
1775 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1776 jvParams[jss::ledger_hash] = ledgerHash;
1777 Json::Value const jrr = env.rpc(
1778 "json", "ledger_entry", to_string(jvParams))[jss::result];
1779 checkErrorValue(jrr, "malformedAddress", "");
1780 }
1781 {
1782 // Malformed offer object. Missing account member.
1783 Json::Value jvParams;
1784 jvParams[jss::offer] = Json::objectValue;
1785 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1786 jvParams[jss::ledger_hash] = ledgerHash;
1787 Json::Value const jrr = env.rpc(
1788 "json", "ledger_entry", to_string(jvParams))[jss::result];
1789 checkErrorValue(jrr, "malformedRequest", "");
1790 }
1791 {
1792 // Malformed offer object. Missing seq member.
1793 Json::Value jvParams;
1794 jvParams[jss::offer] = Json::objectValue;
1795 jvParams[jss::offer][jss::account] = alice.human();
1796 jvParams[jss::ledger_hash] = ledgerHash;
1797 Json::Value const jrr = env.rpc(
1798 "json", "ledger_entry", to_string(jvParams))[jss::result];
1799 checkErrorValue(jrr, "malformedRequest", "");
1800 }
1801 {
1802 // Malformed offer object. Non-integral seq member.
1803 Json::Value jvParams;
1804 jvParams[jss::offer] = Json::objectValue;
1805 jvParams[jss::offer][jss::account] = alice.human();
1806 jvParams[jss::offer][jss::seq] = std::to_string(env.seq(alice) - 1);
1807 jvParams[jss::ledger_hash] = ledgerHash;
1808 Json::Value const jrr = env.rpc(
1809 "json", "ledger_entry", to_string(jvParams))[jss::result];
1810 checkErrorValue(jrr, "malformedRequest", "");
1811 }
1812 }
1813
1814 void
1816 {
1817 testcase("ledger_entry Request Pay Chan");
1818 using namespace test::jtx;
1819 using namespace std::literals::chrono_literals;
1820 Env env{*this};
1821 Account const alice{"alice"};
1822
1823 env.fund(XRP(10000), alice);
1824 env.close();
1825
1826 // Lambda to create a PayChan.
1827 auto payChanCreate = [](test::jtx::Account const& account,
1828 test::jtx::Account const& to,
1829 STAmount const& amount,
1830 NetClock::duration const& settleDelay,
1831 PublicKey const& pk) {
1832 Json::Value jv;
1833 jv[jss::TransactionType] = jss::PaymentChannelCreate;
1834 jv[jss::Account] = account.human();
1835 jv[jss::Destination] = to.human();
1836 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1837 jv[sfSettleDelay.jsonName] = settleDelay.count();
1838 jv[sfPublicKey.jsonName] = strHex(pk.slice());
1839 return jv;
1840 };
1841
1842 env(payChanCreate(alice, env.master, XRP(57), 18s, alice.pk()));
1843 env.close();
1844
1845 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1846
1847 uint256 const payChanIndex{
1848 keylet::payChan(alice, env.master, env.seq(alice) - 1).key};
1849 {
1850 // Request the payment channel using its index.
1851 Json::Value jvParams;
1852 jvParams[jss::payment_channel] = to_string(payChanIndex);
1853 jvParams[jss::ledger_hash] = ledgerHash;
1854 Json::Value const jrr = env.rpc(
1855 "json", "ledger_entry", to_string(jvParams))[jss::result];
1856 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
1857 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
1858 BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
1859 }
1860 {
1861 // Request an index that is not a payment channel.
1862 Json::Value jvParams;
1863 jvParams[jss::payment_channel] = ledgerHash;
1864 jvParams[jss::ledger_hash] = ledgerHash;
1865 Json::Value const jrr = env.rpc(
1866 "json", "ledger_entry", to_string(jvParams))[jss::result];
1867 checkErrorValue(jrr, "entryNotFound", "");
1868 }
1869 }
1870
1871 void
1873 {
1874 testcase("ledger_entry Request RippleState");
1875 using namespace test::jtx;
1876 Env env{*this};
1877 Account const alice{"alice"};
1878 Account const gw{"gateway"};
1879 auto const USD = gw["USD"];
1880 env.fund(XRP(10000), alice, gw);
1881 env.close();
1882
1883 env.trust(USD(999), alice);
1884 env.close();
1885
1886 env(pay(gw, alice, USD(97)));
1887 env.close();
1888
1889 // check both aliases
1890 for (auto const& fieldName : {jss::ripple_state, jss::state})
1891 {
1892 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1893 {
1894 // Request the trust line using the accounts and currency.
1895 Json::Value jvParams;
1896 jvParams[fieldName] = Json::objectValue;
1897 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1898 jvParams[fieldName][jss::accounts][0u] = alice.human();
1899 jvParams[fieldName][jss::accounts][1u] = gw.human();
1900 jvParams[fieldName][jss::currency] = "USD";
1901 jvParams[jss::ledger_hash] = ledgerHash;
1902 Json::Value const jrr = env.rpc(
1903 "json", "ledger_entry", to_string(jvParams))[jss::result];
1904 BEAST_EXPECT(
1905 jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
1906 BEAST_EXPECT(
1907 jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
1908 }
1909 {
1910 // ripple_state is not an object.
1911 Json::Value jvParams;
1912 jvParams[fieldName] = "ripple_state";
1913 jvParams[jss::ledger_hash] = ledgerHash;
1914 Json::Value const jrr = env.rpc(
1915 "json", "ledger_entry", to_string(jvParams))[jss::result];
1916 checkErrorValue(jrr, "malformedRequest", "");
1917 }
1918 {
1919 // ripple_state.currency is missing.
1920 Json::Value jvParams;
1921 jvParams[fieldName] = Json::objectValue;
1922 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1923 jvParams[fieldName][jss::accounts][0u] = alice.human();
1924 jvParams[fieldName][jss::accounts][1u] = gw.human();
1925 jvParams[jss::ledger_hash] = ledgerHash;
1926 Json::Value const jrr = env.rpc(
1927 "json", "ledger_entry", to_string(jvParams))[jss::result];
1928 checkErrorValue(jrr, "malformedRequest", "");
1929 }
1930 {
1931 // ripple_state accounts is not an array.
1932 Json::Value jvParams;
1933 jvParams[fieldName] = Json::objectValue;
1934 jvParams[fieldName][jss::accounts] = 2;
1935 jvParams[fieldName][jss::currency] = "USD";
1936 jvParams[jss::ledger_hash] = ledgerHash;
1937 Json::Value const jrr = env.rpc(
1938 "json", "ledger_entry", to_string(jvParams))[jss::result];
1939 checkErrorValue(jrr, "malformedRequest", "");
1940 }
1941 {
1942 // ripple_state one of the accounts is missing.
1943 Json::Value jvParams;
1944 jvParams[fieldName] = Json::objectValue;
1945 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1946 jvParams[fieldName][jss::accounts][0u] = alice.human();
1947 jvParams[fieldName][jss::currency] = "USD";
1948 jvParams[jss::ledger_hash] = ledgerHash;
1949 Json::Value const jrr = env.rpc(
1950 "json", "ledger_entry", to_string(jvParams))[jss::result];
1951 checkErrorValue(jrr, "malformedRequest", "");
1952 }
1953 {
1954 // ripple_state more than 2 accounts.
1955 Json::Value jvParams;
1956 jvParams[fieldName] = Json::objectValue;
1957 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1958 jvParams[fieldName][jss::accounts][0u] = alice.human();
1959 jvParams[fieldName][jss::accounts][1u] = gw.human();
1960 jvParams[fieldName][jss::accounts][2u] = alice.human();
1961 jvParams[fieldName][jss::currency] = "USD";
1962 jvParams[jss::ledger_hash] = ledgerHash;
1963 Json::Value const jrr = env.rpc(
1964 "json", "ledger_entry", to_string(jvParams))[jss::result];
1965 checkErrorValue(jrr, "malformedRequest", "");
1966 }
1967 {
1968 // ripple_state account[0] is not a string.
1969 Json::Value jvParams;
1970 jvParams[fieldName] = Json::objectValue;
1971 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1972 jvParams[fieldName][jss::accounts][0u] = 44;
1973 jvParams[fieldName][jss::accounts][1u] = gw.human();
1974 jvParams[fieldName][jss::currency] = "USD";
1975 jvParams[jss::ledger_hash] = ledgerHash;
1976 Json::Value const jrr = env.rpc(
1977 "json", "ledger_entry", to_string(jvParams))[jss::result];
1978 checkErrorValue(jrr, "malformedRequest", "");
1979 }
1980 {
1981 // ripple_state account[1] is not a string.
1982 Json::Value jvParams;
1983 jvParams[fieldName] = Json::objectValue;
1984 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1985 jvParams[fieldName][jss::accounts][0u] = alice.human();
1986 jvParams[fieldName][jss::accounts][1u] = 21;
1987 jvParams[fieldName][jss::currency] = "USD";
1988 jvParams[jss::ledger_hash] = ledgerHash;
1989 Json::Value const jrr = env.rpc(
1990 "json", "ledger_entry", to_string(jvParams))[jss::result];
1991 checkErrorValue(jrr, "malformedRequest", "");
1992 }
1993 {
1994 // ripple_state account[0] == account[1].
1995 Json::Value jvParams;
1996 jvParams[fieldName] = Json::objectValue;
1997 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1998 jvParams[fieldName][jss::accounts][0u] = alice.human();
1999 jvParams[fieldName][jss::accounts][1u] = alice.human();
2000 jvParams[fieldName][jss::currency] = "USD";
2001 jvParams[jss::ledger_hash] = ledgerHash;
2002 Json::Value const jrr = env.rpc(
2003 "json", "ledger_entry", to_string(jvParams))[jss::result];
2004 checkErrorValue(jrr, "malformedRequest", "");
2005 }
2006 {
2007 // ripple_state malformed account[0].
2008 Json::Value jvParams;
2009 jvParams[fieldName] = Json::objectValue;
2010 jvParams[fieldName][jss::accounts] = Json::arrayValue;
2011 jvParams[fieldName][jss::accounts][0u] =
2012 makeBadAddress(alice.human());
2013 jvParams[fieldName][jss::accounts][1u] = gw.human();
2014 jvParams[fieldName][jss::currency] = "USD";
2015 jvParams[jss::ledger_hash] = ledgerHash;
2016 Json::Value const jrr = env.rpc(
2017 "json", "ledger_entry", to_string(jvParams))[jss::result];
2018 checkErrorValue(jrr, "malformedAddress", "");
2019 }
2020 {
2021 // ripple_state malformed account[1].
2022 Json::Value jvParams;
2023 jvParams[fieldName] = Json::objectValue;
2024 jvParams[fieldName][jss::accounts] = Json::arrayValue;
2025 jvParams[fieldName][jss::accounts][0u] = alice.human();
2026 jvParams[fieldName][jss::accounts][1u] =
2027 makeBadAddress(gw.human());
2028 jvParams[fieldName][jss::currency] = "USD";
2029 jvParams[jss::ledger_hash] = ledgerHash;
2030 Json::Value const jrr = env.rpc(
2031 "json", "ledger_entry", to_string(jvParams))[jss::result];
2032 checkErrorValue(jrr, "malformedAddress", "");
2033 }
2034 {
2035 // ripple_state malformed currency.
2036 Json::Value jvParams;
2037 jvParams[fieldName] = Json::objectValue;
2038 jvParams[fieldName][jss::accounts] = Json::arrayValue;
2039 jvParams[fieldName][jss::accounts][0u] = alice.human();
2040 jvParams[fieldName][jss::accounts][1u] = gw.human();
2041 jvParams[fieldName][jss::currency] = "USDollars";
2042 jvParams[jss::ledger_hash] = ledgerHash;
2043 Json::Value const jrr = env.rpc(
2044 "json", "ledger_entry", to_string(jvParams))[jss::result];
2045 checkErrorValue(jrr, "malformedCurrency", "");
2046 }
2047 }
2048 }
2049
2050 void
2052 {
2053 testcase("ledger_entry Request Ticket");
2054 using namespace test::jtx;
2055 Env env{*this};
2056 env.close();
2057
2058 // Create two tickets.
2059 std::uint32_t const tkt1{env.seq(env.master) + 1};
2060 env(ticket::create(env.master, 2));
2061 env.close();
2062
2063 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2064 // Request four tickets: one before the first one we created, the
2065 // two created tickets, and the ticket that would come after the
2066 // last created ticket.
2067 {
2068 // Not a valid ticket requested by index.
2069 Json::Value jvParams;
2070 jvParams[jss::ticket] =
2071 to_string(getTicketIndex(env.master, tkt1 - 1));
2072 jvParams[jss::ledger_hash] = ledgerHash;
2073 Json::Value const jrr = env.rpc(
2074 "json", "ledger_entry", to_string(jvParams))[jss::result];
2075 checkErrorValue(jrr, "entryNotFound", "");
2076 }
2077 {
2078 // First real ticket requested by index.
2079 Json::Value jvParams;
2080 jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1));
2081 jvParams[jss::ledger_hash] = ledgerHash;
2082 Json::Value const jrr = env.rpc(
2083 "json", "ledger_entry", to_string(jvParams))[jss::result];
2084 BEAST_EXPECT(
2085 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Ticket);
2086 BEAST_EXPECT(jrr[jss::node][sfTicketSequence.jsonName] == tkt1);
2087 }
2088 {
2089 // Second real ticket requested by account and sequence.
2090 Json::Value jvParams;
2091 jvParams[jss::ticket] = Json::objectValue;
2092 jvParams[jss::ticket][jss::account] = env.master.human();
2093 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 1;
2094 jvParams[jss::ledger_hash] = ledgerHash;
2095 Json::Value const jrr = env.rpc(
2096 "json", "ledger_entry", to_string(jvParams))[jss::result];
2097 BEAST_EXPECT(
2098 jrr[jss::node][jss::index] ==
2099 to_string(getTicketIndex(env.master, tkt1 + 1)));
2100 }
2101 {
2102 // Not a valid ticket requested by account and sequence.
2103 Json::Value jvParams;
2104 jvParams[jss::ticket] = Json::objectValue;
2105 jvParams[jss::ticket][jss::account] = env.master.human();
2106 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 2;
2107 jvParams[jss::ledger_hash] = ledgerHash;
2108 Json::Value const jrr = env.rpc(
2109 "json", "ledger_entry", to_string(jvParams))[jss::result];
2110 checkErrorValue(jrr, "entryNotFound", "");
2111 }
2112 {
2113 // Request a ticket using an account root entry.
2114 Json::Value jvParams;
2115 jvParams[jss::ticket] = to_string(keylet::account(env.master).key);
2116 jvParams[jss::ledger_hash] = ledgerHash;
2117 Json::Value const jrr = env.rpc(
2118 "json", "ledger_entry", to_string(jvParams))[jss::result];
2119 checkErrorValue(jrr, "unexpectedLedgerType", "");
2120 }
2121 {
2122 // Malformed account entry.
2123 Json::Value jvParams;
2124 jvParams[jss::ticket] = Json::objectValue;
2125
2126 std::string const badAddress = makeBadAddress(env.master.human());
2127 jvParams[jss::ticket][jss::account] = badAddress;
2128 jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
2129 jvParams[jss::ledger_hash] = ledgerHash;
2130 Json::Value const jrr = env.rpc(
2131 "json", "ledger_entry", to_string(jvParams))[jss::result];
2132 checkErrorValue(jrr, "malformedAddress", "");
2133 }
2134 {
2135 // Malformed ticket object. Missing account member.
2136 Json::Value jvParams;
2137 jvParams[jss::ticket] = Json::objectValue;
2138 jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
2139 jvParams[jss::ledger_hash] = ledgerHash;
2140 Json::Value const jrr = env.rpc(
2141 "json", "ledger_entry", to_string(jvParams))[jss::result];
2142 checkErrorValue(jrr, "malformedRequest", "");
2143 }
2144 {
2145 // Malformed ticket object. Missing seq member.
2146 Json::Value jvParams;
2147 jvParams[jss::ticket] = Json::objectValue;
2148 jvParams[jss::ticket][jss::account] = env.master.human();
2149 jvParams[jss::ledger_hash] = ledgerHash;
2150 Json::Value const jrr = env.rpc(
2151 "json", "ledger_entry", to_string(jvParams))[jss::result];
2152 checkErrorValue(jrr, "malformedRequest", "");
2153 }
2154 {
2155 // Malformed ticket object. Non-integral seq member.
2156 Json::Value jvParams;
2157 jvParams[jss::ticket] = Json::objectValue;
2158 jvParams[jss::ticket][jss::account] = env.master.human();
2159 jvParams[jss::ticket][jss::ticket_seq] =
2160 std::to_string(env.seq(env.master) - 1);
2161 jvParams[jss::ledger_hash] = ledgerHash;
2162 Json::Value const jrr = env.rpc(
2163 "json", "ledger_entry", to_string(jvParams))[jss::result];
2164 checkErrorValue(jrr, "malformedRequest", "");
2165 }
2166 }
2167
2168 void
2170 {
2171 testcase("ledger_entry Request DID");
2172 using namespace test::jtx;
2173 using namespace std::literals::chrono_literals;
2174 Env env{*this};
2175 Account const alice{"alice"};
2176
2177 env.fund(XRP(10000), alice);
2178 env.close();
2179
2180 // Lambda to create a DID.
2181 auto didCreate = [](test::jtx::Account const& account) {
2182 Json::Value jv;
2183 jv[jss::TransactionType] = jss::DIDSet;
2184 jv[jss::Account] = account.human();
2185 jv[sfDIDDocument.jsonName] = strHex(std::string{"data"});
2186 jv[sfURI.jsonName] = strHex(std::string{"uri"});
2187 return jv;
2188 };
2189
2190 env(didCreate(alice));
2191 env.close();
2192
2193 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2194
2195 {
2196 // Request the DID using its index.
2197 Json::Value jvParams;
2198 jvParams[jss::did] = alice.human();
2199 jvParams[jss::ledger_hash] = ledgerHash;
2200 Json::Value const jrr = env.rpc(
2201 "json", "ledger_entry", to_string(jvParams))[jss::result];
2202 BEAST_EXPECT(
2203 jrr[jss::node][sfDIDDocument.jsonName] ==
2204 strHex(std::string{"data"}));
2205 BEAST_EXPECT(
2206 jrr[jss::node][sfURI.jsonName] == strHex(std::string{"uri"}));
2207 }
2208 {
2209 // Request an index that is not a DID.
2210 Json::Value jvParams;
2211 jvParams[jss::did] = env.master.human();
2212 jvParams[jss::ledger_hash] = ledgerHash;
2213 Json::Value const jrr = env.rpc(
2214 "json", "ledger_entry", to_string(jvParams))[jss::result];
2215 checkErrorValue(jrr, "entryNotFound", "");
2216 }
2217 }
2218
2219 void
2220 testLedgerEntryInvalidParams(unsigned int apiVersion)
2221 {
2222 testcase(
2223 "ledger_entry Request With Invalid Parameters v" +
2224 std::to_string(apiVersion));
2225 using namespace test::jtx;
2226 Env env{*this};
2227
2228 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2229
2230 auto makeParams = [&apiVersion](std::function<void(Json::Value&)> f) {
2231 Json::Value params;
2232 params[jss::api_version] = apiVersion;
2233 f(params);
2234 return params;
2235 };
2236 // "features" is not an option supported by ledger_entry.
2237 {
2238 auto const jvParams =
2239 makeParams([&ledgerHash](Json::Value& jvParams) {
2240 jvParams[jss::features] = ledgerHash;
2241 jvParams[jss::ledger_hash] = ledgerHash;
2242 });
2243 Json::Value const jrr = env.rpc(
2244 "json", "ledger_entry", to_string(jvParams))[jss::result];
2245
2246 if (apiVersion < 2u)
2247 checkErrorValue(jrr, "unknownOption", "");
2248 else
2249 checkErrorValue(jrr, "invalidParams", "");
2250 }
2251 Json::Value const injectObject = []() {
2253 obj[jss::account] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2254 obj[jss::ledger_index] = "validated";
2255 return obj;
2256 }();
2257 Json::Value const injectArray = []() {
2259 arr[0u] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2260 arr[1u] = "validated";
2261 return arr;
2262 }();
2263
2264 // invalid input for fields that can handle an object, but can't handle
2265 // an array
2266 for (auto const& field :
2267 {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm})
2268 {
2269 auto const jvParams =
2270 makeParams([&field, &injectArray](Json::Value& jvParams) {
2271 jvParams[field] = injectArray;
2272 });
2273
2274 Json::Value const jrr = env.rpc(
2275 "json", "ledger_entry", to_string(jvParams))[jss::result];
2276
2277 if (apiVersion < 2u)
2278 checkErrorValue(jrr, "internal", "Internal error.");
2279 else
2280 checkErrorValue(jrr, "invalidParams", "");
2281 }
2282 // Fields that can handle objects just fine
2283 for (auto const& field :
2284 {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm})
2285 {
2286 auto const jvParams =
2287 makeParams([&field, &injectObject](Json::Value& jvParams) {
2288 jvParams[field] = injectObject;
2289 });
2290
2291 Json::Value const jrr = env.rpc(
2292 "json", "ledger_entry", to_string(jvParams))[jss::result];
2293
2294 checkErrorValue(jrr, "malformedRequest", "");
2295 }
2296
2297 for (auto const& inject : {injectObject, injectArray})
2298 {
2299 // invalid input for fields that can't handle an object or an array
2300 for (auto const& field :
2301 {jss::index,
2302 jss::account_root,
2303 jss::check,
2304 jss::payment_channel})
2305 {
2306 auto const jvParams =
2307 makeParams([&field, &inject](Json::Value& jvParams) {
2308 jvParams[field] = inject;
2309 });
2310
2311 Json::Value const jrr = env.rpc(
2312 "json", "ledger_entry", to_string(jvParams))[jss::result];
2313
2314 if (apiVersion < 2u)
2315 checkErrorValue(jrr, "internal", "Internal error.");
2316 else
2317 checkErrorValue(jrr, "invalidParams", "");
2318 }
2319 // directory sub-fields
2320 for (auto const& field : {jss::dir_root, jss::owner})
2321 {
2322 auto const jvParams =
2323 makeParams([&field, &inject](Json::Value& jvParams) {
2324 jvParams[jss::directory][field] = inject;
2325 });
2326
2327 Json::Value const jrr = env.rpc(
2328 "json", "ledger_entry", to_string(jvParams))[jss::result];
2329
2330 if (apiVersion < 2u)
2331 checkErrorValue(jrr, "internal", "Internal error.");
2332 else
2333 checkErrorValue(jrr, "invalidParams", "");
2334 }
2335 // escrow sub-fields
2336 {
2337 auto const jvParams =
2338 makeParams([&inject](Json::Value& jvParams) {
2339 jvParams[jss::escrow][jss::owner] = inject;
2340 jvParams[jss::escrow][jss::seq] = 99;
2341 });
2342
2343 Json::Value const jrr = env.rpc(
2344 "json", "ledger_entry", to_string(jvParams))[jss::result];
2345
2346 if (apiVersion < 2u)
2347 checkErrorValue(jrr, "internal", "Internal error.");
2348 else
2349 checkErrorValue(jrr, "invalidParams", "");
2350 }
2351 // offer sub-fields
2352 {
2353 auto const jvParams =
2354 makeParams([&inject](Json::Value& jvParams) {
2355 jvParams[jss::offer][jss::account] = inject;
2356 jvParams[jss::offer][jss::seq] = 99;
2357 });
2358
2359 Json::Value const jrr = env.rpc(
2360 "json", "ledger_entry", to_string(jvParams))[jss::result];
2361
2362 if (apiVersion < 2u)
2363 checkErrorValue(jrr, "internal", "Internal error.");
2364 else
2365 checkErrorValue(jrr, "invalidParams", "");
2366 }
2367 // ripple_state sub-fields
2368 {
2369 auto const jvParams =
2370 makeParams([&inject](Json::Value& jvParams) {
2372 rs[jss::currency] = "FOO";
2373 rs[jss::accounts] = Json::Value(Json::arrayValue);
2374 rs[jss::accounts][0u] =
2375 "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2376 rs[jss::accounts][1u] =
2377 "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv";
2378 rs[jss::currency] = inject;
2379 jvParams[jss::ripple_state] = std::move(rs);
2380 });
2381
2382 Json::Value const jrr = env.rpc(
2383 "json", "ledger_entry", to_string(jvParams))[jss::result];
2384
2385 if (apiVersion < 2u)
2386 checkErrorValue(jrr, "internal", "Internal error.");
2387 else
2388 checkErrorValue(jrr, "invalidParams", "");
2389 }
2390 // ticket sub-fields
2391 {
2392 auto const jvParams =
2393 makeParams([&inject](Json::Value& jvParams) {
2394 jvParams[jss::ticket][jss::account] = inject;
2395 jvParams[jss::ticket][jss::ticket_seq] = 99;
2396 });
2397
2398 Json::Value const jrr = env.rpc(
2399 "json", "ledger_entry", to_string(jvParams))[jss::result];
2400
2401 if (apiVersion < 2u)
2402 checkErrorValue(jrr, "internal", "Internal error.");
2403 else
2404 checkErrorValue(jrr, "invalidParams", "");
2405 }
2406
2407 // Fields that can handle malformed inputs just fine
2408 for (auto const& field : {jss::nft_page, jss::deposit_preauth})
2409 {
2410 auto const jvParams =
2411 makeParams([&field, &inject](Json::Value& jvParams) {
2412 jvParams[field] = inject;
2413 });
2414
2415 Json::Value const jrr = env.rpc(
2416 "json", "ledger_entry", to_string(jvParams))[jss::result];
2417
2418 checkErrorValue(jrr, "malformedRequest", "");
2419 }
2420 // Subfields of deposit_preauth that can handle malformed inputs
2421 // fine
2422 for (auto const& field : {jss::owner, jss::authorized})
2423 {
2424 auto const jvParams =
2425 makeParams([&field, &inject](Json::Value& jvParams) {
2426 auto pa = Json::Value(Json::objectValue);
2427 pa[jss::owner] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2428 pa[jss::authorized] =
2429 "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv";
2430 pa[field] = inject;
2431 jvParams[jss::deposit_preauth] = std::move(pa);
2432 });
2433
2434 Json::Value const jrr = env.rpc(
2435 "json", "ledger_entry", to_string(jvParams))[jss::result];
2436
2437 checkErrorValue(jrr, "malformedRequest", "");
2438 }
2439 }
2440 }
2441
2446 void
2448 {
2449 testcase("Lookup ledger");
2450 using namespace test::jtx;
2451 Env env{*this, FeatureBitset{}}; // hashes requested below assume
2452 // no amendments
2453 env.fund(XRP(10000), "alice");
2454 env.close();
2455 env.fund(XRP(10000), "bob");
2456 env.close();
2457 env.fund(XRP(10000), "jim");
2458 env.close();
2459 env.fund(XRP(10000), "jill");
2460
2461 {
2462 // access via the legacy ledger field, keyword index values
2463 Json::Value jvParams;
2464 jvParams[jss::ledger] = "closed";
2465 auto jrr = env.rpc(
2466 "json",
2467 "ledger",
2468 boost::lexical_cast<std::string>(jvParams))[jss::result];
2469 BEAST_EXPECT(jrr.isMember(jss::ledger));
2470 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2471 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
2472
2473 jvParams[jss::ledger] = "validated";
2474 jrr = env.rpc(
2475 "json",
2476 "ledger",
2477 boost::lexical_cast<std::string>(jvParams))[jss::result];
2478 BEAST_EXPECT(jrr.isMember(jss::ledger));
2479 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2480 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
2481
2482 jvParams[jss::ledger] = "current";
2483 jrr = env.rpc(
2484 "json",
2485 "ledger",
2486 boost::lexical_cast<std::string>(jvParams))[jss::result];
2487 BEAST_EXPECT(jrr.isMember(jss::ledger));
2488 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
2489
2490 // ask for a bad ledger keyword
2491 jvParams[jss::ledger] = "invalid";
2492 jrr = env.rpc(
2493 "json",
2494 "ledger",
2495 boost::lexical_cast<std::string>(jvParams))[jss::result];
2496 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2497 BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
2498
2499 // numeric index
2500 jvParams[jss::ledger] = 4;
2501 jrr = env.rpc(
2502 "json",
2503 "ledger",
2504 boost::lexical_cast<std::string>(jvParams))[jss::result];
2505 BEAST_EXPECT(jrr.isMember(jss::ledger));
2506 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2507 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "4");
2508
2509 // numeric index - out of range
2510 jvParams[jss::ledger] = 20;
2511 jrr = env.rpc(
2512 "json",
2513 "ledger",
2514 boost::lexical_cast<std::string>(jvParams))[jss::result];
2515 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
2516 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
2517 }
2518
2519 {
2520 std::string const hash3{
2521 "E86DE7F3D7A4D9CE17EF7C8BA08A8F4D"
2522 "8F643B9552F0D895A31CDA78F541DE4E"};
2523 // access via the ledger_hash field
2524 Json::Value jvParams;
2525 jvParams[jss::ledger_hash] = hash3;
2526 auto jrr = env.rpc(
2527 "json",
2528 "ledger",
2529 boost::lexical_cast<std::string>(jvParams))[jss::result];
2530 BEAST_EXPECT(jrr.isMember(jss::ledger));
2531 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2532 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "3");
2533
2534 // extra leading hex chars in hash are not allowed
2535 jvParams[jss::ledger_hash] = "DEADBEEF" + hash3;
2536 jrr = env.rpc(
2537 "json",
2538 "ledger",
2539 boost::lexical_cast<std::string>(jvParams))[jss::result];
2540 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2541 BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
2542
2543 // request with non-string ledger_hash
2544 jvParams[jss::ledger_hash] = 2;
2545 jrr = env.rpc(
2546 "json",
2547 "ledger",
2548 boost::lexical_cast<std::string>(jvParams))[jss::result];
2549 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2550 BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashNotString");
2551
2552 // malformed (non hex) hash
2553 jvParams[jss::ledger_hash] =
2554 "2E81FC6EC0DD943197EGC7E3FBE9AE30"
2555 "7F2775F2F7485BB37307984C3C0F2340";
2556 jrr = env.rpc(
2557 "json",
2558 "ledger",
2559 boost::lexical_cast<std::string>(jvParams))[jss::result];
2560 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2561 BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
2562
2563 // properly formed, but just doesn't exist
2564 jvParams[jss::ledger_hash] =
2565 "8C3EEDB3124D92E49E75D81A8826A2E6"
2566 "5A75FD71FC3FD6F36FEB803C5F1D812D";
2567 jrr = env.rpc(
2568 "json",
2569 "ledger",
2570 boost::lexical_cast<std::string>(jvParams))[jss::result];
2571 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
2572 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
2573 }
2574
2575 {
2576 // access via the ledger_index field, keyword index values
2577 Json::Value jvParams;
2578 jvParams[jss::ledger_index] = "closed";
2579 auto jrr = env.rpc(
2580 "json",
2581 "ledger",
2582 boost::lexical_cast<std::string>(jvParams))[jss::result];
2583 BEAST_EXPECT(jrr.isMember(jss::ledger));
2584 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2585 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
2586 BEAST_EXPECT(jrr.isMember(jss::ledger_index));
2587
2588 jvParams[jss::ledger_index] = "validated";
2589 jrr = env.rpc(
2590 "json",
2591 "ledger",
2592 boost::lexical_cast<std::string>(jvParams))[jss::result];
2593 BEAST_EXPECT(jrr.isMember(jss::ledger));
2594 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2595 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
2596
2597 jvParams[jss::ledger_index] = "current";
2598 jrr = env.rpc(
2599 "json",
2600 "ledger",
2601 boost::lexical_cast<std::string>(jvParams))[jss::result];
2602 BEAST_EXPECT(jrr.isMember(jss::ledger));
2603 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
2604 BEAST_EXPECT(jrr.isMember(jss::ledger_current_index));
2605
2606 // ask for a bad ledger keyword
2607 jvParams[jss::ledger_index] = "invalid";
2608 jrr = env.rpc(
2609 "json",
2610 "ledger",
2611 boost::lexical_cast<std::string>(jvParams))[jss::result];
2612 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2613 BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
2614
2615 // numeric index
2616 for (auto i : {1, 2, 3, 4, 5, 6})
2617 {
2618 jvParams[jss::ledger_index] = i;
2619 jrr = env.rpc(
2620 "json",
2621 "ledger",
2622 boost::lexical_cast<std::string>(jvParams))[jss::result];
2623 BEAST_EXPECT(jrr.isMember(jss::ledger));
2624 if (i < 6)
2625 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2626 BEAST_EXPECT(
2627 jrr[jss::ledger][jss::ledger_index] == std::to_string(i));
2628 }
2629
2630 // numeric index - out of range
2631 jvParams[jss::ledger_index] = 7;
2632 jrr = env.rpc(
2633 "json",
2634 "ledger",
2635 boost::lexical_cast<std::string>(jvParams))[jss::result];
2636 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
2637 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
2638 }
2639 }
2640
2641 void
2643 {
2644 testcase("Ledger with queueing disabled");
2645 using namespace test::jtx;
2646 Env env{*this};
2647
2648 Json::Value jv;
2649 jv[jss::ledger_index] = "current";
2650 jv[jss::queue] = true;
2651 jv[jss::expand] = true;
2652
2653 auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2654 BEAST_EXPECT(!jrr.isMember(jss::queue_data));
2655 }
2656
2657 void
2659 {
2660 testcase("Ledger with Queued Transactions");
2661 using namespace test::jtx;
2662 Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
2663 auto& section = cfg->section("transaction_queue");
2664 section.set("minimum_txn_in_ledger_standalone", "3");
2665 section.set("normal_consensus_increase_percent", "0");
2666 return cfg;
2667 })};
2668
2669 Json::Value jv;
2670 jv[jss::ledger_index] = "current";
2671 jv[jss::queue] = true;
2672 jv[jss::expand] = true;
2673
2674 Account const alice{"alice"};
2675 Account const bob{"bob"};
2676 Account const charlie{"charlie"};
2677 Account const daria{"daria"};
2678 env.fund(XRP(10000), alice);
2679 env.fund(XRP(10000), bob);
2680 env.close();
2681 env.fund(XRP(10000), charlie);
2682 env.fund(XRP(10000), daria);
2683 env.close();
2684
2685 auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2686 BEAST_EXPECT(!jrr.isMember(jss::queue_data));
2687
2688 // Fill the open ledger
2689 for (;;)
2690 {
2691 auto metrics = env.app().getTxQ().getMetrics(*env.current());
2692 if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
2693 break;
2694 env(noop(alice));
2695 }
2696
2697 BEAST_EXPECT(env.current()->info().seq == 5);
2698 // Put some txs in the queue
2699 // Alice
2700 auto aliceSeq = env.seq(alice);
2701 env(pay(alice, "george", XRP(1000)),
2702 json(R"({"LastLedgerSequence":7})"),
2703 ter(terQUEUED));
2704 env(offer(alice, XRP(50000), alice["USD"](5000)),
2705 seq(aliceSeq + 1),
2706 ter(terQUEUED));
2707 env(noop(alice), seq(aliceSeq + 2), ter(terQUEUED));
2708 // Bob
2709 auto batch = [&env](Account a) {
2710 auto aSeq = env.seq(a);
2711 // Enough fee to get in front of alice in the queue
2712 for (int i = 0; i < 10; ++i)
2713 {
2714 env(noop(a), fee(1000 + i), seq(aSeq + i), ter(terQUEUED));
2715 }
2716 };
2717 batch(bob);
2718 // Charlie
2719 batch(charlie);
2720 // Daria
2721 batch(daria);
2722
2723 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2724 BEAST_EXPECT(jrr[jss::queue_data].size() == 33);
2725
2726 // Close enough ledgers so that alice's first tx expires.
2727 env.close();
2728 env.close();
2729 env.close();
2730 BEAST_EXPECT(env.current()->info().seq == 8);
2731
2732 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2733 BEAST_EXPECT(jrr[jss::queue_data].size() == 11);
2734
2735 env.close();
2736
2737 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2738 const std::string txid0 = [&]() {
2739 auto const& parentHash = env.current()->info().parentHash;
2740 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
2741 {
2742 const std::string txid1 = [&]() {
2743 auto const& txj = jrr[jss::queue_data][1u];
2744 BEAST_EXPECT(txj[jss::account] == alice.human());
2745 BEAST_EXPECT(txj[jss::fee_level] == "256");
2746 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2747 BEAST_EXPECT(txj["retries_remaining"] == 10);
2748 BEAST_EXPECT(txj.isMember(jss::tx));
2749 auto const& tx = txj[jss::tx];
2750 BEAST_EXPECT(tx[jss::Account] == alice.human());
2751 BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
2752 return tx[jss::hash].asString();
2753 }();
2754
2755 auto const& txj = jrr[jss::queue_data][0u];
2756 BEAST_EXPECT(txj[jss::account] == alice.human());
2757 BEAST_EXPECT(txj[jss::fee_level] == "256");
2758 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2759 BEAST_EXPECT(txj["retries_remaining"] == 10);
2760 BEAST_EXPECT(txj.isMember(jss::tx));
2761 auto const& tx = txj[jss::tx];
2762 BEAST_EXPECT(tx[jss::Account] == alice.human());
2763 BEAST_EXPECT(tx[jss::TransactionType] == jss::OfferCreate);
2764 const auto txid0 = tx[jss::hash].asString();
2765 uint256 tx0, tx1;
2766 BEAST_EXPECT(tx0.parseHex(txid0));
2767 BEAST_EXPECT(tx1.parseHex(txid1));
2768 BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
2769 return txid0;
2770 }
2771 return std::string{};
2772 }();
2773
2774 env.close();
2775
2776 jv[jss::expand] = false;
2777
2778 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2779 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
2780 {
2781 auto const& parentHash = env.current()->info().parentHash;
2782 auto const txid1 = [&]() {
2783 auto const& txj = jrr[jss::queue_data][1u];
2784 BEAST_EXPECT(txj[jss::account] == alice.human());
2785 BEAST_EXPECT(txj[jss::fee_level] == "256");
2786 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2787 BEAST_EXPECT(txj.isMember(jss::tx));
2788 return txj[jss::tx].asString();
2789 }();
2790 auto const& txj = jrr[jss::queue_data][0u];
2791 BEAST_EXPECT(txj[jss::account] == alice.human());
2792 BEAST_EXPECT(txj[jss::fee_level] == "256");
2793 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2794 BEAST_EXPECT(txj["retries_remaining"] == 9);
2795 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
2796 BEAST_EXPECT(txj.isMember(jss::tx));
2797 BEAST_EXPECT(txj[jss::tx] == txid0);
2798 uint256 tx0, tx1;
2799 BEAST_EXPECT(tx0.parseHex(txid0));
2800 BEAST_EXPECT(tx1.parseHex(txid1));
2801 BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
2802 }
2803
2804 env.close();
2805
2806 jv[jss::expand] = true;
2807 jv[jss::binary] = true;
2808
2809 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2810 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
2811 {
2812 auto const& txj = jrr[jss::queue_data][1u];
2813 BEAST_EXPECT(txj[jss::account] == alice.human());
2814 BEAST_EXPECT(txj[jss::fee_level] == "256");
2815 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2816 BEAST_EXPECT(txj["retries_remaining"] == 8);
2817 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
2818 BEAST_EXPECT(txj.isMember(jss::tx));
2819 BEAST_EXPECT(txj[jss::tx].isMember(jss::tx_blob));
2820
2821 auto const& txj2 = jrr[jss::queue_data][0u];
2822 BEAST_EXPECT(txj2[jss::account] == alice.human());
2823 BEAST_EXPECT(txj2[jss::fee_level] == "256");
2824 BEAST_EXPECT(txj2["preflight_result"] == "tesSUCCESS");
2825 BEAST_EXPECT(txj2["retries_remaining"] == 10);
2826 BEAST_EXPECT(!txj2.isMember("last_result"));
2827 BEAST_EXPECT(txj2.isMember(jss::tx));
2828 BEAST_EXPECT(txj2[jss::tx].isMember(jss::tx_blob));
2829 }
2830
2831 for (int i = 0; i != 9; ++i)
2832 {
2833 env.close();
2834 }
2835
2836 jv[jss::expand] = false;
2837 jv[jss::binary] = false;
2838
2839 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2840 const std::string txid2 = [&]() {
2841 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
2842 {
2843 auto const& txj = jrr[jss::queue_data][0u];
2844 BEAST_EXPECT(txj[jss::account] == alice.human());
2845 BEAST_EXPECT(txj[jss::fee_level] == "256");
2846 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2847 BEAST_EXPECT(txj["retries_remaining"] == 1);
2848 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
2849 BEAST_EXPECT(txj.isMember(jss::tx));
2850 BEAST_EXPECT(txj[jss::tx] != txid0);
2851 return txj[jss::tx].asString();
2852 }
2853 return std::string{};
2854 }();
2855
2856 jv[jss::full] = true;
2857
2858 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2859 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
2860 {
2861 auto const& txj = jrr[jss::queue_data][0u];
2862 BEAST_EXPECT(txj[jss::account] == alice.human());
2863 BEAST_EXPECT(txj[jss::fee_level] == "256");
2864 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2865 BEAST_EXPECT(txj["retries_remaining"] == 1);
2866 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
2867 BEAST_EXPECT(txj.isMember(jss::tx));
2868 auto const& tx = txj[jss::tx];
2869 BEAST_EXPECT(tx[jss::Account] == alice.human());
2870 BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
2871 BEAST_EXPECT(tx[jss::hash] == txid2);
2872 }
2873 }
2874
2875 void
2877 {
2878 testcase("Ledger Request, Accounts Hashes");
2879 using namespace test::jtx;
2880
2881 Env env{*this};
2882
2883 env.close();
2884
2885 std::string index;
2886 {
2887 Json::Value jvParams;
2888 jvParams[jss::ledger_index] = 3u;
2889 jvParams[jss::accounts] = true;
2890 jvParams[jss::expand] = true;
2891 jvParams[jss::type] = "hashes";
2892 auto const jrr =
2893 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
2894 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
2895 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
2896 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
2897 BEAST_EXPECT(
2898 jrr[jss::ledger][jss::accountState][0u]["LedgerEntryType"] ==
2899 jss::LedgerHashes);
2900 index = jrr[jss::ledger][jss::accountState][0u]["index"].asString();
2901 }
2902 {
2903 Json::Value jvParams;
2904 jvParams[jss::ledger_index] = 3u;
2905 jvParams[jss::accounts] = true;
2906 jvParams[jss::expand] = false;
2907 jvParams[jss::type] = "hashes";
2908 auto const jrr =
2909 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
2910 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
2911 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
2912 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
2913 BEAST_EXPECT(jrr[jss::ledger][jss::accountState][0u] == index);
2914 }
2915 }
2916
2917 void
2919 {
2920 testcase("Invalid Oracle Ledger Entry");
2921 using namespace ripple::test::jtx;
2922 using namespace ripple::test::jtx::oracle;
2923
2924 Env env(*this);
2925 Account const owner("owner");
2926 env.fund(XRP(1'000), owner);
2927 Oracle oracle(env, {.owner = owner});
2928
2929 // Malformed document id
2930 auto res = Oracle::ledgerEntry(env, owner, NoneTag);
2931 BEAST_EXPECT(res[jss::error].asString() == "invalidParams");
2932 std::vector<AnyValue> invalid = {-1, 1.2, "", "Invalid"};
2933 for (auto const& v : invalid)
2934 {
2935 auto const res = Oracle::ledgerEntry(env, owner, v);
2936 BEAST_EXPECT(res[jss::error].asString() == "malformedDocumentID");
2937 }
2938 // Missing document id
2939 res = Oracle::ledgerEntry(env, owner, std::nullopt);
2940 BEAST_EXPECT(res[jss::error].asString() == "malformedRequest");
2941
2942 // Missing account
2943 res = Oracle::ledgerEntry(env, std::nullopt, 1);
2944 BEAST_EXPECT(res[jss::error].asString() == "malformedRequest");
2945
2946 // Malformed account
2947 std::string malfAccount = to_string(owner.id());
2948 malfAccount.replace(10, 1, 1, '!');
2949 res = Oracle::ledgerEntry(env, malfAccount, 1);
2950 BEAST_EXPECT(res[jss::error].asString() == "malformedAddress");
2951 }
2952
2953 void
2955 {
2956 testcase("Oracle Ledger Entry");
2957 using namespace ripple::test::jtx;
2958 using namespace ripple::test::jtx::oracle;
2959
2960 Env env(*this);
2961 std::vector<AccountID> accounts;
2963 for (int i = 0; i < 10; ++i)
2964 {
2965 Account const owner(std::string("owner") + std::to_string(i));
2966 env.fund(XRP(1'000), owner);
2967 // different accounts can have the same asset pair
2968 Oracle oracle(env, {.owner = owner, .documentID = i});
2969 accounts.push_back(owner.id());
2970 oracles.push_back(oracle.documentID());
2971 // same account can have different asset pair
2972 Oracle oracle1(env, {.owner = owner, .documentID = i + 10});
2973 accounts.push_back(owner.id());
2974 oracles.push_back(oracle1.documentID());
2975 }
2976 for (int i = 0; i < accounts.size(); ++i)
2977 {
2978 auto const jv = [&]() {
2979 // document id is uint32
2980 if (i % 2)
2981 return Oracle::ledgerEntry(env, accounts[i], oracles[i]);
2982 // document id is string
2983 return Oracle::ledgerEntry(
2984 env, accounts[i], std::to_string(oracles[i]));
2985 }();
2986 try
2987 {
2988 BEAST_EXPECT(
2989 jv[jss::node][jss::Owner] == to_string(accounts[i]));
2990 }
2991 catch (...)
2992 {
2993 fail();
2994 }
2995 }
2996 }
2997
2998 void
3000 {
3001 testcase("ledger_entry Request MPT");
3002 using namespace test::jtx;
3003 using namespace std::literals::chrono_literals;
3004 Env env{*this};
3005 Account const alice{"alice"};
3006 Account const bob("bob");
3007
3008 MPTTester mptAlice(env, alice, {.holders = {bob}});
3009 mptAlice.create(
3010 {.transferFee = 10,
3011 .metadata = "123",
3012 .ownerCount = 1,
3015 mptAlice.authorize({.account = bob, .holderCount = 1});
3016
3017 std::string const ledgerHash{to_string(env.closed()->info().hash)};
3018
3019 std::string const badMptID =
3020 "00000193B9DDCAF401B5B3B26875986043F82CD0D13B4315";
3021 {
3022 // Request the MPTIssuance using its MPTIssuanceID.
3023 Json::Value jvParams;
3024 jvParams[jss::mpt_issuance] = strHex(mptAlice.issuanceID());
3025 jvParams[jss::ledger_hash] = ledgerHash;
3026 Json::Value const jrr = env.rpc(
3027 "json", "ledger_entry", to_string(jvParams))[jss::result];
3028 BEAST_EXPECT(
3029 jrr[jss::node][sfMPTokenMetadata.jsonName] ==
3030 strHex(std::string{"123"}));
3031 BEAST_EXPECT(
3032 jrr[jss::node][jss::mpt_issuance_id] ==
3033 strHex(mptAlice.issuanceID()));
3034 }
3035 {
3036 // Request an index that is not a MPTIssuance.
3037 Json::Value jvParams;
3038 jvParams[jss::mpt_issuance] = badMptID;
3039 jvParams[jss::ledger_hash] = ledgerHash;
3040 Json::Value const jrr = env.rpc(
3041 "json", "ledger_entry", to_string(jvParams))[jss::result];
3042 checkErrorValue(jrr, "entryNotFound", "");
3043 }
3044 {
3045 // Request the MPToken using its owner + mptIssuanceID.
3046 Json::Value jvParams;
3047 jvParams[jss::mptoken] = Json::objectValue;
3048 jvParams[jss::mptoken][jss::account] = bob.human();
3049 jvParams[jss::mptoken][jss::mpt_issuance_id] =
3050 strHex(mptAlice.issuanceID());
3051 jvParams[jss::ledger_hash] = ledgerHash;
3052 Json::Value const jrr = env.rpc(
3053 "json", "ledger_entry", to_string(jvParams))[jss::result];
3054 BEAST_EXPECT(
3055 jrr[jss::node][sfMPTokenIssuanceID.jsonName] ==
3056 strHex(mptAlice.issuanceID()));
3057 }
3058 {
3059 // Request the MPToken using a bad mptIssuanceID.
3060 Json::Value jvParams;
3061 jvParams[jss::mptoken] = Json::objectValue;
3062 jvParams[jss::mptoken][jss::account] = bob.human();
3063 jvParams[jss::mptoken][jss::mpt_issuance_id] = badMptID;
3064 jvParams[jss::ledger_hash] = ledgerHash;
3065 Json::Value const jrr = env.rpc(
3066 "json", "ledger_entry", to_string(jvParams))[jss::result];
3067 checkErrorValue(jrr, "entryNotFound", "");
3068 }
3069 }
3070
3071 void
3073 {
3074 testcase("ledger_entry command-line");
3075 using namespace test::jtx;
3076
3077 Env env{*this};
3078 Account const alice{"alice"};
3079 env.fund(XRP(10000), alice);
3080 env.close();
3081
3082 auto const checkId = keylet::check(env.master, env.seq(env.master));
3083
3084 env(check::create(env.master, alice, XRP(100)));
3085 env.close();
3086
3087 std::string const ledgerHash{to_string(env.closed()->info().hash)};
3088 {
3089 // Request a check.
3090 Json::Value const jrr =
3091 env.rpc("ledger_entry", to_string(checkId.key))[jss::result];
3092 BEAST_EXPECT(
3093 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
3094 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
3095 }
3096 }
3097
3098 void
3100 {
3101 testcase("ledger_entry PermissionedDomain");
3102
3103 using namespace test::jtx;
3104
3105 Env env(*this, supported_amendments() | featurePermissionedDomains);
3106 Account const issuer{"issuer"};
3107 Account const alice{"alice"};
3108 Account const bob{"bob"};
3109
3110 env.fund(XRP(5000), issuer, alice, bob);
3111 env.close();
3112
3113 auto const seq = env.seq(alice);
3114 env(pdomain::setTx(alice, {{alice, "first credential"}}));
3115 env.close();
3116 auto const objects = pdomain::getObjects(alice, env);
3117 if (!BEAST_EXPECT(objects.size() == 1))
3118 return;
3119
3120 {
3121 // Succeed
3122 Json::Value params;
3123 params[jss::ledger_index] = jss::validated;
3124 params[jss::permissioned_domain][jss::account] = alice.human();
3125 params[jss::permissioned_domain][jss::seq] = seq;
3126 auto jv = env.rpc("json", "ledger_entry", to_string(params));
3127 BEAST_EXPECT(
3128 jv.isObject() && jv.isMember(jss::result) &&
3129 !jv[jss::result].isMember(jss::error) &&
3130 jv[jss::result].isMember(jss::node) &&
3131 jv[jss::result][jss::node].isMember(
3132 sfLedgerEntryType.jsonName) &&
3133 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
3134 jss::PermissionedDomain);
3135
3136 std::string const pdIdx = jv[jss::result][jss::index].asString();
3137 BEAST_EXPECT(
3138 strHex(keylet::permissionedDomain(alice, seq).key) == pdIdx);
3139
3140 params.clear();
3141 params[jss::ledger_index] = jss::validated;
3142 params[jss::permissioned_domain] = pdIdx;
3143 jv = env.rpc("json", "ledger_entry", to_string(params));
3144 BEAST_EXPECT(
3145 jv.isObject() && jv.isMember(jss::result) &&
3146 !jv[jss::result].isMember(jss::error) &&
3147 jv[jss::result].isMember(jss::node) &&
3148 jv[jss::result][jss::node].isMember(
3149 sfLedgerEntryType.jsonName) &&
3150 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
3151 jss::PermissionedDomain);
3152 }
3153
3154 {
3155 // Fail, invalid permissioned domain index
3156 Json::Value params;
3157 params[jss::ledger_index] = jss::validated;
3158 params[jss::permissioned_domain] =
3159 "12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A"
3160 "DE";
3161 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3162 checkErrorValue(jrr[jss::result], "entryNotFound", "");
3163 }
3164
3165 {
3166 // Fail, invalid permissioned domain index
3167 Json::Value params;
3168 params[jss::ledger_index] = jss::validated;
3169 params[jss::permissioned_domain] = "NotAHexString";
3170 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3171 checkErrorValue(jrr[jss::result], "malformedRequest", "");
3172 }
3173
3174 {
3175 // Fail, permissioned domain is not an object
3176 Json::Value params;
3177 params[jss::ledger_index] = jss::validated;
3178 params[jss::permissioned_domain] = 10;
3179 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3180 checkErrorValue(jrr[jss::result], "malformedRequest", "");
3181 }
3182
3183 {
3184 // Fail, invalid account
3185 Json::Value params;
3186 params[jss::ledger_index] = jss::validated;
3187 params[jss::permissioned_domain][jss::account] = 1;
3188 params[jss::permissioned_domain][jss::seq] = seq;
3189 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3190 checkErrorValue(jrr[jss::result], "malformedRequest", "");
3191 }
3192
3193 {
3194 // Fail, no account
3195 Json::Value params;
3196 params[jss::ledger_index] = jss::validated;
3197 params[jss::permissioned_domain][jss::account] = "";
3198 params[jss::permissioned_domain][jss::seq] = seq;
3199 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3200 checkErrorValue(jrr[jss::result], "malformedAddress", "");
3201 }
3202
3203 {
3204 // Fail, invalid sequence
3205 Json::Value params;
3206 params[jss::ledger_index] = jss::validated;
3207 params[jss::permissioned_domain][jss::account] = alice.human();
3208 params[jss::permissioned_domain][jss::seq] = "12g";
3209 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3210 checkErrorValue(jrr[jss::result], "malformedRequest", "");
3211 }
3212 }
3213
3214public:
3215 void
3216 run() override
3217 {
3219 testBadInput();
3237 testNoQueue();
3238 testQueue();
3246
3249 }
3250};
3251
3252BEAST_DEFINE_TESTSUITE(LedgerRPC, app, ripple);
3253BEAST_DEFINE_TESTSUITE(LedgerRPC_XChain, app, ripple);
3254
3255} // namespace ripple
T any_of(T... args)
T bind_front(T... args)
Represents a JSON value.
Definition: json_value.h:148
Json::UInt UInt
Definition: json_value.h:155
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:712
void clear()
Remove all object members and array elements.
Definition: json_value.cpp:759
Int asInt() const
Definition: json_value.cpp:509
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:897
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:475
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:949
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition: suite.h:533
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
void run() override
Runs the suite.
void testLookupLedger()
ledger RPC requests as a way to drive input options to lookupLedger.
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
void run() override
Runs the suite.
void testLedgerEntryInvalidParams(unsigned int apiVersion)
std::string makeBadAddress(std::string good)
A public key.
Definition: PublicKey.h:62
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:503
Immutable cryptographic account descriptor.
Definition: Account.h:39
AccountID id() const
Returns the Account ID.
Definition: Account.h:107
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:114
A transaction testing environment.
Definition: Env.h:118
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition: Env.cpp:210
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:115
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:765
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:231
void create(MPTCreate const &arg=MPTCreate{})
Definition: mpt.cpp:85
Oracle class facilitates unit-testing of the Price Oracle feature.
Definition: Oracle.h:120
T empty(T... args)
@ nullValue
'null' value
Definition: json_value.h:37
@ arrayValue
array value (ordered list)
Definition: json_value.h:43
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Definition: ErrorCodes.cpp:201
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition: Indexes.cpp:547
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:175
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition: Indexes.cpp:327
Keylet payChan(AccountID const &src, AccountID const &dst, std::uint32_t seq) noexcept
A PaymentChannel.
Definition: Indexes.cpp:386
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
constexpr std::uint32_t asfDepositAuth
Definition: TxFlags.h:84
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:178
constexpr std::uint32_t const tfMPTCanTransfer
Definition: TxFlags.h:145
constexpr std::uint32_t const tfMPTCanTrade
Definition: TxFlags.h:144
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition: Protocol.h:107
@ invalid
Timely, but invalid signature.
void forAllApiVersions(Fn const &fn, Args &&... args)
Definition: ApiVersion.h:102
constexpr std::uint32_t tfUniversal
Definition: TxFlags.h:61
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition: Indexes.cpp:147
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
@ terQUEUED
Definition: TER.h:225
constexpr std::uint32_t const tfMPTCanEscrow
Definition: TxFlags.h:143
constexpr std::uint32_t const tfMPTRequireAuth
Definition: TxFlags.h:142
constexpr std::uint32_t const tfMPTCanLock
Definition: TxFlags.h:141
constexpr std::uint32_t const tfMPTCanClawback
Definition: TxFlags.h:146
T push_back(T... args)
T replace(T... args)
T size(T... args)
uint256 key
Definition: Keylet.h:40
std::vector< Account > const payee
std::vector< signer > const signers
void createBridgeObjects(Env &mcEnv, Env &scEnv)
Set the sequence number on a JTx.
Definition: seq.h:34
T to_string(T... args)