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
235{
236 if (!dp.isObject())
237 {
238 uint256 uNodeIndex;
239 if (!dp.isString() || !uNodeIndex.parseHex(dp.asString()))
240 {
241 jvResult[jss::error] = "malformedRequest";
242 return std::nullopt;
243 }
244 return uNodeIndex;
245 }
246
247 // clang-format off
248 if (
249 (!dp.isMember(jss::owner) || !dp[jss::owner].isString()) ||
250 (dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)) ||
251 (dp.isMember(jss::authorized) && !dp[jss::authorized].isString()) ||
252 (dp.isMember(jss::authorized_credentials) && !dp[jss::authorized_credentials].isArray())
253 )
254 // clang-format on
255 {
256 jvResult[jss::error] = "malformedRequest";
257 return std::nullopt;
258 }
259
260 auto const owner = parseBase58<AccountID>(dp[jss::owner].asString());
261 if (!owner)
262 {
263 jvResult[jss::error] = "malformedOwner";
264 return std::nullopt;
265 }
266
267 if (dp.isMember(jss::authorized))
268 {
269 auto const authorized =
270 parseBase58<AccountID>(dp[jss::authorized].asString());
271 if (!authorized)
272 {
273 jvResult[jss::error] = "malformedAuthorized";
274 return std::nullopt;
275 }
276 return keylet::depositPreauth(*owner, *authorized).key;
277 }
278
279 auto const& ac(dp[jss::authorized_credentials]);
280 STArray const arr = parseAuthorizeCredentials(ac);
281
282 if (arr.empty() || (arr.size() > maxCredentialsArraySize))
283 {
284 jvResult[jss::error] = "malformedAuthorizedCredentials";
285 return std::nullopt;
286 }
287
288 auto const& sorted = credentials::makeSorted(arr);
289 if (sorted.empty())
290 {
291 jvResult[jss::error] = "malformedAuthorizedCredentials";
292 return std::nullopt;
293 }
294
295 return keylet::depositPreauth(*owner, sorted).key;
296}
297
299parseDID(Json::Value const& params, Json::Value& jvResult)
300{
301 auto const account = parseBase58<AccountID>(params.asString());
302 if (!account || account->isZero())
303 {
304 jvResult[jss::error] = "malformedAddress";
305 return std::nullopt;
306 }
307
308 return keylet::did(*account).key;
309}
310
312parseDirectory(Json::Value const& params, Json::Value& jvResult)
313{
314 if (params.isNull())
315 {
316 jvResult[jss::error] = "malformedRequest";
317 return std::nullopt;
318 }
319
320 if (!params.isObject())
321 {
322 uint256 uNodeIndex;
323 if (!uNodeIndex.parseHex(params.asString()))
324 {
325 jvResult[jss::error] = "malformedRequest";
326 return std::nullopt;
327 }
328 return uNodeIndex;
329 }
330
331 if (params.isMember(jss::sub_index) && !params[jss::sub_index].isIntegral())
332 {
333 jvResult[jss::error] = "malformedRequest";
334 return std::nullopt;
335 }
336
337 std::uint64_t uSubIndex =
338 params.isMember(jss::sub_index) ? params[jss::sub_index].asUInt() : 0;
339
340 if (params.isMember(jss::dir_root))
341 {
342 uint256 uDirRoot;
343
344 if (params.isMember(jss::owner))
345 {
346 // May not specify both dir_root and owner.
347 jvResult[jss::error] = "malformedRequest";
348 return std::nullopt;
349 }
350
351 if (!uDirRoot.parseHex(params[jss::dir_root].asString()))
352 {
353 jvResult[jss::error] = "malformedRequest";
354 return std::nullopt;
355 }
356 return keylet::page(uDirRoot, uSubIndex).key;
357 }
358
359 if (params.isMember(jss::owner))
360 {
361 auto const ownerID =
362 parseBase58<AccountID>(params[jss::owner].asString());
363
364 if (!ownerID)
365 {
366 jvResult[jss::error] = "malformedAddress";
367 return std::nullopt;
368 }
369
370 return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key;
371 }
372
373 jvResult[jss::error] = "malformedRequest";
374 return std::nullopt;
375}
376
378parseEscrow(Json::Value const& params, Json::Value& jvResult)
379{
380 if (!params.isObject())
381 {
382 uint256 uNodeIndex;
383 if (!uNodeIndex.parseHex(params.asString()))
384 {
385 jvResult[jss::error] = "malformedRequest";
386 return std::nullopt;
387 }
388
389 return uNodeIndex;
390 }
391
392 if (!params.isMember(jss::owner) || !params.isMember(jss::seq) ||
393 !params[jss::seq].isIntegral())
394 {
395 jvResult[jss::error] = "malformedRequest";
396 return std::nullopt;
397 }
398
399 auto const id = parseBase58<AccountID>(params[jss::owner].asString());
400
401 if (!id)
402 {
403 jvResult[jss::error] = "malformedOwner";
404 return std::nullopt;
405 }
406
407 return keylet::escrow(*id, params[jss::seq].asUInt()).key;
408}
409
411parseMPToken(Json::Value const& mptJson, Json::Value& jvResult)
412{
413 if (!mptJson.isObject())
414 {
415 uint256 uNodeIndex;
416 if (!uNodeIndex.parseHex(mptJson.asString()))
417 {
418 jvResult[jss::error] = "malformedRequest";
419 return std::nullopt;
420 }
421 return uNodeIndex;
422 }
423
424 if (!mptJson.isMember(jss::mpt_issuance_id) ||
425 !mptJson.isMember(jss::account))
426 {
427 jvResult[jss::error] = "malformedRequest";
428 return std::nullopt;
429 }
430
431 try
432 {
433 auto const mptIssuanceIdStr = mptJson[jss::mpt_issuance_id].asString();
434
435 uint192 mptIssuanceID;
436 if (!mptIssuanceID.parseHex(mptIssuanceIdStr))
437 Throw<std::runtime_error>("Cannot parse mpt_issuance_id");
438
439 auto const account =
440 parseBase58<AccountID>(mptJson[jss::account].asString());
441
442 if (!account || account->isZero())
443 {
444 jvResult[jss::error] = "malformedAddress";
445 return std::nullopt;
446 }
447
448 return keylet::mptoken(mptIssuanceID, *account).key;
449 }
450 catch (std::runtime_error const&)
451 {
452 jvResult[jss::error] = "malformedRequest";
453 return std::nullopt;
454 }
455}
456
459 Json::Value const& unparsedMPTIssuanceID,
460 Json::Value& jvResult)
461{
462 if (unparsedMPTIssuanceID.isString())
463 {
464 uint192 mptIssuanceID;
465 if (!mptIssuanceID.parseHex(unparsedMPTIssuanceID.asString()))
466 {
467 jvResult[jss::error] = "malformedRequest";
468 return std::nullopt;
469 }
470
471 return keylet::mptIssuance(mptIssuanceID).key;
472 }
473
474 jvResult[jss::error] = "malformedRequest";
475 return std::nullopt;
476}
477
479parseNFTokenPage(Json::Value const& params, Json::Value& jvResult)
480{
481 if (params.isString())
482 {
483 uint256 uNodeIndex;
484 if (!uNodeIndex.parseHex(params.asString()))
485 {
486 jvResult[jss::error] = "malformedRequest";
487 return std::nullopt;
488 }
489 return uNodeIndex;
490 }
491
492 jvResult[jss::error] = "malformedRequest";
493 return std::nullopt;
494}
495
497parseOffer(Json::Value const& params, Json::Value& jvResult)
498{
499 if (!params.isObject())
500 {
501 uint256 uNodeIndex;
502 if (!uNodeIndex.parseHex(params.asString()))
503 {
504 jvResult[jss::error] = "malformedRequest";
505 return std::nullopt;
506 }
507 return uNodeIndex;
508 }
509
510 if (!params.isMember(jss::account) || !params.isMember(jss::seq) ||
511 !params[jss::seq].isIntegral())
512 {
513 jvResult[jss::error] = "malformedRequest";
514 return std::nullopt;
515 }
516
517 auto const id = parseBase58<AccountID>(params[jss::account].asString());
518 if (!id)
519 {
520 jvResult[jss::error] = "malformedAddress";
521 return std::nullopt;
522 }
523
524 return keylet::offer(*id, params[jss::seq].asUInt()).key;
525}
526
528parseOracle(Json::Value const& params, Json::Value& jvResult)
529{
530 if (!params.isObject())
531 {
532 uint256 uNodeIndex;
533 if (!uNodeIndex.parseHex(params.asString()))
534 {
535 jvResult[jss::error] = "malformedRequest";
536 return std::nullopt;
537 }
538 return uNodeIndex;
539 }
540
541 if (!params.isMember(jss::oracle_document_id) ||
542 !params.isMember(jss::account))
543 {
544 jvResult[jss::error] = "malformedRequest";
545 return std::nullopt;
546 }
547
548 auto const& oracle = params;
549 auto const documentID = [&]() -> std::optional<std::uint32_t> {
550 auto const id = oracle[jss::oracle_document_id];
551 if (id.isUInt() || (id.isInt() && id.asInt() >= 0))
552 return std::make_optional(id.asUInt());
553
554 if (id.isString())
555 {
557 if (beast::lexicalCastChecked(v, id.asString()))
558 return std::make_optional(v);
559 }
560
561 return std::nullopt;
562 }();
563
564 auto const account =
565 parseBase58<AccountID>(oracle[jss::account].asString());
566 if (!account || account->isZero())
567 {
568 jvResult[jss::error] = "malformedAddress";
569 return std::nullopt;
570 }
571
572 if (!documentID)
573 {
574 jvResult[jss::error] = "malformedDocumentID";
575 return std::nullopt;
576 }
577
578 return keylet::oracle(*account, *documentID).key;
579}
580
583{
584 uint256 uNodeIndex;
585 if (!uNodeIndex.parseHex(params.asString()))
586 {
587 jvResult[jss::error] = "malformedRequest";
588 return std::nullopt;
589 }
590
591 return uNodeIndex;
592}
593
596{
597 if (pd.isString())
598 {
599 auto const index = parseIndex(pd, jvResult);
600 return index;
601 }
602
603 if (!pd.isObject())
604 {
605 jvResult[jss::error] = "malformedRequest";
606 return std::nullopt;
607 }
608
609 if (!pd.isMember(jss::account))
610 {
611 jvResult[jss::error] = "malformedRequest";
612 return std::nullopt;
613 }
614
615 if (!pd[jss::account].isString())
616 {
617 jvResult[jss::error] = "malformedAddress";
618 return std::nullopt;
619 }
620
621 if (!pd.isMember(jss::seq) ||
622 (pd[jss::seq].isInt() && pd[jss::seq].asInt() < 0) ||
623 (!pd[jss::seq].isInt() && !pd[jss::seq].isUInt()))
624 {
625 jvResult[jss::error] = "malformedRequest";
626 return std::nullopt;
627 }
628
629 auto const account = parseBase58<AccountID>(pd[jss::account].asString());
630 if (!account)
631 {
632 jvResult[jss::error] = "malformedAddress";
633 return std::nullopt;
634 }
635
636 return keylet::permissionedDomain(*account, pd[jss::seq].asUInt()).key;
637}
638
640parseRippleState(Json::Value const& jvRippleState, Json::Value& jvResult)
641{
642 Currency uCurrency;
643
644 if (!jvRippleState.isObject() || !jvRippleState.isMember(jss::currency) ||
645 !jvRippleState.isMember(jss::accounts) ||
646 !jvRippleState[jss::accounts].isArray() ||
647 2 != jvRippleState[jss::accounts].size() ||
648 !jvRippleState[jss::accounts][0u].isString() ||
649 !jvRippleState[jss::accounts][1u].isString() ||
650 (jvRippleState[jss::accounts][0u].asString() ==
651 jvRippleState[jss::accounts][1u].asString()))
652 {
653 jvResult[jss::error] = "malformedRequest";
654 return std::nullopt;
655 }
656
657 auto const id1 =
658 parseBase58<AccountID>(jvRippleState[jss::accounts][0u].asString());
659 auto const id2 =
660 parseBase58<AccountID>(jvRippleState[jss::accounts][1u].asString());
661 if (!id1 || !id2)
662 {
663 jvResult[jss::error] = "malformedAddress";
664 return std::nullopt;
665 }
666
667 if (!to_currency(uCurrency, jvRippleState[jss::currency].asString()))
668 {
669 jvResult[jss::error] = "malformedCurrency";
670 return std::nullopt;
671 }
672
673 return keylet::line(*id1, *id2, uCurrency).key;
674}
675
677parseTicket(Json::Value const& params, Json::Value& jvResult)
678{
679 if (!params.isObject())
680 {
681 uint256 uNodeIndex;
682 if (!uNodeIndex.parseHex(params.asString()))
683 {
684 jvResult[jss::error] = "malformedRequest";
685 return std::nullopt;
686 }
687 return uNodeIndex;
688 }
689
690 if (!params.isMember(jss::account) || !params.isMember(jss::ticket_seq) ||
691 !params[jss::ticket_seq].isIntegral())
692 {
693 jvResult[jss::error] = "malformedRequest";
694 return std::nullopt;
695 }
696
697 auto const id = parseBase58<AccountID>(params[jss::account].asString());
698 if (!id)
699 {
700 jvResult[jss::error] = "malformedAddress";
701 return std::nullopt;
702 }
703
704 return getTicketIndex(*id, params[jss::ticket_seq].asUInt());
705}
706
709{
710 if (claim_id.isString())
711 {
712 uint256 uNodeIndex;
713 // we accept a node id as specifier of a xchain claim id
714 if (!uNodeIndex.parseHex(claim_id.asString()))
715 {
716 jvResult[jss::error] = "malformedRequest";
717 return std::nullopt;
718 }
719 return uNodeIndex;
720 }
721
722 if (!claim_id.isObject() ||
723 !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) &&
724 claim_id[sfIssuingChainDoor.getJsonName()].isString()) ||
725 !(claim_id.isMember(sfLockingChainDoor.getJsonName()) &&
726 claim_id[sfLockingChainDoor.getJsonName()].isString()) ||
727 !claim_id.isMember(sfIssuingChainIssue.getJsonName()) ||
728 !claim_id.isMember(sfLockingChainIssue.getJsonName()) ||
729 !claim_id.isMember(jss::xchain_owned_claim_id))
730 {
731 jvResult[jss::error] = "malformedRequest";
732 return std::nullopt;
733 }
734
735 // if not specified with a node id, a claim_id is specified by
736 // four strings defining the bridge (locking_chain_door,
737 // locking_chain_issue, issuing_chain_door, issuing_chain_issue)
738 // and the claim id sequence number.
739 auto const lockingChainDoor = parseBase58<AccountID>(
740 claim_id[sfLockingChainDoor.getJsonName()].asString());
741 auto const issuingChainDoor = parseBase58<AccountID>(
742 claim_id[sfIssuingChainDoor.getJsonName()].asString());
743 Issue lockingChainIssue, issuingChainIssue;
744 bool valid = lockingChainDoor && issuingChainDoor;
745
746 if (valid)
747 {
748 try
749 {
750 lockingChainIssue =
751 issueFromJson(claim_id[sfLockingChainIssue.getJsonName()]);
752 issuingChainIssue =
753 issueFromJson(claim_id[sfIssuingChainIssue.getJsonName()]);
754 }
755 catch (std::runtime_error const& ex)
756 {
757 jvResult[jss::error] = "malformedRequest";
758 return std::nullopt;
759 }
760 }
761
762 if (valid && claim_id[jss::xchain_owned_claim_id].isIntegral())
763 {
764 auto const seq = claim_id[jss::xchain_owned_claim_id].asUInt();
765
766 STXChainBridge bridge_spec(
767 *lockingChainDoor,
768 lockingChainIssue,
769 *issuingChainDoor,
770 issuingChainIssue);
771 Keylet keylet = keylet::xChainClaimID(bridge_spec, seq);
772 return keylet.key;
773 }
774
775 jvResult[jss::error] = "malformedRequest";
776 return std::nullopt;
777}
778
781 Json::Value const& claim_id,
782 Json::Value& jvResult)
783{
784 if (claim_id.isString())
785 {
786 uint256 uNodeIndex;
787 // we accept a node id as specifier of a xchain create account
788 // claim_id
789 if (!uNodeIndex.parseHex(claim_id.asString()))
790 {
791 jvResult[jss::error] = "malformedRequest";
792 return std::nullopt;
793 }
794 return uNodeIndex;
795 }
796
797 if (!claim_id.isObject() ||
798 !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) &&
799 claim_id[sfIssuingChainDoor.getJsonName()].isString()) ||
800 !(claim_id.isMember(sfLockingChainDoor.getJsonName()) &&
801 claim_id[sfLockingChainDoor.getJsonName()].isString()) ||
802 !claim_id.isMember(sfIssuingChainIssue.getJsonName()) ||
803 !claim_id.isMember(sfLockingChainIssue.getJsonName()) ||
804 !claim_id.isMember(jss::xchain_owned_create_account_claim_id))
805 {
806 jvResult[jss::error] = "malformedRequest";
807 return std::nullopt;
808 }
809
810 // if not specified with a node id, a create account claim_id is
811 // specified by four strings defining the bridge
812 // (locking_chain_door, locking_chain_issue, issuing_chain_door,
813 // issuing_chain_issue) and the create account claim id sequence
814 // number.
815 auto const lockingChainDoor = parseBase58<AccountID>(
816 claim_id[sfLockingChainDoor.getJsonName()].asString());
817 auto const issuingChainDoor = parseBase58<AccountID>(
818 claim_id[sfIssuingChainDoor.getJsonName()].asString());
819 Issue lockingChainIssue, issuingChainIssue;
820 bool valid = lockingChainDoor && issuingChainDoor;
821 if (valid)
822 {
823 try
824 {
825 lockingChainIssue =
826 issueFromJson(claim_id[sfLockingChainIssue.getJsonName()]);
827 issuingChainIssue =
828 issueFromJson(claim_id[sfIssuingChainIssue.getJsonName()]);
829 }
830 catch (std::runtime_error const& ex)
831 {
832 valid = false;
833 jvResult[jss::error] = "malformedRequest";
834 }
835 }
836
837 if (valid &&
838 claim_id[jss::xchain_owned_create_account_claim_id].isIntegral())
839 {
840 auto const seq =
841 claim_id[jss::xchain_owned_create_account_claim_id].asUInt();
842
843 STXChainBridge bridge_spec(
844 *lockingChainDoor,
845 lockingChainIssue,
846 *issuingChainDoor,
847 issuingChainIssue);
848 Keylet keylet = keylet::xChainCreateAccountClaimID(bridge_spec, seq);
849 return keylet.key;
850 }
851
852 return std::nullopt;
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: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: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: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