rippled
Loading...
Searching...
No Matches
Tx.cpp
1#include <xrpld/app/ledger/LedgerMaster.h>
2#include <xrpld/app/ledger/TransactionMaster.h>
3#include <xrpld/app/misc/DeliverMax.h>
4#include <xrpld/app/misc/NetworkOPs.h>
5#include <xrpld/app/misc/Transaction.h>
6#include <xrpld/app/rdb/RelationalDatabase.h>
7#include <xrpld/rpc/CTID.h>
8#include <xrpld/rpc/Context.h>
9#include <xrpld/rpc/DeliveredAmount.h>
10#include <xrpld/rpc/GRPCHandlers.h>
11#include <xrpld/rpc/MPTokenIssuanceID.h>
12
13#include <xrpl/basics/ToString.h>
14#include <xrpl/protocol/ErrorCodes.h>
15#include <xrpl/protocol/NFTSyntheticSerializer.h>
16#include <xrpl/protocol/RPCErr.h>
17#include <xrpl/protocol/jss.h>
18
19#include <regex>
20
21namespace ripple {
22
23static bool
25{
26 if (!ledgerMaster.haveLedger(seq))
27 return false;
28
29 if (seq > ledgerMaster.getValidatedLedger()->info().seq)
30 return false;
31
32 return ledgerMaster.getHashBySeq(seq) == hash;
33}
34
45
53
56{
57 TxResult result;
58
60
61 if (args.ledgerRange)
62 {
63 constexpr uint16_t MAX_RANGE = 1000;
64
65 if (args.ledgerRange->second < args.ledgerRange->first)
66 return {result, rpcINVALID_LGR_RANGE};
67
68 if (args.ledgerRange->second - args.ledgerRange->first > MAX_RANGE)
69 return {result, rpcEXCESSIVE_LGR_RANGE};
70
72 args.ledgerRange->first, args.ledgerRange->second);
73 }
74
75 auto ec{rpcSUCCESS};
76
77 using TxPair =
79
82
83 if (args.ctid)
84 {
85 args.hash = context.app.getLedgerMaster().txnIdFromIndex(
86 args.ctid->first, args.ctid->second);
87
88 if (args.hash)
89 range =
90 ClosedInterval<uint32_t>(args.ctid->first, args.ctid->second);
91 }
92
93 if (!args.hash)
94 return {result, rpcTXN_NOT_FOUND};
95
96 if (args.ledgerRange)
97 {
98 v = context.app.getMasterTransaction().fetch(*(args.hash), range, ec);
99 }
100 else
101 {
102 v = context.app.getMasterTransaction().fetch(*(args.hash), ec);
103 }
104
105 if (auto e = std::get_if<TxSearched>(&v))
106 {
107 result.searchedAll = *e;
108 return {result, rpcTXN_NOT_FOUND};
109 }
110
111 auto [txn, meta] = std::get<TxPair>(v);
112
113 if (ec == rpcDB_DESERIALIZATION)
114 {
115 return {result, ec};
116 }
117 if (!txn)
118 {
119 return {result, rpcTXN_NOT_FOUND};
120 }
121
122 // populate transaction data
123 result.txn = txn;
124 if (txn->getLedger() == 0)
125 {
126 return {result, rpcSUCCESS};
127 }
128
130 context.ledgerMaster.getLedgerBySeq(txn->getLedger());
131
132 if (ledger && !ledger->open())
133 result.ledgerHash = ledger->info().hash;
134
135 if (ledger && meta)
136 {
137 if (args.binary)
138 {
139 result.meta = meta->getAsObject().getSerializer().getData();
140 }
141 else
142 {
143 result.meta = meta;
144 }
145 result.validated = isValidated(
146 context.ledgerMaster, ledger->info().seq, ledger->info().hash);
147 if (result.validated)
148 result.closeTime =
149 context.ledgerMaster.getCloseTimeBySeq(txn->getLedger());
150
151 // compute outgoing CTID
152 if (meta->getAsObject().isFieldPresent(sfTransactionIndex))
153 {
154 uint32_t lgrSeq = ledger->info().seq;
155 uint32_t txnIdx =
156 meta->getAsObject().getFieldU32(sfTransactionIndex);
157 uint32_t netID = context.app.config().NETWORK_ID;
158
159 if (txnIdx <= 0xFFFFU && netID < 0xFFFFU && lgrSeq < 0x0FFF'FFFFUL)
160 result.ctid =
161 RPC::encodeCTID(lgrSeq, (uint32_t)txnIdx, (uint32_t)netID);
162 }
163 }
164
165 return {result, rpcSUCCESS};
166}
167
171 TxArgs const& args,
172 RPC::JsonContext const& context)
173{
174 Json::Value response;
175 RPC::Status const& error = res.second;
176 TxResult const& result = res.first;
177 // handle errors
178 if (error.toErrorCode() != rpcSUCCESS)
179 {
180 if (error.toErrorCode() == rpcTXN_NOT_FOUND &&
182 {
183 response = Json::Value(Json::objectValue);
184 response[jss::searched_all] =
185 (result.searchedAll == TxSearched::all);
186 error.inject(response);
187 }
188 else
189 {
190 error.inject(response);
191 }
192 }
193 // no errors
194 else if (result.txn)
195 {
196 auto const& sttx = result.txn->getSTransaction();
197 if (context.apiVersion > 1)
198 {
199 constexpr auto optionsJson =
201 if (args.binary)
202 response[jss::tx_blob] = result.txn->getJson(optionsJson, true);
203 else
204 {
205 response[jss::tx_json] = result.txn->getJson(optionsJson);
207 response[jss::tx_json],
208 sttx->getTxnType(),
209 context.apiVersion);
210 }
211
212 // Note, result.ledgerHash is only set in a closed or validated
213 // ledger - as seen in `doTxHelp`
214 if (result.ledgerHash)
215 response[jss::ledger_hash] = to_string(*result.ledgerHash);
216
217 response[jss::hash] = to_string(result.txn->getID());
218 if (result.validated)
219 {
220 response[jss::ledger_index] = result.txn->getLedger();
221 if (result.closeTime)
222 response[jss::close_time_iso] =
223 to_string_iso(*result.closeTime);
224 }
225 }
226 else
227 {
228 response =
230 if (!args.binary)
232 response, sttx->getTxnType(), context.apiVersion);
233 }
234
235 // populate binary metadata
236 if (auto blob = std::get_if<Blob>(&result.meta))
237 {
238 XRPL_ASSERT(
239 args.binary, "ripple::populateJsonResponse : binary is set");
240 auto json_meta =
241 (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
242 response[json_meta] = strHex(makeSlice(*blob));
243 }
244 // populate meta data
245 else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
246 {
247 auto& meta = *m;
248 if (meta)
249 {
250 response[jss::meta] = meta->getJson(JsonOptions::none);
251 insertDeliveredAmount(
252 response[jss::meta], context, result.txn, *meta);
253 RPC::insertNFTSyntheticInJson(response, sttx, *meta);
254 RPC::insertMPTokenIssuanceID(response[jss::meta], sttx, *meta);
255 }
256 }
257 response[jss::validated] = result.validated;
258
259 if (result.ctid)
260 response[jss::ctid] = *(result.ctid);
261 }
262 return response;
263}
264
267{
268 if (!context.app.config().useTxTables())
269 return rpcError(rpcNOT_ENABLED);
270
271 // Deserialize and validate JSON arguments
272
273 TxArgs args;
274
275 if (context.params.isMember(jss::transaction) &&
276 context.params.isMember(jss::ctid))
277 // specifying both is ambiguous
279
280 if (context.params.isMember(jss::transaction))
281 {
282 uint256 hash;
283 if (!hash.parseHex(context.params[jss::transaction].asString()))
284 return rpcError(rpcNOT_IMPL);
285 args.hash = hash;
286 }
287 else if (context.params.isMember(jss::ctid))
288 {
289 auto ctid = RPC::decodeCTID(context.params[jss::ctid].asString());
290 if (!ctid)
292
293 auto const [lgr_seq, txn_idx, net_id] = *ctid;
294 if (net_id != context.app.config().NETWORK_ID)
295 {
297 out << "Wrong network. You should submit this request to a node "
298 "running on NetworkID: "
299 << net_id;
300 return RPC::make_error(rpcWRONG_NETWORK, out.str());
301 }
302 args.ctid = {lgr_seq, txn_idx};
303 }
304 else
306
307 args.binary = context.params.isMember(jss::binary) &&
308 context.params[jss::binary].asBool();
309
310 if (context.params.isMember(jss::min_ledger) &&
311 context.params.isMember(jss::max_ledger))
312 {
313 try
314 {
316 context.params[jss::min_ledger].asUInt(),
317 context.params[jss::max_ledger].asUInt());
318 }
319 catch (...)
320 {
321 // One of the calls to `asUInt ()` failed.
323 }
324 }
325
326 std::pair<TxResult, RPC::Status> res = doTxHelp(context, args);
327 return populateJsonResponse(res, args, context);
328}
329
330} // namespace ripple
Represents a JSON value.
Definition json_value.h:131
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
bool asBool() const
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
virtual LedgerMaster & getLedgerMaster()=0
virtual TransactionMaster & getMasterTransaction()=0
uint32_t NETWORK_ID
Definition Config.h:137
bool useTxTables() const
Definition Config.h:323
std::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
std::optional< uint256 > txnIdFromIndex(uint32_t ledgerSeq, uint32_t txnIndex)
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
std::variant< std::pair< std::shared_ptr< Transaction >, std::shared_ptr< TxMeta > >, TxSearched > fetch(uint256 const &, error_code_i &ec)
LedgerIndex getLedger() const
Definition Transaction.h:82
std::shared_ptr< STTx const > const & getSTransaction()
Definition Transaction.h:70
Json::Value getJson(JsonOptions options, bool binary=false) const
uint256 const & getID() const
Definition Transaction.h:76
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:484
T get_if(T... args)
T is_same_v
T make_pair(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
std::optional< std::string > encodeCTID(uint32_t ledgerSeq, uint32_t txnIndex, uint32_t networkID) noexcept
Encodes ledger sequence, transaction index, and network ID into a CTID string.
Definition CTID.h:34
Json::Value make_error(error_code_i code)
Returns a new json object that reflects the error code.
void insertNFTSyntheticInJson(Json::Value &, std::shared_ptr< STTx const > const &, TxMeta const &)
Adds common synthetic fields to transaction-related JSON responses.
void insertMPTokenIssuanceID(Json::Value &response, std::shared_ptr< STTx const > const &transaction, TxMeta const &transactionMeta)
void insertDeliverMax(Json::Value &tx_json, TxType txnType, unsigned int apiVersion)
Copy Amount field to DeliverMax field in transaction output JSON.
Definition DeliverMax.cpp:9
std::optional< std::tuple< uint32_t, uint16_t, uint16_t > > decodeCTID(T const ctid) noexcept
Decodes a CTID string or integer into its component parts.
Definition CTID.h:64
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
@ rpcEXCESSIVE_LGR_RANGE
Definition ErrorCodes.h:116
@ rpcINVALID_LGR_RANGE
Definition ErrorCodes.h:117
@ rpcTXN_NOT_FOUND
Definition ErrorCodes.h:61
@ rpcSUCCESS
Definition ErrorCodes.h:25
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:65
@ rpcNOT_IMPL
Definition ErrorCodes.h:112
@ rpcNOT_ENABLED
Definition ErrorCodes.h:40
@ rpcWRONG_NETWORK
Definition ErrorCodes.h:31
@ rpcDB_DESERIALIZATION
Definition ErrorCodes.h:115
std::pair< TxResult, RPC::Status > doTxHelp(RPC::Context &context, TxArgs args)
Definition Tx.cpp:55
Json::Value doTxJson(RPC::JsonContext &)
Definition Tx.cpp:266
Json::Value rpcError(int iError)
Definition RPCErr.cpp:12
boost::icl::closed_interval< T > ClosedInterval
A closed interval over the domain T.
Definition RangeSet.h:26
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
static bool isValidated(LedgerMaster &ledgerMaster, std::uint32_t seq, uint256 const &hash)
Definition Tx.cpp:24
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:225
std::string to_string_iso(date::sys_time< Duration > tp)
Definition chrono.h:73
Json::Value populateJsonResponse(std::pair< AccountTxResult, RPC::Status > const &res, AccountTxArgs const &args, RPC::JsonContext const &context)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition RangeSet.h:35
@ ledgerMaster
ledger master data for signing
The context of information needed to call an RPC.
Definition Context.h:20
unsigned int apiVersion
Definition Context.h:30
Application & app
Definition Context.h:22
LedgerMaster & ledgerMaster
Definition Context.h:25
Status represents the results of an operation that might fail.
Definition Status.h:21
std::optional< std::pair< uint32_t, uint16_t > > ctid
Definition Tx.cpp:49
bool binary
Definition Tx.cpp:50
std::optional< uint256 > hash
Definition Tx.cpp:48
std::optional< std::pair< uint32_t, uint32_t > > ledgerRange
Definition Tx.cpp:51
Transaction::pointer txn
Definition Tx.cpp:37
std::variant< std::shared_ptr< TxMeta >, Blob > meta
Definition Tx.cpp:38
std::optional< NetClock::time_point > closeTime
Definition Tx.cpp:41
std::optional< std::string > ctid
Definition Tx.cpp:40
std::optional< uint256 > ledgerHash
Definition Tx.cpp:42
TxSearched searchedAll
Definition Tx.cpp:43
bool validated
Definition Tx.cpp:39