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
612 auto cfg = envconfig();
613 cfg->FEES.reference_fee = 10;
614 Env env{*this, std::move(cfg)};
615 Account const alice{"alice"};
616 env.fund(XRP(10000), alice);
617 env.close();
618
619 std::string const ledgerHash{to_string(env.closed()->info().hash)};
620 {
621 // Exercise ledger_closed along the way.
622 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
623 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
624 BEAST_EXPECT(jrr[jss::ledger_index] == 3);
625 }
626
627 std::string accountRootIndex;
628 {
629 // Request alice's account root.
630 Json::Value jvParams;
631 jvParams[jss::account_root] = alice.human();
632 jvParams[jss::ledger_hash] = ledgerHash;
633 Json::Value const jrr = env.rpc(
634 "json", "ledger_entry", to_string(jvParams))[jss::result];
635 BEAST_EXPECT(jrr.isMember(jss::node));
636 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
637 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
638 accountRootIndex = jrr[jss::index].asString();
639 }
640 {
641 constexpr char alicesAcctRootBinary[]{
642 "1100612200800000240000000425000000032D00000000559CE54C3B934E4"
643 "73A995B477E92EC229F99CED5B62BF4D2ACE4DC42719103AE2F6240000002"
644 "540BE4008114AE123A8556F3CF91154711376AFB0F894F832B3D"};
645
646 // Request alice's account root, but with binary == true;
647 Json::Value jvParams;
648 jvParams[jss::account_root] = alice.human();
649 jvParams[jss::binary] = 1;
650 jvParams[jss::ledger_hash] = ledgerHash;
651 Json::Value const jrr = env.rpc(
652 "json", "ledger_entry", to_string(jvParams))[jss::result];
653 BEAST_EXPECT(jrr.isMember(jss::node_binary));
654 BEAST_EXPECT(jrr[jss::node_binary] == alicesAcctRootBinary);
655 }
656 {
657 // Request alice's account root using the index.
658 Json::Value jvParams;
659 jvParams[jss::index] = accountRootIndex;
660 Json::Value const jrr = env.rpc(
661 "json", "ledger_entry", to_string(jvParams))[jss::result];
662 BEAST_EXPECT(!jrr.isMember(jss::node_binary));
663 BEAST_EXPECT(jrr.isMember(jss::node));
664 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
665 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
666 }
667 {
668 // Request alice's account root by index, but with binary == false.
669 Json::Value jvParams;
670 jvParams[jss::index] = accountRootIndex;
671 jvParams[jss::binary] = 0;
672 Json::Value const jrr = env.rpc(
673 "json", "ledger_entry", to_string(jvParams))[jss::result];
674 BEAST_EXPECT(jrr.isMember(jss::node));
675 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
676 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
677 }
678 {
679 // Request using a corrupted AccountID.
680 Json::Value jvParams;
681 jvParams[jss::account_root] = makeBadAddress(alice.human());
682 jvParams[jss::ledger_hash] = ledgerHash;
683 Json::Value const jrr = env.rpc(
684 "json", "ledger_entry", to_string(jvParams))[jss::result];
685 checkErrorValue(jrr, "malformedAddress", "");
686 }
687 {
688 // Request an account that is not in the ledger.
689 Json::Value jvParams;
690 jvParams[jss::account_root] = Account("bob").human();
691 jvParams[jss::ledger_hash] = ledgerHash;
692 Json::Value const jrr = env.rpc(
693 "json", "ledger_entry", to_string(jvParams))[jss::result];
694 checkErrorValue(jrr, "entryNotFound", "");
695 }
696 }
697
698 void
700 {
701 testcase("ledger_entry Request Check");
702 using namespace test::jtx;
703 Env env{*this};
704 Account const alice{"alice"};
705 env.fund(XRP(10000), alice);
706 env.close();
707
708 auto const checkId = keylet::check(env.master, env.seq(env.master));
709
710 env(check::create(env.master, alice, XRP(100)));
711 env.close();
712
713 std::string const ledgerHash{to_string(env.closed()->info().hash)};
714 {
715 // Request a check.
716 Json::Value jvParams;
717 jvParams[jss::check] = to_string(checkId.key);
718 jvParams[jss::ledger_hash] = ledgerHash;
719 Json::Value const jrr = env.rpc(
720 "json", "ledger_entry", to_string(jvParams))[jss::result];
721 BEAST_EXPECT(
722 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
723 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
724 }
725 {
726 // Request an index that is not a check. We'll use alice's
727 // account root index.
728 std::string accountRootIndex;
729 {
730 Json::Value jvParams;
731 jvParams[jss::account_root] = alice.human();
732 Json::Value const jrr = env.rpc(
733 "json", "ledger_entry", to_string(jvParams))[jss::result];
734 accountRootIndex = jrr[jss::index].asString();
735 }
736 Json::Value jvParams;
737 jvParams[jss::check] = accountRootIndex;
738 jvParams[jss::ledger_hash] = ledgerHash;
739 Json::Value const jrr = env.rpc(
740 "json", "ledger_entry", to_string(jvParams))[jss::result];
741 checkErrorValue(jrr, "unexpectedLedgerType", "");
742 }
743 }
744
745 void
747 {
748 testcase("ledger_entry credentials");
749
750 using namespace test::jtx;
751
752 Env env(*this);
753 Account const issuer{"issuer"};
754 Account const alice{"alice"};
755 Account const bob{"bob"};
756 const char credType[] = "abcde";
757
758 env.fund(XRP(5000), issuer, alice, bob);
759 env.close();
760
761 // Setup credentials with DepositAuth object for Alice and Bob
762 env(credentials::create(alice, issuer, credType));
763 env.close();
764
765 {
766 // Succeed
767 auto jv = credentials::ledgerEntry(env, alice, issuer, credType);
768 BEAST_EXPECT(
769 jv.isObject() && jv.isMember(jss::result) &&
770 !jv[jss::result].isMember(jss::error) &&
771 jv[jss::result].isMember(jss::node) &&
772 jv[jss::result][jss::node].isMember(
773 sfLedgerEntryType.jsonName) &&
774 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
775 jss::Credential);
776
777 std::string const credIdx = jv[jss::result][jss::index].asString();
778
779 jv = credentials::ledgerEntry(env, credIdx);
780 BEAST_EXPECT(
781 jv.isObject() && jv.isMember(jss::result) &&
782 !jv[jss::result].isMember(jss::error) &&
783 jv[jss::result].isMember(jss::node) &&
784 jv[jss::result][jss::node].isMember(
785 sfLedgerEntryType.jsonName) &&
786 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
787 jss::Credential);
788 }
789
790 {
791 // Fail, index not a hash
792 auto const jv = credentials::ledgerEntry(env, "");
793 checkErrorValue(jv[jss::result], "malformedRequest", "");
794 }
795
796 {
797 // Fail, credential doesn't exist
798 auto const jv = credentials::ledgerEntry(
799 env,
800 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
801 "E4");
802 checkErrorValue(jv[jss::result], "entryNotFound", "");
803 }
804
805 {
806 // Fail, invalid subject
807 Json::Value jv;
808 jv[jss::ledger_index] = jss::validated;
809 jv[jss::credential][jss::subject] = 42;
810 jv[jss::credential][jss::issuer] = issuer.human();
811 jv[jss::credential][jss::credential_type] =
812 strHex(std::string_view(credType));
813 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
814 checkErrorValue(jrr[jss::result], "malformedRequest", "");
815 }
816
817 {
818 // Fail, invalid issuer
819 Json::Value jv;
820 jv[jss::ledger_index] = jss::validated;
821 jv[jss::credential][jss::subject] = alice.human();
822 jv[jss::credential][jss::issuer] = 42;
823 jv[jss::credential][jss::credential_type] =
824 strHex(std::string_view(credType));
825 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
826 checkErrorValue(jrr[jss::result], "malformedRequest", "");
827 }
828
829 {
830 // Fail, invalid credentials type
831 Json::Value jv;
832 jv[jss::ledger_index] = jss::validated;
833 jv[jss::credential][jss::subject] = alice.human();
834 jv[jss::credential][jss::issuer] = issuer.human();
835 jv[jss::credential][jss::credential_type] = 42;
836 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
837 checkErrorValue(jrr[jss::result], "malformedRequest", "");
838 }
839
840 {
841 // Fail, empty subject
842 Json::Value jv;
843 jv[jss::ledger_index] = jss::validated;
844 jv[jss::credential][jss::subject] = "";
845 jv[jss::credential][jss::issuer] = issuer.human();
846 jv[jss::credential][jss::credential_type] =
847 strHex(std::string_view(credType));
848 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
849 checkErrorValue(jrr[jss::result], "malformedRequest", "");
850 }
851
852 {
853 // Fail, empty issuer
854 Json::Value jv;
855 jv[jss::ledger_index] = jss::validated;
856 jv[jss::credential][jss::subject] = alice.human();
857 jv[jss::credential][jss::issuer] = "";
858 jv[jss::credential][jss::credential_type] =
859 strHex(std::string_view(credType));
860 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
861 checkErrorValue(jrr[jss::result], "malformedRequest", "");
862 }
863
864 {
865 // Fail, empty credentials type
866 Json::Value jv;
867 jv[jss::ledger_index] = jss::validated;
868 jv[jss::credential][jss::subject] = alice.human();
869 jv[jss::credential][jss::issuer] = issuer.human();
870 jv[jss::credential][jss::credential_type] = "";
871 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
872 checkErrorValue(jrr[jss::result], "malformedRequest", "");
873 }
874
875 {
876 // Fail, no subject
877 Json::Value jv;
878 jv[jss::ledger_index] = jss::validated;
879 jv[jss::credential][jss::issuer] = issuer.human();
880 jv[jss::credential][jss::credential_type] =
881 strHex(std::string_view(credType));
882 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
883 checkErrorValue(jrr[jss::result], "malformedRequest", "");
884 }
885
886 {
887 // Fail, no issuer
888 Json::Value jv;
889 jv[jss::ledger_index] = jss::validated;
890 jv[jss::credential][jss::subject] = alice.human();
891 jv[jss::credential][jss::credential_type] =
892 strHex(std::string_view(credType));
893 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
894 checkErrorValue(jrr[jss::result], "malformedRequest", "");
895 }
896
897 {
898 // Fail, no credentials type
899 Json::Value jv;
900 jv[jss::ledger_index] = jss::validated;
901 jv[jss::credential][jss::subject] = alice.human();
902 jv[jss::credential][jss::issuer] = issuer.human();
903 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
904 checkErrorValue(jrr[jss::result], "malformedRequest", "");
905 }
906
907 {
908 // Fail, not AccountID subject
909 Json::Value jv;
910 jv[jss::ledger_index] = jss::validated;
911 jv[jss::credential][jss::subject] = "wehsdbvasbdfvj";
912 jv[jss::credential][jss::issuer] = issuer.human();
913 jv[jss::credential][jss::credential_type] =
914 strHex(std::string_view(credType));
915 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
916 checkErrorValue(jrr[jss::result], "malformedRequest", "");
917 }
918
919 {
920 // Fail, not AccountID issuer
921 Json::Value jv;
922 jv[jss::ledger_index] = jss::validated;
923 jv[jss::credential][jss::subject] = alice.human();
924 jv[jss::credential][jss::issuer] = "c4p93ugndfbsiu";
925 jv[jss::credential][jss::credential_type] =
926 strHex(std::string_view(credType));
927 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
928 checkErrorValue(jrr[jss::result], "malformedRequest", "");
929 }
930
931 {
932 // Fail, credentials type isn't hex encoded
933 Json::Value jv;
934 jv[jss::ledger_index] = jss::validated;
935 jv[jss::credential][jss::subject] = alice.human();
936 jv[jss::credential][jss::issuer] = issuer.human();
937 jv[jss::credential][jss::credential_type] = "12KK";
938 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
939 checkErrorValue(jrr[jss::result], "malformedRequest", "");
940 }
941 }
942
943 void
945 {
946 testcase("ledger_entry Deposit Preauth");
947
948 using namespace test::jtx;
949
950 Env env{*this};
951 Account const alice{"alice"};
952 Account const becky{"becky"};
953
954 env.fund(XRP(10000), alice, becky);
955 env.close();
956
957 env(deposit::auth(alice, becky));
958 env.close();
959
960 std::string const ledgerHash{to_string(env.closed()->info().hash)};
961 std::string depositPreauthIndex;
962 {
963 // Request a depositPreauth by owner and authorized.
964 Json::Value jvParams;
965 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
966 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
967 jvParams[jss::ledger_hash] = ledgerHash;
968 Json::Value const jrr = env.rpc(
969 "json", "ledger_entry", to_string(jvParams))[jss::result];
970
971 BEAST_EXPECT(
972 jrr[jss::node][sfLedgerEntryType.jsonName] ==
973 jss::DepositPreauth);
974 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
975 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
976 depositPreauthIndex = jrr[jss::node][jss::index].asString();
977 }
978 {
979 // Request a depositPreauth by index.
980 Json::Value jvParams;
981 jvParams[jss::deposit_preauth] = depositPreauthIndex;
982 jvParams[jss::ledger_hash] = ledgerHash;
983 Json::Value const jrr = env.rpc(
984 "json", "ledger_entry", to_string(jvParams))[jss::result];
985
986 BEAST_EXPECT(
987 jrr[jss::node][sfLedgerEntryType.jsonName] ==
988 jss::DepositPreauth);
989 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
990 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
991 }
992 {
993 // Malformed request: deposit_preauth neither object nor string.
994 Json::Value jvParams;
995 jvParams[jss::deposit_preauth] = -5;
996 jvParams[jss::ledger_hash] = ledgerHash;
997 Json::Value const jrr = env.rpc(
998 "json", "ledger_entry", to_string(jvParams))[jss::result];
999 checkErrorValue(jrr, "malformedRequest", "");
1000 }
1001 {
1002 // Malformed request: deposit_preauth not hex string.
1003 Json::Value jvParams;
1004 jvParams[jss::deposit_preauth] = "0123456789ABCDEFG";
1005 jvParams[jss::ledger_hash] = ledgerHash;
1006 Json::Value const jrr = env.rpc(
1007 "json", "ledger_entry", to_string(jvParams))[jss::result];
1008 checkErrorValue(jrr, "malformedRequest", "");
1009 }
1010 {
1011 // Malformed request: missing [jss::deposit_preauth][jss::owner]
1012 Json::Value jvParams;
1013 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
1014 jvParams[jss::ledger_hash] = ledgerHash;
1015 Json::Value const jrr = env.rpc(
1016 "json", "ledger_entry", to_string(jvParams))[jss::result];
1017 checkErrorValue(jrr, "malformedRequest", "");
1018 }
1019 {
1020 // Malformed request: [jss::deposit_preauth][jss::owner] not string.
1021 Json::Value jvParams;
1022 jvParams[jss::deposit_preauth][jss::owner] = 7;
1023 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
1024 jvParams[jss::ledger_hash] = ledgerHash;
1025 Json::Value const jrr = env.rpc(
1026 "json", "ledger_entry", to_string(jvParams))[jss::result];
1027 checkErrorValue(jrr, "malformedRequest", "");
1028 }
1029 {
1030 // Malformed: missing [jss::deposit_preauth][jss::authorized]
1031 Json::Value jvParams;
1032 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
1033 jvParams[jss::ledger_hash] = ledgerHash;
1034 Json::Value const jrr = env.rpc(
1035 "json", "ledger_entry", to_string(jvParams))[jss::result];
1036 checkErrorValue(jrr, "malformedRequest", "");
1037 }
1038 {
1039 // Malformed: [jss::deposit_preauth][jss::authorized] not string.
1040 Json::Value jvParams;
1041 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
1042 jvParams[jss::deposit_preauth][jss::authorized] = 47;
1043 jvParams[jss::ledger_hash] = ledgerHash;
1044 Json::Value const jrr = env.rpc(
1045 "json", "ledger_entry", to_string(jvParams))[jss::result];
1046 checkErrorValue(jrr, "malformedRequest", "");
1047 }
1048 {
1049 // Malformed: [jss::deposit_preauth][jss::owner] is malformed.
1050 Json::Value jvParams;
1051 jvParams[jss::deposit_preauth][jss::owner] =
1052 "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
1053
1054 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
1055 jvParams[jss::ledger_hash] = ledgerHash;
1056 Json::Value const jrr = env.rpc(
1057 "json", "ledger_entry", to_string(jvParams))[jss::result];
1058 checkErrorValue(jrr, "malformedOwner", "");
1059 }
1060 {
1061 // Malformed: [jss::deposit_preauth][jss::authorized] is malformed.
1062 Json::Value jvParams;
1063 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
1064 jvParams[jss::deposit_preauth][jss::authorized] =
1065 "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
1066
1067 jvParams[jss::ledger_hash] = ledgerHash;
1068 Json::Value const jrr = env.rpc(
1069 "json", "ledger_entry", to_string(jvParams))[jss::result];
1070 checkErrorValue(jrr, "malformedAuthorized", "");
1071 }
1072 }
1073
1074 void
1076 {
1077 testcase("ledger_entry Deposit Preauth with credentials");
1078
1079 using namespace test::jtx;
1080
1081 Env env(*this);
1082 Account const issuer{"issuer"};
1083 Account const alice{"alice"};
1084 Account const bob{"bob"};
1085 const char credType[] = "abcde";
1086
1087 env.fund(XRP(5000), issuer, alice, bob);
1088 env.close();
1089
1090 {
1091 // Setup Bob with DepositAuth
1092 env(fset(bob, asfDepositAuth));
1093 env.close();
1094 env(deposit::authCredentials(bob, {{issuer, credType}}));
1095 env.close();
1096 }
1097
1098 {
1099 // Succeed
1100 Json::Value jvParams;
1101 jvParams[jss::ledger_index] = jss::validated;
1102 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1103
1104 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1106 auto& arr(
1107 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1108
1109 Json::Value jo;
1110 jo[jss::issuer] = issuer.human();
1111 jo[jss::credential_type] = strHex(std::string_view(credType));
1112 arr.append(std::move(jo));
1113 auto const jrr =
1114 env.rpc("json", "ledger_entry", to_string(jvParams));
1115
1116 BEAST_EXPECT(
1117 jrr.isObject() && jrr.isMember(jss::result) &&
1118 !jrr[jss::result].isMember(jss::error) &&
1119 jrr[jss::result].isMember(jss::node) &&
1120 jrr[jss::result][jss::node].isMember(
1121 sfLedgerEntryType.jsonName) &&
1122 jrr[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
1123 jss::DepositPreauth);
1124 }
1125
1126 {
1127 // Failed, invalid account
1128 Json::Value jvParams;
1129 jvParams[jss::ledger_index] = jss::validated;
1130 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1131
1132 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1134 auto& arr(
1135 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1136
1137 Json::Value jo;
1138 jo[jss::issuer] = to_string(xrpAccount());
1139 jo[jss::credential_type] = strHex(std::string_view(credType));
1140 arr.append(std::move(jo));
1141 auto const jrr =
1142 env.rpc("json", "ledger_entry", to_string(jvParams));
1144 jrr[jss::result], "malformedAuthorizedCredentials", "");
1145 }
1146
1147 {
1148 // Failed, duplicates in credentials
1149 Json::Value jvParams;
1150 jvParams[jss::ledger_index] = jss::validated;
1151 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1152
1153 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1155 auto& arr(
1156 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1157
1158 Json::Value jo;
1159 jo[jss::issuer] = issuer.human();
1160 jo[jss::credential_type] = strHex(std::string_view(credType));
1161 arr.append(jo);
1162 arr.append(std::move(jo));
1163 auto const jrr =
1164 env.rpc("json", "ledger_entry", to_string(jvParams));
1166 jrr[jss::result], "malformedAuthorizedCredentials", "");
1167 }
1168
1169 {
1170 // Failed, invalid credential_type
1171 Json::Value jvParams;
1172 jvParams[jss::ledger_index] = jss::validated;
1173 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1174
1175 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1177 auto& arr(
1178 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1179
1180 Json::Value jo;
1181 jo[jss::issuer] = issuer.human();
1182 jo[jss::credential_type] = "";
1183 arr.append(std::move(jo));
1184
1185 auto const jrr =
1186 env.rpc("json", "ledger_entry", to_string(jvParams));
1188 jrr[jss::result], "malformedAuthorizedCredentials", "");
1189 }
1190
1191 {
1192 // Failed, authorized and authorized_credentials both present
1193 Json::Value jvParams;
1194 jvParams[jss::ledger_index] = jss::validated;
1195 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1196 jvParams[jss::deposit_preauth][jss::authorized] = alice.human();
1197
1198 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1200 auto& arr(
1201 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1202
1203 Json::Value jo;
1204 jo[jss::issuer] = issuer.human();
1205 jo[jss::credential_type] = strHex(std::string_view(credType));
1206 arr.append(std::move(jo));
1207
1208 auto const jrr =
1209 env.rpc("json", "ledger_entry", to_string(jvParams));
1210 checkErrorValue(jrr[jss::result], "malformedRequest", "");
1211 }
1212
1213 {
1214 // Failed, authorized_credentials is not an array
1215 Json::Value jvParams;
1216 jvParams[jss::ledger_index] = jss::validated;
1217 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1218 jvParams[jss::deposit_preauth][jss::authorized_credentials] = 42;
1219
1220 auto const jrr =
1221 env.rpc("json", "ledger_entry", to_string(jvParams));
1222 checkErrorValue(jrr[jss::result], "malformedRequest", "");
1223 }
1224
1225 {
1226 // Failed, authorized_credentials contains string data
1227 Json::Value jvParams;
1228 jvParams[jss::ledger_index] = jss::validated;
1229 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1230 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1232 auto& arr(
1233 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1234 arr.append("foobar");
1235
1236 auto const jrr =
1237 env.rpc("json", "ledger_entry", to_string(jvParams));
1239 jrr[jss::result], "malformedAuthorizedCredentials", "");
1240 }
1241
1242 {
1243 // Failed, authorized_credentials contains arrays
1244 Json::Value jvParams;
1245 jvParams[jss::ledger_index] = jss::validated;
1246 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1247 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1249 auto& arr(
1250 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1251 Json::Value payload = Json::arrayValue;
1252 payload.append(42);
1253 arr.append(std::move(payload));
1254
1255 auto const jrr =
1256 env.rpc("json", "ledger_entry", to_string(jvParams));
1258 jrr[jss::result], "malformedAuthorizedCredentials", "");
1259 }
1260
1261 {
1262 // Failed, authorized_credentials is empty array
1263 Json::Value jvParams;
1264 jvParams[jss::ledger_index] = jss::validated;
1265 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1266 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1268
1269 auto const jrr =
1270 env.rpc("json", "ledger_entry", to_string(jvParams));
1272 jrr[jss::result], "malformedAuthorizedCredentials", "");
1273 }
1274
1275 {
1276 // Failed, authorized_credentials is too long
1277
1278 static const std::string_view credTypes[] = {
1279 "cred1",
1280 "cred2",
1281 "cred3",
1282 "cred4",
1283 "cred5",
1284 "cred6",
1285 "cred7",
1286 "cred8",
1287 "cred9"};
1288 static_assert(
1289 sizeof(credTypes) / sizeof(credTypes[0]) >
1291
1292 Json::Value jvParams;
1293 jvParams[jss::ledger_index] = jss::validated;
1294 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1295 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1297
1298 auto& arr(
1299 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1300
1301 for (unsigned i = 0; i < sizeof(credTypes) / sizeof(credTypes[0]);
1302 ++i)
1303 {
1304 Json::Value jo;
1305 jo[jss::issuer] = issuer.human();
1306 jo[jss::credential_type] =
1307 strHex(std::string_view(credTypes[i]));
1308 arr.append(std::move(jo));
1309 }
1310
1311 auto const jrr =
1312 env.rpc("json", "ledger_entry", to_string(jvParams));
1314 jrr[jss::result], "malformedAuthorizedCredentials", "");
1315 }
1316
1317 {
1318 // Failed, issuer is not set
1319 Json::Value jvParams;
1320 jvParams[jss::ledger_index] = jss::validated;
1321 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1322
1323 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1325 auto& arr(
1326 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1327
1328 Json::Value jo;
1329 jo[jss::credential_type] = strHex(std::string_view(credType));
1330 arr.append(std::move(jo));
1331
1332 auto const jrr =
1333 env.rpc("json", "ledger_entry", to_string(jvParams));
1335 jrr[jss::result], "malformedAuthorizedCredentials", "");
1336 }
1337
1338 {
1339 // Failed, issuer isn't string
1340 Json::Value jvParams;
1341 jvParams[jss::ledger_index] = jss::validated;
1342 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1343
1344 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1346 auto& arr(
1347 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1348
1349 Json::Value jo;
1350 jo[jss::issuer] = 42;
1351 jo[jss::credential_type] = strHex(std::string_view(credType));
1352 arr.append(std::move(jo));
1353
1354 auto const jrr =
1355 env.rpc("json", "ledger_entry", to_string(jvParams));
1357 jrr[jss::result], "malformedAuthorizedCredentials", "");
1358 }
1359
1360 {
1361 // Failed, issuer is an array
1362 Json::Value jvParams;
1363 jvParams[jss::ledger_index] = jss::validated;
1364 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1365
1366 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1368 auto& arr(
1369 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1370
1371 Json::Value jo;
1372 Json::Value payload = Json::arrayValue;
1373 payload.append(42);
1374 jo[jss::issuer] = std::move(payload);
1375 jo[jss::credential_type] = strHex(std::string_view(credType));
1376 arr.append(std::move(jo));
1377
1378 auto const jrr =
1379 env.rpc("json", "ledger_entry", to_string(jvParams));
1381 jrr[jss::result], "malformedAuthorizedCredentials", "");
1382 }
1383
1384 {
1385 // Failed, issuer isn't valid encoded account
1386 Json::Value jvParams;
1387 jvParams[jss::ledger_index] = jss::validated;
1388 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1389
1390 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1392 auto& arr(
1393 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1394
1395 Json::Value jo;
1396 jo[jss::issuer] = "invalid_account";
1397 jo[jss::credential_type] = strHex(std::string_view(credType));
1398 arr.append(std::move(jo));
1399
1400 auto const jrr =
1401 env.rpc("json", "ledger_entry", to_string(jvParams));
1403 jrr[jss::result], "malformedAuthorizedCredentials", "");
1404 }
1405
1406 {
1407 // Failed, credential_type is not set
1408 Json::Value jvParams;
1409 jvParams[jss::ledger_index] = jss::validated;
1410 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1411
1412 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1414 auto& arr(
1415 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1416
1417 Json::Value jo;
1418 jo[jss::issuer] = issuer.human();
1419 arr.append(std::move(jo));
1420
1421 auto const jrr =
1422 env.rpc("json", "ledger_entry", to_string(jvParams));
1424 jrr[jss::result], "malformedAuthorizedCredentials", "");
1425 }
1426
1427 {
1428 // Failed, credential_type isn't string
1429 Json::Value jvParams;
1430 jvParams[jss::ledger_index] = jss::validated;
1431 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1432
1433 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1435 auto& arr(
1436 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1437
1438 Json::Value jo;
1439 jo[jss::issuer] = issuer.human();
1440 jo[jss::credential_type] = 42;
1441 arr.append(std::move(jo));
1442
1443 auto const jrr =
1444 env.rpc("json", "ledger_entry", to_string(jvParams));
1446 jrr[jss::result], "malformedAuthorizedCredentials", "");
1447 }
1448
1449 {
1450 // Failed, credential_type is an array
1451 Json::Value jvParams;
1452 jvParams[jss::ledger_index] = jss::validated;
1453 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1454
1455 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1457 auto& arr(
1458 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1459
1460 Json::Value jo;
1461 jo[jss::issuer] = issuer.human();
1462 Json::Value payload = Json::arrayValue;
1463 payload.append(42);
1464 jo[jss::credential_type] = std::move(payload);
1465 arr.append(std::move(jo));
1466
1467 auto const jrr =
1468 env.rpc("json", "ledger_entry", to_string(jvParams));
1470 jrr[jss::result], "malformedAuthorizedCredentials", "");
1471 }
1472
1473 {
1474 // Failed, credential_type isn't hex encoded
1475 Json::Value jvParams;
1476 jvParams[jss::ledger_index] = jss::validated;
1477 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1478
1479 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1481 auto& arr(
1482 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1483
1484 Json::Value jo;
1485 jo[jss::issuer] = issuer.human();
1486 jo[jss::credential_type] = "12KK";
1487 arr.append(std::move(jo));
1488
1489 auto const jrr =
1490 env.rpc("json", "ledger_entry", to_string(jvParams));
1492 jrr[jss::result], "malformedAuthorizedCredentials", "");
1493 }
1494 }
1495
1496 void
1498 {
1499 testcase("ledger_entry Request Directory");
1500 using namespace test::jtx;
1501 Env env{*this};
1502 Account const alice{"alice"};
1503 Account const gw{"gateway"};
1504 auto const USD = gw["USD"];
1505 env.fund(XRP(10000), alice, gw);
1506 env.close();
1507
1508 env.trust(USD(1000), alice);
1509 env.close();
1510
1511 // Run up the number of directory entries so alice has two
1512 // directory nodes.
1513 for (int d = 1'000'032; d >= 1'000'000; --d)
1514 {
1515 env(offer(alice, USD(1), drops(d)));
1516 }
1517 env.close();
1518
1519 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1520 {
1521 // Exercise ledger_closed along the way.
1522 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
1523 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
1524 BEAST_EXPECT(jrr[jss::ledger_index] == 5);
1525 }
1526
1527 std::string const dirRootIndex =
1528 "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D";
1529 {
1530 // Locate directory by index.
1531 Json::Value jvParams;
1532 jvParams[jss::directory] = dirRootIndex;
1533 jvParams[jss::ledger_hash] = ledgerHash;
1534 Json::Value const jrr = env.rpc(
1535 "json", "ledger_entry", to_string(jvParams))[jss::result];
1536 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32);
1537 }
1538 {
1539 // Locate directory by directory root.
1540 Json::Value jvParams;
1541 jvParams[jss::directory] = Json::objectValue;
1542 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1543 Json::Value const jrr = env.rpc(
1544 "json", "ledger_entry", to_string(jvParams))[jss::result];
1545 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
1546 }
1547 {
1548 // Locate directory by owner.
1549 Json::Value jvParams;
1550 jvParams[jss::directory] = Json::objectValue;
1551 jvParams[jss::directory][jss::owner] = alice.human();
1552 jvParams[jss::ledger_hash] = ledgerHash;
1553 Json::Value const jrr = env.rpc(
1554 "json", "ledger_entry", to_string(jvParams))[jss::result];
1555 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
1556 }
1557 {
1558 // Locate directory by directory root and sub_index.
1559 Json::Value jvParams;
1560 jvParams[jss::directory] = Json::objectValue;
1561 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1562 jvParams[jss::directory][jss::sub_index] = 1;
1563 Json::Value const jrr = env.rpc(
1564 "json", "ledger_entry", to_string(jvParams))[jss::result];
1565 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
1566 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
1567 }
1568 {
1569 // Locate directory by owner and sub_index.
1570 Json::Value jvParams;
1571 jvParams[jss::directory] = Json::objectValue;
1572 jvParams[jss::directory][jss::owner] = alice.human();
1573 jvParams[jss::directory][jss::sub_index] = 1;
1574 jvParams[jss::ledger_hash] = ledgerHash;
1575 Json::Value const jrr = env.rpc(
1576 "json", "ledger_entry", to_string(jvParams))[jss::result];
1577 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
1578 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
1579 }
1580 {
1581 // Null directory argument.
1582 Json::Value jvParams;
1583 jvParams[jss::directory] = Json::nullValue;
1584 jvParams[jss::ledger_hash] = ledgerHash;
1585 Json::Value const jrr = env.rpc(
1586 "json", "ledger_entry", to_string(jvParams))[jss::result];
1587 checkErrorValue(jrr, "malformedRequest", "");
1588 }
1589 {
1590 // Non-integer sub_index.
1591 Json::Value jvParams;
1592 jvParams[jss::directory] = Json::objectValue;
1593 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1594 jvParams[jss::directory][jss::sub_index] = 1.5;
1595 jvParams[jss::ledger_hash] = ledgerHash;
1596 Json::Value const jrr = env.rpc(
1597 "json", "ledger_entry", to_string(jvParams))[jss::result];
1598 checkErrorValue(jrr, "malformedRequest", "");
1599 }
1600 {
1601 // Malformed owner entry.
1602 Json::Value jvParams;
1603 jvParams[jss::directory] = Json::objectValue;
1604
1605 std::string const badAddress = makeBadAddress(alice.human());
1606 jvParams[jss::directory][jss::owner] = badAddress;
1607 jvParams[jss::ledger_hash] = ledgerHash;
1608 Json::Value const jrr = env.rpc(
1609 "json", "ledger_entry", to_string(jvParams))[jss::result];
1610 checkErrorValue(jrr, "malformedAddress", "");
1611 }
1612 {
1613 // Malformed directory object. Specify both dir_root and owner.
1614 Json::Value jvParams;
1615 jvParams[jss::directory] = Json::objectValue;
1616 jvParams[jss::directory][jss::owner] = alice.human();
1617 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1618 jvParams[jss::ledger_hash] = ledgerHash;
1619 Json::Value const jrr = env.rpc(
1620 "json", "ledger_entry", to_string(jvParams))[jss::result];
1621 checkErrorValue(jrr, "malformedRequest", "");
1622 }
1623 {
1624 // Incomplete directory object. Missing both dir_root and owner.
1625 Json::Value jvParams;
1626 jvParams[jss::directory] = Json::objectValue;
1627 jvParams[jss::directory][jss::sub_index] = 1;
1628 jvParams[jss::ledger_hash] = ledgerHash;
1629 Json::Value const jrr = env.rpc(
1630 "json", "ledger_entry", to_string(jvParams))[jss::result];
1631 checkErrorValue(jrr, "malformedRequest", "");
1632 }
1633 }
1634
1635 void
1637 {
1638 testcase("ledger_entry Request Escrow");
1639 using namespace test::jtx;
1640 Env env{*this};
1641 Account const alice{"alice"};
1642 env.fund(XRP(10000), alice);
1643 env.close();
1644
1645 // Lambda to create an escrow.
1646 auto escrowCreate = [](test::jtx::Account const& account,
1647 test::jtx::Account const& to,
1648 STAmount const& amount,
1649 NetClock::time_point const& cancelAfter) {
1650 Json::Value jv;
1651 jv[jss::TransactionType] = jss::EscrowCreate;
1652 jv[jss::Flags] = tfUniversal;
1653 jv[jss::Account] = account.human();
1654 jv[jss::Destination] = to.human();
1655 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1656 jv[sfFinishAfter.jsonName] =
1657 cancelAfter.time_since_epoch().count() + 2;
1658 return jv;
1659 };
1660
1661 using namespace std::chrono_literals;
1662 env(escrowCreate(alice, alice, XRP(333), env.now() + 2s));
1663 env.close();
1664
1665 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1666 std::string escrowIndex;
1667 {
1668 // Request the escrow using owner and sequence.
1669 Json::Value jvParams;
1670 jvParams[jss::escrow] = Json::objectValue;
1671 jvParams[jss::escrow][jss::owner] = alice.human();
1672 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1673 Json::Value const jrr = env.rpc(
1674 "json", "ledger_entry", to_string(jvParams))[jss::result];
1675 BEAST_EXPECT(
1676 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1677 escrowIndex = jrr[jss::index].asString();
1678 }
1679 {
1680 // Request the escrow by index.
1681 Json::Value jvParams;
1682 jvParams[jss::escrow] = escrowIndex;
1683 jvParams[jss::ledger_hash] = ledgerHash;
1684 Json::Value const jrr = env.rpc(
1685 "json", "ledger_entry", to_string(jvParams))[jss::result];
1686 BEAST_EXPECT(
1687 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1688 }
1689 {
1690 // Malformed owner entry.
1691 Json::Value jvParams;
1692 jvParams[jss::escrow] = Json::objectValue;
1693
1694 std::string const badAddress = makeBadAddress(alice.human());
1695 jvParams[jss::escrow][jss::owner] = badAddress;
1696 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1697 jvParams[jss::ledger_hash] = ledgerHash;
1698 Json::Value const jrr = env.rpc(
1699 "json", "ledger_entry", to_string(jvParams))[jss::result];
1700 checkErrorValue(jrr, "malformedOwner", "");
1701 }
1702 {
1703 // Missing owner.
1704 Json::Value jvParams;
1705 jvParams[jss::escrow] = Json::objectValue;
1706 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1707 jvParams[jss::ledger_hash] = ledgerHash;
1708 Json::Value const jrr = env.rpc(
1709 "json", "ledger_entry", to_string(jvParams))[jss::result];
1710 checkErrorValue(jrr, "malformedRequest", "");
1711 }
1712 {
1713 // Missing sequence.
1714 Json::Value jvParams;
1715 jvParams[jss::escrow] = Json::objectValue;
1716 jvParams[jss::escrow][jss::owner] = alice.human();
1717 jvParams[jss::ledger_hash] = ledgerHash;
1718 Json::Value const jrr = env.rpc(
1719 "json", "ledger_entry", to_string(jvParams))[jss::result];
1720 checkErrorValue(jrr, "malformedRequest", "");
1721 }
1722 {
1723 // Non-integer sequence.
1724 Json::Value jvParams;
1725 jvParams[jss::escrow] = Json::objectValue;
1726 jvParams[jss::escrow][jss::owner] = alice.human();
1727 jvParams[jss::escrow][jss::seq] =
1728 std::to_string(env.seq(alice) - 1);
1729 jvParams[jss::ledger_hash] = ledgerHash;
1730 Json::Value const jrr = env.rpc(
1731 "json", "ledger_entry", to_string(jvParams))[jss::result];
1732 checkErrorValue(jrr, "malformedRequest", "");
1733 }
1734 }
1735
1736 void
1738 {
1739 testcase("ledger_entry Request Offer");
1740 using namespace test::jtx;
1741 Env env{*this};
1742 Account const alice{"alice"};
1743 Account const gw{"gateway"};
1744 auto const USD = gw["USD"];
1745 env.fund(XRP(10000), alice, gw);
1746 env.close();
1747
1748 env(offer(alice, USD(321), XRP(322)));
1749 env.close();
1750
1751 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1752 std::string offerIndex;
1753 {
1754 // Request the offer using owner and sequence.
1755 Json::Value jvParams;
1756 jvParams[jss::offer] = Json::objectValue;
1757 jvParams[jss::offer][jss::account] = alice.human();
1758 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1759 jvParams[jss::ledger_hash] = ledgerHash;
1760 Json::Value const jrr = env.rpc(
1761 "json", "ledger_entry", to_string(jvParams))[jss::result];
1762 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1763 offerIndex = jrr[jss::index].asString();
1764 }
1765 {
1766 // Request the offer using its index.
1767 Json::Value jvParams;
1768 jvParams[jss::offer] = offerIndex;
1769 Json::Value const jrr = env.rpc(
1770 "json", "ledger_entry", to_string(jvParams))[jss::result];
1771 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1772 }
1773 {
1774 // Malformed account entry.
1775 Json::Value jvParams;
1776 jvParams[jss::offer] = Json::objectValue;
1777
1778 std::string const badAddress = makeBadAddress(alice.human());
1779 jvParams[jss::offer][jss::account] = badAddress;
1780 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1781 jvParams[jss::ledger_hash] = ledgerHash;
1782 Json::Value const jrr = env.rpc(
1783 "json", "ledger_entry", to_string(jvParams))[jss::result];
1784 checkErrorValue(jrr, "malformedAddress", "");
1785 }
1786 {
1787 // Malformed offer object. Missing account member.
1788 Json::Value jvParams;
1789 jvParams[jss::offer] = Json::objectValue;
1790 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1791 jvParams[jss::ledger_hash] = ledgerHash;
1792 Json::Value const jrr = env.rpc(
1793 "json", "ledger_entry", to_string(jvParams))[jss::result];
1794 checkErrorValue(jrr, "malformedRequest", "");
1795 }
1796 {
1797 // Malformed offer object. Missing seq member.
1798 Json::Value jvParams;
1799 jvParams[jss::offer] = Json::objectValue;
1800 jvParams[jss::offer][jss::account] = alice.human();
1801 jvParams[jss::ledger_hash] = ledgerHash;
1802 Json::Value const jrr = env.rpc(
1803 "json", "ledger_entry", to_string(jvParams))[jss::result];
1804 checkErrorValue(jrr, "malformedRequest", "");
1805 }
1806 {
1807 // Malformed offer object. Non-integral seq member.
1808 Json::Value jvParams;
1809 jvParams[jss::offer] = Json::objectValue;
1810 jvParams[jss::offer][jss::account] = alice.human();
1811 jvParams[jss::offer][jss::seq] = std::to_string(env.seq(alice) - 1);
1812 jvParams[jss::ledger_hash] = ledgerHash;
1813 Json::Value const jrr = env.rpc(
1814 "json", "ledger_entry", to_string(jvParams))[jss::result];
1815 checkErrorValue(jrr, "malformedRequest", "");
1816 }
1817 }
1818
1819 void
1821 {
1822 testcase("ledger_entry Request Pay Chan");
1823 using namespace test::jtx;
1824 using namespace std::literals::chrono_literals;
1825 Env env{*this};
1826 Account const alice{"alice"};
1827
1828 env.fund(XRP(10000), alice);
1829 env.close();
1830
1831 // Lambda to create a PayChan.
1832 auto payChanCreate = [](test::jtx::Account const& account,
1833 test::jtx::Account const& to,
1834 STAmount const& amount,
1835 NetClock::duration const& settleDelay,
1836 PublicKey const& pk) {
1837 Json::Value jv;
1838 jv[jss::TransactionType] = jss::PaymentChannelCreate;
1839 jv[jss::Account] = account.human();
1840 jv[jss::Destination] = to.human();
1841 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1842 jv[sfSettleDelay.jsonName] = settleDelay.count();
1843 jv[sfPublicKey.jsonName] = strHex(pk.slice());
1844 return jv;
1845 };
1846
1847 env(payChanCreate(alice, env.master, XRP(57), 18s, alice.pk()));
1848 env.close();
1849
1850 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1851
1852 uint256 const payChanIndex{
1853 keylet::payChan(alice, env.master, env.seq(alice) - 1).key};
1854 {
1855 // Request the payment channel using its index.
1856 Json::Value jvParams;
1857 jvParams[jss::payment_channel] = to_string(payChanIndex);
1858 jvParams[jss::ledger_hash] = ledgerHash;
1859 Json::Value const jrr = env.rpc(
1860 "json", "ledger_entry", to_string(jvParams))[jss::result];
1861 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
1862 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
1863 BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
1864 }
1865 {
1866 // Request an index that is not a payment channel.
1867 Json::Value jvParams;
1868 jvParams[jss::payment_channel] = ledgerHash;
1869 jvParams[jss::ledger_hash] = ledgerHash;
1870 Json::Value const jrr = env.rpc(
1871 "json", "ledger_entry", to_string(jvParams))[jss::result];
1872 checkErrorValue(jrr, "entryNotFound", "");
1873 }
1874 }
1875
1876 void
1878 {
1879 testcase("ledger_entry Request RippleState");
1880 using namespace test::jtx;
1881 Env env{*this};
1882 Account const alice{"alice"};
1883 Account const gw{"gateway"};
1884 auto const USD = gw["USD"];
1885 env.fund(XRP(10000), alice, gw);
1886 env.close();
1887
1888 env.trust(USD(999), alice);
1889 env.close();
1890
1891 env(pay(gw, alice, USD(97)));
1892 env.close();
1893
1894 // check both aliases
1895 for (auto const& fieldName : {jss::ripple_state, jss::state})
1896 {
1897 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1898 {
1899 // Request the trust line using the accounts and currency.
1900 Json::Value jvParams;
1901 jvParams[fieldName] = Json::objectValue;
1902 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1903 jvParams[fieldName][jss::accounts][0u] = alice.human();
1904 jvParams[fieldName][jss::accounts][1u] = gw.human();
1905 jvParams[fieldName][jss::currency] = "USD";
1906 jvParams[jss::ledger_hash] = ledgerHash;
1907 Json::Value const jrr = env.rpc(
1908 "json", "ledger_entry", to_string(jvParams))[jss::result];
1909 BEAST_EXPECT(
1910 jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
1911 BEAST_EXPECT(
1912 jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
1913 }
1914 {
1915 // ripple_state is not an object.
1916 Json::Value jvParams;
1917 jvParams[fieldName] = "ripple_state";
1918 jvParams[jss::ledger_hash] = ledgerHash;
1919 Json::Value const jrr = env.rpc(
1920 "json", "ledger_entry", to_string(jvParams))[jss::result];
1921 checkErrorValue(jrr, "malformedRequest", "");
1922 }
1923 {
1924 // ripple_state.currency is missing.
1925 Json::Value jvParams;
1926 jvParams[fieldName] = Json::objectValue;
1927 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1928 jvParams[fieldName][jss::accounts][0u] = alice.human();
1929 jvParams[fieldName][jss::accounts][1u] = gw.human();
1930 jvParams[jss::ledger_hash] = ledgerHash;
1931 Json::Value const jrr = env.rpc(
1932 "json", "ledger_entry", to_string(jvParams))[jss::result];
1933 checkErrorValue(jrr, "malformedRequest", "");
1934 }
1935 {
1936 // ripple_state accounts is not an array.
1937 Json::Value jvParams;
1938 jvParams[fieldName] = Json::objectValue;
1939 jvParams[fieldName][jss::accounts] = 2;
1940 jvParams[fieldName][jss::currency] = "USD";
1941 jvParams[jss::ledger_hash] = ledgerHash;
1942 Json::Value const jrr = env.rpc(
1943 "json", "ledger_entry", to_string(jvParams))[jss::result];
1944 checkErrorValue(jrr, "malformedRequest", "");
1945 }
1946 {
1947 // ripple_state one of the accounts is missing.
1948 Json::Value jvParams;
1949 jvParams[fieldName] = Json::objectValue;
1950 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1951 jvParams[fieldName][jss::accounts][0u] = alice.human();
1952 jvParams[fieldName][jss::currency] = "USD";
1953 jvParams[jss::ledger_hash] = ledgerHash;
1954 Json::Value const jrr = env.rpc(
1955 "json", "ledger_entry", to_string(jvParams))[jss::result];
1956 checkErrorValue(jrr, "malformedRequest", "");
1957 }
1958 {
1959 // ripple_state more than 2 accounts.
1960 Json::Value jvParams;
1961 jvParams[fieldName] = Json::objectValue;
1962 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1963 jvParams[fieldName][jss::accounts][0u] = alice.human();
1964 jvParams[fieldName][jss::accounts][1u] = gw.human();
1965 jvParams[fieldName][jss::accounts][2u] = alice.human();
1966 jvParams[fieldName][jss::currency] = "USD";
1967 jvParams[jss::ledger_hash] = ledgerHash;
1968 Json::Value const jrr = env.rpc(
1969 "json", "ledger_entry", to_string(jvParams))[jss::result];
1970 checkErrorValue(jrr, "malformedRequest", "");
1971 }
1972 {
1973 // ripple_state account[0] is not a string.
1974 Json::Value jvParams;
1975 jvParams[fieldName] = Json::objectValue;
1976 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1977 jvParams[fieldName][jss::accounts][0u] = 44;
1978 jvParams[fieldName][jss::accounts][1u] = gw.human();
1979 jvParams[fieldName][jss::currency] = "USD";
1980 jvParams[jss::ledger_hash] = ledgerHash;
1981 Json::Value const jrr = env.rpc(
1982 "json", "ledger_entry", to_string(jvParams))[jss::result];
1983 checkErrorValue(jrr, "malformedRequest", "");
1984 }
1985 {
1986 // ripple_state account[1] is not a string.
1987 Json::Value jvParams;
1988 jvParams[fieldName] = Json::objectValue;
1989 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1990 jvParams[fieldName][jss::accounts][0u] = alice.human();
1991 jvParams[fieldName][jss::accounts][1u] = 21;
1992 jvParams[fieldName][jss::currency] = "USD";
1993 jvParams[jss::ledger_hash] = ledgerHash;
1994 Json::Value const jrr = env.rpc(
1995 "json", "ledger_entry", to_string(jvParams))[jss::result];
1996 checkErrorValue(jrr, "malformedRequest", "");
1997 }
1998 {
1999 // ripple_state account[0] == account[1].
2000 Json::Value jvParams;
2001 jvParams[fieldName] = Json::objectValue;
2002 jvParams[fieldName][jss::accounts] = Json::arrayValue;
2003 jvParams[fieldName][jss::accounts][0u] = alice.human();
2004 jvParams[fieldName][jss::accounts][1u] = alice.human();
2005 jvParams[fieldName][jss::currency] = "USD";
2006 jvParams[jss::ledger_hash] = ledgerHash;
2007 Json::Value const jrr = env.rpc(
2008 "json", "ledger_entry", to_string(jvParams))[jss::result];
2009 checkErrorValue(jrr, "malformedRequest", "");
2010 }
2011 {
2012 // ripple_state malformed account[0].
2013 Json::Value jvParams;
2014 jvParams[fieldName] = Json::objectValue;
2015 jvParams[fieldName][jss::accounts] = Json::arrayValue;
2016 jvParams[fieldName][jss::accounts][0u] =
2017 makeBadAddress(alice.human());
2018 jvParams[fieldName][jss::accounts][1u] = gw.human();
2019 jvParams[fieldName][jss::currency] = "USD";
2020 jvParams[jss::ledger_hash] = ledgerHash;
2021 Json::Value const jrr = env.rpc(
2022 "json", "ledger_entry", to_string(jvParams))[jss::result];
2023 checkErrorValue(jrr, "malformedAddress", "");
2024 }
2025 {
2026 // ripple_state malformed account[1].
2027 Json::Value jvParams;
2028 jvParams[fieldName] = Json::objectValue;
2029 jvParams[fieldName][jss::accounts] = Json::arrayValue;
2030 jvParams[fieldName][jss::accounts][0u] = alice.human();
2031 jvParams[fieldName][jss::accounts][1u] =
2032 makeBadAddress(gw.human());
2033 jvParams[fieldName][jss::currency] = "USD";
2034 jvParams[jss::ledger_hash] = ledgerHash;
2035 Json::Value const jrr = env.rpc(
2036 "json", "ledger_entry", to_string(jvParams))[jss::result];
2037 checkErrorValue(jrr, "malformedAddress", "");
2038 }
2039 {
2040 // ripple_state malformed currency.
2041 Json::Value jvParams;
2042 jvParams[fieldName] = Json::objectValue;
2043 jvParams[fieldName][jss::accounts] = Json::arrayValue;
2044 jvParams[fieldName][jss::accounts][0u] = alice.human();
2045 jvParams[fieldName][jss::accounts][1u] = gw.human();
2046 jvParams[fieldName][jss::currency] = "USDollars";
2047 jvParams[jss::ledger_hash] = ledgerHash;
2048 Json::Value const jrr = env.rpc(
2049 "json", "ledger_entry", to_string(jvParams))[jss::result];
2050 checkErrorValue(jrr, "malformedCurrency", "");
2051 }
2052 }
2053 }
2054
2055 void
2057 {
2058 testcase("ledger_entry Request Ticket");
2059 using namespace test::jtx;
2060 Env env{*this};
2061 env.close();
2062
2063 // Create two tickets.
2064 std::uint32_t const tkt1{env.seq(env.master) + 1};
2065 env(ticket::create(env.master, 2));
2066 env.close();
2067
2068 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2069 // Request four tickets: one before the first one we created, the
2070 // two created tickets, and the ticket that would come after the
2071 // last created ticket.
2072 {
2073 // Not a valid ticket requested by index.
2074 Json::Value jvParams;
2075 jvParams[jss::ticket] =
2076 to_string(getTicketIndex(env.master, tkt1 - 1));
2077 jvParams[jss::ledger_hash] = ledgerHash;
2078 Json::Value const jrr = env.rpc(
2079 "json", "ledger_entry", to_string(jvParams))[jss::result];
2080 checkErrorValue(jrr, "entryNotFound", "");
2081 }
2082 {
2083 // First real ticket requested by index.
2084 Json::Value jvParams;
2085 jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1));
2086 jvParams[jss::ledger_hash] = ledgerHash;
2087 Json::Value const jrr = env.rpc(
2088 "json", "ledger_entry", to_string(jvParams))[jss::result];
2089 BEAST_EXPECT(
2090 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Ticket);
2091 BEAST_EXPECT(jrr[jss::node][sfTicketSequence.jsonName] == tkt1);
2092 }
2093 {
2094 // Second real ticket requested by account and sequence.
2095 Json::Value jvParams;
2096 jvParams[jss::ticket] = Json::objectValue;
2097 jvParams[jss::ticket][jss::account] = env.master.human();
2098 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 1;
2099 jvParams[jss::ledger_hash] = ledgerHash;
2100 Json::Value const jrr = env.rpc(
2101 "json", "ledger_entry", to_string(jvParams))[jss::result];
2102 BEAST_EXPECT(
2103 jrr[jss::node][jss::index] ==
2104 to_string(getTicketIndex(env.master, tkt1 + 1)));
2105 }
2106 {
2107 // Not a valid ticket requested by account and sequence.
2108 Json::Value jvParams;
2109 jvParams[jss::ticket] = Json::objectValue;
2110 jvParams[jss::ticket][jss::account] = env.master.human();
2111 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 2;
2112 jvParams[jss::ledger_hash] = ledgerHash;
2113 Json::Value const jrr = env.rpc(
2114 "json", "ledger_entry", to_string(jvParams))[jss::result];
2115 checkErrorValue(jrr, "entryNotFound", "");
2116 }
2117 {
2118 // Request a ticket using an account root entry.
2119 Json::Value jvParams;
2120 jvParams[jss::ticket] = to_string(keylet::account(env.master).key);
2121 jvParams[jss::ledger_hash] = ledgerHash;
2122 Json::Value const jrr = env.rpc(
2123 "json", "ledger_entry", to_string(jvParams))[jss::result];
2124 checkErrorValue(jrr, "unexpectedLedgerType", "");
2125 }
2126 {
2127 // Malformed account entry.
2128 Json::Value jvParams;
2129 jvParams[jss::ticket] = Json::objectValue;
2130
2131 std::string const badAddress = makeBadAddress(env.master.human());
2132 jvParams[jss::ticket][jss::account] = badAddress;
2133 jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
2134 jvParams[jss::ledger_hash] = ledgerHash;
2135 Json::Value const jrr = env.rpc(
2136 "json", "ledger_entry", to_string(jvParams))[jss::result];
2137 checkErrorValue(jrr, "malformedAddress", "");
2138 }
2139 {
2140 // Malformed ticket object. Missing account member.
2141 Json::Value jvParams;
2142 jvParams[jss::ticket] = Json::objectValue;
2143 jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
2144 jvParams[jss::ledger_hash] = ledgerHash;
2145 Json::Value const jrr = env.rpc(
2146 "json", "ledger_entry", to_string(jvParams))[jss::result];
2147 checkErrorValue(jrr, "malformedRequest", "");
2148 }
2149 {
2150 // Malformed ticket object. Missing seq member.
2151 Json::Value jvParams;
2152 jvParams[jss::ticket] = Json::objectValue;
2153 jvParams[jss::ticket][jss::account] = env.master.human();
2154 jvParams[jss::ledger_hash] = ledgerHash;
2155 Json::Value const jrr = env.rpc(
2156 "json", "ledger_entry", to_string(jvParams))[jss::result];
2157 checkErrorValue(jrr, "malformedRequest", "");
2158 }
2159 {
2160 // Malformed ticket object. Non-integral seq member.
2161 Json::Value jvParams;
2162 jvParams[jss::ticket] = Json::objectValue;
2163 jvParams[jss::ticket][jss::account] = env.master.human();
2164 jvParams[jss::ticket][jss::ticket_seq] =
2165 std::to_string(env.seq(env.master) - 1);
2166 jvParams[jss::ledger_hash] = ledgerHash;
2167 Json::Value const jrr = env.rpc(
2168 "json", "ledger_entry", to_string(jvParams))[jss::result];
2169 checkErrorValue(jrr, "malformedRequest", "");
2170 }
2171 }
2172
2173 void
2175 {
2176 testcase("ledger_entry Request DID");
2177 using namespace test::jtx;
2178 using namespace std::literals::chrono_literals;
2179 Env env{*this};
2180 Account const alice{"alice"};
2181
2182 env.fund(XRP(10000), alice);
2183 env.close();
2184
2185 // Lambda to create a DID.
2186 auto didCreate = [](test::jtx::Account const& account) {
2187 Json::Value jv;
2188 jv[jss::TransactionType] = jss::DIDSet;
2189 jv[jss::Account] = account.human();
2190 jv[sfDIDDocument.jsonName] = strHex(std::string{"data"});
2191 jv[sfURI.jsonName] = strHex(std::string{"uri"});
2192 return jv;
2193 };
2194
2195 env(didCreate(alice));
2196 env.close();
2197
2198 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2199
2200 {
2201 // Request the DID using its index.
2202 Json::Value jvParams;
2203 jvParams[jss::did] = alice.human();
2204 jvParams[jss::ledger_hash] = ledgerHash;
2205 Json::Value const jrr = env.rpc(
2206 "json", "ledger_entry", to_string(jvParams))[jss::result];
2207 BEAST_EXPECT(
2208 jrr[jss::node][sfDIDDocument.jsonName] ==
2209 strHex(std::string{"data"}));
2210 BEAST_EXPECT(
2211 jrr[jss::node][sfURI.jsonName] == strHex(std::string{"uri"}));
2212 }
2213 {
2214 // Request an index that is not a DID.
2215 Json::Value jvParams;
2216 jvParams[jss::did] = env.master.human();
2217 jvParams[jss::ledger_hash] = ledgerHash;
2218 Json::Value const jrr = env.rpc(
2219 "json", "ledger_entry", to_string(jvParams))[jss::result];
2220 checkErrorValue(jrr, "entryNotFound", "");
2221 }
2222 }
2223
2224 void
2225 testLedgerEntryInvalidParams(unsigned int apiVersion)
2226 {
2227 testcase(
2228 "ledger_entry Request With Invalid Parameters v" +
2229 std::to_string(apiVersion));
2230 using namespace test::jtx;
2231 Env env{*this};
2232
2233 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2234
2235 auto makeParams = [&apiVersion](std::function<void(Json::Value&)> f) {
2236 Json::Value params;
2237 params[jss::api_version] = apiVersion;
2238 f(params);
2239 return params;
2240 };
2241 // "features" is not an option supported by ledger_entry.
2242 {
2243 auto const jvParams =
2244 makeParams([&ledgerHash](Json::Value& jvParams) {
2245 jvParams[jss::features] = ledgerHash;
2246 jvParams[jss::ledger_hash] = ledgerHash;
2247 });
2248 Json::Value const jrr = env.rpc(
2249 "json", "ledger_entry", to_string(jvParams))[jss::result];
2250
2251 if (apiVersion < 2u)
2252 checkErrorValue(jrr, "unknownOption", "");
2253 else
2254 checkErrorValue(jrr, "invalidParams", "");
2255 }
2256 Json::Value const injectObject = []() {
2258 obj[jss::account] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2259 obj[jss::ledger_index] = "validated";
2260 return obj;
2261 }();
2262 Json::Value const injectArray = []() {
2264 arr[0u] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2265 arr[1u] = "validated";
2266 return arr;
2267 }();
2268
2269 // invalid input for fields that can handle an object, but can't handle
2270 // an array
2271 for (auto const& field :
2272 {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm})
2273 {
2274 auto const jvParams =
2275 makeParams([&field, &injectArray](Json::Value& jvParams) {
2276 jvParams[field] = injectArray;
2277 });
2278
2279 Json::Value const jrr = env.rpc(
2280 "json", "ledger_entry", to_string(jvParams))[jss::result];
2281
2282 if (apiVersion < 2u)
2283 checkErrorValue(jrr, "internal", "Internal error.");
2284 else
2285 checkErrorValue(jrr, "invalidParams", "");
2286 }
2287 // Fields that can handle objects just fine
2288 for (auto const& field :
2289 {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm})
2290 {
2291 auto const jvParams =
2292 makeParams([&field, &injectObject](Json::Value& jvParams) {
2293 jvParams[field] = injectObject;
2294 });
2295
2296 Json::Value const jrr = env.rpc(
2297 "json", "ledger_entry", to_string(jvParams))[jss::result];
2298
2299 checkErrorValue(jrr, "malformedRequest", "");
2300 }
2301
2302 for (auto const& inject : {injectObject, injectArray})
2303 {
2304 // invalid input for fields that can't handle an object or an array
2305 for (auto const& field :
2306 {jss::index,
2307 jss::account_root,
2308 jss::check,
2309 jss::payment_channel})
2310 {
2311 auto const jvParams =
2312 makeParams([&field, &inject](Json::Value& jvParams) {
2313 jvParams[field] = inject;
2314 });
2315
2316 Json::Value const jrr = env.rpc(
2317 "json", "ledger_entry", to_string(jvParams))[jss::result];
2318
2319 if (apiVersion < 2u)
2320 checkErrorValue(jrr, "internal", "Internal error.");
2321 else
2322 checkErrorValue(jrr, "invalidParams", "");
2323 }
2324 // directory sub-fields
2325 for (auto const& field : {jss::dir_root, jss::owner})
2326 {
2327 auto const jvParams =
2328 makeParams([&field, &inject](Json::Value& jvParams) {
2329 jvParams[jss::directory][field] = inject;
2330 });
2331
2332 Json::Value const jrr = env.rpc(
2333 "json", "ledger_entry", to_string(jvParams))[jss::result];
2334
2335 if (apiVersion < 2u)
2336 checkErrorValue(jrr, "internal", "Internal error.");
2337 else
2338 checkErrorValue(jrr, "invalidParams", "");
2339 }
2340 // escrow sub-fields
2341 {
2342 auto const jvParams =
2343 makeParams([&inject](Json::Value& jvParams) {
2344 jvParams[jss::escrow][jss::owner] = inject;
2345 jvParams[jss::escrow][jss::seq] = 99;
2346 });
2347
2348 Json::Value const jrr = env.rpc(
2349 "json", "ledger_entry", to_string(jvParams))[jss::result];
2350
2351 if (apiVersion < 2u)
2352 checkErrorValue(jrr, "internal", "Internal error.");
2353 else
2354 checkErrorValue(jrr, "invalidParams", "");
2355 }
2356 // offer sub-fields
2357 {
2358 auto const jvParams =
2359 makeParams([&inject](Json::Value& jvParams) {
2360 jvParams[jss::offer][jss::account] = inject;
2361 jvParams[jss::offer][jss::seq] = 99;
2362 });
2363
2364 Json::Value const jrr = env.rpc(
2365 "json", "ledger_entry", to_string(jvParams))[jss::result];
2366
2367 if (apiVersion < 2u)
2368 checkErrorValue(jrr, "internal", "Internal error.");
2369 else
2370 checkErrorValue(jrr, "invalidParams", "");
2371 }
2372 // ripple_state sub-fields
2373 {
2374 auto const jvParams =
2375 makeParams([&inject](Json::Value& jvParams) {
2377 rs[jss::currency] = "FOO";
2378 rs[jss::accounts] = Json::Value(Json::arrayValue);
2379 rs[jss::accounts][0u] =
2380 "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2381 rs[jss::accounts][1u] =
2382 "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv";
2383 rs[jss::currency] = inject;
2384 jvParams[jss::ripple_state] = std::move(rs);
2385 });
2386
2387 Json::Value const jrr = env.rpc(
2388 "json", "ledger_entry", to_string(jvParams))[jss::result];
2389
2390 if (apiVersion < 2u)
2391 checkErrorValue(jrr, "internal", "Internal error.");
2392 else
2393 checkErrorValue(jrr, "invalidParams", "");
2394 }
2395 // ticket sub-fields
2396 {
2397 auto const jvParams =
2398 makeParams([&inject](Json::Value& jvParams) {
2399 jvParams[jss::ticket][jss::account] = inject;
2400 jvParams[jss::ticket][jss::ticket_seq] = 99;
2401 });
2402
2403 Json::Value const jrr = env.rpc(
2404 "json", "ledger_entry", to_string(jvParams))[jss::result];
2405
2406 if (apiVersion < 2u)
2407 checkErrorValue(jrr, "internal", "Internal error.");
2408 else
2409 checkErrorValue(jrr, "invalidParams", "");
2410 }
2411
2412 // Fields that can handle malformed inputs just fine
2413 for (auto const& field : {jss::nft_page, jss::deposit_preauth})
2414 {
2415 auto const jvParams =
2416 makeParams([&field, &inject](Json::Value& jvParams) {
2417 jvParams[field] = inject;
2418 });
2419
2420 Json::Value const jrr = env.rpc(
2421 "json", "ledger_entry", to_string(jvParams))[jss::result];
2422
2423 checkErrorValue(jrr, "malformedRequest", "");
2424 }
2425 // Subfields of deposit_preauth that can handle malformed inputs
2426 // fine
2427 for (auto const& field : {jss::owner, jss::authorized})
2428 {
2429 auto const jvParams =
2430 makeParams([&field, &inject](Json::Value& jvParams) {
2431 auto pa = Json::Value(Json::objectValue);
2432 pa[jss::owner] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2433 pa[jss::authorized] =
2434 "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv";
2435 pa[field] = inject;
2436 jvParams[jss::deposit_preauth] = std::move(pa);
2437 });
2438
2439 Json::Value const jrr = env.rpc(
2440 "json", "ledger_entry", to_string(jvParams))[jss::result];
2441
2442 checkErrorValue(jrr, "malformedRequest", "");
2443 }
2444 }
2445 }
2446
2451 void
2453 {
2454 testcase("Lookup ledger");
2455 using namespace test::jtx;
2456
2457 auto cfg = envconfig();
2458 cfg->FEES.reference_fee = 10;
2459 Env env{
2460 *this, std::move(cfg), FeatureBitset{}}; // hashes requested below
2461 // assume no amendments
2462 env.fund(XRP(10000), "alice");
2463 env.close();
2464 env.fund(XRP(10000), "bob");
2465 env.close();
2466 env.fund(XRP(10000), "jim");
2467 env.close();
2468 env.fund(XRP(10000), "jill");
2469
2470 {
2471 // access via the legacy ledger field, keyword index values
2472 Json::Value jvParams;
2473 jvParams[jss::ledger] = "closed";
2474 auto jrr = env.rpc(
2475 "json",
2476 "ledger",
2477 boost::lexical_cast<std::string>(jvParams))[jss::result];
2478 BEAST_EXPECT(jrr.isMember(jss::ledger));
2479 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2480 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
2481
2482 jvParams[jss::ledger] = "validated";
2483 jrr = env.rpc(
2484 "json",
2485 "ledger",
2486 boost::lexical_cast<std::string>(jvParams))[jss::result];
2487 BEAST_EXPECT(jrr.isMember(jss::ledger));
2488 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2489 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
2490
2491 jvParams[jss::ledger] = "current";
2492 jrr = env.rpc(
2493 "json",
2494 "ledger",
2495 boost::lexical_cast<std::string>(jvParams))[jss::result];
2496 BEAST_EXPECT(jrr.isMember(jss::ledger));
2497 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
2498
2499 // ask for a bad ledger keyword
2500 jvParams[jss::ledger] = "invalid";
2501 jrr = env.rpc(
2502 "json",
2503 "ledger",
2504 boost::lexical_cast<std::string>(jvParams))[jss::result];
2505 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2506 BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
2507
2508 // numeric index
2509 jvParams[jss::ledger] = 4;
2510 jrr = env.rpc(
2511 "json",
2512 "ledger",
2513 boost::lexical_cast<std::string>(jvParams))[jss::result];
2514 BEAST_EXPECT(jrr.isMember(jss::ledger));
2515 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2516 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "4");
2517
2518 // numeric index - out of range
2519 jvParams[jss::ledger] = 20;
2520 jrr = env.rpc(
2521 "json",
2522 "ledger",
2523 boost::lexical_cast<std::string>(jvParams))[jss::result];
2524 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
2525 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
2526 }
2527
2528 {
2529 std::string const hash3{
2530 "E86DE7F3D7A4D9CE17EF7C8BA08A8F4D"
2531 "8F643B9552F0D895A31CDA78F541DE4E"};
2532 // access via the ledger_hash field
2533 Json::Value jvParams;
2534 jvParams[jss::ledger_hash] = hash3;
2535 auto jrr = env.rpc(
2536 "json",
2537 "ledger",
2538 boost::lexical_cast<std::string>(jvParams))[jss::result];
2539 BEAST_EXPECT(jrr.isMember(jss::ledger));
2540 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2541 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "3");
2542
2543 // extra leading hex chars in hash are not allowed
2544 jvParams[jss::ledger_hash] = "DEADBEEF" + hash3;
2545 jrr = env.rpc(
2546 "json",
2547 "ledger",
2548 boost::lexical_cast<std::string>(jvParams))[jss::result];
2549 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2550 BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
2551
2552 // request with non-string ledger_hash
2553 jvParams[jss::ledger_hash] = 2;
2554 jrr = env.rpc(
2555 "json",
2556 "ledger",
2557 boost::lexical_cast<std::string>(jvParams))[jss::result];
2558 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2559 BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashNotString");
2560
2561 // malformed (non hex) hash
2562 jvParams[jss::ledger_hash] =
2563 "2E81FC6EC0DD943197EGC7E3FBE9AE30"
2564 "7F2775F2F7485BB37307984C3C0F2340";
2565 jrr = env.rpc(
2566 "json",
2567 "ledger",
2568 boost::lexical_cast<std::string>(jvParams))[jss::result];
2569 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2570 BEAST_EXPECT(jrr[jss::error_message] == "ledgerHashMalformed");
2571
2572 // properly formed, but just doesn't exist
2573 jvParams[jss::ledger_hash] =
2574 "8C3EEDB3124D92E49E75D81A8826A2E6"
2575 "5A75FD71FC3FD6F36FEB803C5F1D812D";
2576 jrr = env.rpc(
2577 "json",
2578 "ledger",
2579 boost::lexical_cast<std::string>(jvParams))[jss::result];
2580 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
2581 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
2582 }
2583
2584 {
2585 // access via the ledger_index field, keyword index values
2586 Json::Value jvParams;
2587 jvParams[jss::ledger_index] = "closed";
2588 auto jrr = env.rpc(
2589 "json",
2590 "ledger",
2591 boost::lexical_cast<std::string>(jvParams))[jss::result];
2592 BEAST_EXPECT(jrr.isMember(jss::ledger));
2593 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2594 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
2595 BEAST_EXPECT(jrr.isMember(jss::ledger_index));
2596
2597 jvParams[jss::ledger_index] = "validated";
2598 jrr = env.rpc(
2599 "json",
2600 "ledger",
2601 boost::lexical_cast<std::string>(jvParams))[jss::result];
2602 BEAST_EXPECT(jrr.isMember(jss::ledger));
2603 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2604 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5");
2605
2606 jvParams[jss::ledger_index] = "current";
2607 jrr = env.rpc(
2608 "json",
2609 "ledger",
2610 boost::lexical_cast<std::string>(jvParams))[jss::result];
2611 BEAST_EXPECT(jrr.isMember(jss::ledger));
2612 BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6");
2613 BEAST_EXPECT(jrr.isMember(jss::ledger_current_index));
2614
2615 // ask for a bad ledger keyword
2616 jvParams[jss::ledger_index] = "invalid";
2617 jrr = env.rpc(
2618 "json",
2619 "ledger",
2620 boost::lexical_cast<std::string>(jvParams))[jss::result];
2621 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
2622 BEAST_EXPECT(jrr[jss::error_message] == "ledgerIndexMalformed");
2623
2624 // numeric index
2625 for (auto i : {1, 2, 3, 4, 5, 6})
2626 {
2627 jvParams[jss::ledger_index] = i;
2628 jrr = env.rpc(
2629 "json",
2630 "ledger",
2631 boost::lexical_cast<std::string>(jvParams))[jss::result];
2632 BEAST_EXPECT(jrr.isMember(jss::ledger));
2633 if (i < 6)
2634 BEAST_EXPECT(jrr.isMember(jss::ledger_hash));
2635 BEAST_EXPECT(
2636 jrr[jss::ledger][jss::ledger_index] == std::to_string(i));
2637 }
2638
2639 // numeric index - out of range
2640 jvParams[jss::ledger_index] = 7;
2641 jrr = env.rpc(
2642 "json",
2643 "ledger",
2644 boost::lexical_cast<std::string>(jvParams))[jss::result];
2645 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
2646 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
2647 }
2648 }
2649
2650 void
2652 {
2653 testcase("Ledger with queueing disabled");
2654 using namespace test::jtx;
2655 Env env{*this};
2656
2657 Json::Value jv;
2658 jv[jss::ledger_index] = "current";
2659 jv[jss::queue] = true;
2660 jv[jss::expand] = true;
2661
2662 auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2663 BEAST_EXPECT(!jrr.isMember(jss::queue_data));
2664 }
2665
2666 void
2668 {
2669 testcase("Ledger with Queued Transactions");
2670 using namespace test::jtx;
2671 auto cfg = envconfig([](std::unique_ptr<Config> cfg) {
2672 auto& section = cfg->section("transaction_queue");
2673 section.set("minimum_txn_in_ledger_standalone", "3");
2674 section.set("normal_consensus_increase_percent", "0");
2675 return cfg;
2676 });
2677
2678 cfg->FEES.reference_fee = 10;
2679 Env env(*this, std::move(cfg));
2680
2681 Json::Value jv;
2682 jv[jss::ledger_index] = "current";
2683 jv[jss::queue] = true;
2684 jv[jss::expand] = true;
2685
2686 Account const alice{"alice"};
2687 Account const bob{"bob"};
2688 Account const charlie{"charlie"};
2689 Account const daria{"daria"};
2690 env.fund(XRP(10000), alice);
2691 env.fund(XRP(10000), bob);
2692 env.close();
2693 env.fund(XRP(10000), charlie);
2694 env.fund(XRP(10000), daria);
2695 env.close();
2696
2697 auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2698 BEAST_EXPECT(!jrr.isMember(jss::queue_data));
2699
2700 // Fill the open ledger
2701 for (;;)
2702 {
2703 auto metrics = env.app().getTxQ().getMetrics(*env.current());
2704 if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
2705 break;
2706 env(noop(alice));
2707 }
2708
2709 BEAST_EXPECT(env.current()->info().seq == 5);
2710 // Put some txs in the queue
2711 // Alice
2712 auto aliceSeq = env.seq(alice);
2713 env(pay(alice, "george", XRP(1000)),
2714 json(R"({"LastLedgerSequence":7})"),
2715 ter(terQUEUED));
2716 env(offer(alice, XRP(50000), alice["USD"](5000)),
2717 seq(aliceSeq + 1),
2718 ter(terQUEUED));
2719 env(noop(alice), seq(aliceSeq + 2), ter(terQUEUED));
2720 // Bob
2721 auto batch = [&env](Account a) {
2722 auto aSeq = env.seq(a);
2723 // Enough fee to get in front of alice in the queue
2724 for (int i = 0; i < 10; ++i)
2725 {
2726 env(noop(a), fee(1000 + i), seq(aSeq + i), ter(terQUEUED));
2727 }
2728 };
2729 batch(bob);
2730 // Charlie
2731 batch(charlie);
2732 // Daria
2733 batch(daria);
2734
2735 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2736 BEAST_EXPECT(jrr[jss::queue_data].size() == 33);
2737
2738 // Close enough ledgers so that alice's first tx expires.
2739 env.close();
2740 env.close();
2741 env.close();
2742 BEAST_EXPECT(env.current()->info().seq == 8);
2743
2744 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2745 BEAST_EXPECT(jrr[jss::queue_data].size() == 11);
2746
2747 env.close();
2748
2749 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2750 const std::string txid0 = [&]() {
2751 auto const& parentHash = env.current()->info().parentHash;
2752 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
2753 {
2754 const std::string txid1 = [&]() {
2755 auto const& txj = jrr[jss::queue_data][1u];
2756 BEAST_EXPECT(txj[jss::account] == alice.human());
2757 BEAST_EXPECT(txj[jss::fee_level] == "256");
2758 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2759 BEAST_EXPECT(txj["retries_remaining"] == 10);
2760 BEAST_EXPECT(txj.isMember(jss::tx));
2761 auto const& tx = txj[jss::tx];
2762 BEAST_EXPECT(tx[jss::Account] == alice.human());
2763 BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
2764 return tx[jss::hash].asString();
2765 }();
2766
2767 auto const& txj = jrr[jss::queue_data][0u];
2768 BEAST_EXPECT(txj[jss::account] == alice.human());
2769 BEAST_EXPECT(txj[jss::fee_level] == "256");
2770 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2771 BEAST_EXPECT(txj["retries_remaining"] == 10);
2772 BEAST_EXPECT(txj.isMember(jss::tx));
2773 auto const& tx = txj[jss::tx];
2774 BEAST_EXPECT(tx[jss::Account] == alice.human());
2775 BEAST_EXPECT(tx[jss::TransactionType] == jss::OfferCreate);
2776 const auto txid0 = tx[jss::hash].asString();
2777 uint256 tx0, tx1;
2778 BEAST_EXPECT(tx0.parseHex(txid0));
2779 BEAST_EXPECT(tx1.parseHex(txid1));
2780 BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
2781 return txid0;
2782 }
2783 return std::string{};
2784 }();
2785
2786 env.close();
2787
2788 jv[jss::expand] = false;
2789
2790 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2791 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
2792 {
2793 auto const& parentHash = env.current()->info().parentHash;
2794 auto const txid1 = [&]() {
2795 auto const& txj = jrr[jss::queue_data][1u];
2796 BEAST_EXPECT(txj[jss::account] == alice.human());
2797 BEAST_EXPECT(txj[jss::fee_level] == "256");
2798 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2799 BEAST_EXPECT(txj.isMember(jss::tx));
2800 return txj[jss::tx].asString();
2801 }();
2802 auto const& txj = jrr[jss::queue_data][0u];
2803 BEAST_EXPECT(txj[jss::account] == alice.human());
2804 BEAST_EXPECT(txj[jss::fee_level] == "256");
2805 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2806 BEAST_EXPECT(txj["retries_remaining"] == 9);
2807 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
2808 BEAST_EXPECT(txj.isMember(jss::tx));
2809 BEAST_EXPECT(txj[jss::tx] == txid0);
2810 uint256 tx0, tx1;
2811 BEAST_EXPECT(tx0.parseHex(txid0));
2812 BEAST_EXPECT(tx1.parseHex(txid1));
2813 BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash));
2814 }
2815
2816 env.close();
2817
2818 jv[jss::expand] = true;
2819 jv[jss::binary] = true;
2820
2821 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2822 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
2823 {
2824 auto const& txj = jrr[jss::queue_data][1u];
2825 BEAST_EXPECT(txj[jss::account] == alice.human());
2826 BEAST_EXPECT(txj[jss::fee_level] == "256");
2827 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2828 BEAST_EXPECT(txj["retries_remaining"] == 8);
2829 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
2830 BEAST_EXPECT(txj.isMember(jss::tx));
2831 BEAST_EXPECT(txj[jss::tx].isMember(jss::tx_blob));
2832
2833 auto const& txj2 = jrr[jss::queue_data][0u];
2834 BEAST_EXPECT(txj2[jss::account] == alice.human());
2835 BEAST_EXPECT(txj2[jss::fee_level] == "256");
2836 BEAST_EXPECT(txj2["preflight_result"] == "tesSUCCESS");
2837 BEAST_EXPECT(txj2["retries_remaining"] == 10);
2838 BEAST_EXPECT(!txj2.isMember("last_result"));
2839 BEAST_EXPECT(txj2.isMember(jss::tx));
2840 BEAST_EXPECT(txj2[jss::tx].isMember(jss::tx_blob));
2841 }
2842
2843 for (int i = 0; i != 9; ++i)
2844 {
2845 env.close();
2846 }
2847
2848 jv[jss::expand] = false;
2849 jv[jss::binary] = false;
2850
2851 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2852 const std::string txid2 = [&]() {
2853 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
2854 {
2855 auto const& txj = jrr[jss::queue_data][0u];
2856 BEAST_EXPECT(txj[jss::account] == alice.human());
2857 BEAST_EXPECT(txj[jss::fee_level] == "256");
2858 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2859 BEAST_EXPECT(txj["retries_remaining"] == 1);
2860 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
2861 BEAST_EXPECT(txj.isMember(jss::tx));
2862 BEAST_EXPECT(txj[jss::tx] != txid0);
2863 return txj[jss::tx].asString();
2864 }
2865 return std::string{};
2866 }();
2867
2868 jv[jss::full] = true;
2869
2870 jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
2871 if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
2872 {
2873 auto const& txj = jrr[jss::queue_data][0u];
2874 BEAST_EXPECT(txj[jss::account] == alice.human());
2875 BEAST_EXPECT(txj[jss::fee_level] == "256");
2876 BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
2877 BEAST_EXPECT(txj["retries_remaining"] == 1);
2878 BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
2879 BEAST_EXPECT(txj.isMember(jss::tx));
2880 auto const& tx = txj[jss::tx];
2881 BEAST_EXPECT(tx[jss::Account] == alice.human());
2882 BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet);
2883 BEAST_EXPECT(tx[jss::hash] == txid2);
2884 }
2885 }
2886
2887 void
2889 {
2890 testcase("Ledger Request, Accounts Hashes");
2891 using namespace test::jtx;
2892
2893 Env env{*this};
2894
2895 env.close();
2896
2897 std::string index;
2898 {
2899 Json::Value jvParams;
2900 jvParams[jss::ledger_index] = 3u;
2901 jvParams[jss::accounts] = true;
2902 jvParams[jss::expand] = true;
2903 jvParams[jss::type] = "hashes";
2904 auto const jrr =
2905 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
2906 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
2907 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
2908 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
2909 BEAST_EXPECT(
2910 jrr[jss::ledger][jss::accountState][0u]["LedgerEntryType"] ==
2911 jss::LedgerHashes);
2912 index = jrr[jss::ledger][jss::accountState][0u]["index"].asString();
2913 }
2914 {
2915 Json::Value jvParams;
2916 jvParams[jss::ledger_index] = 3u;
2917 jvParams[jss::accounts] = true;
2918 jvParams[jss::expand] = false;
2919 jvParams[jss::type] = "hashes";
2920 auto const jrr =
2921 env.rpc("json", "ledger", to_string(jvParams))[jss::result];
2922 BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
2923 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
2924 BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
2925 BEAST_EXPECT(jrr[jss::ledger][jss::accountState][0u] == index);
2926 }
2927 }
2928
2929 void
2931 {
2932 testcase("Invalid Oracle Ledger Entry");
2933 using namespace ripple::test::jtx;
2934 using namespace ripple::test::jtx::oracle;
2935
2936 Env env(*this);
2937 Account const owner("owner");
2938 env.fund(XRP(1'000), owner);
2939 Oracle oracle(
2940 env,
2941 {.owner = owner,
2942 .fee = static_cast<int>(env.current()->fees().base.drops())});
2943
2944 // Malformed document id
2945 auto res = Oracle::ledgerEntry(env, owner, NoneTag);
2946 BEAST_EXPECT(res[jss::error].asString() == "invalidParams");
2947 std::vector<AnyValue> invalid = {-1, 1.2, "", "Invalid"};
2948 for (auto const& v : invalid)
2949 {
2950 auto const res = Oracle::ledgerEntry(env, owner, v);
2951 BEAST_EXPECT(res[jss::error].asString() == "malformedDocumentID");
2952 }
2953 // Missing document id
2954 res = Oracle::ledgerEntry(env, owner, std::nullopt);
2955 BEAST_EXPECT(res[jss::error].asString() == "malformedRequest");
2956
2957 // Missing account
2958 res = Oracle::ledgerEntry(env, std::nullopt, 1);
2959 BEAST_EXPECT(res[jss::error].asString() == "malformedRequest");
2960
2961 // Malformed account
2962 std::string malfAccount = to_string(owner.id());
2963 malfAccount.replace(10, 1, 1, '!');
2964 res = Oracle::ledgerEntry(env, malfAccount, 1);
2965 BEAST_EXPECT(res[jss::error].asString() == "malformedAddress");
2966 }
2967
2968 void
2970 {
2971 testcase("Oracle Ledger Entry");
2972 using namespace ripple::test::jtx;
2973 using namespace ripple::test::jtx::oracle;
2974
2975 Env env(*this);
2976 auto const baseFee =
2977 static_cast<int>(env.current()->fees().base.drops());
2978 std::vector<AccountID> accounts;
2980 for (int i = 0; i < 10; ++i)
2981 {
2982 Account const owner(std::string("owner") + std::to_string(i));
2983 env.fund(XRP(1'000), owner);
2984 // different accounts can have the same asset pair
2985 Oracle oracle(
2986 env, {.owner = owner, .documentID = i, .fee = baseFee});
2987 accounts.push_back(owner.id());
2988 oracles.push_back(oracle.documentID());
2989 // same account can have different asset pair
2990 Oracle oracle1(
2991 env, {.owner = owner, .documentID = i + 10, .fee = baseFee});
2992 accounts.push_back(owner.id());
2993 oracles.push_back(oracle1.documentID());
2994 }
2995 for (int i = 0; i < accounts.size(); ++i)
2996 {
2997 auto const jv = [&]() {
2998 // document id is uint32
2999 if (i % 2)
3000 return Oracle::ledgerEntry(env, accounts[i], oracles[i]);
3001 // document id is string
3002 return Oracle::ledgerEntry(
3003 env, accounts[i], std::to_string(oracles[i]));
3004 }();
3005 try
3006 {
3007 BEAST_EXPECT(
3008 jv[jss::node][jss::Owner] == to_string(accounts[i]));
3009 }
3010 catch (...)
3011 {
3012 fail();
3013 }
3014 }
3015 }
3016
3017 void
3019 {
3020 testcase("ledger_entry Request MPT");
3021 using namespace test::jtx;
3022 using namespace std::literals::chrono_literals;
3023 Env env{*this};
3024 Account const alice{"alice"};
3025 Account const bob("bob");
3026
3027 MPTTester mptAlice(env, alice, {.holders = {bob}});
3028 mptAlice.create(
3029 {.transferFee = 10,
3030 .metadata = "123",
3031 .ownerCount = 1,
3034 mptAlice.authorize({.account = bob, .holderCount = 1});
3035
3036 std::string const ledgerHash{to_string(env.closed()->info().hash)};
3037
3038 std::string const badMptID =
3039 "00000193B9DDCAF401B5B3B26875986043F82CD0D13B4315";
3040 {
3041 // Request the MPTIssuance using its MPTIssuanceID.
3042 Json::Value jvParams;
3043 jvParams[jss::mpt_issuance] = strHex(mptAlice.issuanceID());
3044 jvParams[jss::ledger_hash] = ledgerHash;
3045 Json::Value const jrr = env.rpc(
3046 "json", "ledger_entry", to_string(jvParams))[jss::result];
3047 BEAST_EXPECT(
3048 jrr[jss::node][sfMPTokenMetadata.jsonName] ==
3049 strHex(std::string{"123"}));
3050 BEAST_EXPECT(
3051 jrr[jss::node][jss::mpt_issuance_id] ==
3052 strHex(mptAlice.issuanceID()));
3053 }
3054 {
3055 // Request an index that is not a MPTIssuance.
3056 Json::Value jvParams;
3057 jvParams[jss::mpt_issuance] = badMptID;
3058 jvParams[jss::ledger_hash] = ledgerHash;
3059 Json::Value const jrr = env.rpc(
3060 "json", "ledger_entry", to_string(jvParams))[jss::result];
3061 checkErrorValue(jrr, "entryNotFound", "");
3062 }
3063 {
3064 // Request the MPToken using its owner + mptIssuanceID.
3065 Json::Value jvParams;
3066 jvParams[jss::mptoken] = Json::objectValue;
3067 jvParams[jss::mptoken][jss::account] = bob.human();
3068 jvParams[jss::mptoken][jss::mpt_issuance_id] =
3069 strHex(mptAlice.issuanceID());
3070 jvParams[jss::ledger_hash] = ledgerHash;
3071 Json::Value const jrr = env.rpc(
3072 "json", "ledger_entry", to_string(jvParams))[jss::result];
3073 BEAST_EXPECT(
3074 jrr[jss::node][sfMPTokenIssuanceID.jsonName] ==
3075 strHex(mptAlice.issuanceID()));
3076 }
3077 {
3078 // Request the MPToken using a bad mptIssuanceID.
3079 Json::Value jvParams;
3080 jvParams[jss::mptoken] = Json::objectValue;
3081 jvParams[jss::mptoken][jss::account] = bob.human();
3082 jvParams[jss::mptoken][jss::mpt_issuance_id] = badMptID;
3083 jvParams[jss::ledger_hash] = ledgerHash;
3084 Json::Value const jrr = env.rpc(
3085 "json", "ledger_entry", to_string(jvParams))[jss::result];
3086 checkErrorValue(jrr, "entryNotFound", "");
3087 }
3088 }
3089
3090 void
3092 {
3093 testcase("ledger_entry command-line");
3094 using namespace test::jtx;
3095
3096 Env env{*this};
3097 Account const alice{"alice"};
3098 env.fund(XRP(10000), alice);
3099 env.close();
3100
3101 auto const checkId = keylet::check(env.master, env.seq(env.master));
3102
3103 env(check::create(env.master, alice, XRP(100)));
3104 env.close();
3105
3106 std::string const ledgerHash{to_string(env.closed()->info().hash)};
3107 {
3108 // Request a check.
3109 Json::Value const jrr =
3110 env.rpc("ledger_entry", to_string(checkId.key))[jss::result];
3111 BEAST_EXPECT(
3112 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
3113 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
3114 }
3115 }
3116
3117 void
3119 {
3120 testcase("ledger_entry PermissionedDomain");
3121
3122 using namespace test::jtx;
3123
3124 Env env(*this, supported_amendments() | featurePermissionedDomains);
3125 Account const issuer{"issuer"};
3126 Account const alice{"alice"};
3127 Account const bob{"bob"};
3128
3129 env.fund(XRP(5000), issuer, alice, bob);
3130 env.close();
3131
3132 auto const seq = env.seq(alice);
3133 env(pdomain::setTx(alice, {{alice, "first credential"}}));
3134 env.close();
3135 auto const objects = pdomain::getObjects(alice, env);
3136 if (!BEAST_EXPECT(objects.size() == 1))
3137 return;
3138
3139 {
3140 // Succeed
3141 Json::Value params;
3142 params[jss::ledger_index] = jss::validated;
3143 params[jss::permissioned_domain][jss::account] = alice.human();
3144 params[jss::permissioned_domain][jss::seq] = seq;
3145 auto 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 std::string const pdIdx = jv[jss::result][jss::index].asString();
3156 BEAST_EXPECT(
3157 strHex(keylet::permissionedDomain(alice, seq).key) == pdIdx);
3158
3159 params.clear();
3160 params[jss::ledger_index] = jss::validated;
3161 params[jss::permissioned_domain] = pdIdx;
3162 jv = env.rpc("json", "ledger_entry", to_string(params));
3163 BEAST_EXPECT(
3164 jv.isObject() && jv.isMember(jss::result) &&
3165 !jv[jss::result].isMember(jss::error) &&
3166 jv[jss::result].isMember(jss::node) &&
3167 jv[jss::result][jss::node].isMember(
3168 sfLedgerEntryType.jsonName) &&
3169 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
3170 jss::PermissionedDomain);
3171 }
3172
3173 {
3174 // Fail, invalid permissioned domain index
3175 Json::Value params;
3176 params[jss::ledger_index] = jss::validated;
3177 params[jss::permissioned_domain] =
3178 "12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A"
3179 "DE";
3180 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3181 checkErrorValue(jrr[jss::result], "entryNotFound", "");
3182 }
3183
3184 {
3185 // Fail, invalid permissioned domain index
3186 Json::Value params;
3187 params[jss::ledger_index] = jss::validated;
3188 params[jss::permissioned_domain] = "NotAHexString";
3189 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3190 checkErrorValue(jrr[jss::result], "malformedRequest", "");
3191 }
3192
3193 {
3194 // Fail, permissioned domain is not an object
3195 Json::Value params;
3196 params[jss::ledger_index] = jss::validated;
3197 params[jss::permissioned_domain] = 10;
3198 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3199 checkErrorValue(jrr[jss::result], "malformedRequest", "");
3200 }
3201
3202 {
3203 // Fail, invalid account
3204 Json::Value params;
3205 params[jss::ledger_index] = jss::validated;
3206 params[jss::permissioned_domain][jss::account] = 1;
3207 params[jss::permissioned_domain][jss::seq] = seq;
3208 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3209 checkErrorValue(jrr[jss::result], "malformedAddress", "");
3210 }
3211
3212 {
3213 // Fail, account is an object
3214 Json::Value params;
3215 params[jss::ledger_index] = jss::validated;
3216 params[jss::permissioned_domain][jss::account] =
3218 params[jss::permissioned_domain][jss::seq] = seq;
3219 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3220 checkErrorValue(jrr[jss::result], "malformedAddress", "");
3221 }
3222
3223 {
3224 // Fail, no account
3225 Json::Value params;
3226 params[jss::ledger_index] = jss::validated;
3227 params[jss::permissioned_domain][jss::account] = "";
3228 params[jss::permissioned_domain][jss::seq] = seq;
3229 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3230 checkErrorValue(jrr[jss::result], "malformedAddress", "");
3231 }
3232
3233 {
3234 // Fail, invalid sequence
3235 Json::Value params;
3236 params[jss::ledger_index] = jss::validated;
3237 params[jss::permissioned_domain][jss::account] = alice.human();
3238 params[jss::permissioned_domain][jss::seq] = "12g";
3239 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
3240 checkErrorValue(jrr[jss::result], "malformedRequest", "");
3241 }
3242 }
3243
3244public:
3245 void
3246 run() override
3247 {
3249 testBadInput();
3267 testNoQueue();
3268 testQueue();
3276
3279 }
3280};
3281
3282BEAST_DEFINE_TESTSUITE(LedgerRPC, app, ripple);
3283BEAST_DEFINE_TESTSUITE(LedgerRPC_XChain, app, ripple);
3284
3285} // 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
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:328
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)