xrpld
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/Transaction.h>
5#include <xrpld/rpc/CTID.h>
6#include <xrpld/rpc/Context.h>
7#include <xrpld/rpc/DeliveredAmount.h>
8#include <xrpld/rpc/MPTokenIssuanceID.h>
9#include <xrpld/rpc/Status.h>
10
11#include <xrpl/basics/Blob.h>
12#include <xrpl/basics/RangeSet.h>
13#include <xrpl/basics/Slice.h>
14#include <xrpl/basics/base_uint.h>
15#include <xrpl/basics/chrono.h>
16#include <xrpl/basics/strHex.h>
17#include <xrpl/beast/utility/instrumentation.h>
18#include <xrpl/core/NetworkIDService.h>
19#include <xrpl/json/json_value.h>
20#include <xrpl/protocol/ErrorCodes.h>
21#include <xrpl/protocol/NFTSyntheticSerializer.h>
22#include <xrpl/protocol/RPCErr.h>
23#include <xrpl/protocol/SField.h>
24#include <xrpl/protocol/STBase.h>
25#include <xrpl/protocol/TxSearched.h>
26#include <xrpl/protocol/jss.h>
27#include <xrpl/rdb/RelationalDatabase.h>
28#include <xrpl/server/NetworkOPs.h>
29
30#include <cstdint>
31#include <memory>
32#include <optional>
33#include <sstream>
34#include <string>
35#include <utility>
36#include <variant>
37
38namespace xrpl {
39
40static bool
41isValidated(LedgerMaster& ledgerMaster, std::uint32_t seq, uint256 const& hash)
42{
43 if (!ledgerMaster.haveLedger(seq))
44 return false;
45
46 if (seq > ledgerMaster.getValidatedLedger()->header().seq)
47 return false;
48
49 return ledgerMaster.getHashBySeq(seq) == hash;
50}
51
62
70
73{
74 TxResult result;
75
77
78 if (args.ledgerRange)
79 {
80 static constexpr uint16_t kMaxRange = 1000;
81
82 if (args.ledgerRange->second < args.ledgerRange->first)
83 return {result, RpcInvalidLgrRange};
84
85 if (args.ledgerRange->second - args.ledgerRange->first > kMaxRange)
86 return {result, RpcExcessiveLgrRange};
87
88 range = ClosedInterval<uint32_t>(args.ledgerRange->first, args.ledgerRange->second);
89 }
90
91 auto ec{RpcSuccess};
92
94
97
98 if (args.ctid)
99 {
100 args.hash =
101 context.app.getLedgerMaster().txnIdFromIndex(args.ctid->first, args.ctid->second);
102
103 if (args.hash)
104 range = ClosedInterval<uint32_t>(args.ctid->first, args.ctid->second);
105 }
106
107 if (!args.hash)
108 return {result, RpcTxnNotFound};
109
110 if (args.ledgerRange)
111 {
112 v = context.app.getMasterTransaction().fetch(*(args.hash), range, ec);
113 }
114 else
115 {
116 v = context.app.getMasterTransaction().fetch(*(args.hash), ec);
117 }
118
119 if (auto e = std::get_if<TxSearched>(&v))
120 {
121 result.searchedAll = *e;
122 return {result, RpcTxnNotFound};
123 }
124
125 auto [txn, meta] = std::get<TxPair>(v);
126
127 if (ec == RpcDbDeserialization)
128 {
129 return {result, ec};
130 }
131 if (!txn)
132 {
133 return {result, RpcTxnNotFound};
134 }
135
136 // populate transaction data
137 result.txn = txn;
138 if (txn->getLedger() == 0)
139 {
140 return {result, RpcSuccess};
141 }
142
143 std::shared_ptr<Ledger const> const ledger =
144 context.ledgerMaster.getLedgerBySeq(txn->getLedger());
145
146 if (ledger && !ledger->open())
147 result.ledgerHash = ledger->header().hash;
148
149 if (ledger && meta)
150 {
151 if (args.binary)
152 {
153 result.meta = meta->getAsObject().getSerializer().getData();
154 }
155 else
156 {
157 result.meta = meta;
158 }
159 result.validated =
160 isValidated(context.ledgerMaster, ledger->header().seq, ledger->header().hash);
161 if (result.validated)
162 result.closeTime = context.ledgerMaster.getCloseTimeBySeq(txn->getLedger());
163
164 // compute outgoing CTID
165 if (meta->getAsObject().isFieldPresent(sfTransactionIndex))
166 {
167 uint32_t const lgrSeq = ledger->header().seq;
168 uint32_t const txnIdx = meta->getAsObject().getFieldU32(sfTransactionIndex);
169 uint32_t const netID = context.app.getNetworkIDService().getNetworkID();
170
171 if (txnIdx <= 0xFFFFU && netID < 0xFFFFU && lgrSeq < 0x0FFF'FFFFUL)
172 result.ctid = RPC::encodeCTID(lgrSeq, txnIdx, netID);
173 }
174 }
175
176 return {result, RpcSuccess};
177}
178
182 TxArgs const& args,
183 RPC::JsonContext const& context)
184{
185 json::Value response;
186 RPC::Status const& error = res.second;
187 TxResult const& result = res.first;
188 // handle errors
189 if (error.toErrorCode() != RpcSuccess)
190 {
191 if (error.toErrorCode() == RpcTxnNotFound && result.searchedAll != TxSearched::Unknown)
192 {
194 response[jss::searched_all] = (result.searchedAll == TxSearched::All);
195 error.inject(response);
196 }
197 else
198 {
199 error.inject(response);
200 }
201 }
202 // no errors
203 else if (result.txn)
204 {
205 auto const& sttx = result.txn->getSTransaction();
206 if (context.apiVersion > 1)
207 {
208 static constexpr auto kOptionsJson =
211 if (args.binary)
212 {
213 response[jss::tx_blob] = result.txn->getJson(kOptionsJson, true);
214 }
215 else
216 {
217 response[jss::tx_json] = result.txn->getJson(kOptionsJson);
219 response[jss::tx_json], sttx->getTxnType(), context.apiVersion);
220 }
221
222 // Note, result.ledgerHash is only set in a closed or validated
223 // ledger - as seen in `doTxHelp`
224 if (result.ledgerHash)
225 response[jss::ledger_hash] = to_string(*result.ledgerHash);
226
227 response[jss::hash] = to_string(result.txn->getID());
228 if (result.validated)
229 {
230 response[jss::ledger_index] = result.txn->getLedger();
231 if (result.closeTime)
232 response[jss::close_time_iso] = toStringIso(*result.closeTime);
233 }
234 }
235 else
236 {
237 response = result.txn->getJson(JsonOptions::Values::IncludeDate, args.binary);
238 if (!args.binary)
239 RPC::insertDeliverMax(response, sttx->getTxnType(), context.apiVersion);
240 }
241
242 // populate binary metadata
243 if (auto blob = std::get_if<Blob>(&result.meta))
244 {
245 XRPL_ASSERT(args.binary, "xrpl::populateJsonResponse : binary is set");
246 auto jsonMeta = (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
247 response[jsonMeta] = strHex(makeSlice(*blob));
248 }
249 // populate meta data
250 else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
251 {
252 auto& meta = *m;
253 if (meta)
254 {
255 response[jss::meta] = meta->getJson(JsonOptions::Values::None);
256 insertDeliveredAmount(response[jss::meta], context, result.txn, *meta);
257 RPC::insertNFTSyntheticInJson(response, sttx, *meta);
258 RPC::insertMPTokenIssuanceID(response[jss::meta], sttx, *meta);
259 }
260 }
261 response[jss::validated] = result.validated;
262
263 if (result.ctid)
264 response[jss::ctid] = *(result.ctid);
265 }
266 return response;
267}
268
271{
272 if (!context.app.config().useTxTables())
273 return rpcError(RpcNotEnabled);
274
275 // Deserialize and validate JSON arguments
276
277 TxArgs args;
278
279 if (context.params.isMember(jss::transaction) && context.params.isMember(jss::ctid))
280 {
281 // specifying both is ambiguous
283 }
284
285 if (context.params.isMember(jss::transaction))
286 {
287 uint256 hash;
288 if (!hash.parseHex(context.params[jss::transaction].asString()))
289 return rpcError(RpcNotImpl);
290 args.hash = hash;
291 }
292 else if (context.params.isMember(jss::ctid))
293 {
294 auto ctid = RPC::decodeCTID(context.params[jss::ctid].asString());
295 if (!ctid)
297
298 auto const [lgr_seq, txn_idx, net_id] = *ctid;
299 if (net_id != context.app.getNetworkIDService().getNetworkID())
300 {
302 out << "Wrong network. You should submit this request to a node "
303 "running on NetworkID: "
304 << net_id;
305 return RPC::makeError(RpcWrongNetwork, out.str());
306 }
307 args.ctid = {lgr_seq, txn_idx};
308 }
309 else
310 {
312 }
313
314 args.binary = context.params.isMember(jss::binary) && context.params[jss::binary].asBool();
315
316 if (context.params.isMember(jss::min_ledger) && context.params.isMember(jss::max_ledger))
317 {
318 try
319 {
321 context.params[jss::min_ledger].asUInt(), context.params[jss::max_ledger].asUInt());
322 }
323 catch (...)
324 {
325 // One of the calls to `asUInt ()` failed.
327 }
328 }
329
330 std::pair<TxResult, RPC::Status> const res = doTxHelp(context, args);
331 return populateJsonResponse(res, args, context);
332}
333
334} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
bool asBool() const
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:507
bool useTxTables() const
Definition Config.h:322
bool haveLedger(std::uint32_t seq)
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
uint256 getHashBySeq(std::uint32_t index)
Get a ledger's hash by sequence number using the cache.
std::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
std::shared_ptr< Ledger const > getValidatedLedger()
std::optional< uint256 > txnIdFromIndex(uint32_t ledgerSeq, uint32_t txnIndex)
virtual std::uint32_t getNetworkID() const noexcept=0
Get the configured network ID.
virtual TransactionMaster & getMasterTransaction()=0
virtual NetworkIDService & getNetworkIDService()=0
virtual LedgerMaster & getLedgerMaster()=0
std::variant< std::pair< std::shared_ptr< Transaction >, std::shared_ptr< TxMeta > >, TxSearched > fetch(uint256 const &, ErrorCodeI &ec)
json::Value getJson(JsonOptions options, bool binary=false) const
LedgerIndex getLedger() const
Definition Transaction.h:76
uint256 const & getID() const
Definition Transaction.h:70
std::shared_ptr< Transaction > pointer
Definition Transaction.h:44
std::shared_ptr< STTx const > const & getSTransaction()
Definition Transaction.h:64
T get_if(T... args)
T make_pair(T... args)
@ Object
object value (collection of name/value pairs).
Definition json_value.h:26
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:31
void insertMPTokenIssuanceID(json::Value &response, std::shared_ptr< STTx const > const &transaction, TxMeta const &transactionMeta)
void insertDeliverMax(json::Value &txJson, TxType txnType, unsigned int apiVersion)
Copy Amount field to DeliverMax field in transaction output JSON.
Definition DeliverMax.cpp:9
json::Value makeError(ErrorCodeI 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.
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:58
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ RpcNotImpl
Definition ErrorCodes.h:113
@ RpcSuccess
Definition ErrorCodes.h:26
@ RpcExcessiveLgrRange
Definition ErrorCodes.h:117
@ RpcTxnNotFound
Definition ErrorCodes.h:62
@ RpcNotEnabled
Definition ErrorCodes.h:41
@ RpcInvalidParams
Definition ErrorCodes.h:66
@ RpcWrongNetwork
Definition ErrorCodes.h:32
@ RpcDbDeserialization
Definition ErrorCodes.h:116
@ RpcInvalidLgrRange
Definition ErrorCodes.h:118
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition RangeSet.h:34
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
static bool isValidated(LedgerMaster &ledgerMaster, std::uint32_t seq, uint256 const &hash)
Definition Tx.cpp:41
std::pair< TxResult, RPC::Status > doTxHelp(RPC::Context &context, TxArgs args)
Definition Tx.cpp:72
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
std::string toStringIso(date::sys_time< Duration > tp)
Definition chrono.h:68
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
boost::icl::closed_interval< T > ClosedInterval
A closed interval over the domain T.
Definition RangeSet.h:25
json::Value populateJsonResponse(std::pair< AccountTxResult, RPC::Status > const &res, AccountTxArgs const &args, RPC::JsonContext const &context)
TxSearched
Definition TxSearched.h:5
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:10
BaseUInt< 256 > uint256
Definition base_uint.h:562
std::enable_if_t< std::is_same_v< T, char >||std::is_same_v< T, unsigned char >, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:215
json::Value doTxJson(RPC::JsonContext &)
Definition Tx.cpp:270
T str(T... args)
unsigned int underlying_t
Definition STBase.h:18
The context of information needed to call an RPC.
Definition Context.h:19
Application & app
Definition Context.h:21
unsigned int apiVersion
Definition Context.h:29
LedgerMaster & ledgerMaster
Definition Context.h:24
json::Value params
Definition Context.h:43
Status represents the results of an operation that might fail.
Definition Status.h:19
void inject(json::Value &object) const
Apply the Status to a JsonObject.
Definition Status.h:89
ErrorCodeI toErrorCode() const
Returns the Status as an error_code_i.
Definition Status.h:80
std::optional< std::pair< uint32_t, uint32_t > > ledgerRange
Definition Tx.cpp:68
std::optional< std::pair< uint32_t, uint16_t > > ctid
Definition Tx.cpp:66
bool binary
Definition Tx.cpp:67
std::optional< uint256 > hash
Definition Tx.cpp:65
std::optional< uint256 > ledgerHash
Definition Tx.cpp:59
Transaction::pointer txn
Definition Tx.cpp:54
TxSearched searchedAll
Definition Tx.cpp:60
std::optional< NetClock::time_point > closeTime
Definition Tx.cpp:58
bool validated
Definition Tx.cpp:56
std::optional< std::string > ctid
Definition Tx.cpp:57
std::variant< std::shared_ptr< TxMeta >, Blob > meta
Definition Tx.cpp:55