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