rippled
Loading...
Searching...
No Matches
Tx.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2014 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or 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#include <xrpld/app/ledger/LedgerMaster.h>
21#include <xrpld/app/ledger/TransactionMaster.h>
22#include <xrpld/app/misc/DeliverMax.h>
23#include <xrpld/app/misc/NetworkOPs.h>
24#include <xrpld/app/misc/Transaction.h>
25#include <xrpld/app/rdb/RelationalDatabase.h>
26#include <xrpld/rpc/CTID.h>
27#include <xrpld/rpc/Context.h>
28#include <xrpld/rpc/DeliveredAmount.h>
29#include <xrpld/rpc/GRPCHandlers.h>
30#include <xrpld/rpc/MPTokenIssuanceID.h>
31
32#include <xrpl/basics/ToString.h>
33#include <xrpl/protocol/ErrorCodes.h>
34#include <xrpl/protocol/NFTSyntheticSerializer.h>
35#include <xrpl/protocol/RPCErr.h>
36#include <xrpl/protocol/jss.h>
37
38#include <regex>
39
40namespace ripple {
41
42static bool
44{
45 if (!ledgerMaster.haveLedger(seq))
46 return false;
47
48 if (seq > ledgerMaster.getValidatedLedger()->info().seq)
49 return false;
50
51 return ledgerMaster.getHashBySeq(seq) == hash;
52}
53
64
72
75{
76 TxResult result;
77
79
80 if (args.ledgerRange)
81 {
82 constexpr uint16_t MAX_RANGE = 1000;
83
84 if (args.ledgerRange->second < args.ledgerRange->first)
85 return {result, rpcINVALID_LGR_RANGE};
86
87 if (args.ledgerRange->second - args.ledgerRange->first > MAX_RANGE)
88 return {result, rpcEXCESSIVE_LGR_RANGE};
89
91 args.ledgerRange->first, args.ledgerRange->second);
92 }
93
94 auto ec{rpcSUCCESS};
95
96 using TxPair =
98
101
102 if (args.ctid)
103 {
104 args.hash = context.app.getLedgerMaster().txnIdFromIndex(
105 args.ctid->first, args.ctid->second);
106
107 if (args.hash)
108 range =
109 ClosedInterval<uint32_t>(args.ctid->first, args.ctid->second);
110 }
111
112 if (!args.hash)
113 return {result, rpcTXN_NOT_FOUND};
114
115 if (args.ledgerRange)
116 {
117 v = context.app.getMasterTransaction().fetch(*(args.hash), range, ec);
118 }
119 else
120 {
121 v = context.app.getMasterTransaction().fetch(*(args.hash), ec);
122 }
123
124 if (auto e = std::get_if<TxSearched>(&v))
125 {
126 result.searchedAll = *e;
127 return {result, rpcTXN_NOT_FOUND};
128 }
129
130 auto [txn, meta] = std::get<TxPair>(v);
131
132 if (ec == rpcDB_DESERIALIZATION)
133 {
134 return {result, ec};
135 }
136 if (!txn)
137 {
138 return {result, rpcTXN_NOT_FOUND};
139 }
140
141 // populate transaction data
142 result.txn = txn;
143 if (txn->getLedger() == 0)
144 {
145 return {result, rpcSUCCESS};
146 }
147
149 context.ledgerMaster.getLedgerBySeq(txn->getLedger());
150
151 if (ledger && !ledger->open())
152 result.ledgerHash = ledger->info().hash;
153
154 if (ledger && meta)
155 {
156 if (args.binary)
157 {
158 result.meta = meta->getAsObject().getSerializer().getData();
159 }
160 else
161 {
162 result.meta = meta;
163 }
164 result.validated = isValidated(
165 context.ledgerMaster, ledger->info().seq, ledger->info().hash);
166 if (result.validated)
167 result.closeTime =
168 context.ledgerMaster.getCloseTimeBySeq(txn->getLedger());
169
170 // compute outgoing CTID
171 if (meta->getAsObject().isFieldPresent(sfTransactionIndex))
172 {
173 uint32_t lgrSeq = ledger->info().seq;
174 uint32_t txnIdx =
175 meta->getAsObject().getFieldU32(sfTransactionIndex);
176 uint32_t netID = context.app.config().NETWORK_ID;
177
178 if (txnIdx <= 0xFFFFU && netID < 0xFFFFU && lgrSeq < 0x0FFF'FFFFUL)
179 result.ctid =
180 RPC::encodeCTID(lgrSeq, (uint32_t)txnIdx, (uint32_t)netID);
181 }
182 }
183
184 return {result, rpcSUCCESS};
185}
186
190 TxArgs const& args,
191 RPC::JsonContext const& context)
192{
193 Json::Value response;
194 RPC::Status const& error = res.second;
195 TxResult const& result = res.first;
196 // handle errors
197 if (error.toErrorCode() != rpcSUCCESS)
198 {
199 if (error.toErrorCode() == rpcTXN_NOT_FOUND &&
201 {
202 response = Json::Value(Json::objectValue);
203 response[jss::searched_all] =
204 (result.searchedAll == TxSearched::all);
205 error.inject(response);
206 }
207 else
208 {
209 error.inject(response);
210 }
211 }
212 // no errors
213 else if (result.txn)
214 {
215 auto const& sttx = result.txn->getSTransaction();
216 if (context.apiVersion > 1)
217 {
218 constexpr auto optionsJson =
220 if (args.binary)
221 response[jss::tx_blob] = result.txn->getJson(optionsJson, true);
222 else
223 {
224 response[jss::tx_json] = result.txn->getJson(optionsJson);
226 response[jss::tx_json],
227 sttx->getTxnType(),
228 context.apiVersion);
229 }
230
231 // Note, result.ledgerHash is only set in a closed or validated
232 // ledger - as seen in `doTxHelp`
233 if (result.ledgerHash)
234 response[jss::ledger_hash] = to_string(*result.ledgerHash);
235
236 response[jss::hash] = to_string(result.txn->getID());
237 if (result.validated)
238 {
239 response[jss::ledger_index] = result.txn->getLedger();
240 if (result.closeTime)
241 response[jss::close_time_iso] =
242 to_string_iso(*result.closeTime);
243 }
244 }
245 else
246 {
247 response =
249 if (!args.binary)
251 response, sttx->getTxnType(), context.apiVersion);
252 }
253
254 // populate binary metadata
255 if (auto blob = std::get_if<Blob>(&result.meta))
256 {
257 XRPL_ASSERT(
258 args.binary, "ripple::populateJsonResponse : binary is set");
259 auto json_meta =
260 (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
261 response[json_meta] = strHex(makeSlice(*blob));
262 }
263 // populate meta data
264 else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
265 {
266 auto& meta = *m;
267 if (meta)
268 {
269 response[jss::meta] = meta->getJson(JsonOptions::none);
270 insertDeliveredAmount(
271 response[jss::meta], context, result.txn, *meta);
272 RPC::insertNFTSyntheticInJson(response, sttx, *meta);
273 RPC::insertMPTokenIssuanceID(response[jss::meta], sttx, *meta);
274 }
275 }
276 response[jss::validated] = result.validated;
277
278 if (result.ctid)
279 response[jss::ctid] = *(result.ctid);
280 }
281 return response;
282}
283
286{
287 if (!context.app.config().useTxTables())
288 return rpcError(rpcNOT_ENABLED);
289
290 // Deserialize and validate JSON arguments
291
292 TxArgs args;
293
294 if (context.params.isMember(jss::transaction) &&
295 context.params.isMember(jss::ctid))
296 // specifying both is ambiguous
298
299 if (context.params.isMember(jss::transaction))
300 {
301 uint256 hash;
302 if (!hash.parseHex(context.params[jss::transaction].asString()))
303 return rpcError(rpcNOT_IMPL);
304 args.hash = hash;
305 }
306 else if (context.params.isMember(jss::ctid))
307 {
308 auto ctid = RPC::decodeCTID(context.params[jss::ctid].asString());
309 if (!ctid)
311
312 auto const [lgr_seq, txn_idx, net_id] = *ctid;
313 if (net_id != context.app.config().NETWORK_ID)
314 {
316 out << "Wrong network. You should submit this request to a node "
317 "running on NetworkID: "
318 << net_id;
319 return RPC::make_error(rpcWRONG_NETWORK, out.str());
320 }
321 args.ctid = {lgr_seq, txn_idx};
322 }
323 else
325
326 args.binary = context.params.isMember(jss::binary) &&
327 context.params[jss::binary].asBool();
328
329 if (context.params.isMember(jss::min_ledger) &&
330 context.params.isMember(jss::max_ledger))
331 {
332 try
333 {
335 context.params[jss::min_ledger].asUInt(),
336 context.params[jss::max_ledger].asUInt());
337 }
338 catch (...)
339 {
340 // One of the calls to `asUInt ()` failed.
342 }
343 }
344
345 std::pair<TxResult, RPC::Status> res = doTxHelp(context, args);
346 return populateJsonResponse(res, args, context);
347}
348
349} // namespace ripple
Represents a JSON value.
Definition json_value.h:149
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:156
bool useTxTables() const
Definition Config.h:342
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
std::shared_ptr< STTx const > const & getSTransaction()
Definition Transaction.h:89
Json::Value getJson(JsonOptions options, bool binary=false) const
uint256 const & getID() const
Definition Transaction.h:95
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:503
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:45
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:53
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.
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:83
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
@ rpcEXCESSIVE_LGR_RANGE
Definition ErrorCodes.h:135
@ rpcINVALID_LGR_RANGE
Definition ErrorCodes.h:136
@ rpcTXN_NOT_FOUND
Definition ErrorCodes.h:80
@ rpcSUCCESS
Definition ErrorCodes.h:44
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:84
@ rpcNOT_IMPL
Definition ErrorCodes.h:131
@ rpcNOT_ENABLED
Definition ErrorCodes.h:59
@ rpcWRONG_NETWORK
Definition ErrorCodes.h:50
@ rpcDB_DESERIALIZATION
Definition ErrorCodes.h:134
std::pair< TxResult, RPC::Status > doTxHelp(RPC::Context &context, TxArgs args)
Definition Tx.cpp:74
Json::Value doTxJson(RPC::JsonContext &)
Definition Tx.cpp:285
Json::Value rpcError(int iError)
Definition RPCErr.cpp:31
boost::icl::closed_interval< T > ClosedInterval
A closed interval over the domain T.
Definition RangeSet.h:45
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:30
static bool isValidated(LedgerMaster &ledgerMaster, std::uint32_t seq, uint256 const &hash)
Definition Tx.cpp:43
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:244
std::string to_string_iso(date::sys_time< Duration > tp)
Definition chrono.h:92
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:630
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition RangeSet.h:54
@ ledgerMaster
ledger master data for signing
The context of information needed to call an RPC.
Definition Context.h:39
unsigned int apiVersion
Definition Context.h:49
Application & app
Definition Context.h:41
LedgerMaster & ledgerMaster
Definition Context.h:44
Status represents the results of an operation that might fail.
Definition Status.h:40
std::optional< std::pair< uint32_t, uint16_t > > ctid
Definition Tx.cpp:68
bool binary
Definition Tx.cpp:69
std::optional< uint256 > hash
Definition Tx.cpp:67
std::optional< std::pair< uint32_t, uint32_t > > ledgerRange
Definition Tx.cpp:70
Transaction::pointer txn
Definition Tx.cpp:56
std::variant< std::shared_ptr< TxMeta >, Blob > meta
Definition Tx.cpp:57
std::optional< NetClock::time_point > closeTime
Definition Tx.cpp:60
std::optional< std::string > ctid
Definition Tx.cpp:59
std::optional< uint256 > ledgerHash
Definition Tx.cpp:61
TxSearched searchedAll
Definition Tx.cpp:62
bool validated
Definition Tx.cpp:58