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/UintTypes.h>
33 #include <ripple/protocol/jss.h>
34 #include <ripple/resource/Fees.h>
35 #include <ripple/rpc/Context.h>
36 #include <ripple/rpc/DeliveredAmount.h>
37 #include <ripple/rpc/NFTSyntheticSerializer.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 > 1)
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 > 1)
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);
336  insertNFTSyntheticInJson(
337  jvObj, context, 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  args.limit = params.isMember(jss::limit) ? params[jss::limit].asUInt() : 0;
393  args.binary = params.isMember(jss::binary) && params[jss::binary].asBool();
394  args.forward =
395  params.isMember(jss::forward) && params[jss::forward].asBool();
396 
397  if (!params.isMember(jss::account))
398  return rpcError(rpcINVALID_PARAMS);
399 
400  auto const account =
401  parseBase58<AccountID>(params[jss::account].asString());
402  if (!account)
403  return rpcError(rpcACT_MALFORMED);
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  else
413  {
414  args.ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);
415  }
416 
417  if (params.isMember(jss::marker))
418  {
419  auto& token = params[jss::marker];
420  if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
421  !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
422  !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
423  {
424  RPC::Status status{
426  "invalid marker. Provide ledger index via ledger field, and "
427  "transaction sequence number via seq field"};
428  status.inject(response);
429  return response;
430  }
431  args.marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
432  }
433 
434  auto res = doAccountTxHelp(context, args);
435  JLOG(context.j.debug()) << __func__ << " populating response";
436  return populateJsonResponse(res, args, context);
437 }
438 
439 } // 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:630
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:129
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::RelationalDatabase::AccountTxResult::transactions
std::variant< AccountTxs, MetaTxsList > transactions
Definition: RelationalDatabase.h:108
std::tuple
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:83
ripple::RPC::Context::role
Role role
Definition: Context.h:47
ripple::RelationalDatabase::AccountTxArgs::account
AccountID account
Definition: RelationalDatabase.h:98
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:337
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:343
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:603
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