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 if (!params.isObject())
184 {
185 return parseObjectID(params, fieldName);
186 }
187
188 auto const account =
189 ledger_entry_helpers::requiredAccountID(params, jss::account, "malformedAddress");
190 if (!account)
191 return std::unexpected(account.error());
192
193 auto const sequence =
194 ledger_entry_helpers::requiredUInt32(params, jss::seq, "malformedRequest");
195 if (!sequence)
196 return std::unexpected(sequence.error());
197
198 auto const seqProxy = SeqProxy::rawSequence(*sequence);
199 return keylet::check(*account, seqProxy).key;
200}
201
202static std::expected<UInt256, json::Value>
204 json::Value const& cred,
205 json::StaticString const fieldName,
206 [[maybe_unused]] unsigned const apiVersion)
207{
208 if (!cred.isObject())
209 {
210 return parseObjectID(cred, fieldName);
211 }
212
213 auto const subject =
214 ledger_entry_helpers::requiredAccountID(cred, jss::subject, "malformedRequest");
215 if (!subject)
216 return std::unexpected(subject.error());
217
218 auto const issuer =
219 ledger_entry_helpers::requiredAccountID(cred, jss::issuer, "malformedRequest");
220 if (!issuer)
221 return std::unexpected(issuer.error());
222
223 auto const credType = ledger_entry_helpers::requiredHexBlob(
224 cred, jss::credential_type, kMaxCredentialTypeLength, "malformedRequest");
225 if (!credType)
226 return std::unexpected(credType.error());
227
228 return keylet::credential(*subject, *issuer, Slice(credType->data(), credType->size())).key;
229}
230
231static std::expected<UInt256, json::Value>
233 json::Value const& params,
234 json::StaticString const fieldName,
235 [[maybe_unused]] unsigned const apiVersion)
236{
237 if (!params.isObject())
238 {
239 return parseObjectID(params, fieldName);
240 }
241
242 auto const account =
243 ledger_entry_helpers::requiredAccountID(params, jss::account, "malformedAddress");
244 if (!account)
245 return std::unexpected(account.error());
246
247 auto const authorize =
248 ledger_entry_helpers::requiredAccountID(params, jss::authorize, "malformedAddress");
249 if (!authorize)
250 return std::unexpected(authorize.error());
251
252 return keylet::delegate(*account, *authorize).key;
253}
254
255static std::expected<STArray, json::Value>
257{
258 if (!jv.isArray())
259 {
261 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
262 }
263
264 std::uint32_t const n = jv.size();
266 {
267 return std::unexpected(
269 "malformedAuthorizedCredentials",
270 "Invalid field '" + std::string(jss::authorized_credentials) +
271 "', array too long."));
272 }
273
274 if (n == 0)
275 {
276 return std::unexpected(
278 "malformedAuthorizedCredentials",
279 "Invalid field '" + std::string(jss::authorized_credentials) + "', array empty."));
280 }
281
282 STArray arr(sfAuthorizeCredentials, n);
283 for (auto const& jo : jv)
284 {
285 if (!jo.isObject())
286 {
288 "malformedAuthorizedCredentials", jss::authorized_credentials, "array of objects");
289 }
290
291 if (auto const value = ledger_entry_helpers::hasRequired(
292 jo, {jss::issuer, jss::credential_type}, "malformedAuthorizedCredentials");
293 !value)
294 {
295 return std::unexpected(value.error());
296 }
297
298 auto const issuer = ledger_entry_helpers::requiredAccountID(
299 jo, jss::issuer, "malformedAuthorizedCredentials");
300 if (!issuer)
301 return std::unexpected(issuer.error());
302
303 auto const credentialType = ledger_entry_helpers::requiredHexBlob(
304 jo, jss::credential_type, kMaxCredentialTypeLength, "malformedAuthorizedCredentials");
305 if (!credentialType)
306 return std::unexpected(credentialType.error());
307
308 auto credential = STObject::makeInnerObject(sfCredential);
309 credential.setAccountID(sfIssuer, *issuer);
310 credential.setFieldVL(sfCredentialType, *credentialType);
311 arr.pushBack(std::move(credential));
312 }
313
314 return arr;
315}
316
317static std::expected<UInt256, json::Value>
319 json::Value const& dp,
320 json::StaticString const fieldName,
321 [[maybe_unused]] unsigned const apiVersion)
322{
323 if (!dp.isObject())
324 {
325 return parseObjectID(dp, fieldName);
326 }
327
328 if ((dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)))
329 {
331 "malformedRequest",
332 "Must have exactly one of `authorized` and "
333 "`authorized_credentials`.");
334 }
335
336 auto const owner = ledger_entry_helpers::requiredAccountID(dp, jss::owner, "malformedOwner");
337 if (!owner)
338 {
339 return std::unexpected(owner.error());
340 }
341
342 if (dp.isMember(jss::authorized))
343 {
344 if (auto const authorized = ledger_entry_helpers::parse<AccountID>(dp[jss::authorized]))
345 {
346 return keylet::depositPreauth(*owner, *authorized).key;
347 }
349 "malformedAuthorized", jss::authorized, "AccountID");
350 }
351
352 auto const& ac(dp[jss::authorized_credentials]);
353 auto const arr = parseAuthorizeCredentials(ac);
354 if (!arr.has_value())
355 return std::unexpected(arr.error());
356
357 auto const& sorted = credentials::makeSorted(arr.value());
358 if (sorted.empty())
359 {
360 // TODO: this error message is bad/inaccurate
362 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
363 }
364
365 return keylet::depositPreauth(*owner, sorted).key;
366}
367
368static std::expected<UInt256, json::Value>
370 json::Value const& params,
371 json::StaticString const fieldName,
372 [[maybe_unused]] unsigned const apiVersion)
373{
374 auto const account = ledger_entry_helpers::parse<AccountID>(params);
375 if (!account)
376 {
377 return ledger_entry_helpers::invalidFieldError("malformedAddress", fieldName, "AccountID");
378 }
379
380 return keylet::did(*account).key;
381}
382
383static std::expected<UInt256, json::Value>
385 json::Value const& params,
386 json::StaticString const fieldName,
387 [[maybe_unused]] unsigned const apiVersion)
388{
389 if (!params.isObject())
390 {
391 return parseObjectID(params, fieldName);
392 }
393
394 if (params.isMember(jss::sub_index) &&
395 (!params[jss::sub_index].isConvertibleTo(json::ValueType::UInt) ||
396 params[jss::sub_index].isBool()))
397 {
399 "malformedRequest", jss::sub_index, "number");
400 }
401
402 if (params.isMember(jss::owner) == params.isMember(jss::dir_root))
403 {
405 "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
406 }
407
408 std::uint64_t const uSubIndex = params.get(jss::sub_index, 0).asUInt();
409
410 if (params.isMember(jss::dir_root))
411 {
412 if (auto const uDirRoot = ledger_entry_helpers::parse<UInt256>(params[jss::dir_root]))
413 {
414 return keylet::page(*uDirRoot, uSubIndex).key;
415 }
416
417 return ledger_entry_helpers::invalidFieldError("malformedDirRoot", jss::dir_root, "hash");
418 }
419
420 if (params.isMember(jss::owner))
421 {
422 auto const ownerID = ledger_entry_helpers::parse<AccountID>(params[jss::owner]);
423 if (!ownerID)
424 {
426 "malformedAddress", jss::owner, "AccountID");
427 }
428
429 return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key;
430 }
431
432 return ledger_entry_helpers::malformedError("malformedRequest", "");
433}
434
435static std::expected<UInt256, json::Value>
437 json::Value const& params,
438 json::StaticString const fieldName,
439 [[maybe_unused]] unsigned const apiVersion)
440{
441 if (!params.isObject())
442 {
443 return parseObjectID(params, fieldName);
444 }
445
446 auto const id = ledger_entry_helpers::requiredAccountID(params, jss::owner, "malformedOwner");
447 if (!id)
448 return std::unexpected(id.error());
449 auto const seq = ledger_entry_helpers::requiredUInt32(params, jss::seq, "malformedSeq");
450 if (!seq)
451 return std::unexpected(seq.error());
452
453 auto const seqProxy = SeqProxy::rawSequence(*seq);
454 return keylet::escrow(*id, seqProxy).key;
455}
456
458
459static std::expected<UInt256, json::Value>
461 Keylet const& keylet,
462 json::Value const& params,
463 json::StaticString const& fieldName,
464 [[maybe_unused]] unsigned const apiVersion)
465{
466 if (!params.isBool())
467 {
468 return parseObjectID(params, fieldName, "hex string");
469 }
470 if (!params.asBool())
471 {
472 return ledger_entry_helpers::invalidFieldError("invalidParams", fieldName, "true");
473 }
474
475 return keylet.key;
476}
477
478static std::expected<UInt256, json::Value>
480 json::Value const& params,
481 json::StaticString const fieldName,
482 unsigned const apiVersion)
483{
484 if (params.isUInt() || params.isInt())
485 {
486 // If the index doesn't parse as a UInt, throw
487 auto const index = params.asUInt();
488
489 // Return the "long" skip list for the given ledger index.
490 auto const keylet = keylet::skip(index);
491 return keylet.key;
492 }
493 // Return the key in `params` or the "short" skip list, which contains
494 // hashes since the last flag ledger.
495 return parseFixed(keylet::skip(), params, fieldName, apiVersion);
496}
497
498static std::expected<UInt256, json::Value>
500 json::Value const& params,
501 json::StaticString const fieldName,
502 [[maybe_unused]] unsigned const apiVersion)
503{
504 if (!params.isObject())
505 {
506 return parseObjectID(params, fieldName, "hex string");
507 }
508
509 auto const id = ledger_entry_helpers::requiredAccountID(params, jss::owner, "malformedOwner");
510 if (!id)
511 return std::unexpected(id.error());
512 auto const seq = ledger_entry_helpers::requiredUInt32(params, jss::seq, "malformedSeq");
513 if (!seq)
514 return std::unexpected(seq.error());
515
516 auto const seqProxy = SeqProxy::rawSequence(*seq);
517 return keylet::loanBroker(*id, seqProxy).key;
518}
519
520static std::expected<UInt256, json::Value>
522 json::Value const& params,
523 json::StaticString const fieldName,
524 [[maybe_unused]] unsigned const apiVersion)
525{
526 if (!params.isObject())
527 {
528 return parseObjectID(params, fieldName, "hex string");
529 }
530
531 auto const id =
532 ledger_entry_helpers::requiredUInt256(params, jss::loan_broker_id, "malformedBroker");
533 if (!id)
534 return std::unexpected(id.error());
535 auto const seq = ledger_entry_helpers::requiredUInt32(params, jss::loan_seq, "malformedSeq");
536 if (!seq)
537 return std::unexpected(seq.error());
538
539 auto const seqProxy = SeqProxy::rawSequence(*seq);
540 return keylet::loan(*id, seqProxy).key;
541}
542
543static std::expected<UInt256, json::Value>
545 json::Value const& params,
546 json::StaticString const fieldName,
547 [[maybe_unused]] unsigned const apiVersion)
548{
549 if (!params.isObject())
550 {
551 return parseObjectID(params, fieldName);
552 }
553
554 auto const mptIssuanceID = ledger_entry_helpers::requiredUInt192(
555 params, jss::mpt_issuance_id, "malformedMPTIssuanceID");
556 if (!mptIssuanceID)
557 return std::unexpected(mptIssuanceID.error());
558
559 auto const account =
560 ledger_entry_helpers::requiredAccountID(params, jss::account, "malformedAccount");
561 if (!account)
562 return std::unexpected(account.error());
563
564 return keylet::mptoken(*mptIssuanceID, *account).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 auto const mptIssuanceID = ledger_entry_helpers::parse<UInt192>(params);
574 if (!mptIssuanceID)
575 {
577 "malformedMPTokenIssuance", fieldName, "Hash192");
578 }
579
580 return keylet::mptokenIssuance(*mptIssuanceID).key;
581}
582
583static std::expected<UInt256, json::Value>
585 json::Value const& params,
586 json::StaticString const fieldName,
587 [[maybe_unused]] unsigned const apiVersion)
588{
589 if (!params.isObject())
590 {
591 return parseObjectID(params, fieldName);
592 }
593
594 auto const owner =
595 ledger_entry_helpers::requiredAccountID(params, jss::owner, "malformedOwner");
596 if (!owner)
597 return std::unexpected(owner.error());
598
599 auto const sequence =
600 ledger_entry_helpers::requiredUInt32(params, jss::seq, "malformedRequest");
601 if (!sequence)
602 return std::unexpected(sequence.error());
603
604 auto const seqProxy = SeqProxy::rawSequence(*sequence);
605 return keylet::nftokenOffer(*owner, seqProxy).key;
606}
607
608static std::expected<UInt256, json::Value>
610 json::Value const& params,
611 json::StaticString const fieldName,
612 [[maybe_unused]] unsigned const apiVersion)
613{
614 return parseObjectID(params, fieldName, "hex string");
615}
616
618
619static std::expected<UInt256, json::Value>
621 json::Value const& params,
622 json::StaticString const fieldName,
623 [[maybe_unused]] unsigned const apiVersion)
624{
625 if (!params.isObject())
626 {
627 return parseObjectID(params, fieldName);
628 }
629
630 auto const id =
631 ledger_entry_helpers::requiredAccountID(params, jss::account, "malformedAddress");
632 if (!id)
633 return std::unexpected(id.error());
634
635 auto const seq = ledger_entry_helpers::requiredUInt32(params, jss::seq, "malformedRequest");
636 if (!seq)
637 return std::unexpected(seq.error());
638
639 auto const seqProxy = SeqProxy::rawSequence(*seq);
640 return keylet::offer(*id, seqProxy).key;
641}
642
643static std::expected<UInt256, json::Value>
645 json::Value const& params,
646 json::StaticString const fieldName,
647 [[maybe_unused]] unsigned const apiVersion)
648{
649 if (!params.isObject())
650 {
651 return parseObjectID(params, fieldName);
652 }
653
654 auto const id =
655 ledger_entry_helpers::requiredAccountID(params, jss::account, "malformedAccount");
656 if (!id)
657 return std::unexpected(id.error());
658
660 params, jss::oracle_document_id, "malformedDocumentID");
661 if (!seq)
662 return std::unexpected(seq.error());
663
664 return keylet::oracle(*id, *seq).key;
665}
666
667static std::expected<UInt256, json::Value>
669 json::Value const& params,
670 json::StaticString const fieldName,
671 [[maybe_unused]] unsigned const apiVersion)
672{
673 if (!params.isObject())
674 {
675 return parseObjectID(params, fieldName);
676 }
677
678 auto const account =
679 ledger_entry_helpers::requiredAccountID(params, jss::account, "malformedAddress");
680 if (!account)
681 return std::unexpected(account.error());
682
683 auto const destination =
684 ledger_entry_helpers::requiredAccountID(params, jss::destination, "malformedAddress");
685 if (!destination)
686 return std::unexpected(destination.error());
687
688 auto const sequence =
689 ledger_entry_helpers::requiredUInt32(params, jss::seq, "malformedRequest");
690 if (!sequence)
691 return std::unexpected(sequence.error());
692
693 auto const seqProxy = SeqProxy::rawSequence(*sequence);
694 return keylet::payChannel(*account, *destination, seqProxy).key;
695}
696
697static std::expected<UInt256, json::Value>
699 json::Value const& pd,
700 json::StaticString const fieldName,
701 [[maybe_unused]] unsigned const apiVersion)
702{
703 if (pd.isString())
704 {
705 return parseObjectID(pd, fieldName);
706 }
707
708 if (!pd.isObject())
709 {
711 "malformedRequest", fieldName, "hex string or object");
712 }
713
714 auto const account =
715 ledger_entry_helpers::requiredAccountID(pd, jss::account, "malformedAddress");
716 if (!account)
717 return std::unexpected(account.error());
718
719 auto const seq = ledger_entry_helpers::requiredUInt32(pd, jss::seq, "malformedRequest");
720 if (!seq)
721 return std::unexpected(seq.error());
722
723 auto const seqProxy = SeqProxy::rawSequence(pd[jss::seq].asUInt());
724 return keylet::permissionedDomain(*account, seqProxy).key;
725}
726
727static std::expected<UInt256, json::Value>
729 json::Value const& jvRippleState,
730 json::StaticString const fieldName,
731 [[maybe_unused]] unsigned const apiVersion)
732{
733 Currency uCurrency;
734
735 if (!jvRippleState.isObject())
736 {
737 return parseObjectID(jvRippleState, fieldName);
738 }
739
740 if (auto const value =
741 ledger_entry_helpers::hasRequired(jvRippleState, {jss::currency, jss::accounts});
742 !value)
743 {
744 return std::unexpected(value.error());
745 }
746
747 if (!jvRippleState[jss::accounts].isArray() || jvRippleState[jss::accounts].size() != 2)
748 {
750 "malformedRequest", jss::accounts, "length-2 array of Accounts");
751 }
752
753 auto const id1 = ledger_entry_helpers::parse<AccountID>(jvRippleState[jss::accounts][0u]);
754 auto const id2 = ledger_entry_helpers::parse<AccountID>(jvRippleState[jss::accounts][1u]);
755 if (!id1 || !id2)
756 {
758 "malformedAddress", jss::accounts, "array of Accounts");
759 }
760 if (id1 == id2)
761 {
763 "malformedRequest", "Cannot have a trustline to self.");
764 }
765
766 if (!jvRippleState[jss::currency].isString() || jvRippleState[jss::currency] == "" ||
767 !toCurrency(uCurrency, jvRippleState[jss::currency].asString()))
768 {
770 "malformedCurrency", jss::currency, "Currency");
771 }
772
773 return keylet::trustLine(*id1, *id2, uCurrency).key;
774}
775
776static std::expected<UInt256, json::Value>
778 json::Value const& params,
779 json::StaticString const fieldName,
780 [[maybe_unused]] unsigned const apiVersion)
781{
782 if (!params.isObject())
783 {
784 return parseObjectID(params, fieldName);
785 }
786
787 auto const id =
788 ledger_entry_helpers::requiredAccountID(params, jss::account, "malformedAddress");
789 if (!id)
790 return std::unexpected(id.error());
791
792 return keylet::signerList(*id).key;
793}
794
795static std::expected<UInt256, json::Value>
797 json::Value const& params,
798 json::StaticString const fieldName,
799 [[maybe_unused]] unsigned const apiVersion)
800{
801 if (!params.isObject())
802 return parseObjectID(params, fieldName);
803
804 auto const sponsorID =
805 ledger_entry_helpers::requiredAccountID(params, jss::sponsor, "malformedSponsor");
806 if (!sponsorID)
807 return std::unexpected(sponsorID.error());
808
809 auto const sponseeID =
810 ledger_entry_helpers::requiredAccountID(params, jss::sponsee, "malformedSponsee");
811 if (!sponseeID)
812 return std::unexpected(sponseeID.error());
813
814 return keylet::sponsorship(*sponsorID, *sponseeID).key;
815}
816
817static std::expected<UInt256, json::Value>
819 json::Value const& params,
820 json::StaticString const fieldName,
821 [[maybe_unused]] unsigned const apiVersion)
822{
823 // In the non-object branch the caller must supply the proposal ID as a
824 // hex string (the object form is the {account, ticket_seq} pair handled
825 // below). Passing "hex string" here — rather than the more general
826 // "hex string or object" default — makes the error message name the
827 // exact form still on the table once an object has been ruled out.
828 if (!params.isObject())
829 return parseObjectID(params, fieldName, "hex string");
830
831 auto const targetID =
832 ledger_entry_helpers::requiredAccountID(params, jss::account, "malformedAddress");
833 if (!targetID)
834 return std::unexpected(targetID.error());
835
836 // The proposed transaction's TicketSequence (a proposed transaction is
837 // ticket-only), mirroring how parseTicket looks up a Ticket object.
838 auto const ticketSequence =
839 ledger_entry_helpers::requiredUInt32(params, jss::ticket_seq, "malformedRequest");
840 if (!ticketSequence)
841 return std::unexpected(ticketSequence.error());
842
843 return keylet::txProposal(*targetID, *ticketSequence).key;
844}
845
846static std::expected<UInt256, json::Value>
848 json::Value const& params,
849 json::StaticString const fieldName,
850 [[maybe_unused]] unsigned const apiVersion)
851{
852 if (!params.isObject())
853 {
854 return parseObjectID(params, fieldName);
855 }
856
857 auto const id =
858 ledger_entry_helpers::requiredAccountID(params, jss::account, "malformedAddress");
859 if (!id)
860 return std::unexpected(id.error());
861
862 auto const seq =
863 ledger_entry_helpers::requiredUInt32(params, jss::ticket_seq, "malformedRequest");
864 if (!seq)
865 return std::unexpected(seq.error());
866
867 auto const seqProxy = SeqProxy::rawTicket(*seq);
868 return keylet::ticket(*id, seqProxy).key;
869}
870
871static std::expected<UInt256, json::Value>
873 json::Value const& params,
874 json::StaticString const fieldName,
875 [[maybe_unused]] unsigned const apiVersion)
876{
877 if (!params.isObject())
878 {
879 return parseObjectID(params, fieldName);
880 }
881
882 auto const id = ledger_entry_helpers::requiredAccountID(params, jss::owner, "malformedOwner");
883 if (!id)
884 return std::unexpected(id.error());
885
886 auto const seq = ledger_entry_helpers::requiredUInt32(params, jss::seq, "malformedRequest");
887 if (!seq)
888 return std::unexpected(seq.error());
889
890 auto const seqProxy = SeqProxy::rawSequence(*seq);
891 return keylet::vault(*id, seqProxy).key;
892}
893
894static std::expected<UInt256, json::Value>
896 json::Value const& claimId,
897 json::StaticString const fieldName,
898 [[maybe_unused]] unsigned const apiVersion)
899{
900 if (!claimId.isObject())
901 {
902 return parseObjectID(claimId, fieldName);
903 }
904
905 auto const bridgeSpec = ledger_entry_helpers::parseBridgeFields(claimId);
906 if (!bridgeSpec)
907 return std::unexpected(bridgeSpec.error());
908
910 claimId, jss::xchain_owned_claim_id, "malformedXChainOwnedClaimID");
911 if (!seq)
912 {
913 return std::unexpected(seq.error());
914 }
915
916 Keylet const keylet = keylet::xChainClaimID(*bridgeSpec, *seq);
917 return keylet.key;
918}
919
920static std::expected<UInt256, json::Value>
922 json::Value const& claimId,
923 json::StaticString const fieldName,
924 [[maybe_unused]] unsigned const apiVersion)
925{
926 if (!claimId.isObject())
927 {
928 return parseObjectID(claimId, fieldName);
929 }
930
931 auto const bridgeSpec = ledger_entry_helpers::parseBridgeFields(claimId);
932 if (!bridgeSpec)
933 return std::unexpected(bridgeSpec.error());
934
936 claimId,
937 jss::xchain_owned_create_account_claim_id,
938 "malformedXChainOwnedCreateAccountClaimID");
939 if (!seq)
940 {
941 return std::unexpected(seq.error());
942 }
943
944 Keylet const keylet = keylet::xChainCreateAccountClaimID(*bridgeSpec, *seq);
945 return keylet.key;
946}
947
954
955// {
956// ledger_hash : <ledger>
957// ledger_index : <ledger_index>
958// ...
959// }
962{
963 static auto kLedgerEntryParsers = std::to_array<LedgerEntry>({
964#pragma push_macro("LEDGER_ENTRY")
965#undef LEDGER_ENTRY
966
967#define LEDGER_ENTRY(tag, value, name, rpcName, fields) {jss::rpcName, parse##name, tag},
968
969#include <xrpl/protocol/detail/ledger_entries.macro>
970
971#undef LEDGER_ENTRY
972#pragma pop_macro("LEDGER_ENTRY")
973 {.fieldName = jss::index, .parseFunction = parseIndex, .expectedType = ltANY},
974 // aliases
975 {.fieldName = jss::account_root,
976 .parseFunction = parseAccountRoot,
977 .expectedType = ltACCOUNT_ROOT},
978 {.fieldName = jss::ripple_state,
979 .parseFunction = parseRippleState,
980 .expectedType = ltRIPPLE_STATE},
981 });
982
983 auto const hasMoreThanOneMember = [&]() {
984 int count = 0;
985
986 for (auto const& ledgerEntry : kLedgerEntryParsers)
987 {
988 if (context.params.isMember(ledgerEntry.fieldName))
989 {
990 count++;
991 if (count > 1) // Early exit if more than one is found
992 return true;
993 }
994 }
995 return false; // Return false if <= 1 is found
996 }();
997
998 if (hasMoreThanOneMember)
999 {
1000 return rpc::makeParamError("Too many fields provided.");
1001 }
1002
1004 auto jvResult = rpc::lookupLedger(lpLedger, context);
1005
1006 if (!lpLedger)
1007 return jvResult;
1008
1009 UInt256 uNodeIndex;
1010 LedgerEntryType expectedType = ltANY;
1011
1012 try
1013 {
1014 bool found = false;
1015 for (auto const& ledgerEntry : kLedgerEntryParsers)
1016 {
1017 if (context.params.isMember(ledgerEntry.fieldName))
1018 {
1019 expectedType = ledgerEntry.expectedType;
1020 // `Bridge` is the only type that involves two fields at the
1021 // `ledger_entry` param level.
1022 // So that parser needs to have the whole `params` field.
1023 // All other parsers only need the one field name's info.
1024 json::Value const& params = ledgerEntry.fieldName == jss::bridge
1025 ? context.params
1026 : context.params[ledgerEntry.fieldName];
1027 auto const result =
1028 ledgerEntry.parseFunction(params, ledgerEntry.fieldName, context.apiVersion);
1029 if (!result)
1030 return result.error();
1031
1032 uNodeIndex = result.value();
1033 found = true;
1034 break;
1035 }
1036 }
1037 if (!found)
1038 {
1039 if (context.apiVersion < 2u)
1040 {
1041 jvResult[jss::error] = "unknownOption";
1042 return jvResult;
1043 }
1044 return rpc::makeParamError("No ledger_entry params provided.");
1045 }
1046 }
1047 catch (json::Error const& e)
1048 {
1049 if (context.apiVersion > 1u)
1050 {
1051 // For apiVersion 2 onwards, any parsing failures that throw
1052 // this exception return an invalidParam error.
1054 }
1055
1056 throw;
1057 }
1058
1059 // Return the computed index regardless of whether the node exists.
1060 jvResult[jss::index] = to_string(uNodeIndex);
1061
1062 if (uNodeIndex.isZero())
1063 {
1065 return jvResult;
1066 }
1067
1068 auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
1069
1070 bool bNodeBinary = false;
1071 if (context.params.isMember(jss::binary))
1072 bNodeBinary = context.params[jss::binary].asBool();
1073
1074 if (!sleNode)
1075 {
1076 // Not found.
1078 return jvResult;
1079 }
1080
1081 if ((expectedType != ltANY) && (expectedType != sleNode->getType()))
1082 {
1084 return jvResult;
1085 }
1086
1087 if (bNodeBinary)
1088 {
1089 Serializer s;
1090
1091 sleNode->add(s);
1092
1093 jvResult[jss::node_binary] = strHex(s.peekData());
1094 }
1095 else
1096 {
1097 jvResult[jss::node] = sleNode->getJson(JsonOptions::Values::None);
1098 }
1099
1100 return jvResult;
1101}
1102
1105{
1106 org::xrpl::rpc::v1::GetLedgerEntryRequest const& request = context.params;
1107 org::xrpl::rpc::v1::GetLedgerEntryResponse response;
1108 grpc::Status const status = grpc::Status::OK;
1109
1111 if (auto status = rpc::ledgerFromRequest(ledger, context))
1112 {
1113 grpc::Status errorStatus;
1114 if (status.toErrorCode() == RpcInvalidParams)
1115 {
1116 errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
1117 }
1118 else
1119 {
1120 errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
1121 }
1122 return {response, errorStatus};
1123 }
1124
1125 auto const key = UInt256::fromVoidChecked(request.key());
1126 if (!key)
1127 {
1128 grpc::Status const errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
1129 return {response, errorStatus};
1130 }
1131
1132 auto const sleNode = ledger->read(keylet::unchecked(*key));
1133 if (!sleNode)
1134 {
1135 grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "object not found"};
1136 return {response, errorStatus};
1137 }
1138
1139 Serializer s;
1140 sleNode->add(s);
1141
1142 auto& stateObject = *response.mutable_ledger_object();
1143 stateObject.set_data(s.peekData().data(), s.getLength());
1144 stateObject.set_key(request.key());
1145 *(response.mutable_ledger()) = request.ledger();
1146 return {response, status};
1147}
1148} // 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:273
int getLength() const
Definition Serializer.h:304
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:232
Keylet nftokenOffer(AccountID const &owner, SeqProxy const &seq)
An offer from an account to buy or sell an NFT.
Definition Indexes.cpp:453
Keylet oracle(AccountID const &account, std::uint32_t const documentID) noexcept
Definition Indexes.cpp:561
Keylet payChannel(AccountID const &src, AccountID const &dst, SeqProxy const &seq) noexcept
A PaymentChannel.
Definition Indexes.cpp:424
Keylet const & negativeUNL() noexcept
The (fixed) index of the object containing the ledger negativeUNL.
Definition Indexes.cpp:262
Keylet escrow(AccountID const &src, SeqProxy const &seq) noexcept
An escrow entry.
Definition Indexes.cpp:418
Keylet did(AccountID const &account) noexcept
Definition Indexes.cpp:555
Keylet offer(AccountID const &id, SeqProxy const &seq) noexcept
An offer from an account.
Definition Indexes.cpp:298
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition Indexes.cpp:374
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition Indexes.cpp:517
Keylet const & feeSettings() noexcept
The (fixed) index of the object containing the ledger fees.
Definition Indexes.cpp:255
Keylet const & amendments() noexcept
The index of the amendment table.
Definition Indexes.cpp:248
Keylet signerList(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:348
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:403
Keylet txProposal(AccountID const &target, std::uint32_t ticketSequence) noexcept
A TransactionProposal.
Definition Indexes.cpp:366
Keylet check(AccountID const &id, SeqProxy const &seq) noexcept
A Check.
Definition Indexes.cpp:360
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:471
Keylet unchecked(UInt256 const &key) noexcept
Any ledger entry.
Definition Indexes.cpp:397
Keylet ticket(AccountID const &id, SeqProxy const &ticketSeq)
A ticket belonging to an account.
Definition Indexes.cpp:332
Keylet vault(AccountID const &owner, SeqProxy const &seq) noexcept
Definition Indexes.cpp:591
Keylet permissionedDomain(AccountID const &account, SeqProxy const &seq) noexcept
Definition Indexes.cpp:609
Keylet page(UInt256 const &root, std::uint64_t const index=0) noexcept
A page in a directory.
Definition Indexes.cpp:409
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:573
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:511
Keylet loanBroker(AccountID const &owner, SeqProxy const &seq) noexcept
Definition Indexes.cpp:597
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t const seq)
Definition Indexes.cpp:527
Keylet sponsorship(AccountID const &sponsor, AccountID const &sponsee) noexcept
A Sponsorship.
Definition Indexes.cpp:354
Keylet loan(UInt256 const &loanBrokerID, SeqProxy const &loanSeq) noexcept
Definition Indexes.cpp:603
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t const seq)
Definition Indexes.cpp:541
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition Indexes.cpp:585
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:567
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:275
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< UInt192, json::Value > requiredUInt192(json::Value const &params, json::StaticString const fieldName, std::string const &err)
std::expected< Asset, json::Value > requiredAsset(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::expected< UInt256, json::Value > requiredUInt256(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
std::function< std::expected< UInt256, json::Value >( json::Value const &, json::StaticString const, unsigned const apiVersion)> FunctionType
@ RpcEntryNotFound
Definition ErrorCodes.h:144
@ RpcUnexpectedLedgerType
Definition ErrorCodes.h:145
@ RpcInvalidParams
Definition ErrorCodes.h:67
static std::expected< STArray, json::Value > parseAuthorizeCredentials(json::Value const &jv)
static std::expected< UInt256, json::Value > parseFixed(Keylet const &keylet, json::Value const &params, json::StaticString const &fieldName, unsigned const apiVersion)
auto const parseFeeSettings
static std::expected< UInt256, json::Value > parseEscrow(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:276
static std::expected< UInt256, json::Value > parseCredential(json::Value const &cred, json::StaticString const fieldName, unsigned const apiVersion)
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
static std::expected< UInt256, json::Value > parseSponsorship(json::Value const &params, json::StaticString const fieldName, unsigned const apiVersion)
static std::expected< UInt256, json::Value > parseLoan(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 > parseRippleState(json::Value const &jvRippleState, 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 > parseTransactionProposal(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)
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 > parseLoanBroker(json::Value const &params, 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)
static FunctionType fixed(Keylet const &keylet)
static std::expected< UInt256, json::Value > parseXChainOwnedClaimID(json::Value const &claimId, json::StaticString const fieldName, unsigned const apiVersion)
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
static std::expected< UInt256, json::Value > parseXChainOwnedCreateAccountClaimID(json::Value const &claimId, json::StaticString const fieldName, unsigned const apiVersion)
auto const parseAmendments
static std::expected< UInt256, json::Value > parseTicket(json::Value const &params, json::StaticString const fieldName, unsigned const apiVersion)
BaseUInt< 256 > UInt256
Definition base_uint.h:580
static std::expected< UInt256, json::Value > parseOracle(json::Value const &params, 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:281
static std::expected< UInt256, json::Value > parseAccountRoot(json::Value const &params, json::StaticString const fieldName, unsigned const apiVersion)
static std::expected< UInt256, json::Value > parsePermissionedDomain(json::Value const &pd, json::StaticString const fieldName, unsigned const apiVersion)
static std::expected< UInt256, json::Value > parseLedgerHashes(json::Value const &params, json::StaticString const fieldName, unsigned const apiVersion)
auto const parseNegativeUNL
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 > parseOffer(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)
static std::expected< UInt256, json::Value > parseVault(json::Value const &params, json::StaticString const fieldName, unsigned const apiVersion)
static std::expected< UInt256, json::Value > parseMPTokenIssuance(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)
LedgerEntryType
Identifiers for on-ledger objects.
@ ltANY
A special type, matching any ledger entry type.
static std::expected< UInt256, json::Value > parseDID(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 > parseDepositPreauth(json::Value const &dp, json::StaticString const fieldName, unsigned const apiVersion)
static std::expected< UInt256, json::Value > parseNFTokenPage(json::Value const &params, json::StaticString const fieldName, unsigned const apiVersion)
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 > parseAMM(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)
static std::expected< UInt256, json::Value > parseBridge(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)