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#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 <charconv>
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 uint32_t lgrSeq = ledger->info().seq;
173 uint32_t txnIdx = meta->getAsObject().getFieldU32(sfTransactionIndex);
174 uint32_t netID = context.app.config().NETWORK_ID;
175
176 if (txnIdx <= 0xFFFFU && netID < 0xFFFFU && lgrSeq < 0x0FFF'FFFFUL)
177 result.ctid =
178 RPC::encodeCTID(lgrSeq, (uint16_t)txnIdx, (uint16_t)netID);
179 }
180
181 return {result, rpcSUCCESS};
182}
183
187 TxArgs const& args,
188 RPC::JsonContext const& context)
189{
190 Json::Value response;
191 RPC::Status const& error = res.second;
192 TxResult const& result = res.first;
193 // handle errors
194 if (error.toErrorCode() != rpcSUCCESS)
195 {
196 if (error.toErrorCode() == rpcTXN_NOT_FOUND &&
198 {
199 response = Json::Value(Json::objectValue);
200 response[jss::searched_all] =
201 (result.searchedAll == TxSearched::all);
202 error.inject(response);
203 }
204 else
205 {
206 error.inject(response);
207 }
208 }
209 // no errors
210 else if (result.txn)
211 {
212 auto const& sttx = result.txn->getSTransaction();
213 if (context.apiVersion > 1)
214 {
215 constexpr auto optionsJson =
217 if (args.binary)
218 response[jss::tx_blob] = result.txn->getJson(optionsJson, true);
219 else
220 {
221 response[jss::tx_json] = result.txn->getJson(optionsJson);
223 response[jss::tx_json],
224 sttx->getTxnType(),
225 context.apiVersion);
226 }
227
228 // Note, result.ledgerHash is only set in a closed or validated
229 // ledger - as seen in `doTxHelp`
230 if (result.ledgerHash)
231 response[jss::ledger_hash] = to_string(*result.ledgerHash);
232
233 response[jss::hash] = to_string(result.txn->getID());
234 if (result.validated)
235 {
236 response[jss::ledger_index] = result.txn->getLedger();
237 if (result.closeTime)
238 response[jss::close_time_iso] =
239 to_string_iso(*result.closeTime);
240 }
241 }
242 else
243 {
244 response =
246 if (!args.binary)
248 response, sttx->getTxnType(), context.apiVersion);
249 }
250
251 // populate binary metadata
252 if (auto blob = std::get_if<Blob>(&result.meta))
253 {
254 XRPL_ASSERT(
255 args.binary, "ripple::populateJsonResponse : binary is set");
256 auto json_meta =
257 (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
258 response[json_meta] = strHex(makeSlice(*blob));
259 }
260 // populate meta data
261 else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
262 {
263 auto& meta = *m;
264 if (meta)
265 {
266 response[jss::meta] = meta->getJson(JsonOptions::none);
267 insertDeliveredAmount(
268 response[jss::meta], context, result.txn, *meta);
269 insertNFTSyntheticInJson(response, sttx, *meta);
270 RPC::insertMPTokenIssuanceID(response[jss::meta], sttx, *meta);
271 }
272 }
273 response[jss::validated] = result.validated;
274
275 if (result.ctid)
276 response[jss::ctid] = *(result.ctid);
277 }
278 return response;
279}
280
283{
284 if (!context.app.config().useTxTables())
285 return rpcError(rpcNOT_ENABLED);
286
287 // Deserialize and validate JSON arguments
288
289 TxArgs args;
290
291 if (context.params.isMember(jss::transaction) &&
292 context.params.isMember(jss::ctid))
293 // specifying both is ambiguous
295
296 if (context.params.isMember(jss::transaction))
297 {
298 uint256 hash;
299 if (!hash.parseHex(context.params[jss::transaction].asString()))
300 return rpcError(rpcNOT_IMPL);
301 args.hash = hash;
302 }
303 else if (context.params.isMember(jss::ctid))
304 {
305 auto ctid = RPC::decodeCTID(context.params[jss::ctid].asString());
306 if (!ctid)
308
309 auto const [lgr_seq, txn_idx, net_id] = *ctid;
310 if (net_id != context.app.config().NETWORK_ID)
311 {
313 out << "Wrong network. You should submit this request to a node "
314 "running on NetworkID: "
315 << net_id;
316 return RPC::make_error(rpcWRONG_NETWORK, out.str());
317 }
318 args.ctid = {lgr_seq, txn_idx};
319 }
320 else
322
323 args.binary = context.params.isMember(jss::binary) &&
324 context.params[jss::binary].asBool();
325
326 if (context.params.isMember(jss::min_ledger) &&
327 context.params.isMember(jss::max_ledger))
328 {
329 try
330 {
332 context.params[jss::min_ledger].asUInt(),
333 context.params[jss::max_ledger].asUInt());
334 }
335 catch (...)
336 {
337 // One of the calls to `asUInt ()` failed.
339 }
340 }
341
342 std::pair<TxResult, RPC::Status> res = doTxHelp(context, args);
343 return populateJsonResponse(res, args, context);
344}
345
346} // namespace ripple
Represents a JSON value.
Definition: json_value.h:147
UInt asUInt() const
Definition: json_value.cpp:545
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469
bool asBool() const
Definition: json_value.cpp:619
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:943
virtual Config & config()=0
virtual LedgerMaster & getLedgerMaster()=0
virtual TransactionMaster & getMasterTransaction()=0
uint32_t NETWORK_ID
Definition: Config.h:163
bool useTxTables() const
Definition: Config.h:350
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:502
T get_if(T... args)
T make_pair(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
std::optional< std::tuple< uint32_t, uint16_t, uint16_t > > decodeCTID(const T ctid) noexcept
Definition: CTID.h:54
Json::Value make_error(error_code_i code)
Returns a new json object that reflects the error code.
Definition: ErrorCodes.cpp:180
void insertMPTokenIssuanceID(Json::Value &response, std::shared_ptr< STTx const > const &transaction, TxMeta const &transactionMeta)
std::optional< std::string > encodeCTID(uint32_t ledger_seq, uint16_t txn_index, uint16_t network_id) noexcept
Definition: CTID.h:34
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:282
Json::Value rpcError(int iError)
Definition: RPCErr.cpp:29
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:243
std::string to_string_iso(date::sys_time< Duration > tp)
Definition: chrono.h:93
Json::Value populateJsonResponse(std::pair< AccountTxResult, RPC::Status > const &res, AccountTxArgs const &args, RPC::JsonContext const &context)
Definition: AccountTx.cpp:286
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
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:40
unsigned int apiVersion
Definition: Context.h:50
Application & app
Definition: Context.h:42
LedgerMaster & ledgerMaster
Definition: Context.h:45
Json::Value params
Definition: Context.h:64
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