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