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 xrpl {
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) || params.isMember(jss::ledger_index_max)) &&
43 (params.isMember(jss::ledger_hash) || params.isMember(jss::ledger_index)))
44 {
45 RPC::Status status{rpcINVALID_PARAMS, "invalidParams"};
46 status.inject(response);
47 return response;
48 }
49 }
50 if (params.isMember(jss::ledger_index_min) || params.isMember(jss::ledger_index_max))
51 {
52 uint32_t min = params.isMember(jss::ledger_index_min) && params[jss::ledger_index_min].asInt() >= 0
53 ? params[jss::ledger_index_min].asUInt()
54 : 0;
55 uint32_t max = params.isMember(jss::ledger_index_max) && params[jss::ledger_index_max].asInt() >= 0
56 ? params[jss::ledger_index_max].asUInt()
57 : UINT32_MAX;
58
59 return LedgerRange{min, max};
60 }
61 else if (params.isMember(jss::ledger_hash))
62 {
63 auto& hashValue = params[jss::ledger_hash];
64 if (!hashValue.isString())
65 {
66 RPC::Status status{rpcINVALID_PARAMS, "ledgerHashNotString"};
67 status.inject(response);
68 return response;
69 }
70
71 LedgerHash hash;
72 if (!hash.parseHex(hashValue.asString()))
73 {
74 RPC::Status status{rpcINVALID_PARAMS, "ledgerHashMalformed"};
75 status.inject(response);
76 return response;
77 }
78 return hash;
79 }
80 else if (params.isMember(jss::ledger_index))
81 {
82 LedgerSpecifier ledger;
83 if (params[jss::ledger_index].isNumeric())
84 ledger = params[jss::ledger_index].asUInt();
85 else
86 {
87 std::string ledgerStr = params[jss::ledger_index].asString();
88
89 if (ledgerStr == "current" || ledgerStr.empty())
90 ledger = LedgerShortcut::Current;
91 else if (ledgerStr == "closed")
92 ledger = LedgerShortcut::Closed;
93 else if (ledgerStr == "validated")
94 ledger = LedgerShortcut::Validated;
95 else
96 {
97 RPC::Status status{rpcINVALID_PARAMS, "ledger_index string malformed"};
98 status.inject(response);
99 return response;
100 }
101 }
102 return ledger;
103 }
105}
106
109{
110 std::uint32_t uValidatedMin;
111 std::uint32_t uValidatedMax;
112 bool bValidated = context.ledgerMaster.getValidatedRange(uValidatedMin, uValidatedMax);
113
114 if (!bValidated)
115 {
116 // Don't have a validated ledger range.
117 if (context.apiVersion == 1)
118 return rpcLGR_IDXS_INVALID;
119 return rpcNOT_SYNCED;
120 }
121
122 std::uint32_t uLedgerMin = uValidatedMin;
123 std::uint32_t uLedgerMax = uValidatedMax;
124 // Does request specify a ledger or ledger range?
125 if (ledgerSpecifier)
126 {
127 auto const status = std::visit(
128 [&](auto const& ls) -> RPC::Status {
129 using T = std::decay_t<decltype(ls)>;
130 if constexpr (std::is_same_v<T, LedgerRange>)
131 {
132 // if ledger_index_min or ledger_index_max is out of
133 // valid ledger range, error out. exclude -1 as
134 // it is a valid input
135 if (context.apiVersion > 1u)
136 {
137 if ((ls.max > uValidatedMax && ls.max != -1) || (ls.min < uValidatedMin && ls.min != 0))
138 {
140 }
141 }
142 if (ls.min > uValidatedMin)
143 {
144 uLedgerMin = ls.min;
145 }
146 if (ls.max < uValidatedMax)
147 {
148 uLedgerMax = ls.max;
149 }
150 if (uLedgerMax < uLedgerMin)
151 {
152 if (context.apiVersion == 1)
153 return rpcLGR_IDXS_INVALID;
155 }
156 }
157 else
158 {
160 auto const status = getLedger(ledgerView, ls, context);
161 if (!ledgerView)
162 {
163 return status;
164 }
165
166 bool validated = context.ledgerMaster.isValidated(*ledgerView);
167
168 if (!validated || ledgerView->header().seq > uValidatedMax ||
169 ledgerView->header().seq < uValidatedMin)
170 {
172 }
173 uLedgerMin = uLedgerMax = ledgerView->header().seq;
174 }
175 return RPC::Status::OK;
176 },
177 *ledgerSpecifier);
178
179 if (status)
180 return status;
181 }
182 return LedgerRange{uLedgerMin, uLedgerMax};
183}
184
187{
189
190 AccountTxResult result;
191
192 auto lgrRange = getLedgerRange(context, args.ledger);
193 if (auto stat = std::get_if<RPC::Status>(&lgrRange))
194 {
195 // An error occurred getting the requested ledger range
196 return {result, *stat};
197 }
198
199 result.ledgerRange = std::get<LedgerRange>(lgrRange);
200
201 result.marker = args.marker;
202
204 args.account,
205 result.ledgerRange.min,
206 result.ledgerRange.max,
207 result.marker,
208 args.limit,
209 isUnlimited(context.role)};
210
211 auto const db = dynamic_cast<SQLiteDatabase*>(&context.app.getRelationalDatabase());
212
213 if (!db)
214 Throw<std::runtime_error>("Failed to get relational database");
215
216 if (args.binary)
217 {
218 if (args.forward)
219 {
220 auto [tx, marker] = db->oldestAccountTxPageB(options);
221 result.transactions = tx;
222 result.marker = marker;
223 }
224 else
225 {
226 auto [tx, marker] = db->newestAccountTxPageB(options);
227 result.transactions = tx;
228 result.marker = marker;
229 }
230 }
231 else
232 {
233 if (args.forward)
234 {
235 auto [tx, marker] = db->oldestAccountTxPage(options);
236 result.transactions = tx;
237 result.marker = marker;
238 }
239 else
240 {
241 auto [tx, marker] = db->newestAccountTxPage(options);
242 result.transactions = tx;
243 result.marker = marker;
244 }
245 }
246
247 result.limit = args.limit;
248 JLOG(context.j.debug()) << __func__ << " : finished";
249
250 return {result, rpcSUCCESS};
251}
252
256 AccountTxArgs const& args,
257 RPC::JsonContext const& context)
258{
259 Json::Value response;
260 RPC::Status const& error = res.second;
261 if (error.toErrorCode() != rpcSUCCESS)
262 {
263 error.inject(response);
264 }
265 else
266 {
267 AccountTxResult const& result = res.first;
268 response[jss::validated] = true;
269 response[jss::limit] = result.limit;
270 response[jss::account] = context.params[jss::account].asString();
271 response[jss::ledger_index_min] = result.ledgerRange.min;
272 response[jss::ledger_index_max] = result.ledgerRange.max;
273
274 Json::Value& jvTxns = (response[jss::transactions] = Json::arrayValue);
275
276 if (auto txnsData = std::get_if<TxnsData>(&result.transactions))
277 {
278 XRPL_ASSERT(!args.binary, "xrpl::populateJsonResponse : binary is not set");
279
280 for (auto const& [txn, txnMeta] : *txnsData)
281 {
282 if (txn)
283 {
284 Json::Value& jvObj = jvTxns.append(Json::objectValue);
285 jvObj[jss::validated] = true;
286
287 auto const json_tx = (context.apiVersion > 1 ? jss::tx_json : jss::tx);
288 if (context.apiVersion > 1)
289 {
290 jvObj[json_tx] =
292 jvObj[jss::hash] = to_string(txn->getID());
293 jvObj[jss::ledger_index] = txn->getLedger();
294 jvObj[jss::ledger_hash] = to_string(context.ledgerMaster.getHashBySeq(txn->getLedger()));
295
296 if (auto closeTime = context.ledgerMaster.getCloseTimeBySeq(txn->getLedger()))
297 jvObj[jss::close_time_iso] = to_string_iso(*closeTime);
298 }
299 else
300 jvObj[json_tx] = txn->getJson(JsonOptions::include_date);
301
302 auto const& sttx = txn->getSTransaction();
303 RPC::insertDeliverMax(jvObj[json_tx], sttx->getTxnType(), context.apiVersion);
304 if (txnMeta)
305 {
306 jvObj[jss::meta] = txnMeta->getJson(JsonOptions::include_date);
307 insertDeliveredAmount(jvObj[jss::meta], context, txn, *txnMeta);
308 RPC::insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
309 RPC::insertMPTokenIssuanceID(jvObj[jss::meta], sttx, *txnMeta);
310 }
311 else
312 {
313 // LCOV_EXCL_START
314 UNREACHABLE(
315 "xrpl::populateJsonResponse : missing "
316 "transaction metadata");
317 // LCOV_EXCL_STOP
318 }
319 }
320 }
321 }
322 else
323 {
324 XRPL_ASSERT(args.binary, "xrpl::populateJsonResponse : binary is set");
325
326 for (auto const& binaryData : std::get<TxnsDataBinary>(result.transactions))
327 {
328 Json::Value& jvObj = jvTxns.append(Json::objectValue);
329
330 jvObj[jss::tx_blob] = strHex(std::get<0>(binaryData));
331 auto const json_meta = (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
332 jvObj[json_meta] = strHex(std::get<1>(binaryData));
333 jvObj[jss::ledger_index] = std::get<2>(binaryData);
334 jvObj[jss::validated] = true;
335 }
336 }
337
338 if (result.marker)
339 {
340 response[jss::marker] = Json::objectValue;
341 response[jss::marker][jss::ledger] = result.marker->ledgerSeq;
342 response[jss::marker][jss::seq] = result.marker->txnSeq;
343 }
344 }
345
346 JLOG(context.j.debug()) << __func__ << " : finished";
347 return response;
348}
349
350// {
351// account: account,
352// ledger_index_min: ledger_index // optional, defaults to earliest
353// ledger_index_max: ledger_index, // optional, defaults to latest
354// binary: boolean, // optional, defaults to false
355// forward: boolean, // optional, defaults to false
356// limit: integer, // optional
357// marker: object {ledger: ledger_index, seq: txn_sequence} // optional,
358// resume previous query
359// }
362{
363 if (!context.app.config().useTxTables())
364 return rpcError(rpcNOT_ENABLED);
365
366 auto& params = context.params;
367 AccountTxArgs args;
368 Json::Value response;
369
370 // The document[https://xrpl.org/account_tx.html#account_tx] states that
371 // binary and forward params are both boolean values, however, assigning any
372 // string value works. Do not allow this. This check is for api Version 2
373 // onwards only
374 if (context.apiVersion > 1u && params.isMember(jss::binary) && !params[jss::binary].isBool())
375 {
376 return RPC::invalid_field_error(jss::binary);
377 }
378 if (context.apiVersion > 1u && params.isMember(jss::forward) && !params[jss::forward].isBool())
379 {
380 return RPC::invalid_field_error(jss::forward);
381 }
382
383 if (auto const err = RPC::readLimitField(args.limit, RPC::Tuning::accountTx, context))
384 return *err;
385
386 args.binary = params.isMember(jss::binary) && params[jss::binary].asBool();
387 args.forward = params.isMember(jss::forward) && params[jss::forward].asBool();
388
389 if (!params.isMember(jss::account))
390 return RPC::missing_field_error(jss::account);
391
392 if (!params[jss::account].isString())
393 return RPC::invalid_field_error(jss::account);
394
395 auto const account = parseBase58<AccountID>(params[jss::account].asString());
396 if (!account)
398
399 args.account = *account;
400
401 auto parseRes = parseLedgerArgs(context, params);
402 if (auto jv = std::get_if<Json::Value>(&parseRes))
403 {
404 return *jv;
405 }
406 else
407 {
409 }
410
411 if (params.isMember(jss::marker))
412 {
413 auto& token = params[jss::marker];
414 if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
415 !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
416 !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
417 {
418 RPC::Status status{
420 "invalid marker. Provide ledger index via ledger field, and "
421 "transaction sequence number via seq field"};
422 status.inject(response);
423 return response;
424 }
425 args.marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
426 }
427
428 auto res = doAccountTxHelp(context, args);
429 JLOG(context.j.debug()) << __func__ << " populating response";
430 return populateJsonResponse(res, args, context);
431}
432
433} // namespace xrpl
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:301
virtual Config & config()=0
virtual RelationalDatabase & getRelationalDatabase()=0
bool useTxTables() const
Definition Config.h:318
bool getValidatedRange(std::uint32_t &minVal, std::uint32_t &maxVal)
uint256 getHashBySeq(std::uint32_t index)
Get a ledger's hash by sequence number using the cache.
std::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
bool isValidated(ReadView const &ledger)
std::vector< AccountTx > AccountTxs
RPC::LedgerShortcut LedgerShortcut
std::vector< txnMetaLedgerType > MetaTxsList
std::variant< LedgerRange, LedgerShortcut, LedgerSequence, LedgerHash > LedgerSpecifier
std::tuple< Blob, Blob, std::uint32_t > txnMetaLedgerType
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:472
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:269
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:227
void insertMPTokenIssuanceID(Json::Value &response, std::shared_ptr< STTx const > const &transaction, TxMeta const &transactionMeta)
void insertNFTSyntheticInJson(Json::Value &, std::shared_ptr< STTx const > const &, TxMeta const &)
Adds common synthetic fields to transaction-related JSON responses.
std::optional< Json::Value > readLimitField(unsigned int &limit, Tuning::LimitRange const &range, JsonContext const &context)
Retrieves the limit value from a JsonContext or sets a default.
Charge const feeMediumBurdenRPC
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
Json::Value populateJsonResponse(std::pair< AccountTxResult, RPC::Status > const &res, AccountTxArgs const &args, RPC::JsonContext const &context)
std::variant< std::optional< LedgerSpecifier >, Json::Value > parseLedgerArgs(RPC::Context &context, Json::Value const &params)
Definition AccountTx.cpp:35
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:598
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
Json::Value doAccountTxJson(RPC::JsonContext &context)
std::variant< LedgerRange, RPC::Status > getLedgerRange(RPC::Context &context, std::optional< LedgerSpecifier > const &ledgerSpecifier)
Json::Value rpcError(error_code_i iError)
Definition RPCErr.cpp:12
std::string to_string_iso(date::sys_time< Duration > tp)
Definition chrono.h:68
std::pair< AccountTxResult, RPC::Status > doAccountTxHelp(RPC::Context &context, AccountTxArgs const &args)
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:96
@ rpcNOT_ENABLED
Definition ErrorCodes.h:40
@ rpcLGR_NOT_VALIDATED
Definition ErrorCodes.h:54
@ rpcLGR_IDX_MALFORMED
Definition ErrorCodes.h:94
@ rpcLGR_IDXS_INVALID
Definition ErrorCodes.h:93
@ rpcNOT_SYNCED
Definition ErrorCodes.h:48
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:65
@ rpcACT_MALFORMED
Definition ErrorCodes.h:71
@ rpcINVALID_LGR_RANGE
Definition ErrorCodes.h:117
@ rpcSUCCESS
Definition ErrorCodes.h:25
The context of information needed to call an RPC.
Definition Context.h:20
beast::Journal const j
Definition Context.h:21
Application & app
Definition Context.h:22
Resource::Charge & loadType
Definition Context.h:23
unsigned int apiVersion
Definition Context.h:30
LedgerMaster & ledgerMaster
Definition Context.h:25
Json::Value params
Definition Context.h:44
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::variant< AccountTxs, MetaTxsList > transactions
std::optional< AccountTxMarker > marker
T visit(T... args)