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