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/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/CredentialHelpers.h>
11#include <xrpl/ledger/ReadView.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 Expected<uint256, Json::Value>(Json::Value const&, Json::StaticString const, unsigned const apiVersion)>;
23
26 Keylet const& keylet,
27 Json::Value const& params,
28 Json::StaticString const& fieldName,
29 unsigned const apiVersion);
30
31// Helper function to return FunctionType for objects that have a fixed
32// location. That is, they don't take parameters to compute the index.
33// e.g. amendments, fees, negative UNL, etc.
34static FunctionType
35fixed(Keylet const& keylet)
36{
37 return [keylet](Json::Value const& params, Json::StaticString const fieldName, unsigned const apiVersion)
38 -> Expected<uint256, Json::Value> { return parseFixed(keylet, params, fieldName, apiVersion); };
39}
40
41static Expected<uint256, Json::Value>
43 Json::Value const& params,
44 Json::StaticString const fieldName,
45 std::string const& expectedType = "hex string or object")
46{
47 if (auto const uNodeIndex = LedgerEntryHelpers::parse<uint256>(params))
48 {
49 return *uNodeIndex;
50 }
51 return LedgerEntryHelpers::invalidFieldError("malformedRequest", fieldName, expectedType);
52}
53
54static Expected<uint256, Json::Value>
55parseIndex(Json::Value const& params, Json::StaticString const fieldName, unsigned const apiVersion)
56{
57 if (apiVersion > 2u && params.isString())
58 {
59 std::string const index = params.asString();
60 if (index == jss::amendments.c_str())
61 return keylet::amendments().key;
62 if (index == jss::fee.c_str())
63 return keylet::fees().key;
64 if (index == jss::nunl)
65 return keylet::negativeUNL().key;
66 if (index == jss::hashes)
67 // Note this only finds the "short" skip list. Use "hashes":index to
68 // get the long list.
69 return keylet::skip().key;
70 }
71 return parseObjectID(params, fieldName, "hex string");
72}
73
74static Expected<uint256, Json::Value>
76 Json::Value const& params,
77 Json::StaticString const fieldName,
78 [[maybe_unused]] unsigned const apiVersion)
79{
80 if (auto const account = LedgerEntryHelpers::parse<AccountID>(params))
81 {
82 return keylet::account(*account).key;
83 }
84
85 return LedgerEntryHelpers::invalidFieldError("malformedAddress", fieldName, "AccountID");
86}
87
89
91parseAMM(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
92{
93 if (!params.isObject())
94 {
95 return parseObjectID(params, fieldName);
96 }
97
98 if (auto const value = LedgerEntryHelpers::hasRequired(params, {jss::asset, jss::asset2}); !value)
99 {
100 return Unexpected(value.error());
101 }
102
103 auto const asset = LedgerEntryHelpers::requiredIssue(params, jss::asset, "malformedRequest");
104 if (!asset)
105 return Unexpected(asset.error());
106
107 auto const asset2 = LedgerEntryHelpers::requiredIssue(params, jss::asset2, "malformedRequest");
108 if (!asset2)
109 return Unexpected(asset2.error());
110
111 return keylet::amm(*asset, *asset2).key;
112}
113
114static Expected<uint256, Json::Value>
115parseBridge(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
116{
117 if (!params.isMember(jss::bridge))
118 {
120 }
121
122 if (params[jss::bridge].isString())
123 {
124 return parseObjectID(params, fieldName);
125 }
126
127 auto const bridge = LedgerEntryHelpers::parseBridgeFields(params[jss::bridge]);
128 if (!bridge)
129 return Unexpected(bridge.error());
130
131 auto const account = LedgerEntryHelpers::requiredAccountID(params, jss::bridge_account, "malformedBridgeAccount");
132 if (!account)
133 return Unexpected(account.error());
134
135 STXChainBridge::ChainType const chainType = STXChainBridge::srcChain(account.value() == bridge->lockingChainDoor());
136 if (account.value() != bridge->door(chainType))
137 return LedgerEntryHelpers::malformedError("malformedRequest", "");
138
139 return keylet::bridge(*bridge, chainType).key;
140}
141
142static Expected<uint256, Json::Value>
143parseCheck(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
144{
145 return parseObjectID(params, fieldName, "hex string");
146}
147
148static Expected<uint256, Json::Value>
149parseCredential(Json::Value const& cred, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
150{
151 if (!cred.isObject())
152 {
153 return parseObjectID(cred, fieldName);
154 }
155
156 auto const subject = LedgerEntryHelpers::requiredAccountID(cred, jss::subject, "malformedRequest");
157 if (!subject)
158 return Unexpected(subject.error());
159
160 auto const issuer = LedgerEntryHelpers::requiredAccountID(cred, jss::issuer, "malformedRequest");
161 if (!issuer)
162 return Unexpected(issuer.error());
163
164 auto const credType =
165 LedgerEntryHelpers::requiredHexBlob(cred, jss::credential_type, maxCredentialTypeLength, "malformedRequest");
166 if (!credType)
167 return Unexpected(credType.error());
168
169 return keylet::credential(*subject, *issuer, Slice(credType->data(), credType->size())).key;
170}
171
172static Expected<uint256, Json::Value>
173parseDelegate(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
174{
175 if (!params.isObject())
176 {
177 return parseObjectID(params, fieldName);
178 }
179
180 auto const account = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
181 if (!account)
182 return Unexpected(account.error());
183
184 auto const authorize = LedgerEntryHelpers::requiredAccountID(params, jss::authorize, "malformedAddress");
185 if (!authorize)
186 return Unexpected(authorize.error());
187
188 return keylet::delegate(*account, *authorize).key;
189}
190
191static Expected<STArray, Json::Value>
193{
194 if (!jv.isArray())
195 {
197 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
198 }
199
200 std::uint32_t const n = jv.size();
202 {
204 "malformedAuthorizedCredentials",
205 "Invalid field '" + std::string(jss::authorized_credentials) + "', array too long."));
206 }
207
208 if (n == 0)
209 {
211 "malformedAuthorizedCredentials",
212 "Invalid field '" + std::string(jss::authorized_credentials) + "', array empty."));
213 }
214
215 STArray arr(sfAuthorizeCredentials, n);
216 for (auto const& jo : jv)
217 {
218 if (!jo.isObject())
219 {
221 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
222 }
223
224 if (auto const value = LedgerEntryHelpers::hasRequired(
225 jo, {jss::issuer, jss::credential_type}, "malformedAuthorizedCredentials");
226 !value)
227 {
228 return Unexpected(value.error());
229 }
230
231 auto const issuer = LedgerEntryHelpers::requiredAccountID(jo, jss::issuer, "malformedAuthorizedCredentials");
232 if (!issuer)
233 return Unexpected(issuer.error());
234
235 auto const credentialType = LedgerEntryHelpers::requiredHexBlob(
236 jo, jss::credential_type, maxCredentialTypeLength, "malformedAuthorizedCredentials");
237 if (!credentialType)
238 return Unexpected(credentialType.error());
239
240 auto credential = STObject::makeInnerObject(sfCredential);
241 credential.setAccountID(sfIssuer, *issuer);
242 credential.setFieldVL(sfCredentialType, *credentialType);
243 arr.push_back(std::move(credential));
244 }
245
246 return arr;
247}
248
249static Expected<uint256, Json::Value>
251 Json::Value const& dp,
252 Json::StaticString const fieldName,
253 [[maybe_unused]] unsigned const apiVersion)
254{
255 if (!dp.isObject())
256 {
257 return parseObjectID(dp, fieldName);
258 }
259
260 if ((dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)))
261 {
263 "malformedRequest",
264 "Must have exactly one of `authorized` and "
265 "`authorized_credentials`.");
266 }
267
268 auto const owner = LedgerEntryHelpers::requiredAccountID(dp, jss::owner, "malformedOwner");
269 if (!owner)
270 {
271 return Unexpected(owner.error());
272 }
273
274 if (dp.isMember(jss::authorized))
275 {
276 if (auto const authorized = LedgerEntryHelpers::parse<AccountID>(dp[jss::authorized]))
277 {
278 return keylet::depositPreauth(*owner, *authorized).key;
279 }
280 return LedgerEntryHelpers::invalidFieldError("malformedAuthorized", jss::authorized, "AccountID");
281 }
282
283 auto const& ac(dp[jss::authorized_credentials]);
284 auto const arr = parseAuthorizeCredentials(ac);
285 if (!arr.has_value())
286 return Unexpected(arr.error());
287
288 auto const& sorted = credentials::makeSorted(arr.value());
289 if (sorted.empty())
290 {
291 // TODO: this error message is bad/inaccurate
293 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
294 }
295
296 return keylet::depositPreauth(*owner, std::move(sorted)).key;
297}
298
299static Expected<uint256, Json::Value>
300parseDID(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
301{
302 auto const account = LedgerEntryHelpers::parse<AccountID>(params);
303 if (!account)
304 {
305 return LedgerEntryHelpers::invalidFieldError("malformedAddress", fieldName, "AccountID");
306 }
307
308 return keylet::did(*account).key;
309}
310
311static Expected<uint256, Json::Value>
313 Json::Value const& params,
314 Json::StaticString const fieldName,
315 [[maybe_unused]] unsigned const apiVersion)
316{
317 if (!params.isObject())
318 {
319 return parseObjectID(params, fieldName);
320 }
321
322 if (params.isMember(jss::sub_index) &&
323 (!params[jss::sub_index].isConvertibleTo(Json::uintValue) || params[jss::sub_index].isBool()))
324 {
325 return LedgerEntryHelpers::invalidFieldError("malformedRequest", jss::sub_index, "number");
326 }
327
328 if (params.isMember(jss::owner) == params.isMember(jss::dir_root))
329 {
331 "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
332 }
333
334 std::uint64_t uSubIndex = params.get(jss::sub_index, 0).asUInt();
335
336 if (params.isMember(jss::dir_root))
337 {
338 if (auto const uDirRoot = LedgerEntryHelpers::parse<uint256>(params[jss::dir_root]))
339 {
340 return keylet::page(*uDirRoot, uSubIndex).key;
341 }
342
343 return LedgerEntryHelpers::invalidFieldError("malformedDirRoot", jss::dir_root, "hash");
344 }
345
346 if (params.isMember(jss::owner))
347 {
348 auto const ownerID = LedgerEntryHelpers::parse<AccountID>(params[jss::owner]);
349 if (!ownerID)
350 {
351 return LedgerEntryHelpers::invalidFieldError("malformedAddress", jss::owner, "AccountID");
352 }
353
354 return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key;
355 }
356
357 return LedgerEntryHelpers::malformedError("malformedRequest", "");
358}
359
360static Expected<uint256, Json::Value>
361parseEscrow(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
362{
363 if (!params.isObject())
364 {
365 return parseObjectID(params, fieldName);
366 }
367
368 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
369 if (!id)
370 return Unexpected(id.error());
371 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq");
372 if (!seq)
373 return Unexpected(seq.error());
374
375 return keylet::escrow(*id, *seq).key;
376}
377
379
382 Keylet const& keylet,
383 Json::Value const& params,
384 Json::StaticString const& fieldName,
385 [[maybe_unused]] unsigned const apiVersion)
386{
387 if (!params.isBool())
388 {
389 return parseObjectID(params, fieldName, "hex string");
390 }
391 if (!params.asBool())
392 {
393 return LedgerEntryHelpers::invalidFieldError("invalidParams", fieldName, "true");
394 }
395
396 return keylet.key;
397}
398
399static Expected<uint256, Json::Value>
400parseLedgerHashes(Json::Value const& params, Json::StaticString const fieldName, unsigned const apiVersion)
401{
402 if (params.isUInt() || params.isInt())
403 {
404 // If the index doesn't parse as a UInt, throw
405 auto const index = params.asUInt();
406
407 // Return the "long" skip list for the given ledger index.
408 auto const keylet = keylet::skip(index);
409 return keylet.key;
410 }
411 // Return the key in `params` or the "short" skip list, which contains
412 // hashes since the last flag ledger.
413 return parseFixed(keylet::skip(), params, fieldName, apiVersion);
414}
415
416static Expected<uint256, Json::Value>
418 Json::Value const& params,
419 Json::StaticString const fieldName,
420 [[maybe_unused]] unsigned const apiVersion)
421{
422 if (!params.isObject())
423 {
424 return parseObjectID(params, fieldName, "hex string");
425 }
426
427 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
428 if (!id)
429 return Unexpected(id.error());
430 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq");
431 if (!seq)
432 return Unexpected(seq.error());
433
434 return keylet::loanbroker(*id, *seq).key;
435}
436
437static Expected<uint256, Json::Value>
438parseLoan(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
439{
440 if (!params.isObject())
441 {
442 return parseObjectID(params, fieldName, "hex string");
443 }
444
445 auto const id = LedgerEntryHelpers::requiredUInt256(params, jss::loan_broker_id, "malformedBroker");
446 if (!id)
447 return Unexpected(id.error());
448 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::loan_seq, "malformedSeq");
449 if (!seq)
450 return Unexpected(seq.error());
451
452 return keylet::loan(*id, *seq).key;
453}
454
455static Expected<uint256, Json::Value>
456parseMPToken(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
457{
458 if (!params.isObject())
459 {
460 return parseObjectID(params, fieldName);
461 }
462
463 auto const mptIssuanceID =
464 LedgerEntryHelpers::requiredUInt192(params, jss::mpt_issuance_id, "malformedMPTIssuanceID");
465 if (!mptIssuanceID)
466 return Unexpected(mptIssuanceID.error());
467
468 auto const account = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount");
469 if (!account)
470 return Unexpected(account.error());
471
472 return keylet::mptoken(*mptIssuanceID, *account).key;
473}
474
475static Expected<uint256, Json::Value>
477 Json::Value const& params,
478 Json::StaticString const fieldName,
479 [[maybe_unused]] unsigned const apiVersion)
480{
481 auto const mptIssuanceID = LedgerEntryHelpers::parse<uint192>(params);
482 if (!mptIssuanceID)
483 return LedgerEntryHelpers::invalidFieldError("malformedMPTokenIssuance", fieldName, "Hash192");
484
485 return keylet::mptIssuance(*mptIssuanceID).key;
486}
487
488static Expected<uint256, Json::Value>
490 Json::Value const& params,
491 Json::StaticString const fieldName,
492 [[maybe_unused]] unsigned const apiVersion)
493{
494 return parseObjectID(params, fieldName, "hex string");
495}
496
497static Expected<uint256, Json::Value>
499 Json::Value const& params,
500 Json::StaticString const fieldName,
501 [[maybe_unused]] unsigned const apiVersion)
502{
503 return parseObjectID(params, fieldName, "hex string");
504}
505
507
509parseOffer(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
510{
511 if (!params.isObject())
512 {
513 return parseObjectID(params, fieldName);
514 }
515
516 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
517 if (!id)
518 return Unexpected(id.error());
519
520 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest");
521 if (!seq)
522 return Unexpected(seq.error());
523
524 return keylet::offer(*id, *seq).key;
525}
526
527static Expected<uint256, Json::Value>
528parseOracle(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
529{
530 if (!params.isObject())
531 {
532 return parseObjectID(params, fieldName);
533 }
534
535 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount");
536 if (!id)
537 return Unexpected(id.error());
538
539 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::oracle_document_id, "malformedDocumentID");
540 if (!seq)
541 return Unexpected(seq.error());
542
543 return keylet::oracle(*id, *seq).key;
544}
545
546static Expected<uint256, Json::Value>
548 Json::Value const& params,
549 Json::StaticString const fieldName,
550 [[maybe_unused]] unsigned const apiVersion)
551{
552 return parseObjectID(params, fieldName, "hex string");
553}
554
555static Expected<uint256, Json::Value>
557 Json::Value const& pd,
558 Json::StaticString const fieldName,
559 [[maybe_unused]] unsigned const apiVersion)
560{
561 if (pd.isString())
562 {
563 return parseObjectID(pd, fieldName);
564 }
565
566 if (!pd.isObject())
567 {
568 return LedgerEntryHelpers::invalidFieldError("malformedRequest", fieldName, "hex string or object");
569 }
570
571 auto const account = LedgerEntryHelpers::requiredAccountID(pd, jss::account, "malformedAddress");
572 if (!account)
573 return Unexpected(account.error());
574
575 auto const seq = LedgerEntryHelpers::requiredUInt32(pd, jss::seq, "malformedRequest");
576 if (!seq)
577 return Unexpected(seq.error());
578
579 return keylet::permissionedDomain(*account, pd[jss::seq].asUInt()).key;
580}
581
582static Expected<uint256, Json::Value>
584 Json::Value const& jvRippleState,
585 Json::StaticString const fieldName,
586 [[maybe_unused]] unsigned const apiVersion)
587{
588 Currency uCurrency;
589
590 if (!jvRippleState.isObject())
591 {
592 return parseObjectID(jvRippleState, fieldName);
593 }
594
595 if (auto const value = LedgerEntryHelpers::hasRequired(jvRippleState, {jss::currency, jss::accounts}); !value)
596 {
597 return Unexpected(value.error());
598 }
599
600 if (!jvRippleState[jss::accounts].isArray() || jvRippleState[jss::accounts].size() != 2)
601 {
602 return LedgerEntryHelpers::invalidFieldError("malformedRequest", jss::accounts, "length-2 array of Accounts");
603 }
604
605 auto const id1 = LedgerEntryHelpers::parse<AccountID>(jvRippleState[jss::accounts][0u]);
606 auto const id2 = LedgerEntryHelpers::parse<AccountID>(jvRippleState[jss::accounts][1u]);
607 if (!id1 || !id2)
608 {
609 return LedgerEntryHelpers::invalidFieldError("malformedAddress", jss::accounts, "array of Accounts");
610 }
611 if (id1 == id2)
612 {
613 return LedgerEntryHelpers::malformedError("malformedRequest", "Cannot have a trustline to self.");
614 }
615
616 if (!jvRippleState[jss::currency].isString() || jvRippleState[jss::currency] == "" ||
617 !to_currency(uCurrency, jvRippleState[jss::currency].asString()))
618 {
619 return LedgerEntryHelpers::invalidFieldError("malformedCurrency", jss::currency, "Currency");
620 }
621
622 return keylet::line(*id1, *id2, uCurrency).key;
623}
624
625static Expected<uint256, Json::Value>
627 Json::Value const& params,
628 Json::StaticString const fieldName,
629 [[maybe_unused]] unsigned const apiVersion)
630{
631 return parseObjectID(params, fieldName, "hex string");
632}
633
634static Expected<uint256, Json::Value>
635parseTicket(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
636{
637 if (!params.isObject())
638 {
639 return parseObjectID(params, fieldName);
640 }
641
642 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
643 if (!id)
644 return Unexpected(id.error());
645
646 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::ticket_seq, "malformedRequest");
647 if (!seq)
648 return Unexpected(seq.error());
649
650 return getTicketIndex(*id, *seq);
651}
652
653static Expected<uint256, Json::Value>
654parseVault(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
655{
656 if (!params.isObject())
657 {
658 return parseObjectID(params, fieldName);
659 }
660
661 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
662 if (!id)
663 return Unexpected(id.error());
664
665 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest");
666 if (!seq)
667 return Unexpected(seq.error());
668
669 return keylet::vault(*id, *seq).key;
670}
671
672static Expected<uint256, Json::Value>
674 Json::Value const& claim_id,
675 Json::StaticString const fieldName,
676 [[maybe_unused]] unsigned const apiVersion)
677{
678 if (!claim_id.isObject())
679 {
680 return parseObjectID(claim_id, fieldName);
681 }
682
683 auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id);
684 if (!bridge_spec)
685 return Unexpected(bridge_spec.error());
686
687 auto const seq =
688 LedgerEntryHelpers::requiredUInt32(claim_id, jss::xchain_owned_claim_id, "malformedXChainOwnedClaimID");
689 if (!seq)
690 {
691 return Unexpected(seq.error());
692 }
693
694 Keylet keylet = keylet::xChainClaimID(*bridge_spec, *seq);
695 return keylet.key;
696}
697
698static Expected<uint256, Json::Value>
700 Json::Value const& claim_id,
701 Json::StaticString const fieldName,
702 [[maybe_unused]] unsigned const apiVersion)
703{
704 if (!claim_id.isObject())
705 {
706 return parseObjectID(claim_id, fieldName);
707 }
708
709 auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id);
710 if (!bridge_spec)
711 return Unexpected(bridge_spec.error());
712
713 auto const seq = LedgerEntryHelpers::requiredUInt32(
714 claim_id, jss::xchain_owned_create_account_claim_id, "malformedXChainOwnedCreateAccountClaimID");
715 if (!seq)
716 {
717 return Unexpected(seq.error());
718 }
719
720 Keylet keylet = keylet::xChainCreateAccountClaimID(*bridge_spec, *seq);
721 return keylet.key;
722}
723
730
731// {
732// ledger_hash : <ledger>
733// ledger_index : <ledger_index>
734// ...
735// }
738{
739 static auto ledgerEntryParsers = std::to_array<LedgerEntry>({
740#pragma push_macro("LEDGER_ENTRY")
741#undef LEDGER_ENTRY
742
743#define LEDGER_ENTRY(tag, value, name, rpcName, fields) {jss::rpcName, parse##name, tag},
744
745#include <xrpl/protocol/detail/ledger_entries.macro>
746
747#undef LEDGER_ENTRY
748#pragma pop_macro("LEDGER_ENTRY")
749 {jss::index, parseIndex, ltANY},
750 // aliases
751 {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT},
752 {jss::ripple_state, parseRippleState, ltRIPPLE_STATE},
753 });
754
755 auto const hasMoreThanOneMember = [&]() {
756 int count = 0;
757
758 for (auto const& ledgerEntry : ledgerEntryParsers)
759 {
760 if (context.params.isMember(ledgerEntry.fieldName))
761 {
762 count++;
763 if (count > 1) // Early exit if more than one is found
764 return true;
765 }
766 }
767 return false; // Return false if <= 1 is found
768 }();
769
770 if (hasMoreThanOneMember)
771 {
772 return RPC::make_param_error("Too many fields provided.");
773 }
774
776 auto jvResult = RPC::lookupLedger(lpLedger, context);
777
778 if (!lpLedger)
779 return jvResult;
780
781 uint256 uNodeIndex;
782 LedgerEntryType expectedType = ltANY;
783
784 try
785 {
786 bool found = false;
787 for (auto const& ledgerEntry : ledgerEntryParsers)
788 {
789 if (context.params.isMember(ledgerEntry.fieldName))
790 {
791 expectedType = ledgerEntry.expectedType;
792 // `Bridge` is the only type that involves two fields at the
793 // `ledger_entry` param level.
794 // So that parser needs to have the whole `params` field.
795 // All other parsers only need the one field name's info.
796 Json::Value const& params =
797 ledgerEntry.fieldName == jss::bridge ? context.params : context.params[ledgerEntry.fieldName];
798 auto const result = ledgerEntry.parseFunction(params, ledgerEntry.fieldName, context.apiVersion);
799 if (!result)
800 return result.error();
801
802 uNodeIndex = result.value();
803 found = true;
804 break;
805 }
806 }
807 if (!found)
808 {
809 if (context.apiVersion < 2u)
810 {
811 jvResult[jss::error] = "unknownOption";
812 return jvResult;
813 }
814 return RPC::make_param_error("No ledger_entry params provided.");
815 }
816 }
817 catch (Json::error& e)
818 {
819 if (context.apiVersion > 1u)
820 {
821 // For apiVersion 2 onwards, any parsing failures that throw
822 // this exception return an invalidParam error.
824 }
825 else
826 throw;
827 }
828
829 // Return the computed index regardless of whether the node exists.
830 jvResult[jss::index] = to_string(uNodeIndex);
831
832 if (uNodeIndex.isZero())
833 {
835 return jvResult;
836 }
837
838 auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
839
840 bool bNodeBinary = false;
841 if (context.params.isMember(jss::binary))
842 bNodeBinary = context.params[jss::binary].asBool();
843
844 if (!sleNode)
845 {
846 // Not found.
848 return jvResult;
849 }
850
851 if ((expectedType != ltANY) && (expectedType != sleNode->getType()))
852 {
854 return jvResult;
855 }
856
857 if (bNodeBinary)
858 {
859 Serializer s;
860
861 sleNode->add(s);
862
863 jvResult[jss::node_binary] = strHex(s.peekData());
864 }
865 else
866 {
867 jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
868 }
869
870 return jvResult;
871}
872
875{
876 org::xrpl::rpc::v1::GetLedgerEntryRequest& request = context.params;
877 org::xrpl::rpc::v1::GetLedgerEntryResponse response;
878 grpc::Status status = grpc::Status::OK;
879
881 if (auto status = RPC::ledgerFromRequest(ledger, context))
882 {
883 grpc::Status errorStatus;
884 if (status.toErrorCode() == rpcINVALID_PARAMS)
885 {
886 errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
887 }
888 else
889 {
890 errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
891 }
892 return {response, errorStatus};
893 }
894
895 auto const key = uint256::fromVoidChecked(request.key());
896 if (!key)
897 {
898 grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
899 return {response, errorStatus};
900 }
901
902 auto const sleNode = ledger->read(keylet::unchecked(*key));
903 if (!sleNode)
904 {
905 grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "object not found"};
906 return {response, errorStatus};
907 }
908
909 Serializer s;
910 sleNode->add(s);
911
912 auto& stateObject = *response.mutable_ledger_object();
913 stateObject.set_data(s.peekData().data(), s.getLength());
914 stateObject.set_key(request.key());
915 *(response.mutable_ledger()) = request.ledger();
916 return {response, status};
917}
918} // 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:187
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:72
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:298
bool isZero() const
Definition base_uint.h:508
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:214
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:172
Keylet oracle(AccountID const &account, std::uint32_t const &documentID) noexcept
Definition Indexes.cpp:456
Keylet const & negativeUNL() noexcept
The (fixed) index of the object containing the ledger negativeUNL.
Definition Indexes.cpp:201
Keylet did(AccountID const &account) noexcept
Definition Indexes.cpp:450
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition Indexes.cpp:319
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition Indexes.cpp:299
Keylet loanbroker(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:498
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Definition Indexes.cpp:340
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition Indexes.cpp:412
Keylet const & amendments() noexcept
The index of the amendment table.
Definition Indexes.cpp:187
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:325
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:462
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:393
Keylet loan(uint256 const &loanBrokerID, std::uint32_t loanSeq) noexcept
Definition Indexes.cpp:504
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition Indexes.cpp:235
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:492
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:214
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:436
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:474
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:406
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
Keylet const & fees() noexcept
The (fixed) index of the object containing the ledger fees.
Definition Indexes.cpp:194
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:510
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:331
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition Indexes.cpp:486
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:422
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:597
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:133
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:223
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:62
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:220
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