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