22#include "data/BackendInterface.hpp"
23#include "data/Types.hpp"
24#include "etlng/ETLServiceInterface.hpp"
28#include "rpc/common/JsonBool.hpp"
29#include "rpc/common/Specs.hpp"
30#include "rpc/common/Types.hpp"
31#include "rpc/common/Validators.hpp"
32#include "util/Assert.hpp"
33#include "util/JsonUtils.hpp"
35#include <boost/asio/spawn.hpp>
36#include <boost/json/conversion.hpp>
37#include <boost/json/object.hpp>
38#include <boost/json/value.hpp>
39#include <boost/json/value_to.hpp>
40#include <fmt/format.h>
41#include <xrpl/basics/base_uint.h>
42#include <xrpl/basics/chrono.h>
43#include <xrpl/basics/strHex.h>
44#include <xrpl/protocol/ErrorCodes.h>
45#include <xrpl/protocol/LedgerHeader.h>
46#include <xrpl/protocol/jss.h>
62 std::shared_ptr<BackendInterface> sharedPtrBackend_;
63 std::shared_ptr<etlng::ETLServiceInterface const> etl_;
71 std::string hash = {};
72 uint32_t ledgerIndex = 0u;
73 std::optional<boost::json::object> meta = std::nullopt;
74 std::optional<boost::json::object> tx = std::nullopt;
75 std::optional<std::string> metaStr = std::nullopt;
76 std::optional<std::string> txStr = std::nullopt;
77 std::optional<std::string> ctid =
79 std::optional<ripple::LedgerHeader> ledgerHeader =
81 uint32_t apiVersion = 0u;
82 bool validated =
true;
89 std::optional<std::string> transaction;
90 std::optional<std::string> ctid;
92 std::optional<uint32_t> minLedger;
93 std::optional<uint32_t> maxLedger;
105 std::shared_ptr<BackendInterface>
const& sharedPtrBackend,
106 std::shared_ptr<etlng::ETLServiceInterface const>
const&
etl
108 : sharedPtrBackend_(sharedPtrBackend), etl_(
etl)
121 static RpcSpec const kRPC_SPEC_FOR_V1 = {
130 return apiVersion == 1 ? kRPC_SPEC_FOR_V1 : kRPC_SPEC;
143 if (input.ctid && input.transaction)
144 return Error{
Status{RippledError::rpcINVALID_PARAMS}};
146 if (!input.ctid && !input.transaction)
147 return Error{
Status{RippledError::rpcINVALID_PARAMS}};
149 static constexpr auto kMAX_LEDGER_RANGE = 1000u;
150 auto const rangeSupplied = input.minLedger && input.maxLedger;
153 if (*input.minLedger > *input.maxLedger)
154 return Error{
Status{RippledError::rpcINVALID_LGR_RANGE}};
156 if (*input.maxLedger - *input.minLedger > kMAX_LEDGER_RANGE)
157 return Error{
Status{RippledError::rpcEXCESSIVE_LGR_RANGE}};
160 std::optional<uint32_t> currentNetId = std::nullopt;
161 if (
auto const& etlState = etl_->getETLState(); etlState.has_value())
162 currentNetId = etlState->networkID;
164 std::optional<data::TransactionAndMetadata> dbResponse;
169 return Error{
Status{RippledError::rpcINVALID_PARAMS}};
171 auto const [lgrSeq, txnIdx, netId] = *ctid;
173 if (currentNetId && netId != *currentNetId) {
175 RippledError::rpcWRONG_NETWORK,
177 "Wrong network. You should submit this request to a node running on NetworkID: {}", netId
182 dbResponse = fetchTxViaCtid(lgrSeq, txnIdx, ctx.yield);
184 dbResponse = sharedPtrBackend_->fetchTransaction(ripple::uint256{input.transaction->c_str()}, ctx.yield);
190 if (rangeSupplied && input.transaction)
192 auto const range = sharedPtrBackend_->fetchLedgerRange();
193 ASSERT(range.has_value(),
"Tx's ledger range must be available");
195 auto const searchedAll =
196 range->maxSequence >= *input.maxLedger && range->minSequence <= *input.minLedger;
197 boost::json::object extra;
198 extra[
"searched_all"] = searchedAll;
200 return Error{
Status{RippledError::rpcTXN_NOT_FOUND, std::move(extra)}};
203 return Error{
Status{RippledError::rpcTXN_NOT_FOUND}};
206 auto const [txn, meta] =
toExpandedJson(*dbResponse, ctx.apiVersion, NFTokenjson::ENABLE, currentNetId);
212 output.txStr = ripple::strHex(dbResponse->transaction);
213 output.metaStr = ripple::strHex(dbResponse->metadata);
216 if (txn.contains(JS(hash)))
217 output.hash = txn.at(JS(hash)).as_string();
221 auto const txnIdx = boost::json::value_to<uint64_t>(meta.at(
"TransactionIndex"));
222 if (txnIdx <= 0xFFFFU && dbResponse->ledgerSequence < 0x0FFF'FFFFUL && currentNetId &&
223 *currentNetId <= 0xFFFFU) {
225 dbResponse->ledgerSequence,
static_cast<uint16_t
>(txnIdx),
static_cast<uint16_t
>(*currentNetId)
229 output.date = dbResponse->date;
230 output.ledgerIndex = dbResponse->ledgerSequence;
233 if (ctx.apiVersion > 1u)
234 output.ledgerHeader = sharedPtrBackend_->fetchLedgerBySequence(dbResponse->ledgerSequence, ctx.yield);
240 std::optional<data::TransactionAndMetadata>
241 fetchTxViaCtid(uint32_t ledgerSeq, uint32_t txId, boost::asio::yield_context yield)
const
243 auto const txs = sharedPtrBackend_->fetchAllTransactionsInLedger(ledgerSeq, yield);
245 for (
auto const& tx : txs) {
248 if (meta->getIndex() == txId)
256 tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output
const& output)
258 auto const getJsonV1 = [&]() {
259 auto obj = boost::json::object{};
263 obj[JS(meta)] = *output.meta;
265 obj[JS(meta)] = *output.metaStr;
266 obj[JS(tx)] = *output.txStr;
267 obj[JS(hash)] = output.hash;
270 obj[JS(validated)] = output.validated;
271 obj[JS(date)] = output.date;
272 obj[JS(ledger_index)] = output.ledgerIndex;
273 obj[JS(inLedger)] = output.ledgerIndex;
277 auto const getJsonV2 = [&]() {
278 auto obj = boost::json::object{};
281 obj[JS(tx_json)] = *output.tx;
282 obj[JS(tx_json)].as_object()[JS(date)] = output.date;
284 obj[JS(tx_json)].as_object()[JS(ctid)] = *output.ctid;
286 obj[JS(tx_json)].as_object()[JS(ledger_index)] = output.ledgerIndex;
288 if (obj[JS(tx_json)].as_object().contains(JS(hash))) {
289 obj[JS(hash)] = obj[JS(tx_json)].as_object()[JS(hash)];
290 obj[JS(tx_json)].as_object().erase(JS(hash));
292 obj[JS(meta)] = *output.meta;
294 obj[JS(meta_blob)] = *output.metaStr;
295 obj[JS(tx_blob)] = *output.txStr;
296 obj[JS(hash)] = output.hash;
299 obj[JS(validated)] = output.validated;
300 obj[JS(ledger_index)] = output.ledgerIndex;
302 if (output.ledgerHeader) {
303 obj[JS(ledger_hash)] = ripple::strHex(output.ledgerHeader->hash);
304 obj[JS(close_time_iso)] = ripple::to_string_iso(output.ledgerHeader->closeTime);
309 auto obj = output.apiVersion > 1u ? getJsonV2() : getJsonV1();
312 obj[JS(ctid)] = *output.ctid;
318 tag_invoke(boost::json::value_to_tag<Input>, boost::json::value
const& jv)
320 auto input = TxHandler::Input{};
321 auto const& jsonObject = jv.as_object();
323 if (jsonObject.contains(JS(transaction)))
324 input.transaction = boost::json::value_to<std::string>(jv.at(JS(transaction)));
326 if (jsonObject.contains(JS(ctid))) {
327 input.ctid = boost::json::value_to<std::string>(jv.at(JS(ctid)));
331 if (jsonObject.contains(JS(binary)))
332 input.binary = boost::json::value_to<JsonBool>(jsonObject.at(JS(binary)));
334 if (jsonObject.contains(JS(min_ledger)))
337 if (jsonObject.contains(JS(max_ledger)))
The tx method retrieves information on a single transaction, by its identifying hash.
Definition Tx.hpp:61
TxHandler(std::shared_ptr< BackendInterface > const &sharedPtrBackend, std::shared_ptr< etlng::ETLServiceInterface const > const &etl)
Construct a new TxHandler object.
Definition Tx.hpp:104
Result process(Input const &input, Context const &ctx) const
Process the Tx command.
Definition Tx.hpp:141
static RpcSpecConstRef spec(uint32_t apiVersion)
Returns the API specification for the command.
Definition Tx.hpp:119
This namespace contains everything to do with the ETL and ETL sources.
Definition CacheLoader.hpp:39
This namespace contains all the RPC logic and handlers.
Definition AMMHelpers.cpp:37
std::pair< std::shared_ptr< ripple::STTx const >, std::shared_ptr< ripple::STObject const > > deserializeTxPlusMeta(data::TransactionAndMetadata const &blobs)
Deserialize a TransactionAndMetadata into a pair of STTx and STObject.
Definition RPCHelpers.cpp:203
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::optional< std::string > encodeCTID(uint32_t ledgerSeq, uint16_t txnIndex, uint16_t networkId) noexcept
Encode CTID as string.
Definition RPCHelpers.cpp:285
std::optional< std::tuple< uint32_t, uint16_t, uint16_t > > decodeCTID(T const ctid) noexcept
Decode the CTID from a string or a uint64_t.
Definition RPCHelpers.hpp:719
std::pair< boost::json::object, boost::json::object > toExpandedJson(data::TransactionAndMetadata const &blobs, std::uint32_t const apiVersion, NFTokenjson nftEnabled, std::optional< uint16_t > networkId)
Convert a TransactionAndMetadata to two JSON objects.
Definition RPCHelpers.cpp:249
std::unexpected< Status > Error
The type that represents just the error part of MaybeError.
Definition Types.hpp:75
Type integralValueAs(boost::json::value const &value)
Detects the type of number stored in value and casts it back to the requested Type.
Definition JsonUtils.hpp:102
std::string toUpper(std::string str)
Convert a string to uppercase.
Definition JsonUtils.hpp:56
Context of an RPC call.
Definition Types.hpp:118
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:83
A struct to hold the output data of the command.
Definition Tx.hpp:69
static CustomValidator uint256HexStringValidator
Provides a commonly used validator for uint256 hex string.
Definition Validators.hpp:551
Validates that the type of the value is one of the given types.
Definition Validators.hpp:142