rippled
Loading...
Searching...
No Matches
AccountTx.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/main/Application.h>
22#include <xrpld/app/misc/DeliverMax.h>
23#include <xrpld/app/misc/Transaction.h>
24#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
25#include <xrpld/rpc/Context.h>
26#include <xrpld/rpc/DeliveredAmount.h>
27#include <xrpld/rpc/MPTokenIssuanceID.h>
28#include <xrpld/rpc/Role.h>
29
30#include <xrpl/json/json_value.h>
31#include <xrpl/ledger/ReadView.h>
32#include <xrpl/protocol/ErrorCodes.h>
33#include <xrpl/protocol/NFTSyntheticSerializer.h>
34#include <xrpl/protocol/RPCErr.h>
35#include <xrpl/protocol/UintTypes.h>
36#include <xrpl/protocol/jss.h>
37#include <xrpl/resource/Fees.h>
38
39namespace ripple {
40
46
49
50// parses args into a ledger specifier, or returns a Json object on error
53{
54 Json::Value response;
55 // if ledger_index_min or max is specified, then ledger_hash or ledger_index
56 // should not be specified. Error out if it is
57 if (context.apiVersion > 1u)
58 {
59 if ((params.isMember(jss::ledger_index_min) ||
60 params.isMember(jss::ledger_index_max)) &&
61 (params.isMember(jss::ledger_hash) ||
62 params.isMember(jss::ledger_index)))
63 {
64 RPC::Status status{rpcINVALID_PARAMS, "invalidParams"};
65 status.inject(response);
66 return response;
67 }
68 }
69 if (params.isMember(jss::ledger_index_min) ||
70 params.isMember(jss::ledger_index_max))
71 {
72 uint32_t min = params.isMember(jss::ledger_index_min) &&
73 params[jss::ledger_index_min].asInt() >= 0
74 ? params[jss::ledger_index_min].asUInt()
75 : 0;
76 uint32_t max = params.isMember(jss::ledger_index_max) &&
77 params[jss::ledger_index_max].asInt() >= 0
78 ? params[jss::ledger_index_max].asUInt()
79 : UINT32_MAX;
80
81 return LedgerRange{min, max};
82 }
83 else if (params.isMember(jss::ledger_hash))
84 {
85 auto& hashValue = params[jss::ledger_hash];
86 if (!hashValue.isString())
87 {
88 RPC::Status status{rpcINVALID_PARAMS, "ledgerHashNotString"};
89 status.inject(response);
90 return response;
91 }
92
93 LedgerHash hash;
94 if (!hash.parseHex(hashValue.asString()))
95 {
96 RPC::Status status{rpcINVALID_PARAMS, "ledgerHashMalformed"};
97 status.inject(response);
98 return response;
99 }
100 return hash;
101 }
102 else if (params.isMember(jss::ledger_index))
103 {
104 LedgerSpecifier ledger;
105 if (params[jss::ledger_index].isNumeric())
106 ledger = params[jss::ledger_index].asUInt();
107 else
108 {
109 std::string ledgerStr = params[jss::ledger_index].asString();
110
111 if (ledgerStr == "current" || ledgerStr.empty())
112 ledger = LedgerShortcut::CURRENT;
113 else if (ledgerStr == "closed")
114 ledger = LedgerShortcut::CLOSED;
115 else if (ledgerStr == "validated")
116 ledger = LedgerShortcut::VALIDATED;
117 else
118 {
119 RPC::Status status{
120 rpcINVALID_PARAMS, "ledger_index string malformed"};
121 status.inject(response);
122 return response;
123 }
124 }
125 return ledger;
126 }
128}
129
132 RPC::Context& context,
133 std::optional<LedgerSpecifier> const& ledgerSpecifier)
134{
135 std::uint32_t uValidatedMin;
136 std::uint32_t uValidatedMax;
137 bool bValidated =
138 context.ledgerMaster.getValidatedRange(uValidatedMin, uValidatedMax);
139
140 if (!bValidated)
141 {
142 // Don't have a validated ledger range.
143 if (context.apiVersion == 1)
144 return rpcLGR_IDXS_INVALID;
145 return rpcNOT_SYNCED;
146 }
147
148 std::uint32_t uLedgerMin = uValidatedMin;
149 std::uint32_t uLedgerMax = uValidatedMax;
150 // Does request specify a ledger or ledger range?
151 if (ledgerSpecifier)
152 {
153 auto const status = std::visit(
154 [&](auto const& ls) -> RPC::Status {
155 using T = std::decay_t<decltype(ls)>;
156 if constexpr (std::is_same_v<T, LedgerRange>)
157 {
158 // if ledger_index_min or ledger_index_max is out of
159 // valid ledger range, error out. exclude -1 as
160 // it is a valid input
161 if (context.apiVersion > 1u)
162 {
163 if ((ls.max > uValidatedMax && ls.max != -1) ||
164 (ls.min < uValidatedMin && ls.min != 0))
165 {
167 }
168 }
169 if (ls.min > uValidatedMin)
170 {
171 uLedgerMin = ls.min;
172 }
173 if (ls.max < uValidatedMax)
174 {
175 uLedgerMax = ls.max;
176 }
177 if (uLedgerMax < uLedgerMin)
178 {
179 if (context.apiVersion == 1)
180 return rpcLGR_IDXS_INVALID;
182 }
183 }
184 else
185 {
187 auto const status = getLedger(ledgerView, ls, context);
188 if (!ledgerView)
189 {
190 return status;
191 }
192
193 bool validated =
194 context.ledgerMaster.isValidated(*ledgerView);
195
196 if (!validated || ledgerView->info().seq > uValidatedMax ||
197 ledgerView->info().seq < uValidatedMin)
198 {
200 }
201 uLedgerMin = uLedgerMax = ledgerView->info().seq;
202 }
203 return RPC::Status::OK;
204 },
205 *ledgerSpecifier);
206
207 if (status)
208 return status;
209 }
210 return LedgerRange{uLedgerMin, uLedgerMax};
211}
212
215{
217
218 AccountTxResult result;
219
220 auto lgrRange = getLedgerRange(context, args.ledger);
221 if (auto stat = std::get_if<RPC::Status>(&lgrRange))
222 {
223 // An error occurred getting the requested ledger range
224 return {result, *stat};
225 }
226
227 result.ledgerRange = std::get<LedgerRange>(lgrRange);
228
229 result.marker = args.marker;
230
232 args.account,
233 result.ledgerRange.min,
234 result.ledgerRange.max,
235 result.marker,
236 args.limit,
237 isUnlimited(context.role)};
238
239 auto const db =
240 dynamic_cast<SQLiteDatabase*>(&context.app.getRelationalDatabase());
241
242 if (!db)
243 Throw<std::runtime_error>("Failed to get relational database");
244
245 if (args.binary)
246 {
247 if (args.forward)
248 {
249 auto [tx, marker] = db->oldestAccountTxPageB(options);
250 result.transactions = tx;
251 result.marker = marker;
252 }
253 else
254 {
255 auto [tx, marker] = db->newestAccountTxPageB(options);
256 result.transactions = tx;
257 result.marker = marker;
258 }
259 }
260 else
261 {
262 if (args.forward)
263 {
264 auto [tx, marker] = db->oldestAccountTxPage(options);
265 result.transactions = tx;
266 result.marker = marker;
267 }
268 else
269 {
270 auto [tx, marker] = db->newestAccountTxPage(options);
271 result.transactions = tx;
272 result.marker = marker;
273 }
274 }
275
276 result.limit = args.limit;
277 JLOG(context.j.debug()) << __func__ << " : finished";
278
279 return {result, rpcSUCCESS};
280}
281
285 AccountTxArgs const& args,
286 RPC::JsonContext const& context)
287{
288 Json::Value response;
289 RPC::Status const& error = res.second;
290 if (error.toErrorCode() != rpcSUCCESS)
291 {
292 error.inject(response);
293 }
294 else
295 {
296 AccountTxResult const& result = res.first;
297 response[jss::validated] = true;
298 response[jss::limit] = result.limit;
299 response[jss::account] = context.params[jss::account].asString();
300 response[jss::ledger_index_min] = result.ledgerRange.min;
301 response[jss::ledger_index_max] = result.ledgerRange.max;
302
303 Json::Value& jvTxns = (response[jss::transactions] = Json::arrayValue);
304
305 if (auto txnsData = std::get_if<TxnsData>(&result.transactions))
306 {
307 XRPL_ASSERT(
308 !args.binary,
309 "ripple::populateJsonResponse : binary is not set");
310
311 for (auto const& [txn, txnMeta] : *txnsData)
312 {
313 if (txn)
314 {
315 Json::Value& jvObj = jvTxns.append(Json::objectValue);
316 jvObj[jss::validated] = true;
317
318 auto const json_tx =
319 (context.apiVersion > 1 ? jss::tx_json : jss::tx);
320 if (context.apiVersion > 1)
321 {
322 jvObj[json_tx] = txn->getJson(
325 false);
326 jvObj[jss::hash] = to_string(txn->getID());
327 jvObj[jss::ledger_index] = txn->getLedger();
328 jvObj[jss::ledger_hash] =
330 txn->getLedger()));
331
332 if (auto closeTime =
334 txn->getLedger()))
335 jvObj[jss::close_time_iso] =
336 to_string_iso(*closeTime);
337 }
338 else
339 jvObj[json_tx] =
340 txn->getJson(JsonOptions::include_date);
341
342 auto const& sttx = txn->getSTransaction();
344 jvObj[json_tx], sttx->getTxnType(), context.apiVersion);
345 if (txnMeta)
346 {
347 jvObj[jss::meta] =
348 txnMeta->getJson(JsonOptions::include_date);
349 insertDeliveredAmount(
350 jvObj[jss::meta], context, txn, *txnMeta);
351 RPC::insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
353 jvObj[jss::meta], sttx, *txnMeta);
354 }
355 else
356 {
357 // LCOV_EXCL_START
358 UNREACHABLE(
359 "ripple::populateJsonResponse : missing "
360 "transaction medatata");
361 // LCOV_EXCL_STOP
362 }
363 }
364 }
365 }
366 else
367 {
368 XRPL_ASSERT(
369 args.binary, "ripple::populateJsonResponse : binary is set");
370
371 for (auto const& binaryData :
373 {
374 Json::Value& jvObj = jvTxns.append(Json::objectValue);
375
376 jvObj[jss::tx_blob] = strHex(std::get<0>(binaryData));
377 auto const json_meta =
378 (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
379 jvObj[json_meta] = strHex(std::get<1>(binaryData));
380 jvObj[jss::ledger_index] = std::get<2>(binaryData);
381 jvObj[jss::validated] = true;
382 }
383 }
384
385 if (result.marker)
386 {
387 response[jss::marker] = Json::objectValue;
388 response[jss::marker][jss::ledger] = result.marker->ledgerSeq;
389 response[jss::marker][jss::seq] = result.marker->txnSeq;
390 }
391 }
392
393 JLOG(context.j.debug()) << __func__ << " : finished";
394 return response;
395}
396
397// {
398// account: account,
399// ledger_index_min: ledger_index // optional, defaults to earliest
400// ledger_index_max: ledger_index, // optional, defaults to latest
401// binary: boolean, // optional, defaults to false
402// forward: boolean, // optional, defaults to false
403// limit: integer, // optional
404// marker: object {ledger: ledger_index, seq: txn_sequence} // optional,
405// resume previous query
406// }
409{
410 if (!context.app.config().useTxTables())
411 return rpcError(rpcNOT_ENABLED);
412
413 auto& params = context.params;
414 AccountTxArgs args;
415 Json::Value response;
416
417 // The document[https://xrpl.org/account_tx.html#account_tx] states that
418 // binary and forward params are both boolean values, however, assigning any
419 // string value works. Do not allow this. This check is for api Version 2
420 // onwards only
421 if (context.apiVersion > 1u && params.isMember(jss::binary) &&
422 !params[jss::binary].isBool())
423 {
424 return RPC::invalid_field_error(jss::binary);
425 }
426 if (context.apiVersion > 1u && params.isMember(jss::forward) &&
427 !params[jss::forward].isBool())
428 {
429 return RPC::invalid_field_error(jss::forward);
430 }
431
432 args.limit = params.isMember(jss::limit) ? params[jss::limit].asUInt() : 0;
433 args.binary = params.isMember(jss::binary) && params[jss::binary].asBool();
434 args.forward =
435 params.isMember(jss::forward) && params[jss::forward].asBool();
436
437 if (!params.isMember(jss::account))
438 return RPC::missing_field_error(jss::account);
439
440 if (!params[jss::account].isString())
441 return RPC::invalid_field_error(jss::account);
442
443 auto const account =
444 parseBase58<AccountID>(params[jss::account].asString());
445 if (!account)
447
448 args.account = *account;
449
450 auto parseRes = parseLedgerArgs(context, params);
451 if (auto jv = std::get_if<Json::Value>(&parseRes))
452 {
453 return *jv;
454 }
455 else
456 {
458 }
459
460 if (params.isMember(jss::marker))
461 {
462 auto& token = params[jss::marker];
463 if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
464 !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
465 !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
466 {
467 RPC::Status status{
469 "invalid marker. Provide ledger index via ledger field, and "
470 "transaction sequence number via seq field"};
471 status.inject(response);
472 return response;
473 }
474 args.marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
475 }
476
477 auto res = doAccountTxHelp(context, args);
478 JLOG(context.j.debug()) << __func__ << " populating response";
479 return populateJsonResponse(res, args, context);
480}
481
482} // namespace ripple
Represents a JSON value.
Definition json_value.h:149
Value & append(Value const &value)
Append value to array at the end.
Int asInt() 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.
Stream debug() const
Definition Journal.h:328
virtual Config & config()=0
virtual RelationalDatabase & getRelationalDatabase()=0
bool useTxTables() const
Definition Config.h:342
std::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
bool isValidated(ReadView const &ledger)
uint256 getHashBySeq(std::uint32_t index)
Get a ledger's hash by sequence number using the cache.
bool getValidatedRange(std::uint32_t &minVal, std::uint32_t &maxVal)
std::vector< txnMetaLedgerType > MetaTxsList
std::vector< AccountTx > AccountTxs
RPC::LedgerShortcut LedgerShortcut
std::tuple< Blob, Blob, std::uint32_t > txnMetaLedgerType
std::variant< LedgerRange, LedgerShortcut, LedgerSequence, LedgerHash > LedgerSpecifier
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:503
T empty(T... args)
T is_same_v
@ arrayValue
array value (ordered list)
Definition json_value.h:44
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:45
@ uintValue
unsigned integer value
Definition json_value.h:40
Json::Value invalid_field_error(std::string const &name)
Definition ErrorCodes.h:325
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.
Json::Value missing_field_error(std::string const &name)
Definition ErrorCodes.h:283
Charge const feeMediumBurdenRPC
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::variant< LedgerRange, RPC::Status > getLedgerRange(RPC::Context &context, std::optional< LedgerSpecifier > const &ledgerSpecifier)
@ rpcINVALID_LGR_RANGE
Definition ErrorCodes.h:136
@ rpcLGR_NOT_VALIDATED
Definition ErrorCodes.h:73
@ rpcACT_MALFORMED
Definition ErrorCodes.h:90
@ rpcSUCCESS
Definition ErrorCodes.h:44
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:84
@ rpcLGR_IDXS_INVALID
Definition ErrorCodes.h:112
@ rpcLGR_IDX_MALFORMED
Definition ErrorCodes.h:113
@ rpcNOT_ENABLED
Definition ErrorCodes.h:59
@ rpcNOT_SYNCED
Definition ErrorCodes.h:67
Json::Value doAccountTxJson(RPC::JsonContext &context)
Json::Value rpcError(int iError)
Definition RPCErr.cpp:31
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:125
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:30
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
std::variant< std::optional< LedgerSpecifier >, Json::Value > parseLedgerArgs(RPC::Context &context, Json::Value const &params)
Definition AccountTx.cpp:52
std::pair< AccountTxResult, RPC::Status > doAccountTxHelp(RPC::Context &context, AccountTxArgs const &args)
The context of information needed to call an RPC.
Definition Context.h:39
unsigned int apiVersion
Definition Context.h:49
Resource::Charge & loadType
Definition Context.h:42
Application & app
Definition Context.h:41
beast::Journal const j
Definition Context.h:40
LedgerMaster & ledgerMaster
Definition Context.h:44
Status represents the results of an operation that might fail.
Definition Status.h:40
static constexpr Code OK
Definition Status.h:46
std::optional< AccountTxMarker > marker
std::optional< LedgerSpecifier > ledger
std::optional< AccountTxMarker > marker
std::variant< AccountTxs, MetaTxsList > transactions
T visit(T... args)