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/RPCLedgerHelpers.h>
12#include <xrpld/rpc/detail/Tuning.h>
13
14#include <xrpl/json/json_value.h>
15#include <xrpl/ledger/ReadView.h>
16#include <xrpl/protocol/ErrorCodes.h>
17#include <xrpl/protocol/NFTSyntheticSerializer.h>
18#include <xrpl/protocol/RPCErr.h>
19#include <xrpl/protocol/UintTypes.h>
20#include <xrpl/protocol/jss.h>
21#include <xrpl/resource/Fees.h>
22
23namespace xrpl {
24
31
32// parses args into a ledger specifier, or returns a Json object on error
35{
36 Json::Value response;
37 // if ledger_index_min or max is specified, then ledger_hash or ledger_index
38 // should not be specified. Error out if it is
39 if (context.apiVersion > 1u)
40 {
41 if ((params.isMember(jss::ledger_index_min) || params.isMember(jss::ledger_index_max)) &&
42 (params.isMember(jss::ledger_hash) || params.isMember(jss::ledger_index)))
43 {
44 RPC::Status const status{rpcINVALID_PARAMS, "invalidParams"};
45 status.inject(response);
46 return response;
47 }
48 }
49 if (params.isMember(jss::ledger_index_min) || params.isMember(jss::ledger_index_max))
50 {
51 uint32_t const min =
52 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 const max =
56 params.isMember(jss::ledger_index_max) && params[jss::ledger_index_max].asInt() >= 0
57 ? params[jss::ledger_index_max].asUInt()
58 : UINT32_MAX;
59
60 return LedgerRange{min, max};
61 }
62 if (params.isMember(jss::ledger_hash))
63 {
64 auto& hashValue = params[jss::ledger_hash];
65 if (!hashValue.isString())
66 {
67 RPC::Status const status{rpcINVALID_PARAMS, "ledgerHashNotString"};
68 status.inject(response);
69 return response;
70 }
71
72 LedgerHash hash;
73 if (!hash.parseHex(hashValue.asString()))
74 {
75 RPC::Status const status{rpcINVALID_PARAMS, "ledgerHashMalformed"};
76 status.inject(response);
77 return response;
78 }
79 return hash;
80 }
81 if (params.isMember(jss::ledger_index))
82 {
83 LedgerSpecifier ledger;
84 if (params[jss::ledger_index].isNumeric())
85 {
86 ledger = params[jss::ledger_index].asUInt();
87 }
88 else
89 {
90 std::string const ledgerStr = params[jss::ledger_index].asString();
91
92 if (ledgerStr == "current" || ledgerStr.empty())
93 {
95 }
96 else if (ledgerStr == "closed")
97 {
99 }
100 else if (ledgerStr == "validated")
101 {
103 }
104 else
105 {
106 RPC::Status const status{rpcINVALID_PARAMS, "ledger_index string malformed"};
107 status.inject(response);
108 return response;
109 }
110 }
111 return ledger;
112 }
114}
115
118{
119 std::uint32_t uValidatedMin = 0;
120 std::uint32_t uValidatedMax = 0;
121 bool const bValidated = 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 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 status = getLedger(ledgerView, ls, context);
171 if (!ledgerView)
172 {
173 return status;
174 }
175
176 bool const validated = context.ledgerMaster.isValidated(*ledgerView);
177
178 if (!validated || ledgerView->header().seq > uValidatedMax ||
179 ledgerView->header().seq < uValidatedMin)
180 {
182 }
183 uLedgerMin = uLedgerMax = ledgerView->header().seq;
184 }
185 return RPC::Status::OK;
186 },
187 *ledgerSpecifier);
188
189 if (status)
190 return status;
191 }
192 return LedgerRange{uLedgerMin, uLedgerMax};
193}
194
197{
199
200 AccountTxResult result;
201
202 auto lgrRange = getLedgerRange(context, args.ledger);
203 if (auto stat = std::get_if<RPC::Status>(&lgrRange))
204 {
205 // An error occurred getting the requested ledger range
206 return {result, *stat};
207 }
208
209 result.ledgerRange = std::get<LedgerRange>(lgrRange);
210
211 result.marker = args.marker;
212
214 args.account, result.ledgerRange, result.marker, args.limit, isUnlimited(context.role)};
215
216 auto& db = context.app.getRelationalDatabase();
217
218 if (args.binary)
219 {
220 if (args.forward)
221 {
222 auto [tx, marker] = db.oldestAccountTxPageB(options);
223 result.transactions = tx;
224 result.marker = marker;
225 }
226 else
227 {
228 auto [tx, marker] = db.newestAccountTxPageB(options);
229 result.transactions = tx;
230 result.marker = marker;
231 }
232 }
233 else
234 {
235 if (args.forward)
236 {
237 auto [tx, marker] = db.oldestAccountTxPage(options);
238 result.transactions = tx;
239 result.marker = marker;
240 }
241 else
242 {
243 auto [tx, marker] = db.newestAccountTxPage(options);
244 result.transactions = tx;
245 result.marker = marker;
246 }
247 }
248
249 result.limit = args.limit;
250 JLOG(context.j.debug()) << __func__ << " : finished";
251
252 return {result, rpcSUCCESS};
253}
254
258 AccountTxArgs const& args,
259 RPC::JsonContext const& context)
260{
261 Json::Value response;
262 RPC::Status const& error = res.second;
263 if (error.toErrorCode() != rpcSUCCESS)
264 {
265 error.inject(response);
266 }
267 else
268 {
269 AccountTxResult const& result = res.first;
270 response[jss::validated] = true;
271 response[jss::limit] = result.limit;
272 response[jss::account] = context.params[jss::account].asString();
273 response[jss::ledger_index_min] = result.ledgerRange.min;
274 response[jss::ledger_index_max] = result.ledgerRange.max;
275
276 Json::Value& jvTxns = (response[jss::transactions] = Json::arrayValue);
277
278 if (auto txnsData = std::get_if<TxnsData>(&result.transactions))
279 {
280 XRPL_ASSERT(!args.binary, "xrpl::populateJsonResponse : binary is not set");
281
282 for (auto const& [txn, txnMeta] : *txnsData)
283 {
284 if (txn)
285 {
286 Json::Value& jvObj = jvTxns.append(Json::objectValue);
287 jvObj[jss::validated] = true;
288
289 auto const json_tx = (context.apiVersion > 1 ? jss::tx_json : jss::tx);
290 if (context.apiVersion > 1)
291 {
292 jvObj[json_tx] = txn->getJson(
294 jvObj[jss::hash] = to_string(txn->getID());
295 jvObj[jss::ledger_index] = txn->getLedger();
296 jvObj[jss::ledger_hash] =
297 to_string(context.ledgerMaster.getHashBySeq(txn->getLedger()));
298
299 if (auto closeTime =
300 context.ledgerMaster.getCloseTimeBySeq(txn->getLedger()))
301 jvObj[jss::close_time_iso] = to_string_iso(*closeTime);
302 }
303 else
304 {
305 jvObj[json_tx] = txn->getJson(JsonOptions::include_date);
306 }
307
308 auto const& sttx = txn->getSTransaction();
309 RPC::insertDeliverMax(jvObj[json_tx], sttx->getTxnType(), context.apiVersion);
310 if (txnMeta)
311 {
312 jvObj[jss::meta] = txnMeta->getJson(JsonOptions::include_date);
313 insertDeliveredAmount(jvObj[jss::meta], context, txn, *txnMeta);
314 RPC::insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
315 RPC::insertMPTokenIssuanceID(jvObj[jss::meta], sttx, *txnMeta);
316 }
317 else
318 {
319 // LCOV_EXCL_START
320 UNREACHABLE(
321 "xrpl::populateJsonResponse : missing "
322 "transaction metadata");
323 // LCOV_EXCL_STOP
324 }
325 }
326 }
327 }
328 else
329 {
330 XRPL_ASSERT(args.binary, "xrpl::populateJsonResponse : binary is set");
331
332 for (auto const& binaryData : std::get<TxnsDataBinary>(result.transactions))
333 {
334 Json::Value& jvObj = jvTxns.append(Json::objectValue);
335
336 jvObj[jss::tx_blob] = strHex(std::get<0>(binaryData));
337 auto const json_meta = (context.apiVersion > 1 ? jss::meta_blob : jss::meta);
338 jvObj[json_meta] = strHex(std::get<1>(binaryData));
339 jvObj[jss::ledger_index] = std::get<2>(binaryData);
340 jvObj[jss::validated] = true;
341 }
342 }
343
344 if (result.marker)
345 {
346 response[jss::marker] = Json::objectValue;
347 response[jss::marker][jss::ledger] = result.marker->ledgerSeq;
348 response[jss::marker][jss::seq] = result.marker->txnSeq;
349 }
350 }
351
352 JLOG(context.j.debug()) << __func__ << " : finished";
353 return response;
354}
355
356// {
357// account: account,
358// ledger_index_min: ledger_index // optional, defaults to earliest
359// ledger_index_max: ledger_index, // optional, defaults to latest
360// binary: boolean, // optional, defaults to false
361// forward: boolean, // optional, defaults to false
362// limit: integer, // optional
363// marker: object {ledger: ledger_index, seq: txn_sequence} // optional,
364// resume previous query
365// }
368{
369 if (!context.app.config().useTxTables())
370 return rpcError(rpcNOT_ENABLED);
371
372 auto& params = context.params;
373 AccountTxArgs args;
374 Json::Value response;
375
376 // The document[https://xrpl.org/account_tx.html#account_tx] states that
377 // binary and forward params are both boolean values, however, assigning any
378 // string value works. Do not allow this. This check is for api Version 2
379 // onwards only
380 if (context.apiVersion > 1u && params.isMember(jss::binary) && !params[jss::binary].isBool())
381 {
382 return RPC::invalid_field_error(jss::binary);
383 }
384 if (context.apiVersion > 1u && params.isMember(jss::forward) && !params[jss::forward].isBool())
385 {
386 return RPC::invalid_field_error(jss::forward);
387 }
388
389 if (auto const err = RPC::readLimitField(args.limit, RPC::Tuning::accountTx, context))
390 return *err;
391
392 args.binary = params.isMember(jss::binary) && params[jss::binary].asBool();
393 args.forward = params.isMember(jss::forward) && params[jss::forward].asBool();
394
395 if (!params.isMember(jss::account))
396 return RPC::missing_field_error(jss::account);
397
398 if (!params[jss::account].isString())
399 return RPC::invalid_field_error(jss::account);
400
401 auto const account = parseBase58<AccountID>(params[jss::account].asString());
402 if (!account)
404
405 args.account = *account;
406
407 auto parseRes = parseLedgerArgs(context, params);
408 if (auto jv = std::get_if<Json::Value>(&parseRes))
409 {
410 return *jv;
411 }
412
414
415 if (params.isMember(jss::marker))
416 {
417 auto& token = params[jss::marker];
418 if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
419 !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
420 !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
421 {
422 RPC::Status const status{
424 "invalid marker. Provide ledger index via ledger field, and "
425 "transaction sequence number via seq field"};
426 status.inject(response);
427 return response;
428 }
429 args.marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
430 }
431
432 auto res = doAccountTxHelp(context, args);
433 JLOG(context.j.debug()) << __func__ << " populating response";
434 return populateJsonResponse(res, args, context);
435}
436
437} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
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
bool useTxTables() const
Definition Config.h:322
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)
virtual std::pair< MetaTxsList, std::optional< AccountTxMarker > > oldestAccountTxPageB(AccountTxPageOptions const &options)=0
oldestAccountTxPageB Returns the oldest transactions in binary form for the account that matches the ...
std::vector< AccountTx > AccountTxs
std::vector< txnMetaLedgerType > MetaTxsList
std::variant< LedgerRange, LedgerShortcut, LedgerSequence, LedgerHash > LedgerSpecifier
std::tuple< Blob, Blob, std::uint32_t > txnMetaLedgerType
virtual RelationalDatabase & getRelationalDatabase()=0
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:476
T empty(T... args)
T is_same_v
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
@ uintValue
unsigned integer value
Definition json_value.h:21
static LimitRange constexpr accountTx
Limits for the account_tx command.
Json::Value invalid_field_error(std::string const &name)
Definition ErrorCodes.h:273
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:231
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:5
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:34
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
@ Closed
The most recently closed ledger (may not be validated)
@ Current
The current working ledger (open, not yet closed)
@ Validated
The most recently validated ledger.
std::variant< LedgerRange, RPC::Status > getLedgerRange(RPC::Context &context, std::optional< LedgerSpecifier > const &ledgerSpecifier)
Json::Value doAccountTx(RPC::JsonContext &context)
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:98
@ rpcNOT_ENABLED
Definition ErrorCodes.h:39
@ rpcLGR_NOT_VALIDATED
Definition ErrorCodes.h:53
@ rpcLGR_IDX_MALFORMED
Definition ErrorCodes.h:93
@ rpcLGR_IDXS_INVALID
Definition ErrorCodes.h:92
@ rpcNOT_SYNCED
Definition ErrorCodes.h:47
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:64
@ rpcACT_MALFORMED
Definition ErrorCodes.h:70
@ rpcINVALID_LGR_RANGE
Definition ErrorCodes.h:116
@ rpcSUCCESS
Definition ErrorCodes.h:24
The context of information needed to call an RPC.
Definition Context.h:19
beast::Journal const j
Definition Context.h:20
Application & app
Definition Context.h:21
Resource::Charge & loadType
Definition Context.h:22
unsigned int apiVersion
Definition Context.h:29
LedgerMaster & ledgerMaster
Definition Context.h:24
Json::Value params
Definition Context.h:43
Status represents the results of an operation that might fail.
Definition Status.h:20
static constexpr Code OK
Definition Status.h:26
std::optional< AccountTxMarker > marker
std::optional< LedgerSpecifier > ledger
std::variant< AccountTxs, MetaTxsList > transactions
std::optional< AccountTxMarker > marker
T visit(T... args)