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