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