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
57 {
58  Json::Value response;
59  if (params.isMember(jss::ledger_index_min) ||
60  params.isMember(jss::ledger_index_max))
61  {
62  uint32_t min = params.isMember(jss::ledger_index_min) &&
63  params[jss::ledger_index_min].asInt() >= 0
64  ? params[jss::ledger_index_min].asUInt()
65  : 0;
66  uint32_t max = params.isMember(jss::ledger_index_max) &&
67  params[jss::ledger_index_max].asInt() >= 0
68  ? params[jss::ledger_index_max].asUInt()
69  : UINT32_MAX;
70 
71  return LedgerRange{min, max};
72  }
73  else if (params.isMember(jss::ledger_hash))
74  {
75  auto& hashValue = params[jss::ledger_hash];
76  if (!hashValue.isString())
77  {
78  RPC::Status status{rpcINVALID_PARAMS, "ledgerHashNotString"};
79  status.inject(response);
80  return response;
81  }
82 
83  LedgerHash hash;
84  if (!hash.parseHex(hashValue.asString()))
85  {
86  RPC::Status status{rpcINVALID_PARAMS, "ledgerHashMalformed"};
87  status.inject(response);
88  return response;
89  }
90  return hash;
91  }
92  else if (params.isMember(jss::ledger_index))
93  {
94  LedgerSpecifier ledger;
95  if (params[jss::ledger_index].isNumeric())
96  ledger = params[jss::ledger_index].asUInt();
97  else
98  {
99  std::string ledgerStr = params[jss::ledger_index].asString();
100 
101  if (ledgerStr == "current" || ledgerStr.empty())
102  ledger = LedgerShortcut::CURRENT;
103  else if (ledgerStr == "closed")
104  ledger = LedgerShortcut::CLOSED;
105  else if (ledgerStr == "validated")
106  ledger = LedgerShortcut::VALIDATED;
107  else
108  {
109  RPC::Status status{
110  rpcINVALID_PARAMS, "ledger_index string malformed"};
111  status.inject(response);
112  return response;
113  }
114  }
115  return ledger;
116  }
118 }
119 
122  RPC::Context& context,
123  std::optional<LedgerSpecifier> const& ledgerSpecifier)
124 {
125  std::uint32_t uValidatedMin;
126  std::uint32_t uValidatedMax;
127  bool bValidated =
128  context.ledgerMaster.getValidatedRange(uValidatedMin, uValidatedMax);
129 
130  if (!bValidated)
131  {
132  // Don't have a validated ledger range.
133  if (context.apiVersion == 1)
134  return rpcLGR_IDXS_INVALID;
135  return rpcNOT_SYNCED;
136  }
137 
138  std::uint32_t uLedgerMin = uValidatedMin;
139  std::uint32_t uLedgerMax = uValidatedMax;
140  // Does request specify a ledger or ledger range?
141  if (ledgerSpecifier)
142  {
143  auto const status = std::visit(
144  [&](auto const& ls) -> RPC::Status {
145  using T = std::decay_t<decltype(ls)>;
146  if constexpr (std::is_same_v<T, LedgerRange>)
147  {
148  if (ls.min > uValidatedMin)
149  {
150  uLedgerMin = ls.min;
151  }
152  if (ls.max < uValidatedMax)
153  {
154  uLedgerMax = ls.max;
155  }
156  if (uLedgerMax < uLedgerMin)
157  {
158  if (context.apiVersion == 1)
159  return rpcLGR_IDXS_INVALID;
160  return rpcINVALID_LGR_RANGE;
161  }
162  }
163  else
164  {
166  auto const status = getLedger(ledgerView, ls, context);
167  if (!ledgerView)
168  {
169  return status;
170  }
171 
172  bool validated = RPC::isValidated(
173  context.ledgerMaster, *ledgerView, context.app);
174 
175  if (!validated || ledgerView->info().seq > uValidatedMax ||
176  ledgerView->info().seq < uValidatedMin)
177  {
178  return rpcLGR_NOT_VALIDATED;
179  }
180  uLedgerMin = uLedgerMax = ledgerView->info().seq;
181  }
182  return RPC::Status::OK;
183  },
184  *ledgerSpecifier);
185 
186  if (status)
187  return status;
188  }
189  return LedgerRange{uLedgerMin, uLedgerMax};
190 }
191 
194 {
196  if (context.app.config().reporting())
197  {
198  auto const db = dynamic_cast<PostgresDatabase*>(
199  &context.app.getRelationalDatabase());
200 
201  if (!db)
202  Throw<std::runtime_error>("Failed to get relational database");
203 
204  return db->getAccountTx(args);
205  }
206 
207  AccountTxResult result;
208 
209  auto lgrRange = getLedgerRange(context, args.ledger);
210  if (auto stat = std::get_if<RPC::Status>(&lgrRange))
211  {
212  // An error occurred getting the requested ledger range
213  return {result, *stat};
214  }
215 
216  result.ledgerRange = std::get<LedgerRange>(lgrRange);
217 
218  result.marker = args.marker;
219 
221  args.account,
222  result.ledgerRange.min,
223  result.ledgerRange.max,
224  result.marker,
225  args.limit,
226  isUnlimited(context.role)};
227 
228  auto const db =
229  dynamic_cast<SQLiteDatabase*>(&context.app.getRelationalDatabase());
230 
231  if (!db)
232  Throw<std::runtime_error>("Failed to get relational database");
233 
234  if (args.binary)
235  {
236  if (args.forward)
237  {
238  auto [tx, marker] = db->oldestAccountTxPageB(options);
239  result.transactions = tx;
240  result.marker = marker;
241  }
242  else
243  {
244  auto [tx, marker] = db->newestAccountTxPageB(options);
245  result.transactions = tx;
246  result.marker = marker;
247  }
248  }
249  else
250  {
251  if (args.forward)
252  {
253  auto [tx, marker] = db->oldestAccountTxPage(options);
254  result.transactions = tx;
255  result.marker = marker;
256  }
257  else
258  {
259  auto [tx, marker] = db->newestAccountTxPage(options);
260  result.transactions = tx;
261  result.marker = marker;
262  }
263  }
264 
265  result.limit = args.limit;
266  JLOG(context.j.debug()) << __func__ << " : finished";
267 
268  return {result, rpcSUCCESS};
269 }
270 
274  AccountTxArgs const& args,
275  RPC::JsonContext const& context)
276 {
277  Json::Value response;
278  RPC::Status const& error = res.second;
279  if (error.toErrorCode() != rpcSUCCESS)
280  {
281  error.inject(response);
282  }
283  else
284  {
285  AccountTxResult const& result = res.first;
286  response[jss::validated] = true;
287  response[jss::limit] = result.limit;
288  response[jss::account] = context.params[jss::account].asString();
289  response[jss::ledger_index_min] = result.ledgerRange.min;
290  response[jss::ledger_index_max] = result.ledgerRange.max;
291 
292  Json::Value& jvTxns = (response[jss::transactions] = Json::arrayValue);
293 
294  if (auto txnsData = std::get_if<TxnsData>(&result.transactions))
295  {
296  assert(!args.binary);
297  for (auto const& [txn, txnMeta] : *txnsData)
298  {
299  if (txn)
300  {
301  Json::Value& jvObj = jvTxns.append(Json::objectValue);
302 
303  jvObj[jss::tx] = txn->getJson(JsonOptions::include_date);
304  if (txnMeta)
305  {
306  jvObj[jss::meta] =
307  txnMeta->getJson(JsonOptions::include_date);
308  jvObj[jss::validated] = true;
309  insertDeliveredAmount(
310  jvObj[jss::meta], context, txn, *txnMeta);
311  insertNFTSyntheticInJson(
312  jvObj, context, txn->getSTransaction(), *txnMeta);
313  }
314  }
315  }
316  }
317  else
318  {
319  assert(args.binary);
320 
321  for (auto const& binaryData :
322  std::get<TxnsDataBinary>(result.transactions))
323  {
324  Json::Value& jvObj = jvTxns.append(Json::objectValue);
325 
326  jvObj[jss::tx_blob] = strHex(std::get<0>(binaryData));
327  jvObj[jss::meta] = strHex(std::get<1>(binaryData));
328  jvObj[jss::ledger_index] = std::get<2>(binaryData);
329  jvObj[jss::validated] = true;
330  }
331  }
332 
333  if (result.marker)
334  {
335  response[jss::marker] = Json::objectValue;
336  response[jss::marker][jss::ledger] = result.marker->ledgerSeq;
337  response[jss::marker][jss::seq] = result.marker->txnSeq;
338  }
339  if (context.app.config().reporting())
340  response["used_postgres"] = true;
341  }
342 
343  JLOG(context.j.debug()) << __func__ << " : finished";
344  return response;
345 }
346 
347 // {
348 // account: account,
349 // ledger_index_min: ledger_index // optional, defaults to earliest
350 // ledger_index_max: ledger_index, // optional, defaults to latest
351 // binary: boolean, // optional, defaults to false
352 // forward: boolean, // optional, defaults to false
353 // limit: integer, // optional
354 // marker: object {ledger: ledger_index, seq: txn_sequence} // optional,
355 // resume previous query
356 // }
359 {
360  if (!context.app.config().useTxTables())
361  return rpcError(rpcNOT_ENABLED);
362 
363  auto& params = context.params;
364  AccountTxArgs args;
365  Json::Value response;
366 
367  args.limit = params.isMember(jss::limit) ? params[jss::limit].asUInt() : 0;
368  args.binary = params.isMember(jss::binary) && params[jss::binary].asBool();
369  args.forward =
370  params.isMember(jss::forward) && params[jss::forward].asBool();
371 
372  if (!params.isMember(jss::account))
373  return rpcError(rpcINVALID_PARAMS);
374 
375  auto const account =
376  parseBase58<AccountID>(params[jss::account].asString());
377  if (!account)
378  return rpcError(rpcACT_MALFORMED);
379 
380  args.account = *account;
381 
382  auto parseRes = parseLedgerArgs(params);
383  if (auto jv = std::get_if<Json::Value>(&parseRes))
384  {
385  return *jv;
386  }
387  else
388  {
389  args.ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);
390  }
391 
392  if (params.isMember(jss::marker))
393  {
394  auto& token = params[jss::marker];
395  if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
396  !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
397  !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
398  {
399  RPC::Status status{
401  "invalid marker. Provide ledger index via ledger field, and "
402  "transaction sequence number via seq field"};
403  status.inject(response);
404  return response;
405  }
406  args.marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
407  }
408 
409  auto res = doAccountTxHelp(context, args);
410  JLOG(context.j.debug()) << __func__ << " populating response";
411  return populateJsonResponse(res, args, context);
412 }
413 
414 } // 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::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:193
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:121
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:272
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:358
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
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::parseLedgerArgs
std::variant< std::optional< LedgerSpecifier >, Json::Value > parseLedgerArgs(Json::Value const &params)
Definition: AccountTx.cpp:56
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