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