rippled
Loading...
Searching...
No Matches
LedgerEntry.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2014 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 <xrpld/app/misc/CredentialHelpers.h>
21#include <xrpld/ledger/ReadView.h>
22#include <xrpld/rpc/Context.h>
23#include <xrpld/rpc/GRPCHandlers.h>
24#include <xrpld/rpc/detail/RPCHelpers.h>
25
26#include <xrpl/basics/StringUtilities.h>
27#include <xrpl/basics/strHex.h>
28#include <xrpl/beast/core/LexicalCast.h>
29#include <xrpl/json/json_errors.h>
30#include <xrpl/protocol/ErrorCodes.h>
31#include <xrpl/protocol/Indexes.h>
32#include <xrpl/protocol/RPCErr.h>
33#include <xrpl/protocol/STXChainBridge.h>
34#include <xrpl/protocol/jss.h>
35
36#include <functional>
37
38namespace ripple {
39
40static STArray
42{
43 STArray arr(sfAuthorizeCredentials, jv.size());
44 for (auto const& jo : jv)
45 {
46 if (!jo.isObject() || //
47 !jo.isMember(jss::issuer) || !jo[jss::issuer].isString() ||
48 !jo.isMember(jss::credential_type) ||
49 !jo[jss::credential_type].isString())
50 return {};
51
52 auto const issuer = parseBase58<AccountID>(jo[jss::issuer].asString());
53 if (!issuer || !*issuer)
54 return {};
55
56 auto const credentialType =
57 strUnHex(jo[jss::credential_type].asString());
58 if (!credentialType || credentialType->empty() ||
59 credentialType->size() > maxCredentialTypeLength)
60 return {};
61
62 auto credential = STObject::makeInnerObject(sfCredential);
63 credential.setAccountID(sfIssuer, *issuer);
64 credential.setFieldVL(sfCredentialType, *credentialType);
65 arr.push_back(std::move(credential));
66 }
67
68 return arr;
69}
70
72parseIndex(Json::Value const& params, Json::Value& jvResult)
73{
74 uint256 uNodeIndex;
75 if (!uNodeIndex.parseHex(params.asString()))
76 {
77 jvResult[jss::error] = "malformedRequest";
78 return std::nullopt;
79 }
80
81 return uNodeIndex;
82}
83
85parseAccountRoot(Json::Value const& params, Json::Value& jvResult)
86{
87 auto const account = parseBase58<AccountID>(params.asString());
88 if (!account || account->isZero())
89 {
90 jvResult[jss::error] = "malformedAddress";
91 return std::nullopt;
92 }
93
94 return keylet::account(*account).key;
95}
96
98parseCheck(Json::Value const& params, Json::Value& jvResult)
99{
100 uint256 uNodeIndex;
101 if (!uNodeIndex.parseHex(params.asString()))
102 {
103 jvResult[jss::error] = "malformedRequest";
104 return std::nullopt;
105 }
106
107 return uNodeIndex;
108}
109
112{
113 if (!dp.isObject())
114 {
115 uint256 uNodeIndex;
116 if (!dp.isString() || !uNodeIndex.parseHex(dp.asString()))
117 {
118 jvResult[jss::error] = "malformedRequest";
119 return std::nullopt;
120 }
121 return uNodeIndex;
122 }
123
124 // clang-format off
125 if (
126 (!dp.isMember(jss::owner) || !dp[jss::owner].isString()) ||
127 (dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)) ||
128 (dp.isMember(jss::authorized) && !dp[jss::authorized].isString()) ||
129 (dp.isMember(jss::authorized_credentials) && !dp[jss::authorized_credentials].isArray())
130 )
131 // clang-format on
132 {
133 jvResult[jss::error] = "malformedRequest";
134 return std::nullopt;
135 }
136
137 auto const owner = parseBase58<AccountID>(dp[jss::owner].asString());
138 if (!owner)
139 {
140 jvResult[jss::error] = "malformedOwner";
141 return std::nullopt;
142 }
143
144 if (dp.isMember(jss::authorized))
145 {
146 auto const authorized =
147 parseBase58<AccountID>(dp[jss::authorized].asString());
148 if (!authorized)
149 {
150 jvResult[jss::error] = "malformedAuthorized";
151 return std::nullopt;
152 }
153 return keylet::depositPreauth(*owner, *authorized).key;
154 }
155
156 auto const& ac(dp[jss::authorized_credentials]);
157 STArray const arr = parseAuthorizeCredentials(ac);
158
159 if (arr.empty() || (arr.size() > maxCredentialsArraySize))
160 {
161 jvResult[jss::error] = "malformedAuthorizedCredentials";
162 return std::nullopt;
163 }
164
165 auto const& sorted = credentials::makeSorted(arr);
166 if (sorted.empty())
167 {
168 jvResult[jss::error] = "malformedAuthorizedCredentials";
169 return std::nullopt;
170 }
171
172 return keylet::depositPreauth(*owner, sorted).key;
173}
174
176parseDirectory(Json::Value const& params, Json::Value& jvResult)
177{
178 if (params.isNull())
179 {
180 jvResult[jss::error] = "malformedRequest";
181 return std::nullopt;
182 }
183
184 if (!params.isObject())
185 {
186 uint256 uNodeIndex;
187 if (!uNodeIndex.parseHex(params.asString()))
188 {
189 jvResult[jss::error] = "malformedRequest";
190 return std::nullopt;
191 }
192 return uNodeIndex;
193 }
194
195 if (params.isMember(jss::sub_index) && !params[jss::sub_index].isIntegral())
196 {
197 jvResult[jss::error] = "malformedRequest";
198 return std::nullopt;
199 }
200
201 std::uint64_t uSubIndex =
202 params.isMember(jss::sub_index) ? params[jss::sub_index].asUInt() : 0;
203
204 if (params.isMember(jss::dir_root))
205 {
206 uint256 uDirRoot;
207
208 if (params.isMember(jss::owner))
209 {
210 // May not specify both dir_root and owner.
211 jvResult[jss::error] = "malformedRequest";
212 return std::nullopt;
213 }
214
215 if (!uDirRoot.parseHex(params[jss::dir_root].asString()))
216 {
217 jvResult[jss::error] = "malformedRequest";
218 return std::nullopt;
219 }
220 return keylet::page(uDirRoot, uSubIndex).key;
221 }
222
223 if (params.isMember(jss::owner))
224 {
225 auto const ownerID =
226 parseBase58<AccountID>(params[jss::owner].asString());
227
228 if (!ownerID)
229 {
230 jvResult[jss::error] = "malformedAddress";
231 return std::nullopt;
232 }
233
234 return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key;
235 }
236
237 jvResult[jss::error] = "malformedRequest";
238 return std::nullopt;
239}
240
242parseEscrow(Json::Value const& params, Json::Value& jvResult)
243{
244 if (!params.isObject())
245 {
246 uint256 uNodeIndex;
247 if (!uNodeIndex.parseHex(params.asString()))
248 {
249 jvResult[jss::error] = "malformedRequest";
250 return std::nullopt;
251 }
252
253 return uNodeIndex;
254 }
255
256 if (!params.isMember(jss::owner) || !params.isMember(jss::seq) ||
257 !params[jss::seq].isIntegral())
258 {
259 jvResult[jss::error] = "malformedRequest";
260 return std::nullopt;
261 }
262
263 auto const id = parseBase58<AccountID>(params[jss::owner].asString());
264
265 if (!id)
266 {
267 jvResult[jss::error] = "malformedOwner";
268 return std::nullopt;
269 }
270
271 return keylet::escrow(*id, params[jss::seq].asUInt()).key;
272}
273
275parseOffer(Json::Value const& params, Json::Value& jvResult)
276{
277 if (!params.isObject())
278 {
279 uint256 uNodeIndex;
280 if (!uNodeIndex.parseHex(params.asString()))
281 {
282 jvResult[jss::error] = "malformedRequest";
283 return std::nullopt;
284 }
285 return uNodeIndex;
286 }
287
288 if (!params.isMember(jss::account) || !params.isMember(jss::seq) ||
289 !params[jss::seq].isIntegral())
290 {
291 jvResult[jss::error] = "malformedRequest";
292 return std::nullopt;
293 }
294
295 auto const id = parseBase58<AccountID>(params[jss::account].asString());
296 if (!id)
297 {
298 jvResult[jss::error] = "malformedAddress";
299 return std::nullopt;
300 }
301
302 return keylet::offer(*id, params[jss::seq].asUInt()).key;
303}
304
307{
308 uint256 uNodeIndex;
309 if (!uNodeIndex.parseHex(params.asString()))
310 {
311 jvResult[jss::error] = "malformedRequest";
312 return std::nullopt;
313 }
314
315 return uNodeIndex;
316}
317
319parseRippleState(Json::Value const& jvRippleState, Json::Value& jvResult)
320{
321 Currency uCurrency;
322
323 if (!jvRippleState.isObject() || !jvRippleState.isMember(jss::currency) ||
324 !jvRippleState.isMember(jss::accounts) ||
325 !jvRippleState[jss::accounts].isArray() ||
326 2 != jvRippleState[jss::accounts].size() ||
327 !jvRippleState[jss::accounts][0u].isString() ||
328 !jvRippleState[jss::accounts][1u].isString() ||
329 (jvRippleState[jss::accounts][0u].asString() ==
330 jvRippleState[jss::accounts][1u].asString()))
331 {
332 jvResult[jss::error] = "malformedRequest";
333 return std::nullopt;
334 }
335
336 auto const id1 =
337 parseBase58<AccountID>(jvRippleState[jss::accounts][0u].asString());
338 auto const id2 =
339 parseBase58<AccountID>(jvRippleState[jss::accounts][1u].asString());
340 if (!id1 || !id2)
341 {
342 jvResult[jss::error] = "malformedAddress";
343 return std::nullopt;
344 }
345
346 if (!to_currency(uCurrency, jvRippleState[jss::currency].asString()))
347 {
348 jvResult[jss::error] = "malformedCurrency";
349 return std::nullopt;
350 }
351
352 return keylet::line(*id1, *id2, uCurrency).key;
353}
354
356parseTicket(Json::Value const& params, Json::Value& jvResult)
357{
358 if (!params.isObject())
359 {
360 uint256 uNodeIndex;
361 if (!uNodeIndex.parseHex(params.asString()))
362 {
363 jvResult[jss::error] = "malformedRequest";
364 return std::nullopt;
365 }
366 return uNodeIndex;
367 }
368
369 if (!params.isMember(jss::account) || !params.isMember(jss::ticket_seq) ||
370 !params[jss::ticket_seq].isIntegral())
371 {
372 jvResult[jss::error] = "malformedRequest";
373 return std::nullopt;
374 }
375
376 auto const id = parseBase58<AccountID>(params[jss::account].asString());
377 if (!id)
378 {
379 jvResult[jss::error] = "malformedAddress";
380 return std::nullopt;
381 }
382
383 return getTicketIndex(*id, params[jss::ticket_seq].asUInt());
384}
385
387parseNFTokenPage(Json::Value const& params, Json::Value& jvResult)
388{
389 if (params.isString())
390 {
391 uint256 uNodeIndex;
392 if (!uNodeIndex.parseHex(params.asString()))
393 {
394 jvResult[jss::error] = "malformedRequest";
395 return std::nullopt;
396 }
397 return uNodeIndex;
398 }
399
400 jvResult[jss::error] = "malformedRequest";
401 return std::nullopt;
402}
403
405parseAMM(Json::Value const& params, Json::Value& jvResult)
406{
407 if (!params.isObject())
408 {
409 uint256 uNodeIndex;
410 if (!uNodeIndex.parseHex(params.asString()))
411 {
412 jvResult[jss::error] = "malformedRequest";
413 return std::nullopt;
414 }
415 return uNodeIndex;
416 }
417
418 if (!params.isMember(jss::asset) || !params.isMember(jss::asset2))
419 {
420 jvResult[jss::error] = "malformedRequest";
421 return std::nullopt;
422 }
423
424 try
425 {
426 auto const issue = issueFromJson(params[jss::asset]);
427 auto const issue2 = issueFromJson(params[jss::asset2]);
428 return keylet::amm(issue, issue2).key;
429 }
430 catch (std::runtime_error const&)
431 {
432 jvResult[jss::error] = "malformedRequest";
433 return std::nullopt;
434 }
435}
436
438parseBridge(Json::Value const& params, Json::Value& jvResult)
439{
440 // return the keylet for the specified bridge or nullopt if the
441 // request is malformed
442 auto const maybeKeylet = [&]() -> std::optional<Keylet> {
443 try
444 {
445 if (!params.isMember(jss::bridge_account))
446 return std::nullopt;
447
448 auto const& jsBridgeAccount = params[jss::bridge_account];
449 if (!jsBridgeAccount.isString())
450 {
451 return std::nullopt;
452 }
453
454 auto const account =
455 parseBase58<AccountID>(jsBridgeAccount.asString());
456 if (!account || account->isZero())
457 {
458 return std::nullopt;
459 }
460
461 // This may throw and is the reason for the `try` block. The
462 // try block has a larger scope so the `bridge` variable
463 // doesn't need to be an optional.
464 STXChainBridge const bridge(params[jss::bridge]);
465 STXChainBridge::ChainType const chainType =
466 STXChainBridge::srcChain(account == bridge.lockingChainDoor());
467
468 if (account != bridge.door(chainType))
469 return std::nullopt;
470
471 return keylet::bridge(bridge, chainType);
472 }
473 catch (...)
474 {
475 return std::nullopt;
476 }
477 }();
478
479 if (maybeKeylet)
480 {
481 return maybeKeylet->key;
482 }
483
484 jvResult[jss::error] = "malformedRequest";
485 return std::nullopt;
486}
487
490{
491 if (claim_id.isString())
492 {
493 uint256 uNodeIndex;
494 // we accept a node id as specifier of a xchain claim id
495 if (!uNodeIndex.parseHex(claim_id.asString()))
496 {
497 jvResult[jss::error] = "malformedRequest";
498 return std::nullopt;
499 }
500 return uNodeIndex;
501 }
502
503 if (!claim_id.isObject() ||
504 !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) &&
505 claim_id[sfIssuingChainDoor.getJsonName()].isString()) ||
506 !(claim_id.isMember(sfLockingChainDoor.getJsonName()) &&
507 claim_id[sfLockingChainDoor.getJsonName()].isString()) ||
508 !claim_id.isMember(sfIssuingChainIssue.getJsonName()) ||
509 !claim_id.isMember(sfLockingChainIssue.getJsonName()) ||
510 !claim_id.isMember(jss::xchain_owned_claim_id))
511 {
512 jvResult[jss::error] = "malformedRequest";
513 return std::nullopt;
514 }
515
516 // if not specified with a node id, a claim_id is specified by
517 // four strings defining the bridge (locking_chain_door,
518 // locking_chain_issue, issuing_chain_door, issuing_chain_issue)
519 // and the claim id sequence number.
520 auto const lockingChainDoor = parseBase58<AccountID>(
521 claim_id[sfLockingChainDoor.getJsonName()].asString());
522 auto const issuingChainDoor = parseBase58<AccountID>(
523 claim_id[sfIssuingChainDoor.getJsonName()].asString());
524 Issue lockingChainIssue, issuingChainIssue;
525 bool valid = lockingChainDoor && issuingChainDoor;
526
527 if (valid)
528 {
529 try
530 {
531 lockingChainIssue =
532 issueFromJson(claim_id[sfLockingChainIssue.getJsonName()]);
533 issuingChainIssue =
534 issueFromJson(claim_id[sfIssuingChainIssue.getJsonName()]);
535 }
536 catch (std::runtime_error const& ex)
537 {
538 jvResult[jss::error] = "malformedRequest";
539 return std::nullopt;
540 }
541 }
542
543 if (valid && claim_id[jss::xchain_owned_claim_id].isIntegral())
544 {
545 auto const seq = claim_id[jss::xchain_owned_claim_id].asUInt();
546
547 STXChainBridge bridge_spec(
548 *lockingChainDoor,
549 lockingChainIssue,
550 *issuingChainDoor,
551 issuingChainIssue);
552 Keylet keylet = keylet::xChainClaimID(bridge_spec, seq);
553 return keylet.key;
554 }
555
556 jvResult[jss::error] = "malformedRequest";
557 return std::nullopt;
558}
559
562 Json::Value const& claim_id,
563 Json::Value& jvResult)
564{
565 if (claim_id.isString())
566 {
567 uint256 uNodeIndex;
568 // we accept a node id as specifier of a xchain create account
569 // claim_id
570 if (!uNodeIndex.parseHex(claim_id.asString()))
571 {
572 jvResult[jss::error] = "malformedRequest";
573 return std::nullopt;
574 }
575 return uNodeIndex;
576 }
577
578 if (!claim_id.isObject() ||
579 !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) &&
580 claim_id[sfIssuingChainDoor.getJsonName()].isString()) ||
581 !(claim_id.isMember(sfLockingChainDoor.getJsonName()) &&
582 claim_id[sfLockingChainDoor.getJsonName()].isString()) ||
583 !claim_id.isMember(sfIssuingChainIssue.getJsonName()) ||
584 !claim_id.isMember(sfLockingChainIssue.getJsonName()) ||
585 !claim_id.isMember(jss::xchain_owned_create_account_claim_id))
586 {
587 jvResult[jss::error] = "malformedRequest";
588 return std::nullopt;
589 }
590
591 // if not specified with a node id, a create account claim_id is
592 // specified by four strings defining the bridge
593 // (locking_chain_door, locking_chain_issue, issuing_chain_door,
594 // issuing_chain_issue) and the create account claim id sequence
595 // number.
596 auto const lockingChainDoor = parseBase58<AccountID>(
597 claim_id[sfLockingChainDoor.getJsonName()].asString());
598 auto const issuingChainDoor = parseBase58<AccountID>(
599 claim_id[sfIssuingChainDoor.getJsonName()].asString());
600 Issue lockingChainIssue, issuingChainIssue;
601 bool valid = lockingChainDoor && issuingChainDoor;
602 if (valid)
603 {
604 try
605 {
606 lockingChainIssue =
607 issueFromJson(claim_id[sfLockingChainIssue.getJsonName()]);
608 issuingChainIssue =
609 issueFromJson(claim_id[sfIssuingChainIssue.getJsonName()]);
610 }
611 catch (std::runtime_error const& ex)
612 {
613 valid = false;
614 jvResult[jss::error] = "malformedRequest";
615 }
616 }
617
618 if (valid &&
619 claim_id[jss::xchain_owned_create_account_claim_id].isIntegral())
620 {
621 auto const seq =
622 claim_id[jss::xchain_owned_create_account_claim_id].asUInt();
623
624 STXChainBridge bridge_spec(
625 *lockingChainDoor,
626 lockingChainIssue,
627 *issuingChainDoor,
628 issuingChainIssue);
629 Keylet keylet = keylet::xChainCreateAccountClaimID(bridge_spec, seq);
630 return keylet.key;
631 }
632
633 return std::nullopt;
634}
635
637parseDID(Json::Value const& params, Json::Value& jvResult)
638{
639 auto const account = parseBase58<AccountID>(params.asString());
640 if (!account || account->isZero())
641 {
642 jvResult[jss::error] = "malformedAddress";
643 return std::nullopt;
644 }
645
646 return keylet::did(*account).key;
647}
648
650parseOracle(Json::Value const& params, Json::Value& jvResult)
651{
652 if (!params.isObject())
653 {
654 uint256 uNodeIndex;
655 if (!uNodeIndex.parseHex(params.asString()))
656 {
657 jvResult[jss::error] = "malformedRequest";
658 return std::nullopt;
659 }
660 return uNodeIndex;
661 }
662
663 if (!params.isMember(jss::oracle_document_id) ||
664 !params.isMember(jss::account))
665 {
666 jvResult[jss::error] = "malformedRequest";
667 return std::nullopt;
668 }
669
670 auto const& oracle = params;
671 auto const documentID = [&]() -> std::optional<std::uint32_t> {
672 auto const id = oracle[jss::oracle_document_id];
673 if (id.isUInt() || (id.isInt() && id.asInt() >= 0))
674 return std::make_optional(id.asUInt());
675
676 if (id.isString())
677 {
679 if (beast::lexicalCastChecked(v, id.asString()))
680 return std::make_optional(v);
681 }
682
683 return std::nullopt;
684 }();
685
686 auto const account =
687 parseBase58<AccountID>(oracle[jss::account].asString());
688 if (!account || account->isZero())
689 {
690 jvResult[jss::error] = "malformedAddress";
691 return std::nullopt;
692 }
693
694 if (!documentID)
695 {
696 jvResult[jss::error] = "malformedDocumentID";
697 return std::nullopt;
698 }
699
700 return keylet::oracle(*account, *documentID).key;
701}
702
705{
706 if (cred.isString())
707 {
708 uint256 uNodeIndex;
709 if (!uNodeIndex.parseHex(cred.asString()))
710 {
711 jvResult[jss::error] = "malformedRequest";
712 return std::nullopt;
713 }
714 return uNodeIndex;
715 }
716
717 if ((!cred.isMember(jss::subject) || !cred[jss::subject].isString()) ||
718 (!cred.isMember(jss::issuer) || !cred[jss::issuer].isString()) ||
719 (!cred.isMember(jss::credential_type) ||
720 !cred[jss::credential_type].isString()))
721 {
722 jvResult[jss::error] = "malformedRequest";
723 return std::nullopt;
724 }
725
726 auto const subject = parseBase58<AccountID>(cred[jss::subject].asString());
727 auto const issuer = parseBase58<AccountID>(cred[jss::issuer].asString());
728 auto const credType = strUnHex(cred[jss::credential_type].asString());
729
730 if (!subject || subject->isZero() || !issuer || issuer->isZero() ||
731 !credType || credType->empty())
732 {
733 jvResult[jss::error] = "malformedRequest";
734 return std::nullopt;
735 }
736
737 return keylet::credential(
738 *subject, *issuer, Slice(credType->data(), credType->size()))
739 .key;
740}
741
744 Json::Value const& unparsedMPTIssuanceID,
745 Json::Value& jvResult)
746{
747 if (unparsedMPTIssuanceID.isString())
748 {
749 uint192 mptIssuanceID;
750 if (!mptIssuanceID.parseHex(unparsedMPTIssuanceID.asString()))
751 {
752 jvResult[jss::error] = "malformedRequest";
753 return std::nullopt;
754 }
755
756 return keylet::mptIssuance(mptIssuanceID).key;
757 }
758
759 jvResult[jss::error] = "malformedRequest";
760 return std::nullopt;
761}
762
764parseMPToken(Json::Value const& mptJson, Json::Value& jvResult)
765{
766 if (!mptJson.isObject())
767 {
768 uint256 uNodeIndex;
769 if (!uNodeIndex.parseHex(mptJson.asString()))
770 {
771 jvResult[jss::error] = "malformedRequest";
772 return std::nullopt;
773 }
774 return uNodeIndex;
775 }
776
777 if (!mptJson.isMember(jss::mpt_issuance_id) ||
778 !mptJson.isMember(jss::account))
779 {
780 jvResult[jss::error] = "malformedRequest";
781 return std::nullopt;
782 }
783
784 try
785 {
786 auto const mptIssuanceIdStr = mptJson[jss::mpt_issuance_id].asString();
787
788 uint192 mptIssuanceID;
789 if (!mptIssuanceID.parseHex(mptIssuanceIdStr))
790 Throw<std::runtime_error>("Cannot parse mpt_issuance_id");
791
792 auto const account =
793 parseBase58<AccountID>(mptJson[jss::account].asString());
794
795 if (!account || account->isZero())
796 {
797 jvResult[jss::error] = "malformedAddress";
798 return std::nullopt;
799 }
800
801 return keylet::mptoken(mptIssuanceID, *account).key;
802 }
803 catch (std::runtime_error const&)
804 {
805 jvResult[jss::error] = "malformedRequest";
806 return std::nullopt;
807 }
808}
809
812{
813 if (pd.isString())
814 {
815 auto const index = parseIndex(pd, jvResult);
816 return index;
817 }
818
819 if (!pd.isObject())
820 {
821 jvResult[jss::error] = "malformedRequest";
822 return std::nullopt;
823 }
824
825 if (!pd.isMember(jss::account))
826 {
827 jvResult[jss::error] = "malformedRequest";
828 return std::nullopt;
829 }
830
831 if (!pd[jss::account].isString())
832 {
833 jvResult[jss::error] = "malformedAddress";
834 return std::nullopt;
835 }
836
837 if (!pd.isMember(jss::seq) ||
838 (pd[jss::seq].isInt() && pd[jss::seq].asInt() < 0) ||
839 (!pd[jss::seq].isInt() && !pd[jss::seq].isUInt()))
840 {
841 jvResult[jss::error] = "malformedRequest";
842 return std::nullopt;
843 }
844
845 auto const account = parseBase58<AccountID>(pd[jss::account].asString());
846 if (!account)
847 {
848 jvResult[jss::error] = "malformedAddress";
849 return std::nullopt;
850 }
851
852 return keylet::permissionedDomain(*account, pd[jss::seq].asUInt()).key;
853}
854
857
859{
863};
864
865// {
866// ledger_hash : <ledger>
867// ledger_index : <ledger_index>
868// ...
869// }
872{
874 auto jvResult = RPC::lookupLedger(lpLedger, context);
875
876 if (!lpLedger)
877 return jvResult;
878
879 static auto ledgerEntryParsers = std::to_array<LedgerEntry>({
880 {jss::index, parseIndex, ltANY},
881 {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT},
882 // TODO: add amendments
883 {jss::amm, parseAMM, ltAMM},
884 {jss::bridge, parseBridge, ltBRIDGE},
885 {jss::check, parseCheck, ltCHECK},
886 {jss::credential, parseCredential, ltCREDENTIAL},
887 {jss::deposit_preauth, parseDepositPreauth, ltDEPOSIT_PREAUTH},
888 {jss::did, parseDID, ltDID},
889 {jss::directory, parseDirectory, ltDIR_NODE},
890 {jss::escrow, parseEscrow, ltESCROW},
891 // TODO: add fee, hashes
892 {jss::mpt_issuance, parseMPTokenIssuance, ltMPTOKEN_ISSUANCE},
893 {jss::mptoken, parseMPToken, ltMPTOKEN},
894 // TODO: add NFT Offers
895 {jss::nft_page, parseNFTokenPage, ltNFTOKEN_PAGE},
896 // TODO: add NegativeUNL
897 {jss::offer, parseOffer, ltOFFER},
898 {jss::oracle, parseOracle, ltORACLE},
899 {jss::payment_channel, parsePaymentChannel, ltPAYCHAN},
900 {jss::permissioned_domain,
902 ltPERMISSIONED_DOMAIN},
903 {jss::ripple_state, parseRippleState, ltRIPPLE_STATE},
904 // This is an alias, since the `ledger_data` filter uses jss::state
905 {jss::state, parseRippleState, ltRIPPLE_STATE},
906 {jss::ticket, parseTicket, ltTICKET},
907 {jss::xchain_owned_claim_id,
909 ltXCHAIN_OWNED_CLAIM_ID},
910 {jss::xchain_owned_create_account_claim_id,
912 ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID},
913 });
914
915 uint256 uNodeIndex;
916 LedgerEntryType expectedType = ltANY;
917
918 try
919 {
920 bool found = false;
921 for (const auto& ledgerEntry : ledgerEntryParsers)
922 {
923 if (context.params.isMember(ledgerEntry.fieldName))
924 {
925 expectedType = ledgerEntry.expectedType;
926 // `Bridge` is the only type that involves two fields at the
927 // `ledger_entry` param level.
928 // So that parser needs to have the whole `params` field.
929 // All other parsers only need the one field name's info.
930 Json::Value const& params = ledgerEntry.fieldName == jss::bridge
931 ? context.params
932 : context.params[ledgerEntry.fieldName];
933 uNodeIndex = ledgerEntry.parseFunction(params, jvResult)
934 .value_or(beast::zero);
935 if (jvResult.isMember(jss::error))
936 {
937 return jvResult;
938 }
939 found = true;
940 break;
941 }
942 }
943
944 if (!found)
945 {
946 if (context.apiVersion < 2u)
947 jvResult[jss::error] = "unknownOption";
948 else
949 jvResult[jss::error] = "invalidParams";
950 return jvResult;
951 }
952 }
953 catch (Json::error& e)
954 {
955 if (context.apiVersion > 1u)
956 {
957 // For apiVersion 2 onwards, any parsing failures that throw this
958 // exception return an invalidParam error.
959 jvResult[jss::error] = "invalidParams";
960 return jvResult;
961 }
962 else
963 throw;
964 }
965
966 if (uNodeIndex.isZero())
967 {
968 jvResult[jss::error] = "entryNotFound";
969 return jvResult;
970 }
971
972 auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
973
974 bool bNodeBinary = false;
975 if (context.params.isMember(jss::binary))
976 bNodeBinary = context.params[jss::binary].asBool();
977
978 if (!sleNode)
979 {
980 // Not found.
981 jvResult[jss::error] = "entryNotFound";
982 return jvResult;
983 }
984
985 if ((expectedType != ltANY) && (expectedType != sleNode->getType()))
986 {
987 jvResult[jss::error] = "unexpectedLedgerType";
988 return jvResult;
989 }
990
991 if (bNodeBinary)
992 {
993 Serializer s;
994
995 sleNode->add(s);
996
997 jvResult[jss::node_binary] = strHex(s.peekData());
998 jvResult[jss::index] = to_string(uNodeIndex);
999 }
1000 else
1001 {
1002 jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
1003 jvResult[jss::index] = to_string(uNodeIndex);
1004 }
1005
1006 return jvResult;
1007}
1008
1012{
1013 org::xrpl::rpc::v1::GetLedgerEntryRequest& request = context.params;
1014 org::xrpl::rpc::v1::GetLedgerEntryResponse response;
1015 grpc::Status status = grpc::Status::OK;
1016
1018 if (auto const status = RPC::ledgerFromRequest(ledger, context))
1019 {
1020 grpc::Status errorStatus;
1021 if (status.toErrorCode() == rpcINVALID_PARAMS)
1022 {
1023 errorStatus = grpc::Status(
1024 grpc::StatusCode::INVALID_ARGUMENT, status.message());
1025 }
1026 else
1027 {
1028 errorStatus =
1029 grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
1030 }
1031 return {response, errorStatus};
1032 }
1033
1034 auto const key = uint256::fromVoidChecked(request.key());
1035 if (!key)
1036 {
1037 grpc::Status errorStatus{
1038 grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
1039 return {response, errorStatus};
1040 }
1041
1042 auto const sleNode = ledger->read(keylet::unchecked(*key));
1043 if (!sleNode)
1044 {
1045 grpc::Status errorStatus{
1046 grpc::StatusCode::NOT_FOUND, "object not found"};
1047 return {response, errorStatus};
1048 }
1049
1050 Serializer s;
1051 sleNode->add(s);
1052
1053 auto& stateObject = *response.mutable_ledger_object();
1054 stateObject.set_data(s.peekData().data(), s.getLength());
1055 stateObject.set_key(request.key());
1056 *(response.mutable_ledger()) = request.ledger();
1057 return {response, status};
1058}
1059} // namespace ripple
Lightweight wrapper to tag static string.
Definition: json_value.h:62
Represents a JSON value.
Definition: json_value.h:148
bool isArray() const
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:712
Int asInt() const
Definition: json_value.cpp:509
bool isString() const
UInt asUInt() const
Definition: json_value.cpp:551
bool isObject() const
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:475
bool isIntegral() const
bool asBool() const
Definition: json_value.cpp:625
bool isUInt() const
bool isNull() const
isNull() tests to see if this field is null.
Definition: json_value.cpp:986
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:949
bool isInt() const
Definition: json_value.cpp:998
A currency issued by an account.
Definition: Issue.h:36
bool empty() const
Definition: STArray.h:254
void push_back(STObject const &object)
Definition: STArray.h:212
size_type size() const
Definition: STArray.h:248
static STObject makeInnerObject(SField const &name)
Definition: STObject.cpp:95
static ChainType srcChain(bool wasLockingChainSend)
Blob const & peekData() const
Definition: Serializer.h:203
int getLength() const
Definition: Serializer.h:234
An immutable linear range of bytes.
Definition: Slice.h:46
static std::optional< base_uint > fromVoidChecked(T const &from)
Definition: base_uint.h:326
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:503
bool isZero() const
Definition: base_uint.h:540
T data(T... args)
T make_optional(T... args)
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
Definition: LexicalCast.h:202
Status ledgerFromRequest(T &ledger, GRPCContext< R > &context)
Definition: RPCHelpers.cpp:408
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext &context, Json::Value &result)
Look up a ledger from a request and fill a Json::Result with the data representing a ledger.
Definition: RPCHelpers.cpp:622
TER authorized(ApplyContext const &ctx, AccountID const &dst)
TER valid(PreclaimContext const &ctx, AccountID const &src)
std::set< std::pair< AccountID, Slice > > makeSorted(STArray const &credentials)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition: Indexes.cpp:523
Keylet oracle(AccountID const &account, std::uint32_t const &documentID) noexcept
Definition: Indexes.cpp:503
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition: Indexes.cpp:547
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:437
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:235
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:509
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition: Indexes.cpp:469
Keylet did(AccountID const &account) noexcept
Definition: Indexes.cpp:497
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition: Indexes.cpp:536
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:175
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition: Indexes.cpp:371
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition: Indexes.cpp:359
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Definition: Indexes.cpp:380
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition: Indexes.cpp:456
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:365
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition: Indexes.cpp:483
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:265
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition: Indexes.cpp:333
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
static std::optional< uint256 > parseMPToken(Json::Value const &mptJson, Json::Value &jvResult)
static std::optional< uint256 > parseIndex(Json::Value const &params, Json::Value &jvResult)
Definition: LedgerEntry.cpp:72
static STArray parseAuthorizeCredentials(Json::Value const &jv)
Definition: LedgerEntry.cpp:41
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
Json::Value doLedgerEntry(RPC::JsonContext &)
static std::optional< uint256 > parsePaymentChannel(Json::Value const &params, Json::Value &jvResult)
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
static std::optional< uint256 > parseAMM(Json::Value const &params, Json::Value &jvResult)
static std::optional< uint256 > parseMPTokenIssuance(Json::Value const &unparsedMPTIssuanceID, Json::Value &jvResult)
static std::optional< uint256 > parseBridge(Json::Value const &params, Json::Value &jvResult)
static std::optional< uint256 > parseDepositPreauth(Json::Value const &dp, Json::Value &jvResult)
static std::optional< uint256 > parseNFTokenPage(Json::Value const &params, Json::Value &jvResult)
static std::optional< uint256 > parseCheck(Json::Value const &params, Json::Value &jvResult)
Definition: LedgerEntry.cpp:98
static std::optional< uint256 > parseTicket(Json::Value const &params, Json::Value &jvResult)
static std::optional< uint256 > parseCredential(Json::Value const &cred, Json::Value &jvResult)
static std::optional< uint256 > parseOracle(Json::Value const &params, Json::Value &jvResult)
Issue issueFromJson(Json::Value const &v)
Definition: Issue.cpp:95
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
static std::optional< uint256 > parseEscrow(Json::Value const &params, Json::Value &jvResult)
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition: Protocol.h:107
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
static std::optional< uint256 > parseXChainOwnedCreateAccountClaimID(Json::Value const &claim_id, Json::Value &jvResult)
std::size_t constexpr maxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
Definition: Protocol.h:104
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition: Indexes.cpp:147
static std::optional< uint256 > parseOffer(Json::Value const &params, Json::Value &jvResult)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:54
@ ltANY
A special type, matching any ledger entry type.
Definition: LedgerFormats.h:78
static std::optional< uint256 > parseAccountRoot(Json::Value const &params, Json::Value &jvResult)
Definition: LedgerEntry.cpp:85
static std::optional< uint256 > parseRippleState(Json::Value const &jvRippleState, Json::Value &jvResult)
@ credential
Credentials signature.
static std::optional< uint256 > parseXChainOwnedClaimID(Json::Value const &claim_id, Json::Value &jvResult)
static std::optional< uint256 > parseDirectory(Json::Value const &params, Json::Value &jvResult)
static std::optional< uint256 > parseDID(Json::Value const &params, Json::Value &jvResult)
static std::optional< uint256 > parsePermissionedDomains(Json::Value const &pd, Json::Value &jvResult)
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition: UintTypes.cpp:84
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:39
uint256 key
Definition: Keylet.h:40
LedgerEntryType expectedType
Json::StaticString fieldName
FunctionType parseFunction
unsigned int apiVersion
Definition: Context.h:49
RequestType params
Definition: Context.h:71
Json::Value params
Definition: Context.h:63