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