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#include <xrpld/rpc/detail/RPCHelpers.h>
32
33#include <xrpl/basics/ToString.h>
34#include <xrpl/protocol/ErrorCodes.h>
35#include <xrpl/protocol/NFTSyntheticSerializer.h>
36#include <xrpl/protocol/RPCErr.h>
37#include <xrpl/protocol/jss.h>
38
39#include <regex>
40
41namespace ripple {
42
43static bool
45{
46 if (!ledgerMaster.haveLedger(seq))
47 return false;
48
49 if (seq > ledgerMaster.getValidatedLedger()->info().seq)
50 return false;
51
52 return ledgerMaster.getHashBySeq(seq) == hash;
53}
54
56{
59 bool validated = false;
64};
65
66struct TxArgs
67{
70 bool binary = false;
72};
73
76{
77 TxResult result;
78
80
81 if (args.ledgerRange)
82 {
83 constexpr uint16_t MAX_RANGE = 1000;
84
85 if (args.ledgerRange->second < args.ledgerRange->first)
86 return {result, rpcINVALID_LGR_RANGE};
87
88 if (args.ledgerRange->second - args.ledgerRange->first > MAX_RANGE)
89 return {result, rpcEXCESSIVE_LGR_RANGE};
90
92 args.ledgerRange->first, args.ledgerRange->second);
93 }
94
95 auto ec{rpcSUCCESS};
96
97 using TxPair =
99
102
103 if (args.ctid)
104 {
105 args.hash = context.app.getLedgerMaster().txnIdFromIndex(
106 args.ctid->first, args.ctid->second);
107
108 if (args.hash)
109 range =
110 ClosedInterval<uint32_t>(args.ctid->first, args.ctid->second);
111 }
112
113 if (!args.hash)
114 return {result, rpcTXN_NOT_FOUND};
115
116 if (args.ledgerRange)
117 {
118 v = context.app.getMasterTransaction().fetch(*(args.hash), range, ec);
119 }
120 else
121 {
122 v = context.app.getMasterTransaction().fetch(*(args.hash), ec);
123 }
124
125 if (auto e = std::get_if<TxSearched>(&v))
126 {
127 result.searchedAll = *e;
128 return {result, rpcTXN_NOT_FOUND};
129 }
130
131 auto [txn, meta] = std::get<TxPair>(v);
132
133 if (ec == rpcDB_DESERIALIZATION)
134 {
135 return {result, ec};
136 }
137 if (!txn)
138 {
139 return {result, rpcTXN_NOT_FOUND};
140 }
141
142 // populate transaction data
143 result.txn = txn;
144 if (txn->getLedger() == 0)
145 {
146 return {result, rpcSUCCESS};
147 }
148
150 context.ledgerMaster.getLedgerBySeq(txn->getLedger());
151
152 if (ledger && !ledger->open())
153 result.ledgerHash = ledger->info().hash;
154
155 if (ledger && meta)
156 {
157 if (args.binary)
158 {
159 result.meta = meta->getAsObject().getSerializer().getData();
160 }
161 else
162 {
163 result.meta = meta;
164 }
165 result.validated = isValidated(
166 context.ledgerMaster, ledger->info().seq, ledger->info().hash);
167 if (result.validated)
168 result.closeTime =
169 context.ledgerMaster.getCloseTimeBySeq(txn->getLedger());
170
171 // compute outgoing CTID
172 if (meta->getAsObject().isFieldPresent(sfTransactionIndex))
173 {
174 uint32_t lgrSeq = ledger->info().seq;
175 uint32_t txnIdx =
176 meta->getAsObject().getFieldU32(sfTransactionIndex);
177 uint32_t netID = context.app.config().NETWORK_ID;
178
179 if (txnIdx <= 0xFFFFU && netID < 0xFFFFU && lgrSeq < 0x0FFF'FFFFUL)
180 result.ctid =
181 RPC::encodeCTID(lgrSeq, (uint32_t)txnIdx, (uint32_t)netID);
182 }
183 }
184
185 return {result, rpcSUCCESS};
186}
187
191 TxArgs const& args,
192 RPC::JsonContext const& context)
193{
194 Json::Value response;
195 RPC::Status const& error = res.second;
196 TxResult const& result = res.first;
197 // handle errors
198 if (error.toErrorCode() != rpcSUCCESS)
199 {
200 if (error.toErrorCode() == rpcTXN_NOT_FOUND &&
202 {
203 response = Json::Value(Json::objectValue);
204 response[jss::searched_all] =
205 (result.searchedAll == TxSearched::all);
206 error.inject(response);
207 }
208 else
209 {
210 error.inject(response);
211 }
212 }
213 // no errors
214 else if (result.txn)
215 {
216 auto const& sttx = result.txn->getSTransaction();
217 if (context.apiVersion > 1)
218 {
219 constexpr auto optionsJson =
221 if (args.binary)
222 response[jss::tx_blob] = result.txn->getJson(optionsJson, true);
223 else
224 {
225 response[jss::tx_json] = result.txn->getJson(optionsJson);
227 response[jss::tx_json],
228 sttx->getTxnType(),
229 context.apiVersion);
230 }
231
232 // Note, result.ledgerHash is only set in a closed or validated
233 // ledger - as seen in `doTxHelp`
234 if (result.ledgerHash)
235 response[jss::ledger_hash] = to_string(*result.ledgerHash);
236
237 response[jss::hash] = to_string(result.txn->getID());
238 if (result.validated)
239 {
240 response[jss::ledger_index] = result.txn->getLedger();
241 if (result.closeTime)
242 response[jss::close_time_iso] =
243 to_string_iso(*result.closeTime);
244 }
245 }
246 else
247 {
248 response =
250 if (!args.binary)
252 response, sttx->getTxnType(), context.apiVersion);
253 }
254
255 // populate binary metadata
256 if (auto blob = std::get_if<Blob>(&result.meta))
257 {
258 XRPL_ASSERT(
259 args.binary, "ripple::populateJsonResponse : binary is set");
260 auto json_meta =
261 (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
262 response[json_meta] = strHex(makeSlice(*blob));
263 }
264 // populate meta data
265 else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
266 {
267 auto& meta = *m;
268 if (meta)
269 {
270 response[jss::meta] = meta->getJson(JsonOptions::none);
271 insertDeliveredAmount(
272 response[jss::meta], context, result.txn, *meta);
273 insertNFTSyntheticInJson(response, sttx, *meta);
274 RPC::insertMPTokenIssuanceID(response[jss::meta], sttx, *meta);
275 }
276 }
277 response[jss::validated] = result.validated;
278
279 if (result.ctid)
280 response[jss::ctid] = *(result.ctid);
281 }
282 return response;
283}
284
287{
288 if (!context.app.config().useTxTables())
289 return rpcError(rpcNOT_ENABLED);
290
291 // Deserialize and validate JSON arguments
292
293 TxArgs args;
294
295 if (context.params.isMember(jss::transaction) &&
296 context.params.isMember(jss::ctid))
297 // specifying both is ambiguous
299
300 if (context.params.isMember(jss::transaction))
301 {
302 uint256 hash;
303 if (!hash.parseHex(context.params[jss::transaction].asString()))
304 return rpcError(rpcNOT_IMPL);
305 args.hash = hash;
306 }
307 else if (context.params.isMember(jss::ctid))
308 {
309 auto ctid = RPC::decodeCTID(context.params[jss::ctid].asString());
310 if (!ctid)
312
313 auto const [lgr_seq, txn_idx, net_id] = *ctid;
314 if (net_id != context.app.config().NETWORK_ID)
315 {
317 out << "Wrong network. You should submit this request to a node "
318 "running on NetworkID: "
319 << net_id;
320 return RPC::make_error(rpcWRONG_NETWORK, out.str());
321 }
322 args.ctid = {lgr_seq, txn_idx};
323 }
324 else
326
327 args.binary = context.params.isMember(jss::binary) &&
328 context.params[jss::binary].asBool();
329
330 if (context.params.isMember(jss::min_ledger) &&
331 context.params.isMember(jss::max_ledger))
332 {
333 try
334 {
336 context.params[jss::min_ledger].asUInt(),
337 context.params[jss::max_ledger].asUInt());
338 }
339 catch (...)
340 {
341 // One of the calls to `asUInt ()` failed.
343 }
344 }
345
346 std::pair<TxResult, RPC::Status> res = doTxHelp(context, args);
347 return populateJsonResponse(res, args, context);
348}
349
350} // namespace ripple
Represents a JSON value.
Definition: json_value.h:148
UInt asUInt() const
Definition: json_value.cpp:551
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:475
bool asBool() const
Definition: json_value.cpp:625
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:949
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:343
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:101
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 make_pair(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
std::optional< std::tuple< uint32_t, uint16_t, uint16_t > > decodeCTID(const T ctid) noexcept
Definition: CTID.h:60
std::optional< std::string > encodeCTID(uint32_t ledgerSeq, uint32_t txnIndex, uint32_t networkID) noexcept
Definition: CTID.h:43
Json::Value make_error(error_code_i code)
Returns a new json object that reflects the error code.
Definition: ErrorCodes.cpp:186
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:28
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
@ 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
void insertNFTSyntheticInJson(Json::Value &, std::shared_ptr< STTx const > const &, TxMeta const &)
Adds common synthetic fields to transaction-related JSON responses.
std::pair< TxResult, RPC::Status > doTxHelp(RPC::Context &context, TxArgs args)
Definition: Tx.cpp:75
Json::Value doTxJson(RPC::JsonContext &)
Definition: Tx.cpp:286
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:44
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)
Definition: AccountTx.cpp:283
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
Json::Value params
Definition: Context.h:63
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:69
bool binary
Definition: Tx.cpp:70
std::optional< uint256 > hash
Definition: Tx.cpp:68
std::optional< std::pair< uint32_t, uint32_t > > ledgerRange
Definition: Tx.cpp:71
Transaction::pointer txn
Definition: Tx.cpp:57
std::variant< std::shared_ptr< TxMeta >, Blob > meta
Definition: Tx.cpp:58
std::optional< NetClock::time_point > closeTime
Definition: Tx.cpp:61
std::optional< std::string > ctid
Definition: Tx.cpp:60
std::optional< uint256 > ledgerHash
Definition: Tx.cpp:62
TxSearched searchedAll
Definition: Tx.cpp:63
bool validated
Definition: Tx.cpp:59