rippled
Loading...
Searching...
No Matches
LedgerEntry.cpp
1#include <xrpld/rpc/Context.h>
2#include <xrpld/rpc/GRPCHandlers.h>
3#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
4#include <xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h>
5
6#include <xrpl/basics/StringUtilities.h>
7#include <xrpl/basics/strHex.h>
8#include <xrpl/beast/core/LexicalCast.h>
9#include <xrpl/json/json_errors.h>
10#include <xrpl/ledger/ReadView.h>
11#include <xrpl/ledger/helpers/CredentialHelpers.h>
12#include <xrpl/protocol/ErrorCodes.h>
13#include <xrpl/protocol/Indexes.h>
14#include <xrpl/protocol/LedgerFormats.h>
15#include <xrpl/protocol/RPCErr.h>
16#include <xrpl/protocol/STXChainBridge.h>
17#include <xrpl/protocol/jss.h>
18
19namespace xrpl {
20
22 Json::Value const&,
24 unsigned const apiVersion)>;
25
28 Keylet const& keylet,
29 Json::Value const& params,
30 Json::StaticString const& fieldName,
31 unsigned const apiVersion);
32
33// Helper function to return FunctionType for objects that have a fixed
34// location. That is, they don't take parameters to compute the index.
35// e.g. amendments, fees, negative UNL, etc.
36static FunctionType
37fixed(Keylet const& keylet)
38{
39 return [keylet](
40 Json::Value const& params,
41 Json::StaticString const fieldName,
42 unsigned const apiVersion) -> Expected<uint256, Json::Value> {
43 return parseFixed(keylet, params, fieldName, apiVersion);
44 };
45}
46
47static Expected<uint256, Json::Value>
49 Json::Value const& params,
50 Json::StaticString const fieldName,
51 std::string const& expectedType = "hex string or object")
52{
53 if (auto const uNodeIndex = LedgerEntryHelpers::parse<uint256>(params))
54 {
55 return *uNodeIndex;
56 }
57 return LedgerEntryHelpers::invalidFieldError("malformedRequest", fieldName, expectedType);
58}
59
60static Expected<uint256, Json::Value>
61parseIndex(Json::Value const& params, Json::StaticString const fieldName, unsigned const apiVersion)
62{
63 if (apiVersion > 2u && params.isString())
64 {
65 std::string const index = params.asString();
66 if (index == jss::amendments.c_str())
67 return keylet::amendments().key;
68 if (index == jss::fee.c_str())
69 return keylet::fees().key;
70 if (index == jss::nunl)
71 return keylet::negativeUNL().key;
72 if (index == jss::hashes)
73 {
74 // Note this only finds the "short" skip list. Use "hashes":index to
75 // get the long list.
76 return keylet::skip().key;
77 }
78 }
79 return parseObjectID(params, fieldName, "hex string");
80}
81
82static Expected<uint256, Json::Value>
84 Json::Value const& params,
85 Json::StaticString const fieldName,
86 [[maybe_unused]] unsigned const apiVersion)
87{
88 if (auto const account = LedgerEntryHelpers::parse<AccountID>(params))
89 {
90 return keylet::account(*account).key;
91 }
92
93 return LedgerEntryHelpers::invalidFieldError("malformedAddress", fieldName, "AccountID");
94}
95
97
100 Json::Value const& params,
101 Json::StaticString const fieldName,
102 [[maybe_unused]] unsigned const apiVersion)
103{
104 if (!params.isObject())
105 {
106 return parseObjectID(params, fieldName);
107 }
108
109 if (auto const value = LedgerEntryHelpers::hasRequired(params, {jss::asset, jss::asset2});
110 !value)
111 {
112 return Unexpected(value.error());
113 }
114
115 auto const asset = LedgerEntryHelpers::requiredIssue(params, jss::asset, "malformedRequest");
116 if (!asset)
117 return Unexpected(asset.error());
118
119 auto const asset2 = LedgerEntryHelpers::requiredIssue(params, jss::asset2, "malformedRequest");
120 if (!asset2)
121 return Unexpected(asset2.error());
122
123 return keylet::amm(*asset, *asset2).key;
124}
125
126static Expected<uint256, Json::Value>
128 Json::Value const& params,
129 Json::StaticString const fieldName,
130 [[maybe_unused]] unsigned const apiVersion)
131{
132 if (!params.isMember(jss::bridge))
133 {
135 }
136
137 if (params[jss::bridge].isString())
138 {
139 return parseObjectID(params, fieldName);
140 }
141
142 auto const bridge = LedgerEntryHelpers::parseBridgeFields(params[jss::bridge]);
143 if (!bridge)
144 return Unexpected(bridge.error());
145
146 auto const account = LedgerEntryHelpers::requiredAccountID(
147 params, jss::bridge_account, "malformedBridgeAccount");
148 if (!account)
149 return Unexpected(account.error());
150
151 STXChainBridge::ChainType const chainType =
152 STXChainBridge::srcChain(account.value() == bridge->lockingChainDoor());
153 if (account.value() != bridge->door(chainType))
154 return LedgerEntryHelpers::malformedError("malformedRequest", "");
155
156 return keylet::bridge(*bridge, chainType).key;
157}
158
159static Expected<uint256, Json::Value>
161 Json::Value const& params,
162 Json::StaticString const fieldName,
163 [[maybe_unused]] unsigned const apiVersion)
164{
165 return parseObjectID(params, fieldName, "hex string");
166}
167
168static Expected<uint256, Json::Value>
170 Json::Value const& cred,
171 Json::StaticString const fieldName,
172 [[maybe_unused]] unsigned const apiVersion)
173{
174 if (!cred.isObject())
175 {
176 return parseObjectID(cred, fieldName);
177 }
178
179 auto const subject =
180 LedgerEntryHelpers::requiredAccountID(cred, jss::subject, "malformedRequest");
181 if (!subject)
182 return Unexpected(subject.error());
183
184 auto const issuer =
185 LedgerEntryHelpers::requiredAccountID(cred, jss::issuer, "malformedRequest");
186 if (!issuer)
187 return Unexpected(issuer.error());
188
189 auto const credType = LedgerEntryHelpers::requiredHexBlob(
190 cred, jss::credential_type, maxCredentialTypeLength, "malformedRequest");
191 if (!credType)
192 return Unexpected(credType.error());
193
194 return keylet::credential(*subject, *issuer, Slice(credType->data(), credType->size())).key;
195}
196
197static Expected<uint256, Json::Value>
199 Json::Value const& params,
200 Json::StaticString const fieldName,
201 [[maybe_unused]] unsigned const apiVersion)
202{
203 if (!params.isObject())
204 {
205 return parseObjectID(params, fieldName);
206 }
207
208 auto const account =
209 LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
210 if (!account)
211 return Unexpected(account.error());
212
213 auto const authorize =
214 LedgerEntryHelpers::requiredAccountID(params, jss::authorize, "malformedAddress");
215 if (!authorize)
216 return Unexpected(authorize.error());
217
218 return keylet::delegate(*account, *authorize).key;
219}
220
221static Expected<STArray, Json::Value>
223{
224 if (!jv.isArray())
225 {
227 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
228 }
229
230 std::uint32_t const n = jv.size();
232 {
233 return Unexpected(
235 "malformedAuthorizedCredentials",
236 "Invalid field '" + std::string(jss::authorized_credentials) +
237 "', array too long."));
238 }
239
240 if (n == 0)
241 {
242 return Unexpected(
244 "malformedAuthorizedCredentials",
245 "Invalid field '" + std::string(jss::authorized_credentials) + "', array empty."));
246 }
247
248 STArray arr(sfAuthorizeCredentials, n);
249 for (auto const& jo : jv)
250 {
251 if (!jo.isObject())
252 {
254 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
255 }
256
257 if (auto const value = LedgerEntryHelpers::hasRequired(
258 jo, {jss::issuer, jss::credential_type}, "malformedAuthorizedCredentials");
259 !value)
260 {
261 return Unexpected(value.error());
262 }
263
264 auto const issuer = LedgerEntryHelpers::requiredAccountID(
265 jo, jss::issuer, "malformedAuthorizedCredentials");
266 if (!issuer)
267 return Unexpected(issuer.error());
268
269 auto const credentialType = LedgerEntryHelpers::requiredHexBlob(
270 jo, jss::credential_type, maxCredentialTypeLength, "malformedAuthorizedCredentials");
271 if (!credentialType)
272 return Unexpected(credentialType.error());
273
274 auto credential = STObject::makeInnerObject(sfCredential);
275 credential.setAccountID(sfIssuer, *issuer);
276 credential.setFieldVL(sfCredentialType, *credentialType);
277 arr.push_back(std::move(credential));
278 }
279
280 return arr;
281}
282
283static Expected<uint256, Json::Value>
285 Json::Value const& dp,
286 Json::StaticString const fieldName,
287 [[maybe_unused]] unsigned const apiVersion)
288{
289 if (!dp.isObject())
290 {
291 return parseObjectID(dp, fieldName);
292 }
293
294 if ((dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)))
295 {
297 "malformedRequest",
298 "Must have exactly one of `authorized` and "
299 "`authorized_credentials`.");
300 }
301
302 auto const owner = LedgerEntryHelpers::requiredAccountID(dp, jss::owner, "malformedOwner");
303 if (!owner)
304 {
305 return Unexpected(owner.error());
306 }
307
308 if (dp.isMember(jss::authorized))
309 {
310 if (auto const authorized = LedgerEntryHelpers::parse<AccountID>(dp[jss::authorized]))
311 {
312 return keylet::depositPreauth(*owner, *authorized).key;
313 }
315 "malformedAuthorized", jss::authorized, "AccountID");
316 }
317
318 auto const& ac(dp[jss::authorized_credentials]);
319 auto const arr = parseAuthorizeCredentials(ac);
320 if (!arr.has_value())
321 return Unexpected(arr.error());
322
323 auto const& sorted = credentials::makeSorted(arr.value());
324 if (sorted.empty())
325 {
326 // TODO: this error message is bad/inaccurate
328 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
329 }
330
331 return keylet::depositPreauth(*owner, sorted).key;
332}
333
334static Expected<uint256, Json::Value>
336 Json::Value const& params,
337 Json::StaticString const fieldName,
338 [[maybe_unused]] unsigned const apiVersion)
339{
340 auto const account = LedgerEntryHelpers::parse<AccountID>(params);
341 if (!account)
342 {
343 return LedgerEntryHelpers::invalidFieldError("malformedAddress", fieldName, "AccountID");
344 }
345
346 return keylet::did(*account).key;
347}
348
349static Expected<uint256, Json::Value>
351 Json::Value const& params,
352 Json::StaticString const fieldName,
353 [[maybe_unused]] unsigned const apiVersion)
354{
355 if (!params.isObject())
356 {
357 return parseObjectID(params, fieldName);
358 }
359
360 if (params.isMember(jss::sub_index) &&
361 (!params[jss::sub_index].isConvertibleTo(Json::uintValue) ||
362 params[jss::sub_index].isBool()))
363 {
364 return LedgerEntryHelpers::invalidFieldError("malformedRequest", jss::sub_index, "number");
365 }
366
367 if (params.isMember(jss::owner) == params.isMember(jss::dir_root))
368 {
370 "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
371 }
372
373 std::uint64_t const uSubIndex = params.get(jss::sub_index, 0).asUInt();
374
375 if (params.isMember(jss::dir_root))
376 {
377 if (auto const uDirRoot = LedgerEntryHelpers::parse<uint256>(params[jss::dir_root]))
378 {
379 return keylet::page(*uDirRoot, uSubIndex).key;
380 }
381
382 return LedgerEntryHelpers::invalidFieldError("malformedDirRoot", jss::dir_root, "hash");
383 }
384
385 if (params.isMember(jss::owner))
386 {
387 auto const ownerID = LedgerEntryHelpers::parse<AccountID>(params[jss::owner]);
388 if (!ownerID)
389 {
391 "malformedAddress", jss::owner, "AccountID");
392 }
393
394 return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key;
395 }
396
397 return LedgerEntryHelpers::malformedError("malformedRequest", "");
398}
399
400static Expected<uint256, Json::Value>
402 Json::Value const& params,
403 Json::StaticString const fieldName,
404 [[maybe_unused]] unsigned const apiVersion)
405{
406 if (!params.isObject())
407 {
408 return parseObjectID(params, fieldName);
409 }
410
411 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
412 if (!id)
413 return Unexpected(id.error());
414 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq");
415 if (!seq)
416 return Unexpected(seq.error());
417
418 return keylet::escrow(*id, *seq).key;
419}
420
422
425 Keylet const& keylet,
426 Json::Value const& params,
427 Json::StaticString const& fieldName,
428 [[maybe_unused]] unsigned const apiVersion)
429{
430 if (!params.isBool())
431 {
432 return parseObjectID(params, fieldName, "hex string");
433 }
434 if (!params.asBool())
435 {
436 return LedgerEntryHelpers::invalidFieldError("invalidParams", fieldName, "true");
437 }
438
439 return keylet.key;
440}
441
442static Expected<uint256, Json::Value>
444 Json::Value const& params,
445 Json::StaticString const fieldName,
446 unsigned const apiVersion)
447{
448 if (params.isUInt() || params.isInt())
449 {
450 // If the index doesn't parse as a UInt, throw
451 auto const index = params.asUInt();
452
453 // Return the "long" skip list for the given ledger index.
454 auto const keylet = keylet::skip(index);
455 return keylet.key;
456 }
457 // Return the key in `params` or the "short" skip list, which contains
458 // hashes since the last flag ledger.
459 return parseFixed(keylet::skip(), params, fieldName, apiVersion);
460}
461
462static Expected<uint256, Json::Value>
464 Json::Value const& params,
465 Json::StaticString const fieldName,
466 [[maybe_unused]] unsigned const apiVersion)
467{
468 if (!params.isObject())
469 {
470 return parseObjectID(params, fieldName, "hex string");
471 }
472
473 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
474 if (!id)
475 return Unexpected(id.error());
476 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq");
477 if (!seq)
478 return Unexpected(seq.error());
479
480 return keylet::loanbroker(*id, *seq).key;
481}
482
483static Expected<uint256, Json::Value>
485 Json::Value const& params,
486 Json::StaticString const fieldName,
487 [[maybe_unused]] unsigned const apiVersion)
488{
489 if (!params.isObject())
490 {
491 return parseObjectID(params, fieldName, "hex string");
492 }
493
494 auto const id =
495 LedgerEntryHelpers::requiredUInt256(params, jss::loan_broker_id, "malformedBroker");
496 if (!id)
497 return Unexpected(id.error());
498 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::loan_seq, "malformedSeq");
499 if (!seq)
500 return Unexpected(seq.error());
501
502 return keylet::loan(*id, *seq).key;
503}
504
505static Expected<uint256, Json::Value>
507 Json::Value const& params,
508 Json::StaticString const fieldName,
509 [[maybe_unused]] unsigned const apiVersion)
510{
511 if (!params.isObject())
512 {
513 return parseObjectID(params, fieldName);
514 }
515
516 auto const mptIssuanceID =
517 LedgerEntryHelpers::requiredUInt192(params, jss::mpt_issuance_id, "malformedMPTIssuanceID");
518 if (!mptIssuanceID)
519 return Unexpected(mptIssuanceID.error());
520
521 auto const account =
522 LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount");
523 if (!account)
524 return Unexpected(account.error());
525
526 return keylet::mptoken(*mptIssuanceID, *account).key;
527}
528
529static Expected<uint256, Json::Value>
531 Json::Value const& params,
532 Json::StaticString const fieldName,
533 [[maybe_unused]] unsigned const apiVersion)
534{
535 auto const mptIssuanceID = LedgerEntryHelpers::parse<uint192>(params);
536 if (!mptIssuanceID)
537 {
539 "malformedMPTokenIssuance", fieldName, "Hash192");
540 }
541
542 return keylet::mptIssuance(*mptIssuanceID).key;
543}
544
545static Expected<uint256, Json::Value>
547 Json::Value const& params,
548 Json::StaticString const fieldName,
549 [[maybe_unused]] unsigned const apiVersion)
550{
551 return parseObjectID(params, fieldName, "hex string");
552}
553
554static Expected<uint256, Json::Value>
556 Json::Value const& params,
557 Json::StaticString const fieldName,
558 [[maybe_unused]] unsigned const apiVersion)
559{
560 return parseObjectID(params, fieldName, "hex string");
561}
562
564
567 Json::Value const& params,
568 Json::StaticString const fieldName,
569 [[maybe_unused]] unsigned const apiVersion)
570{
571 if (!params.isObject())
572 {
573 return parseObjectID(params, fieldName);
574 }
575
576 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
577 if (!id)
578 return Unexpected(id.error());
579
580 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest");
581 if (!seq)
582 return Unexpected(seq.error());
583
584 return keylet::offer(*id, *seq).key;
585}
586
587static Expected<uint256, Json::Value>
589 Json::Value const& params,
590 Json::StaticString const fieldName,
591 [[maybe_unused]] unsigned const apiVersion)
592{
593 if (!params.isObject())
594 {
595 return parseObjectID(params, fieldName);
596 }
597
598 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount");
599 if (!id)
600 return Unexpected(id.error());
601
602 auto const seq =
603 LedgerEntryHelpers::requiredUInt32(params, jss::oracle_document_id, "malformedDocumentID");
604 if (!seq)
605 return Unexpected(seq.error());
606
607 return keylet::oracle(*id, *seq).key;
608}
609
610static Expected<uint256, Json::Value>
612 Json::Value const& params,
613 Json::StaticString const fieldName,
614 [[maybe_unused]] unsigned const apiVersion)
615{
616 return parseObjectID(params, fieldName, "hex string");
617}
618
619static Expected<uint256, Json::Value>
621 Json::Value const& pd,
622 Json::StaticString const fieldName,
623 [[maybe_unused]] unsigned const apiVersion)
624{
625 if (pd.isString())
626 {
627 return parseObjectID(pd, fieldName);
628 }
629
630 if (!pd.isObject())
631 {
633 "malformedRequest", fieldName, "hex string or object");
634 }
635
636 auto const account =
637 LedgerEntryHelpers::requiredAccountID(pd, jss::account, "malformedAddress");
638 if (!account)
639 return Unexpected(account.error());
640
641 auto const seq = LedgerEntryHelpers::requiredUInt32(pd, jss::seq, "malformedRequest");
642 if (!seq)
643 return Unexpected(seq.error());
644
645 return keylet::permissionedDomain(*account, pd[jss::seq].asUInt()).key;
646}
647
648static Expected<uint256, Json::Value>
650 Json::Value const& jvRippleState,
651 Json::StaticString const fieldName,
652 [[maybe_unused]] unsigned const apiVersion)
653{
654 Currency uCurrency;
655
656 if (!jvRippleState.isObject())
657 {
658 return parseObjectID(jvRippleState, fieldName);
659 }
660
661 if (auto const value =
662 LedgerEntryHelpers::hasRequired(jvRippleState, {jss::currency, jss::accounts});
663 !value)
664 {
665 return Unexpected(value.error());
666 }
667
668 if (!jvRippleState[jss::accounts].isArray() || jvRippleState[jss::accounts].size() != 2)
669 {
671 "malformedRequest", jss::accounts, "length-2 array of Accounts");
672 }
673
674 auto const id1 = LedgerEntryHelpers::parse<AccountID>(jvRippleState[jss::accounts][0u]);
675 auto const id2 = LedgerEntryHelpers::parse<AccountID>(jvRippleState[jss::accounts][1u]);
676 if (!id1 || !id2)
677 {
679 "malformedAddress", jss::accounts, "array of Accounts");
680 }
681 if (id1 == id2)
682 {
684 "malformedRequest", "Cannot have a trustline to self.");
685 }
686
687 if (!jvRippleState[jss::currency].isString() || jvRippleState[jss::currency] == "" ||
688 !to_currency(uCurrency, jvRippleState[jss::currency].asString()))
689 {
691 "malformedCurrency", jss::currency, "Currency");
692 }
693
694 return keylet::line(*id1, *id2, uCurrency).key;
695}
696
697static Expected<uint256, Json::Value>
699 Json::Value const& params,
700 Json::StaticString const fieldName,
701 [[maybe_unused]] unsigned const apiVersion)
702{
703 return parseObjectID(params, fieldName, "hex string");
704}
705
706static Expected<uint256, Json::Value>
708 Json::Value const& params,
709 Json::StaticString const fieldName,
710 [[maybe_unused]] unsigned const apiVersion)
711{
712 if (!params.isObject())
713 {
714 return parseObjectID(params, fieldName);
715 }
716
717 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
718 if (!id)
719 return Unexpected(id.error());
720
721 auto const seq =
722 LedgerEntryHelpers::requiredUInt32(params, jss::ticket_seq, "malformedRequest");
723 if (!seq)
724 return Unexpected(seq.error());
725
726 return getTicketIndex(*id, *seq);
727}
728
729static Expected<uint256, Json::Value>
731 Json::Value const& params,
732 Json::StaticString const fieldName,
733 [[maybe_unused]] unsigned const apiVersion)
734{
735 if (!params.isObject())
736 {
737 return parseObjectID(params, fieldName);
738 }
739
740 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
741 if (!id)
742 return Unexpected(id.error());
743
744 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest");
745 if (!seq)
746 return Unexpected(seq.error());
747
748 return keylet::vault(*id, *seq).key;
749}
750
751static Expected<uint256, Json::Value>
753 Json::Value const& claim_id,
754 Json::StaticString const fieldName,
755 [[maybe_unused]] unsigned const apiVersion)
756{
757 if (!claim_id.isObject())
758 {
759 return parseObjectID(claim_id, fieldName);
760 }
761
762 auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id);
763 if (!bridge_spec)
764 return Unexpected(bridge_spec.error());
765
766 auto const seq = LedgerEntryHelpers::requiredUInt32(
767 claim_id, jss::xchain_owned_claim_id, "malformedXChainOwnedClaimID");
768 if (!seq)
769 {
770 return Unexpected(seq.error());
771 }
772
773 Keylet keylet = keylet::xChainClaimID(*bridge_spec, *seq);
774 return keylet.key;
775}
776
777static Expected<uint256, Json::Value>
779 Json::Value const& claim_id,
780 Json::StaticString const fieldName,
781 [[maybe_unused]] unsigned const apiVersion)
782{
783 if (!claim_id.isObject())
784 {
785 return parseObjectID(claim_id, fieldName);
786 }
787
788 auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id);
789 if (!bridge_spec)
790 return Unexpected(bridge_spec.error());
791
792 auto const seq = LedgerEntryHelpers::requiredUInt32(
793 claim_id,
794 jss::xchain_owned_create_account_claim_id,
795 "malformedXChainOwnedCreateAccountClaimID");
796 if (!seq)
797 {
798 return Unexpected(seq.error());
799 }
800
801 Keylet keylet = keylet::xChainCreateAccountClaimID(*bridge_spec, *seq);
802 return keylet.key;
803}
804
811
812// {
813// ledger_hash : <ledger>
814// ledger_index : <ledger_index>
815// ...
816// }
819{
820 static auto ledgerEntryParsers = std::to_array<LedgerEntry>({
821#pragma push_macro("LEDGER_ENTRY")
822#undef LEDGER_ENTRY
823
824#define LEDGER_ENTRY(tag, value, name, rpcName, fields) {jss::rpcName, parse##name, tag},
825
826#include <xrpl/protocol/detail/ledger_entries.macro>
827
828#undef LEDGER_ENTRY
829#pragma pop_macro("LEDGER_ENTRY")
830 {jss::index, parseIndex, ltANY},
831 // aliases
832 {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT},
833 {jss::ripple_state, parseRippleState, ltRIPPLE_STATE},
834 });
835
836 auto const hasMoreThanOneMember = [&]() {
837 int count = 0;
838
839 for (auto const& ledgerEntry : ledgerEntryParsers)
840 {
841 if (context.params.isMember(ledgerEntry.fieldName))
842 {
843 count++;
844 if (count > 1) // Early exit if more than one is found
845 return true;
846 }
847 }
848 return false; // Return false if <= 1 is found
849 }();
850
851 if (hasMoreThanOneMember)
852 {
853 return RPC::make_param_error("Too many fields provided.");
854 }
855
857 auto jvResult = RPC::lookupLedger(lpLedger, context);
858
859 if (!lpLedger)
860 return jvResult;
861
862 uint256 uNodeIndex;
863 LedgerEntryType expectedType = ltANY;
864
865 try
866 {
867 bool found = false;
868 for (auto const& ledgerEntry : ledgerEntryParsers)
869 {
870 if (context.params.isMember(ledgerEntry.fieldName))
871 {
872 expectedType = ledgerEntry.expectedType;
873 // `Bridge` is the only type that involves two fields at the
874 // `ledger_entry` param level.
875 // So that parser needs to have the whole `params` field.
876 // All other parsers only need the one field name's info.
877 Json::Value const& params = ledgerEntry.fieldName == jss::bridge
878 ? context.params
879 : context.params[ledgerEntry.fieldName];
880 auto const result =
881 ledgerEntry.parseFunction(params, ledgerEntry.fieldName, context.apiVersion);
882 if (!result)
883 return result.error();
884
885 uNodeIndex = result.value();
886 found = true;
887 break;
888 }
889 }
890 if (!found)
891 {
892 if (context.apiVersion < 2u)
893 {
894 jvResult[jss::error] = "unknownOption";
895 return jvResult;
896 }
897 return RPC::make_param_error("No ledger_entry params provided.");
898 }
899 }
900 catch (Json::error& e)
901 {
902 if (context.apiVersion > 1u)
903 {
904 // For apiVersion 2 onwards, any parsing failures that throw
905 // this exception return an invalidParam error.
907 }
908
909 throw;
910 }
911
912 // Return the computed index regardless of whether the node exists.
913 jvResult[jss::index] = to_string(uNodeIndex);
914
915 if (uNodeIndex.isZero())
916 {
918 return jvResult;
919 }
920
921 auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
922
923 bool bNodeBinary = false;
924 if (context.params.isMember(jss::binary))
925 bNodeBinary = context.params[jss::binary].asBool();
926
927 if (!sleNode)
928 {
929 // Not found.
931 return jvResult;
932 }
933
934 if ((expectedType != ltANY) && (expectedType != sleNode->getType()))
935 {
937 return jvResult;
938 }
939
940 if (bNodeBinary)
941 {
942 Serializer s;
943
944 sleNode->add(s);
945
946 jvResult[jss::node_binary] = strHex(s.peekData());
947 }
948 else
949 {
950 jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
951 }
952
953 return jvResult;
954}
955
958{
959 org::xrpl::rpc::v1::GetLedgerEntryRequest const& request = context.params;
960 org::xrpl::rpc::v1::GetLedgerEntryResponse response;
961 grpc::Status const status = grpc::Status::OK;
962
964 if (auto status = RPC::ledgerFromRequest(ledger, context))
965 {
966 grpc::Status errorStatus;
967 if (status.toErrorCode() == rpcINVALID_PARAMS)
968 {
969 errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
970 }
971 else
972 {
973 errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
974 }
975 return {response, errorStatus};
976 }
977
978 auto const key = uint256::fromVoidChecked(request.key());
979 if (!key)
980 {
981 grpc::Status const errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
982 return {response, errorStatus};
983 }
984
985 auto const sleNode = ledger->read(keylet::unchecked(*key));
986 if (!sleNode)
987 {
988 grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "object not found"};
989 return {response, errorStatus};
990 }
991
992 Serializer s;
993 sleNode->add(s);
994
995 auto& stateObject = *response.mutable_ledger_object();
996 stateObject.set_data(s.peekData().data(), s.getLength());
997 stateObject.set_key(request.key());
998 *(response.mutable_ledger()) = request.ledger();
999 return {response, status};
1000}
1001} // namespace xrpl
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
bool isArray() const
UInt size() const
Number of values in array or object.
bool isString() const
UInt asUInt() const
bool isObject() const
std::string asString() const
Returns the unquoted string value.
bool isBool() const
bool asBool() const
bool isUInt() const
bool isMember(char const *key) const
Return true if the object has a member named key.
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
bool isConvertibleTo(ValueType other) const
bool isInt() const
void push_back(STObject const &object)
Definition STArray.h:189
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:73
static ChainType srcChain(bool wasLockingChainSend)
Blob const & peekData() const
Definition Serializer.h:176
int getLength() const
Definition Serializer.h:207
An immutable linear range of bytes.
Definition Slice.h:26
static std::optional< base_uint > fromVoidChecked(T const &from)
Definition base_uint.h:300
bool isZero() const
Definition base_uint.h:513
T data(T... args)
T is_same_v
@ uintValue
unsigned integer value
Definition json_value.h:21
Expected< std::uint32_t, Json::Value > requiredUInt32(Json::Value const &params, Json::StaticString const fieldName, std::string const &err)
Expected< AccountID, Json::Value > requiredAccountID(Json::Value const &params, Json::StaticString const fieldName, std::string const &err)
Unexpected< Json::Value > missingFieldError(Json::StaticString const field, std::optional< std::string > err=std::nullopt)
Expected< uint192, Json::Value > requiredUInt192(Json::Value const &params, Json::StaticString const fieldName, std::string const &err)
Expected< bool, Json::Value > hasRequired(Json::Value const &params, std::initializer_list< Json::StaticString > fields, std::optional< std::string > err=std::nullopt)
Expected< Blob, Json::Value > requiredHexBlob(Json::Value const &params, Json::StaticString const fieldName, std::size_t maxLength, std::string const &err)
Expected< Issue, Json::Value > requiredIssue(Json::Value const &params, Json::StaticString const fieldName, std::string const &err)
Expected< STXChainBridge, Json::Value > parseBridgeFields(Json::Value const &params)
Unexpected< Json::Value > malformedError(std::string const &err, std::string const &message)
Expected< uint256, Json::Value > requiredUInt256(Json::Value const &params, Json::StaticString const fieldName, std::string const &err)
Unexpected< Json::Value > invalidFieldError(std::string const &err, Json::StaticString const field, std::string const &type)
Json::Value make_param_error(std::string const &message)
Returns a new json object that indicates invalid parameters.
Definition ErrorCodes.h:219
Status ledgerFromRequest(T &ledger, GRPCContext< R > const &context)
Retrieves a ledger from a gRPC request context.
void inject_error(error_code_i code, Json::Value &json)
Add or update the json update to reflect the error code.
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext const &context, Json::Value &result)
Looks up a ledger from a request and fills a Json::Value with ledger data.
Json::Value make_error(error_code_i code)
Returns a new json object that reflects the error code.
std::set< std::pair< AccountID, Slice > > makeSorted(STArray const &credentials)
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition Indexes.cpp:177
Keylet oracle(AccountID const &account, std::uint32_t const &documentID) noexcept
Definition Indexes.cpp:468
Keylet const & negativeUNL() noexcept
The (fixed) index of the object containing the ledger negativeUNL.
Definition Indexes.cpp:207
Keylet did(AccountID const &account) noexcept
Definition Indexes.cpp:462
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition Indexes.cpp:330
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition Indexes.cpp:307
Keylet loanbroker(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:510
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Definition Indexes.cpp:351
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition Indexes.cpp:424
Keylet const & amendments() noexcept
The index of the amendment table.
Definition Indexes.cpp:193
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:336
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:474
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:404
Keylet loan(uint256 const &loanBrokerID, std::uint32_t loanSeq) noexcept
Definition Indexes.cpp:516
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition Indexes.cpp:243
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:504
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 xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:448
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:486
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:418
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet const & fees() noexcept
The (fixed) index of the object containing the ledger fees.
Definition Indexes.cpp:200
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:522
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:342
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition Indexes.cpp:498
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:434
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static Expected< uint256, Json::Value > parseDirectoryNode(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseMPToken(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parsePermissionedDomain(Json::Value const &pd, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseAMM(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
auto const parseFeeSettings
static Expected< uint256, Json::Value > parseSignerList(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
static Expected< uint256, Json::Value > parseOracle(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseCheck(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition Indexes.cpp:138
static Expected< uint256, Json::Value > parseIndex(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseDID(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseLoan(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseXChainOwnedClaimID(Json::Value const &claim_id, Json::StaticString const fieldName, unsigned const apiVersion)
static bool authorized(Port const &port, std::map< std::string, std::string > const &h)
static FunctionType fixed(Keylet const &keylet)
static Expected< uint256, Json::Value > parseDelegate(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
auto const parseAmendments
static Expected< uint256, Json::Value > parseFixed(Keylet const &keylet, Json::Value const &params, Json::StaticString const &fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseLedgerHashes(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseEscrow(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseTicket(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseCredential(Json::Value const &cred, Json::StaticString const fieldName, unsigned const apiVersion)
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition Protocol.h:224
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
auto const parseNegativeUNL
static Expected< uint256, Json::Value > parsePayChannel(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseObjectID(Json::Value const &params, Json::StaticString const fieldName, std::string const &expectedType="hex string or object")
static Expected< uint256, Json::Value > parseRippleState(Json::Value const &jvRippleState, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseNFTokenPage(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< STArray, Json::Value > parseAuthorizeCredentials(Json::Value const &jv)
static Expected< uint256, Json::Value > parseOffer(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseMPTokenIssuance(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseBridge(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseDepositPreauth(Json::Value const &dp, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseVault(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
LedgerEntryType
Identifiers for on-ledger objects.
@ ltANY
A special type, matching any ledger entry type.
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:64
static Expected< uint256, Json::Value > parseNFTokenOffer(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseLoanBroker(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseAccountRoot(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
@ rpcUNEXPECTED_LEDGER_TYPE
Definition ErrorCodes.h:142
@ rpcENTRY_NOT_FOUND
Definition ErrorCodes.h:141
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:64
static Expected< uint256, Json::Value > parseXChainOwnedCreateAccountClaimID(Json::Value const &claim_id, Json::StaticString const fieldName, unsigned const apiVersion)
std::size_t constexpr maxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
Definition Protocol.h:221
Json::Value doLedgerEntry(RPC::JsonContext &)
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
uint256 key
Definition Keylet.h:20
Json::StaticString fieldName
FunctionType parseFunction
LedgerEntryType expectedType
unsigned int apiVersion
Definition Context.h:29
RequestType params
Definition Context.h:51
Json::Value params
Definition Context.h:43