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