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 UNREACHABLE(
357 "ripple::populateJsonResponse : missing "
358 "transaction medatata");
359 }
360 }
361 }
362 else
363 {
364 XRPL_ASSERT(
365 args.binary, "ripple::populateJsonResponse : binary is set");
366
367 for (auto const& binaryData :
369 {
370 Json::Value& jvObj = jvTxns.append(Json::objectValue);
371
372 jvObj[jss::tx_blob] = strHex(std::get<0>(binaryData));
373 auto const json_meta =
374 (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
375 jvObj[json_meta] = strHex(std::get<1>(binaryData));
376 jvObj[jss::ledger_index] = std::get<2>(binaryData);
377 jvObj[jss::validated] = true;
378 }
379 }
380
381 if (result.marker)
382 {
383 response[jss::marker] = Json::objectValue;
384 response[jss::marker][jss::ledger] = result.marker->ledgerSeq;
385 response[jss::marker][jss::seq] = result.marker->txnSeq;
386 }
387 }
388
389 JLOG(context.j.debug()) << __func__ << " : finished";
390 return response;
391}
392
393// {
394// account: account,
395// ledger_index_min: ledger_index // optional, defaults to earliest
396// ledger_index_max: ledger_index, // optional, defaults to latest
397// binary: boolean, // optional, defaults to false
398// forward: boolean, // optional, defaults to false
399// limit: integer, // optional
400// marker: object {ledger: ledger_index, seq: txn_sequence} // optional,
401// resume previous query
402// }
405{
406 if (!context.app.config().useTxTables())
407 return rpcError(rpcNOT_ENABLED);
408
409 auto& params = context.params;
410 AccountTxArgs args;
411 Json::Value response;
412
413 // The document[https://xrpl.org/account_tx.html#account_tx] states that
414 // binary and forward params are both boolean values, however, assigning any
415 // string value works. Do not allow this. This check is for api Version 2
416 // onwards only
417 if (context.apiVersion > 1u && params.isMember(jss::binary) &&
418 !params[jss::binary].isBool())
419 {
420 return RPC::invalid_field_error(jss::binary);
421 }
422 if (context.apiVersion > 1u && params.isMember(jss::forward) &&
423 !params[jss::forward].isBool())
424 {
425 return RPC::invalid_field_error(jss::forward);
426 }
427
428 args.limit = params.isMember(jss::limit) ? params[jss::limit].asUInt() : 0;
429 args.binary = params.isMember(jss::binary) && params[jss::binary].asBool();
430 args.forward =
431 params.isMember(jss::forward) && params[jss::forward].asBool();
432
433 if (!params.isMember(jss::account))
434 return RPC::missing_field_error(jss::account);
435
436 if (!params[jss::account].isString())
437 return RPC::invalid_field_error(jss::account);
438
439 auto const account =
440 parseBase58<AccountID>(params[jss::account].asString());
441 if (!account)
443
444 args.account = *account;
445
446 auto parseRes = parseLedgerArgs(context, params);
447 if (auto jv = std::get_if<Json::Value>(&parseRes))
448 {
449 return *jv;
450 }
451 else
452 {
454 }
455
456 if (params.isMember(jss::marker))
457 {
458 auto& token = params[jss::marker];
459 if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
460 !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
461 !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
462 {
463 RPC::Status status{
465 "invalid marker. Provide ledger index via ledger field, and "
466 "transaction sequence number via seq field"};
467 status.inject(response);
468 return response;
469 }
470 args.marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
471 }
472
473 auto res = doAccountTxHelp(context, args);
474 JLOG(context.j.debug()) << __func__ << " populating response";
475 return populateJsonResponse(res, args, context);
476}
477
478} // 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)