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
65
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 RPC::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: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: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)
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: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