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