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