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#include <xrpld/rpc/detail/RPCHelpers.h>
30#include <xrpld/rpc/detail/Tuning.h>
31
32#include <xrpl/json/json_value.h>
33#include <xrpl/ledger/ReadView.h>
34#include <xrpl/protocol/ErrorCodes.h>
35#include <xrpl/protocol/NFTSyntheticSerializer.h>
36#include <xrpl/protocol/RPCErr.h>
37#include <xrpl/protocol/UintTypes.h>
38#include <xrpl/protocol/jss.h>
39#include <xrpl/resource/Fees.h>
40
41namespace ripple {
42
48
51
52// parses args into a ledger specifier, or returns a Json object on error
55{
56 Json::Value response;
57 // if ledger_index_min or max is specified, then ledger_hash or ledger_index
58 // should not be specified. Error out if it is
59 if (context.apiVersion > 1u)
60 {
61 if ((params.isMember(jss::ledger_index_min) ||
62 params.isMember(jss::ledger_index_max)) &&
63 (params.isMember(jss::ledger_hash) ||
64 params.isMember(jss::ledger_index)))
65 {
66 RPC::Status status{rpcINVALID_PARAMS, "invalidParams"};
67 status.inject(response);
68 return response;
69 }
70 }
71 if (params.isMember(jss::ledger_index_min) ||
72 params.isMember(jss::ledger_index_max))
73 {
74 uint32_t min = params.isMember(jss::ledger_index_min) &&
75 params[jss::ledger_index_min].asInt() >= 0
76 ? params[jss::ledger_index_min].asUInt()
77 : 0;
78 uint32_t max = params.isMember(jss::ledger_index_max) &&
79 params[jss::ledger_index_max].asInt() >= 0
80 ? params[jss::ledger_index_max].asUInt()
81 : UINT32_MAX;
82
83 return LedgerRange{min, max};
84 }
85 else if (params.isMember(jss::ledger_hash))
86 {
87 auto& hashValue = params[jss::ledger_hash];
88 if (!hashValue.isString())
89 {
90 RPC::Status status{rpcINVALID_PARAMS, "ledgerHashNotString"};
91 status.inject(response);
92 return response;
93 }
94
95 LedgerHash hash;
96 if (!hash.parseHex(hashValue.asString()))
97 {
98 RPC::Status status{rpcINVALID_PARAMS, "ledgerHashMalformed"};
99 status.inject(response);
100 return response;
101 }
102 return hash;
103 }
104 else if (params.isMember(jss::ledger_index))
105 {
106 LedgerSpecifier ledger;
107 if (params[jss::ledger_index].isNumeric())
108 ledger = params[jss::ledger_index].asUInt();
109 else
110 {
111 std::string ledgerStr = params[jss::ledger_index].asString();
112
113 if (ledgerStr == "current" || ledgerStr.empty())
114 ledger = LedgerShortcut::CURRENT;
115 else if (ledgerStr == "closed")
116 ledger = LedgerShortcut::CLOSED;
117 else if (ledgerStr == "validated")
118 ledger = LedgerShortcut::VALIDATED;
119 else
120 {
121 RPC::Status status{
122 rpcINVALID_PARAMS, "ledger_index string malformed"};
123 status.inject(response);
124 return response;
125 }
126 }
127 return ledger;
128 }
130}
131
134 RPC::Context& context,
135 std::optional<LedgerSpecifier> const& ledgerSpecifier)
136{
137 std::uint32_t uValidatedMin;
138 std::uint32_t uValidatedMax;
139 bool bValidated =
140 context.ledgerMaster.getValidatedRange(uValidatedMin, uValidatedMax);
141
142 if (!bValidated)
143 {
144 // Don't have a validated ledger range.
145 if (context.apiVersion == 1)
146 return rpcLGR_IDXS_INVALID;
147 return rpcNOT_SYNCED;
148 }
149
150 std::uint32_t uLedgerMin = uValidatedMin;
151 std::uint32_t uLedgerMax = uValidatedMax;
152 // Does request specify a ledger or ledger range?
153 if (ledgerSpecifier)
154 {
155 auto const status = std::visit(
156 [&](auto const& ls) -> RPC::Status {
157 using T = std::decay_t<decltype(ls)>;
158 if constexpr (std::is_same_v<T, LedgerRange>)
159 {
160 // if ledger_index_min or ledger_index_max is out of
161 // valid ledger range, error out. exclude -1 as
162 // it is a valid input
163 if (context.apiVersion > 1u)
164 {
165 if ((ls.max > uValidatedMax && ls.max != -1) ||
166 (ls.min < uValidatedMin && ls.min != 0))
167 {
169 }
170 }
171 if (ls.min > uValidatedMin)
172 {
173 uLedgerMin = ls.min;
174 }
175 if (ls.max < uValidatedMax)
176 {
177 uLedgerMax = ls.max;
178 }
179 if (uLedgerMax < uLedgerMin)
180 {
181 if (context.apiVersion == 1)
182 return rpcLGR_IDXS_INVALID;
184 }
185 }
186 else
187 {
189 auto const status = getLedger(ledgerView, ls, context);
190 if (!ledgerView)
191 {
192 return status;
193 }
194
195 bool validated =
196 context.ledgerMaster.isValidated(*ledgerView);
197
198 if (!validated || ledgerView->info().seq > uValidatedMax ||
199 ledgerView->info().seq < uValidatedMin)
200 {
202 }
203 uLedgerMin = uLedgerMax = ledgerView->info().seq;
204 }
205 return RPC::Status::OK;
206 },
207 *ledgerSpecifier);
208
209 if (status)
210 return status;
211 }
212 return LedgerRange{uLedgerMin, uLedgerMax};
213}
214
217{
219
220 AccountTxResult result;
221
222 auto lgrRange = getLedgerRange(context, args.ledger);
223 if (auto stat = std::get_if<RPC::Status>(&lgrRange))
224 {
225 // An error occurred getting the requested ledger range
226 return {result, *stat};
227 }
228
229 result.ledgerRange = std::get<LedgerRange>(lgrRange);
230
231 result.marker = args.marker;
232
234 args.account,
235 result.ledgerRange.min,
236 result.ledgerRange.max,
237 result.marker,
238 args.limit,
239 isUnlimited(context.role)};
240
241 auto const db =
242 dynamic_cast<SQLiteDatabase*>(&context.app.getRelationalDatabase());
243
244 if (!db)
245 Throw<std::runtime_error>("Failed to get relational database");
246
247 if (args.binary)
248 {
249 if (args.forward)
250 {
251 auto [tx, marker] = db->oldestAccountTxPageB(options);
252 result.transactions = tx;
253 result.marker = marker;
254 }
255 else
256 {
257 auto [tx, marker] = db->newestAccountTxPageB(options);
258 result.transactions = tx;
259 result.marker = marker;
260 }
261 }
262 else
263 {
264 if (args.forward)
265 {
266 auto [tx, marker] = db->oldestAccountTxPage(options);
267 result.transactions = tx;
268 result.marker = marker;
269 }
270 else
271 {
272 auto [tx, marker] = db->newestAccountTxPage(options);
273 result.transactions = tx;
274 result.marker = marker;
275 }
276 }
277
278 result.limit = args.limit;
279 JLOG(context.j.debug()) << __func__ << " : finished";
280
281 return {result, rpcSUCCESS};
282}
283
287 AccountTxArgs const& args,
288 RPC::JsonContext const& context)
289{
290 Json::Value response;
291 RPC::Status const& error = res.second;
292 if (error.toErrorCode() != rpcSUCCESS)
293 {
294 error.inject(response);
295 }
296 else
297 {
298 AccountTxResult const& result = res.first;
299 response[jss::validated] = true;
300 response[jss::limit] = result.limit;
301 response[jss::account] = context.params[jss::account].asString();
302 response[jss::ledger_index_min] = result.ledgerRange.min;
303 response[jss::ledger_index_max] = result.ledgerRange.max;
304
305 Json::Value& jvTxns = (response[jss::transactions] = Json::arrayValue);
306
307 if (auto txnsData = std::get_if<TxnsData>(&result.transactions))
308 {
309 XRPL_ASSERT(
310 !args.binary,
311 "ripple::populateJsonResponse : binary is not set");
312
313 for (auto const& [txn, txnMeta] : *txnsData)
314 {
315 if (txn)
316 {
317 Json::Value& jvObj = jvTxns.append(Json::objectValue);
318 jvObj[jss::validated] = true;
319
320 auto const json_tx =
321 (context.apiVersion > 1 ? jss::tx_json : jss::tx);
322 if (context.apiVersion > 1)
323 {
324 jvObj[json_tx] = txn->getJson(
327 false);
328 jvObj[jss::hash] = to_string(txn->getID());
329 jvObj[jss::ledger_index] = txn->getLedger();
330 jvObj[jss::ledger_hash] =
332 txn->getLedger()));
333
334 if (auto closeTime =
336 txn->getLedger()))
337 jvObj[jss::close_time_iso] =
338 to_string_iso(*closeTime);
339 }
340 else
341 jvObj[json_tx] =
342 txn->getJson(JsonOptions::include_date);
343
344 auto const& sttx = txn->getSTransaction();
346 jvObj[json_tx], sttx->getTxnType(), context.apiVersion);
347 if (txnMeta)
348 {
349 jvObj[jss::meta] =
350 txnMeta->getJson(JsonOptions::include_date);
351 insertDeliveredAmount(
352 jvObj[jss::meta], context, txn, *txnMeta);
353 RPC::insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
355 jvObj[jss::meta], sttx, *txnMeta);
356 }
357 else
358 {
359 // LCOV_EXCL_START
360 UNREACHABLE(
361 "ripple::populateJsonResponse : missing "
362 "transaction medatata");
363 // LCOV_EXCL_STOP
364 }
365 }
366 }
367 }
368 else
369 {
370 XRPL_ASSERT(
371 args.binary, "ripple::populateJsonResponse : binary is set");
372
373 for (auto const& binaryData :
375 {
376 Json::Value& jvObj = jvTxns.append(Json::objectValue);
377
378 jvObj[jss::tx_blob] = strHex(std::get<0>(binaryData));
379 auto const json_meta =
380 (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
381 jvObj[json_meta] = strHex(std::get<1>(binaryData));
382 jvObj[jss::ledger_index] = std::get<2>(binaryData);
383 jvObj[jss::validated] = true;
384 }
385 }
386
387 if (result.marker)
388 {
389 response[jss::marker] = Json::objectValue;
390 response[jss::marker][jss::ledger] = result.marker->ledgerSeq;
391 response[jss::marker][jss::seq] = result.marker->txnSeq;
392 }
393 }
394
395 JLOG(context.j.debug()) << __func__ << " : finished";
396 return response;
397}
398
399// {
400// account: account,
401// ledger_index_min: ledger_index // optional, defaults to earliest
402// ledger_index_max: ledger_index, // optional, defaults to latest
403// binary: boolean, // optional, defaults to false
404// forward: boolean, // optional, defaults to false
405// limit: integer, // optional
406// marker: object {ledger: ledger_index, seq: txn_sequence} // optional,
407// resume previous query
408// }
411{
412 if (!context.app.config().useTxTables())
413 return rpcError(rpcNOT_ENABLED);
414
415 auto& params = context.params;
416 AccountTxArgs args;
417 Json::Value response;
418
419 // The document[https://xrpl.org/account_tx.html#account_tx] states that
420 // binary and forward params are both boolean values, however, assigning any
421 // string value works. Do not allow this. This check is for api Version 2
422 // onwards only
423 if (context.apiVersion > 1u && params.isMember(jss::binary) &&
424 !params[jss::binary].isBool())
425 {
426 return RPC::invalid_field_error(jss::binary);
427 }
428 if (context.apiVersion > 1u && params.isMember(jss::forward) &&
429 !params[jss::forward].isBool())
430 {
431 return RPC::invalid_field_error(jss::forward);
432 }
433
434 if (auto const err =
436 return *err;
437
438 args.binary = params.isMember(jss::binary) && params[jss::binary].asBool();
439 args.forward =
440 params.isMember(jss::forward) && params[jss::forward].asBool();
441
442 if (!params.isMember(jss::account))
443 return RPC::missing_field_error(jss::account);
444
445 if (!params[jss::account].isString())
446 return RPC::invalid_field_error(jss::account);
447
448 auto const account =
449 parseBase58<AccountID>(params[jss::account].asString());
450 if (!account)
452
453 args.account = *account;
454
455 auto parseRes = parseLedgerArgs(context, params);
456 if (auto jv = std::get_if<Json::Value>(&parseRes))
457 {
458 return *jv;
459 }
460 else
461 {
463 }
464
465 if (params.isMember(jss::marker))
466 {
467 auto& token = params[jss::marker];
468 if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
469 !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
470 !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
471 {
472 RPC::Status status{
474 "invalid marker. Provide ledger index via ledger field, and "
475 "transaction sequence number via seq field"};
476 status.inject(response);
477 return response;
478 }
479 args.marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
480 }
481
482 auto res = doAccountTxHelp(context, args);
483 JLOG(context.j.debug()) << __func__ << " populating response";
484 return populateJsonResponse(res, args, context);
485}
486
487} // 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
static LimitRange constexpr accountTx
Limits for the account_tx command.
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)
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.
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:54
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)