61 std::shared_ptr<BackendInterface> sharedPtrBackend_;
62 std::shared_ptr<etlng::ETLServiceInterface const> etl_;
70 std::string hash = {};
71 uint32_t ledgerIndex = 0u;
72 std::optional<boost::json::object> meta = std::nullopt;
73 std::optional<boost::json::object> tx = std::nullopt;
74 std::optional<std::string> metaStr = std::nullopt;
75 std::optional<std::string> txStr = std::nullopt;
76 std::optional<std::string> ctid =
78 std::optional<ripple::LedgerHeader> ledgerHeader =
80 uint32_t apiVersion = 0u;
81 bool validated =
true;
88 std::optional<std::string> transaction;
89 std::optional<std::string> ctid;
91 std::optional<uint32_t> minLedger;
92 std::optional<uint32_t> maxLedger;
104 std::shared_ptr<BackendInterface>
const& sharedPtrBackend,
105 std::shared_ptr<etlng::ETLServiceInterface const>
const&
etl
107 : sharedPtrBackend_(sharedPtrBackend), etl_(
etl)
120 static RpcSpec const kRPC_SPEC_FOR_V1 = {
129 return apiVersion == 1 ? kRPC_SPEC_FOR_V1 : kRPC_SPEC;
142 if (input.ctid && input.transaction)
143 return Error{
Status{RippledError::rpcINVALID_PARAMS}};
145 if (!input.ctid && !input.transaction)
146 return Error{
Status{RippledError::rpcINVALID_PARAMS}};
148 static constexpr auto kMAX_LEDGER_RANGE = 1000u;
149 auto const rangeSupplied = input.minLedger && input.maxLedger;
152 if (*input.minLedger > *input.maxLedger)
153 return Error{
Status{RippledError::rpcINVALID_LGR_RANGE}};
155 if (*input.maxLedger - *input.minLedger > kMAX_LEDGER_RANGE)
156 return Error{
Status{RippledError::rpcEXCESSIVE_LGR_RANGE}};
159 std::optional<uint32_t> currentNetId = std::nullopt;
160 if (
auto const& etlState = etl_->getETLState(); etlState.has_value())
161 currentNetId = etlState->networkID;
163 std::optional<data::TransactionAndMetadata> dbResponse;
168 return Error{
Status{RippledError::rpcINVALID_PARAMS}};
170 auto const [lgrSeq, txnIdx, netId] = *ctid;
172 if (currentNetId && netId != *currentNetId) {
174 RippledError::rpcWRONG_NETWORK,
176 "Wrong network. You should submit this request to a node running on NetworkID: {}", netId
181 dbResponse = fetchTxViaCtid(lgrSeq, txnIdx, ctx.yield);
183 dbResponse = sharedPtrBackend_->fetchTransaction(ripple::uint256{input.transaction->c_str()}, ctx.yield);
189 if (rangeSupplied && input.transaction)
191 auto const range = sharedPtrBackend_->fetchLedgerRange();
192 ASSERT(range.has_value(),
"Tx's ledger range must be available");
194 auto const searchedAll =
195 range->maxSequence >= *input.maxLedger && range->minSequence <= *input.minLedger;
196 boost::json::object extra;
197 extra[
"searched_all"] = searchedAll;
199 return Error{
Status{RippledError::rpcTXN_NOT_FOUND, std::move(extra)}};
202 return Error{
Status{RippledError::rpcTXN_NOT_FOUND}};
205 auto const [txn, meta] =
toExpandedJson(*dbResponse, ctx.apiVersion, NFTokenjson::ENABLE, currentNetId);
211 output.txStr = ripple::strHex(dbResponse->transaction);
212 output.metaStr = ripple::strHex(dbResponse->metadata);
215 if (txn.contains(JS(hash)))
216 output.hash = txn.at(JS(hash)).as_string();
220 auto const txnIdx = boost::json::value_to<uint64_t>(meta.at(
"TransactionIndex"));
221 if (txnIdx <= 0xFFFFU && dbResponse->ledgerSequence < 0x0FFF'FFFFUL && currentNetId &&
222 *currentNetId <= 0xFFFFU) {
224 dbResponse->ledgerSequence,
static_cast<uint16_t
>(txnIdx),
static_cast<uint16_t
>(*currentNetId)
228 output.date = dbResponse->date;
229 output.ledgerIndex = dbResponse->ledgerSequence;
232 if (ctx.apiVersion > 1u)
233 output.ledgerHeader = sharedPtrBackend_->fetchLedgerBySequence(dbResponse->ledgerSequence, ctx.yield);
239 std::optional<data::TransactionAndMetadata>
240 fetchTxViaCtid(uint32_t ledgerSeq, uint32_t txId, boost::asio::yield_context yield)
const
242 auto const txs = sharedPtrBackend_->fetchAllTransactionsInLedger(ledgerSeq, yield);
244 for (
auto const& tx : txs) {
247 if (meta->getIndex() == txId)
255 tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output
const& output)
257 auto const getJsonV1 = [&]() {
258 auto obj = boost::json::object{};
262 obj[JS(meta)] = *output.meta;
264 obj[JS(meta)] = *output.metaStr;
265 obj[JS(tx)] = *output.txStr;
266 obj[JS(hash)] = output.hash;
269 obj[JS(validated)] = output.validated;
270 obj[JS(date)] = output.date;
271 obj[JS(ledger_index)] = output.ledgerIndex;
272 obj[JS(inLedger)] = output.ledgerIndex;
276 auto const getJsonV2 = [&]() {
277 auto obj = boost::json::object{};
280 obj[JS(tx_json)] = *output.tx;
281 obj[JS(tx_json)].as_object()[JS(date)] = output.date;
283 obj[JS(tx_json)].as_object()[JS(ctid)] = *output.ctid;
285 obj[JS(tx_json)].as_object()[JS(ledger_index)] = output.ledgerIndex;
287 if (obj[JS(tx_json)].as_object().contains(JS(hash))) {
288 obj[JS(hash)] = obj[JS(tx_json)].as_object()[JS(hash)];
289 obj[JS(tx_json)].as_object().erase(JS(hash));
291 obj[JS(meta)] = *output.meta;
293 obj[JS(meta_blob)] = *output.metaStr;
294 obj[JS(tx_blob)] = *output.txStr;
295 obj[JS(hash)] = output.hash;
298 obj[JS(validated)] = output.validated;
299 obj[JS(ledger_index)] = output.ledgerIndex;
301 if (output.ledgerHeader) {
302 obj[JS(ledger_hash)] = ripple::strHex(output.ledgerHeader->hash);
303 obj[JS(close_time_iso)] = ripple::to_string_iso(output.ledgerHeader->closeTime);
308 auto obj = output.apiVersion > 1u ? getJsonV2() : getJsonV1();
311 obj[JS(ctid)] = *output.ctid;
317 tag_invoke(boost::json::value_to_tag<Input>, boost::json::value
const& jv)
319 auto input = TxHandler::Input{};
320 auto const& jsonObject = jv.as_object();
322 if (jsonObject.contains(JS(transaction)))
323 input.transaction = boost::json::value_to<std::string>(jv.at(JS(transaction)));
325 if (jsonObject.contains(JS(ctid))) {
326 input.ctid = boost::json::value_to<std::string>(jv.at(JS(ctid)));
330 if (jsonObject.contains(JS(binary)))
331 input.binary = boost::json::value_to<JsonBool>(jsonObject.at(JS(binary)));
333 if (jsonObject.contains(JS(min_ledger)))
334 input.minLedger = jv.at(JS(min_ledger)).as_int64();
336 if (jsonObject.contains(JS(max_ledger)))
337 input.maxLedger = jv.at(JS(max_ledger)).as_int64();
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:210
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:709
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:254