Clio develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
LedgerEntry.hpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of clio: https://github.com/XRPLF/clio
4 Copyright (c) 2023, the clio developers.
5
6 Permission to use, copy, modify, and distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#pragma once
21
22#include "data/BackendInterface.hpp"
23#include "rpc/Errors.hpp"
24#include "rpc/JS.hpp"
25#include "rpc/common/Checkers.hpp"
26#include "rpc/common/MetaProcessors.hpp"
27#include "rpc/common/Modifiers.hpp"
28#include "rpc/common/Specs.hpp"
29#include "rpc/common/Types.hpp"
30#include "rpc/common/Validators.hpp"
31#include "util/AccountUtils.hpp"
32
33#include <boost/json/array.hpp>
34#include <boost/json/conversion.hpp>
35#include <boost/json/object.hpp>
36#include <boost/json/value.hpp>
37#include <boost/json/value_to.hpp>
38#include <xrpl/basics/base_uint.h>
39#include <xrpl/beast/core/LexicalCast.h>
40#include <xrpl/protocol/AccountID.h>
41#include <xrpl/protocol/ErrorCodes.h>
42#include <xrpl/protocol/Issue.h>
43#include <xrpl/protocol/LedgerFormats.h>
44#include <xrpl/protocol/SField.h>
45#include <xrpl/protocol/jss.h>
46#include <xrpl/protocol/tokens.h>
47
48#include <cstdint>
49#include <memory>
50#include <optional>
51#include <string>
52#include <string_view>
53#include <variant>
54
55namespace rpc {
56
63 std::shared_ptr<BackendInterface> sharedPtrBackend_;
64
65public:
69 struct Output {
70 std::string index;
71 uint32_t ledgerIndex;
72 std::string ledgerHash;
73 std::optional<boost::json::object> node;
74 std::optional<std::string> nodeBinary;
75 std::optional<uint32_t> deletedLedgerIndex;
76 bool validated = true;
77 };
78
82 struct Input {
83 std::optional<std::string> ledgerHash;
84 std::optional<uint32_t> ledgerIndex;
85 bool binary = false;
86 // id of this ledger entry: 256 bits hex string
87 std::optional<std::string> index;
88 // index can be extracted from payment_channel, check, escrow, offer
89 // etc, expectedType is used to save the type of index
90 ripple::LedgerEntryType expectedType = ripple::ltANY;
91 // account id to address account root object
92 std::optional<std::string> accountRoot;
93 // account id to address did object
94 std::optional<std::string> did;
95 // mpt issuance id to address mptIssuance object
96 std::optional<std::string> mptIssuance;
97 // TODO: extract into custom objects, remove json from Input
98 std::optional<boost::json::object> directory;
99 std::optional<boost::json::object> offer;
100 std::optional<boost::json::object> rippleStateAccount;
101 std::optional<boost::json::object> escrow;
102 std::optional<boost::json::object> depositPreauth;
103 std::optional<boost::json::object> ticket;
104 std::optional<boost::json::object> amm;
105 std::optional<boost::json::object> mptoken;
106 std::optional<boost::json::object> permissionedDomain;
107 std::optional<ripple::STXChainBridge> bridge;
108 std::optional<std::string> bridgeAccount;
109 std::optional<uint32_t> chainClaimId;
110 std::optional<uint32_t> createAccountClaimId;
111 std::optional<ripple::uint256> oracleNode;
112 std::optional<ripple::uint256> credential;
113 bool includeDeleted = false;
114 };
115
116 using Result = HandlerReturnType<Output>;
117
123 LedgerEntryHandler(std::shared_ptr<BackendInterface> const& sharedPtrBackend) : sharedPtrBackend_(sharedPtrBackend)
124 {
125 }
126
133 static RpcSpecConstRef
134 spec([[maybe_unused]] uint32_t apiVersion)
135 {
136 // Validator only works in this handler
137 // The accounts array must have two different elements
138 // Each element must be a valid address
139 static auto const kRIPPLE_STATE_ACCOUNTS_CHECK =
140 validation::CustomValidator{[](boost::json::value const& value, std::string_view /* key */) -> MaybeError {
141 if (!value.is_array() || value.as_array().size() != 2 || !value.as_array()[0].is_string() ||
142 !value.as_array()[1].is_string() ||
143 value.as_array()[0].as_string() == value.as_array()[1].as_string()) {
144 return Error{Status{RippledError::rpcINVALID_PARAMS, "malformedAccounts"}};
145 }
146
147 auto const id1 =
148 util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(value.as_array()[0])
149 );
150 auto const id2 =
151 util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(value.as_array()[1])
152 );
153
154 if (!id1 || !id2)
155 return Error{Status{ClioError::RpcMalformedAddress, "malformedAddresses"}};
156
157 return MaybeError{};
158 }};
159
160 static auto const kMALFORMED_REQUEST_HEX_STRING_VALIDATOR = meta::WithCustomError{
162 };
163
164 static auto const kMALFORMED_REQUEST_INT_VALIDATOR =
165 meta::WithCustomError{validation::Type<uint32_t>{}, Status(ClioError::RpcMalformedRequest)};
166
167 static auto const kBRIDGE_JSON_VALIDATOR = meta::WithCustomError{
169 {ripple::sfLockingChainDoor.getJsonName().c_str(),
172 {ripple::sfIssuingChainDoor.getJsonName().c_str(),
175 {ripple::sfLockingChainIssue.getJsonName().c_str(),
178 {ripple::sfIssuingChainIssue.getJsonName().c_str(),
181 }},
182 Status(ClioError::RpcMalformedRequest)
183 };
184
185 static auto const kRPC_SPEC = RpcSpec{
186 {JS(binary), validation::Type<bool>{}},
189 {JS(index), kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
192 {JS(check), kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
193 {JS(deposit_preauth),
195 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
198 {JS(owner),
202 }},
205 },
206 }},
207 {JS(directory),
209 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
213 {JS(sub_index), kMALFORMED_REQUEST_INT_VALIDATOR}
214 }}},
215 {JS(escrow),
217 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
220 {JS(owner),
224 }},
225 {JS(seq), validation::Required{}, kMALFORMED_REQUEST_INT_VALIDATOR},
226 },
227 }},
228 {JS(offer),
230 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
234 {JS(seq), validation::Required{}, kMALFORMED_REQUEST_INT_VALIDATOR},
235 },
236 }},
237 {JS(payment_channel), kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
238 {JS(ripple_state),
241 {JS(accounts), validation::Required{}, kRIPPLE_STATE_ACCOUNTS_CHECK},
243 }},
244 {JS(ticket),
246 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
250 {JS(ticket_seq), validation::Required{}, kMALFORMED_REQUEST_INT_VALIDATOR},
251 },
252 }},
253 {JS(nft_page), kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
254 {JS(amm),
256 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
259 {JS(asset),
260 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
262 validation::Type<boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
263 },
265 {JS(asset2),
266 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
268 validation::Type<boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
269 },
271 },
272 }},
273 {JS(bridge),
274 meta::WithCustomError{validation::Type<boost::json::object>{}, Status(ClioError::RpcMalformedRequest)},
275 kBRIDGE_JSON_VALIDATOR},
276 {JS(bridge_account),
278 validation::CustomValidators::accountBase58Validator, Status(ClioError::RpcMalformedRequest)
279 }},
280 {JS(xchain_owned_claim_id),
282 validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
283 },
284 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
285 kBRIDGE_JSON_VALIDATOR,
288 meta::Section{{JS(xchain_owned_claim_id), validation::Required{}, validation::Type<uint32_t>{}}}
289 },
290 Status(ClioError::RpcMalformedRequest)
291 }},
292 {JS(xchain_owned_create_account_claim_id),
294 validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
295 },
296 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
297 kBRIDGE_JSON_VALIDATOR,
300 {JS(xchain_owned_create_account_claim_id), validation::Required{}, validation::Type<uint32_t>{}}
301 }},
302 Status(ClioError::RpcMalformedRequest)
303 }},
304 {JS(oracle),
306 validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
307 },
309 meta::WithCustomError{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR, Status(ClioError::RpcMalformedAddress)}
310 },
312 {JS(account),
313 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
315 validation::CustomValidators::accountBase58Validator, Status(ClioError::RpcMalformedAddress)
316 }},
317 // note: Unlike `rippled`, Clio only supports UInt as input, no string, no `null`, etc.:
318 {JS(oracle_document_id),
319 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
321 validation::Type<uint32_t, std::string>{}, Status(ClioError::RpcMalformedOracleDocumentId)
322 },
323 meta::WithCustomError{modifiers::ToNumber{}, Status(ClioError::RpcMalformedOracleDocumentId)}},
324 }}},
325 {JS(credential),
327 validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
328 },
330 meta::WithCustomError{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR, Status(ClioError::RpcMalformedAddress)}
331 },
333 {JS(subject),
334 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
336 validation::CustomValidators::accountBase58Validator, Status(ClioError::RpcMalformedAddress)
337 }},
338 {JS(issuer),
339 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
341 validation::CustomValidators::accountBase58Validator, Status(ClioError::RpcMalformedAddress)
342 }},
343 {
344 JS(credential_type),
345 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
346 meta::WithCustomError{validation::Type<std::string>{}, Status(ClioError::RpcMalformedRequest)},
347 },
348 }}},
349 {JS(mpt_issuance),
352 }},
353 {JS(mptoken),
355 validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
356 },
357 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
360 {
361 JS(account),
362 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
365 Status(ClioError::RpcMalformedAddress)
366 },
367 },
368 {
369 JS(mpt_issuance_id),
370 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
373 Status(ClioError::RpcMalformedRequest)
374 },
375 },
376 },
377 }},
378 {JS(permissioned_domain),
380 validation::Type<std::string, boost::json::object>{}, Status(ClioError::RpcMalformedRequest)
381 },
382 meta::IfType<std::string>{kMALFORMED_REQUEST_HEX_STRING_VALIDATOR},
384 {JS(seq),
385 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
386 meta::WithCustomError{validation::Type<uint32_t>{}, Status(ClioError::RpcMalformedRequest)}},
387 {
388 JS(account),
389 meta::WithCustomError{validation::Required{}, Status(ClioError::RpcMalformedRequest)},
391 validation::CustomValidators::accountBase58Validator, Status(ClioError::RpcMalformedAddress)
392 },
393 },
394 }}},
395 {JS(ledger), check::Deprecated{}},
396 {"include_deleted", validation::Type<bool>{}},
397 };
398
399 return kRPC_SPEC;
400 }
401
409 Result
410 process(Input input, Context const& ctx) const;
411
412private:
413 // dir_root and owner can not be both empty or filled at the same time
414 // This function will return an error if this is the case
415 static std::variant<ripple::uint256, Status>
416 composeKeyFromDirectory(boost::json::object const& directory) noexcept;
417
424 friend void
425 tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);
426
433 friend Input
434 tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
435};
436
437} // namespace rpc
The ledger_entry method returns a single ledger object from the XRP Ledger in its raw format.
Definition LedgerEntry.hpp:62
static RpcSpecConstRef spec(uint32_t apiVersion)
Returns the API specification for the command.
Definition LedgerEntry.hpp:134
Result process(Input input, Context const &ctx) const
Process the LedgerEntry command.
Definition LedgerEntry.cpp:62
friend void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, Output const &output)
Convert the Output to a JSON object.
Definition LedgerEntry.cpp:264
LedgerEntryHandler(std::shared_ptr< BackendInterface > const &sharedPtrBackend)
Construct a new LedgerEntryHandler object.
Definition LedgerEntry.hpp:123
Check for a deprecated fields.
Definition Checkers.hpp:62
A meta-processor that specifies a list of requirements to run against when the type matches the templ...
Definition MetaProcessors.hpp:101
A meta-processor that acts as a spec for a sub-object/section.
Definition MetaProcessors.hpp:43
A meta-processor that wraps a validator and produces a custom error in case the wrapped validator fai...
Definition MetaProcessors.hpp:166
A meta-validator that allows to specify a custom validation function.
Definition Validators.hpp:423
This namespace contains all the RPC logic and handlers.
Definition AMMHelpers.cpp:36
RpcSpec const & RpcSpecConstRef
An alias for a const reference to RpcSpec.
Definition Specs.hpp:145
std::expected< OutputType, Status > HandlerReturnType
Return type for each individual handler.
Definition Types.hpp:81
std::expected< void, Status > MaybeError
Return type used for Validators that can return error but don't have specific value to return.
Definition Types.hpp:55
std::unexpected< Status > Error
The type that represents just the error part of MaybeError.
Definition Types.hpp:75
std::optional< T > parseBase58Wrapper(std::string const &str)
A wrapper of parseBase58 function. It adds the check if all characters in the input string are alphan...
Definition AccountUtils.hpp:42
Context of an RPC call.
Definition Types.hpp:118
A struct to hold the input data for the command.
Definition LedgerEntry.hpp:82
A struct to hold the output data of the command.
Definition LedgerEntry.hpp:69
Result type used to return responses or error statuses to the Webserver subsystem.
Definition Types.hpp:129
Represents a Specification of an entire RPC command.
Definition Specs.hpp:98
A status returned from any RPC handler.
Definition Errors.hpp:82
Convert input string to integer.
Definition Modifiers.hpp:112
static CustomValidator currencyValidator
Provides a commonly used validator for currency, including standard currency code and token code.
Definition Validators.hpp:535
static CustomValidator ledgerIndexValidator
Provides a commonly used validator for ledger index.
Definition Validators.hpp:484
static CustomValidator currencyIssueValidator
Validates an asset (ripple::Issue).
Definition Validators.hpp:559
static CustomValidator authorizeCredentialValidator
Provides a validator for validating authorized_credentials json array.
Definition Validators.hpp:566
static CustomValidator uint192HexStringValidator
Provides a commonly used validator for uint192 hex string.
Definition Validators.hpp:522
static CustomValidator uint256HexStringValidator
Provides a commonly used validator for uint256 hex string.
Definition Validators.hpp:530
static CustomValidator accountBase58Validator
Provides a commonly used validator for accounts.
Definition Validators.hpp:498
A validator that simply requires a field to be present.
Definition Validators.hpp:47
Validates that the type of the value is one of the given types.
Definition Validators.hpp:142