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/core/Pg.h>
25 #include <ripple/json/json_reader.h>
26 #include <ripple/json/json_value.h>
27 #include <ripple/ledger/ReadView.h>
28 #include <ripple/net/RPCErr.h>
29 #include <ripple/protocol/ErrorCodes.h>
30 #include <ripple/protocol/UintTypes.h>
31 #include <ripple/protocol/jss.h>
32 #include <ripple/resource/Fees.h>
33 #include <ripple/rpc/Context.h>
34 #include <ripple/rpc/DeliveredAmount.h>
35 #include <ripple/rpc/Role.h>
36 #include <ripple/rpc/impl/GRPCHelpers.h>
37 #include <ripple/rpc/impl/RPCHelpers.h>
38 
39 #include <grpcpp/grpcpp.h>
40 
41 namespace ripple {
42 
43 using LedgerSequence = uint32_t;
44 using LedgerHash = uint256;
46 
48 
50 {
51  uint32_t min;
52  uint32_t max;
53 };
54 
55 using LedgerSpecifier =
57 
59 {
62  bool binary = false;
63  bool forward = false;
64  uint32_t limit = 0;
66 };
67 
71 
73 {
76  uint32_t limit;
78 };
79 
80 // parses args into a ledger specifier, or returns a grpc status object on error
83  org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest const& params)
84 {
85  grpc::Status status;
86  if (params.has_ledger_range())
87  {
88  uint32_t min = params.ledger_range().ledger_index_min();
89  uint32_t max = params.ledger_range().ledger_index_max();
90 
91  // if min is set but not max, need to set max
92  if (min != 0 && max == 0)
93  {
94  max = UINT32_MAX;
95  }
96 
97  return LedgerRange{min, max};
98  }
99  else if (params.has_ledger_specifier())
100  {
101  LedgerSpecifier ledger;
102 
103  auto& specifier = params.ledger_specifier();
104  using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
105  LedgerCase ledgerCase = specifier.ledger_case();
106 
107  if (ledgerCase == LedgerCase::kShortcut)
108  {
109  using LedgerSpecifier = org::xrpl::rpc::v1::LedgerSpecifier;
110 
111  if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_VALIDATED)
112  ledger = LedgerShortcut::VALIDATED;
113  else if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_CLOSED)
114  ledger = LedgerShortcut::CLOSED;
115  else if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_CURRENT)
116  ledger = LedgerShortcut::CURRENT;
117  else
118  return {};
119  }
120  else if (ledgerCase == LedgerCase::kSequence)
121  {
122  ledger = specifier.sequence();
123  }
124  else if (ledgerCase == LedgerCase::kHash)
125  {
126  if (uint256::size() != specifier.hash().size())
127  {
128  grpc::Status errorStatus{
129  grpc::StatusCode::INVALID_ARGUMENT,
130  "ledger hash malformed"};
131  return errorStatus;
132  }
133  ledger = uint256::fromVoid(specifier.hash().data());
134  }
135  return ledger;
136  }
138 }
139 
140 // parses args into a ledger specifier, or returns a Json object on error
143 {
144  Json::Value response;
145  if (params.isMember(jss::ledger_index_min) ||
146  params.isMember(jss::ledger_index_max))
147  {
148  uint32_t min = params.isMember(jss::ledger_index_min) &&
149  params[jss::ledger_index_min].asInt() >= 0
150  ? params[jss::ledger_index_min].asUInt()
151  : 0;
152  uint32_t max = params.isMember(jss::ledger_index_max) &&
153  params[jss::ledger_index_max].asInt() >= 0
154  ? params[jss::ledger_index_max].asUInt()
155  : UINT32_MAX;
156 
157  return LedgerRange{min, max};
158  }
159  else if (params.isMember(jss::ledger_hash))
160  {
161  auto& hashValue = params[jss::ledger_hash];
162  if (!hashValue.isString())
163  {
164  RPC::Status status{rpcINVALID_PARAMS, "ledgerHashNotString"};
165  status.inject(response);
166  return response;
167  }
168 
169  LedgerHash hash;
170  if (!hash.parseHex(hashValue.asString()))
171  {
172  RPC::Status status{rpcINVALID_PARAMS, "ledgerHashMalformed"};
173  status.inject(response);
174  return response;
175  }
176  return hash;
177  }
178  else if (params.isMember(jss::ledger_index))
179  {
180  LedgerSpecifier ledger;
181  if (params[jss::ledger_index].isNumeric())
182  ledger = params[jss::ledger_index].asUInt();
183  else
184  {
185  std::string ledgerStr = params[jss::ledger_index].asString();
186 
187  if (ledgerStr == "current" || ledgerStr.empty())
188  ledger = LedgerShortcut::CURRENT;
189  else if (ledgerStr == "closed")
190  ledger = LedgerShortcut::CLOSED;
191  else if (ledgerStr == "validated")
192  ledger = LedgerShortcut::VALIDATED;
193  else
194  {
195  RPC::Status status{
196  rpcINVALID_PARAMS, "ledger_index string malformed"};
197  status.inject(response);
198  return response;
199  }
200  }
201  return ledger;
202  }
204 }
205 
208  RPC::Context& context,
209  std::optional<LedgerSpecifier> const& ledgerSpecifier)
210 {
211  std::uint32_t uValidatedMin;
212  std::uint32_t uValidatedMax;
213  bool bValidated =
214  context.ledgerMaster.getValidatedRange(uValidatedMin, uValidatedMax);
215 
216  if (!bValidated)
217  {
218  // Don't have a validated ledger range.
219  if (context.apiVersion == 1)
220  return rpcLGR_IDXS_INVALID;
221  return rpcNOT_SYNCED;
222  }
223 
224  std::uint32_t uLedgerMin = uValidatedMin;
225  std::uint32_t uLedgerMax = uValidatedMax;
226  // Does request specify a ledger or ledger range?
227  if (ledgerSpecifier)
228  {
229  auto const status = std::visit(
230  [&](auto const& ls) -> RPC::Status {
231  using T = std::decay_t<decltype(ls)>;
232  if constexpr (std::is_same_v<T, LedgerRange>)
233  {
234  if (ls.min > uValidatedMin)
235  {
236  uLedgerMin = ls.min;
237  }
238  if (ls.max < uValidatedMax)
239  {
240  uLedgerMax = ls.max;
241  }
242  if (uLedgerMax < uLedgerMin)
243  {
244  if (context.apiVersion == 1)
245  return rpcLGR_IDXS_INVALID;
246  return rpcINVALID_LGR_RANGE;
247  }
248  }
249  else
250  {
252  auto const status = getLedger(ledgerView, ls, context);
253  if (!ledgerView)
254  {
255  return status;
256  }
257 
258  bool validated = RPC::isValidated(
259  context.ledgerMaster, *ledgerView, context.app);
260 
261  if (!validated || ledgerView->info().seq > uValidatedMax ||
262  ledgerView->info().seq < uValidatedMin)
263  {
264  return rpcLGR_NOT_VALIDATED;
265  }
266  uLedgerMin = uLedgerMax = ledgerView->info().seq;
267  }
268  return RPC::Status::OK;
269  },
270  *ledgerSpecifier);
271 
272  if (status)
273  return status;
274  }
275  return LedgerRange{uLedgerMin, uLedgerMax};
276 }
277 
278 enum class DataFormat { binary, expanded };
281  RPC::Context& context,
282  std::vector<uint256>& nodestoreHashes,
283  std::vector<uint32_t>& ledgerSequences,
284  DataFormat format)
285 {
287  if (format == DataFormat::binary)
288  ret = TxnsDataBinary();
289  else
290  ret = TxnsData();
291 
292  std::vector<
294  txns = flatFetchTransactions(context.app, nodestoreHashes);
295  for (size_t i = 0; i < txns.size(); ++i)
296  {
297  auto& [txn, meta] = txns[i];
298  if (format == DataFormat::binary)
299  {
300  auto& transactions = std::get<TxnsDataBinary>(ret);
301  Serializer txnSer = txn->getSerializer();
302  Serializer metaSer = meta->getSerializer();
303  // SerialIter it(item->slice());
304  Blob txnBlob = txnSer.getData();
305  Blob metaBlob = metaSer.getData();
306  transactions.push_back(
307  std::make_tuple(txnBlob, metaBlob, ledgerSequences[i]));
308  }
309  else
310  {
311  auto& transactions = std::get<TxnsData>(ret);
312  std::string reason;
313  auto txnRet =
314  std::make_shared<Transaction>(txn, reason, context.app);
315  txnRet->setLedger(ledgerSequences[i]);
316  txnRet->setStatus(COMMITTED);
317  auto txMeta = std::make_shared<TxMeta>(
318  txnRet->getID(), ledgerSequences[i], *meta);
319  transactions.push_back(std::make_pair(txnRet, txMeta));
320  }
321  }
322  return ret;
323 }
324 
327  AccountTxArgs const& args,
328  Json::Value& result,
329  RPC::Context& context)
330 {
331  AccountTxResult ret;
332  ret.limit = args.limit;
333 
334  try
335  {
336  if (result.isMember("transactions"))
337  {
338  std::vector<uint256> nodestoreHashes;
339  std::vector<uint32_t> ledgerSequences;
340  for (auto& t : result["transactions"])
341  {
342  if (t.isMember("ledger_seq") && t.isMember("nodestore_hash"))
343  {
344  uint32_t ledgerSequence = t["ledger_seq"].asUInt();
345  std::string nodestoreHashHex =
346  t["nodestore_hash"].asString();
347  nodestoreHashHex.erase(0, 2);
348  uint256 nodestoreHash;
349  if (!nodestoreHash.parseHex(nodestoreHashHex))
350  assert(false);
351 
352  if (nodestoreHash.isNonZero())
353  {
354  ledgerSequences.push_back(ledgerSequence);
355  nodestoreHashes.push_back(nodestoreHash);
356  }
357  else
358  {
359  assert(false);
360  return {ret, {rpcINTERNAL, "nodestoreHash is zero"}};
361  }
362  }
363  else
364  {
365  assert(false);
366  return {ret, {rpcINTERNAL, "missing postgres fields"}};
367  }
368  }
369 
370  assert(nodestoreHashes.size() == ledgerSequences.size());
372  context,
373  nodestoreHashes,
374  ledgerSequences,
376 
377  JLOG(context.j.trace()) << __func__ << " : processed db results";
378 
379  if (result.isMember("marker"))
380  {
381  auto& marker = result["marker"];
382  assert(marker.isMember("ledger"));
383  assert(marker.isMember("seq"));
384  ret.marker = {
385  marker["ledger"].asUInt(), marker["seq"].asUInt()};
386  }
387  assert(result.isMember("ledger_index_min"));
388  assert(result.isMember("ledger_index_max"));
389  ret.ledgerRange = {
390  result["ledger_index_min"].asUInt(),
391  result["ledger_index_max"].asUInt()};
392  return {ret, rpcSUCCESS};
393  }
394  else if (result.isMember("error"))
395  {
396  JLOG(context.j.debug())
397  << __func__ << " : error = " << result["error"].asString();
398  return {
399  ret,
400  RPC::Status{rpcINVALID_PARAMS, result["error"].asString()}};
401  }
402  else
403  {
404  return {ret, {rpcINTERNAL, "unexpected Postgres response"}};
405  }
406  }
407  catch (std::exception& e)
408  {
409  JLOG(context.j.debug()) << __func__ << " : "
410  << "Caught exception : " << e.what();
411  return {ret, {rpcINTERNAL, e.what()}};
412  }
413 }
414 
417 {
418 #ifdef RIPPLED_REPORTING
419  pg_params dbParams;
420 
421  char const*& command = dbParams.first;
422  std::vector<std::optional<std::string>>& values = dbParams.second;
423  command =
424  "SELECT account_tx($1::bytea, $2::bool, "
425  "$3::bigint, $4::bigint, $5::bigint, $6::bytea, "
426  "$7::bigint, $8::bool, $9::bigint, $10::bigint)";
427  values.resize(10);
428  values[0] = "\\x" + strHex(args.account);
429  values[1] = args.forward ? "true" : "false";
430 
431  static std::uint32_t const page_length(200);
432  if (args.limit == 0 || args.limit > page_length)
433  values[2] = std::to_string(page_length);
434  else
435  values[2] = std::to_string(args.limit);
436 
437  if (args.ledger)
438  {
439  if (auto range = std::get_if<LedgerRange>(&args.ledger.value()))
440  {
441  values[3] = std::to_string(range->min);
442  values[4] = std::to_string(range->max);
443  }
444  else if (auto hash = std::get_if<LedgerHash>(&args.ledger.value()))
445  {
446  values[5] = ("\\x" + strHex(*hash));
447  }
448  else if (
449  auto sequence = std::get_if<LedgerSequence>(&args.ledger.value()))
450  {
451  values[6] = std::to_string(*sequence);
452  }
453  else if (std::get_if<LedgerShortcut>(&args.ledger.value()))
454  {
455  // current, closed and validated are all treated as validated
456  values[7] = "true";
457  }
458  else
459  {
460  JLOG(context.j.error()) << "doAccountTxStoredProcedure - "
461  << "Error parsing ledger args";
462  return {};
463  }
464  }
465 
466  if (args.marker)
467  {
468  values[8] = std::to_string(args.marker->ledgerSeq);
469  values[9] = std::to_string(args.marker->txnSeq);
470  }
471  for (size_t i = 0; i < values.size(); ++i)
472  {
473  JLOG(context.j.trace()) << "value " << std::to_string(i) << " = "
474  << (values[i] ? values[i].value() : "null");
475  }
476 
477  auto res = PgQuery(context.app.getPgPool())(dbParams);
478  if (!res)
479  {
480  JLOG(context.j.error())
481  << __func__ << " : Postgres response is null - account = "
482  << strHex(args.account);
483  assert(false);
484  return {{}, {rpcINTERNAL, "Postgres error"}};
485  }
486  else if (res.status() != PGRES_TUPLES_OK)
487  {
488  JLOG(context.j.error()) << __func__
489  << " : Postgres response should have been "
490  "PGRES_TUPLES_OK but instead was "
491  << res.status() << " - msg = " << res.msg()
492  << " - account = " << strHex(args.account);
493  assert(false);
494  return {{}, {rpcINTERNAL, "Postgres error"}};
495  }
496 
497  JLOG(context.j.trace())
498  << __func__ << " Postgres result msg : " << res.msg();
499  if (res.isNull() || res.ntuples() == 0)
500  {
501  JLOG(context.j.debug())
502  << __func__ << " : No data returned from Postgres : account = "
503  << strHex(args.account);
504 
505  assert(false);
506  return {{}, {rpcINTERNAL, "Postgres error"}};
507  }
508 
509  char const* resultStr = res.c_str();
510  JLOG(context.j.trace()) << __func__ << " : "
511  << "postgres result = " << resultStr
512  << " : account = " << strHex(args.account);
513 
514  Json::Value v;
515  Json::Reader reader;
516  bool success = reader.parse(resultStr, resultStr + strlen(resultStr), v);
517  if (success)
518  {
519  return processAccountTxStoredProcedureResult(args, v, context);
520  }
521 #endif
522  // This shouldn't happen. Postgres should return a parseable error
523  assert(false);
524  return {{}, {rpcINTERNAL, "Failed to deserialize Postgres result"}};
525 }
526 
529 {
531  if (context.app.config().reporting())
532  return doAccountTxStoredProcedure(args, context);
533 
534  AccountTxResult result;
535 
536  auto lgrRange = getLedgerRange(context, args.ledger);
537  if (auto stat = std::get_if<RPC::Status>(&lgrRange))
538  {
539  // An error occurred getting the requested ledger range
540  return {result, *stat};
541  }
542 
543  result.ledgerRange = std::get<LedgerRange>(lgrRange);
544 
545  result.marker = args.marker;
546  if (args.binary)
547  {
548  result.transactions = context.netOps.getTxsAccountB(
549  args.account,
550  result.ledgerRange.min,
551  result.ledgerRange.max,
552  args.forward,
553  result.marker,
554  args.limit,
555  isUnlimited(context.role));
556  }
557  else
558  {
559  result.transactions = context.netOps.getTxsAccount(
560  args.account,
561  result.ledgerRange.min,
562  result.ledgerRange.max,
563  args.forward,
564  result.marker,
565  args.limit,
566  isUnlimited(context.role));
567  }
568 
569  result.limit = args.limit;
570  JLOG(context.j.debug()) << __func__ << " : finished";
571 
572  return {result, rpcSUCCESS};
573 }
574 
575 std::pair<
576  org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
577  grpc::Status>
580  AccountTxArgs const& args,
582  org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest> const& context)
583 {
584  org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
585  grpc::Status status = grpc::Status::OK;
586 
587  RPC::Status const& error = res.second;
588  if (error.toErrorCode() != rpcSUCCESS)
589  {
590  if (error.toErrorCode() == rpcLGR_NOT_FOUND)
591  {
592  status = {grpc::StatusCode::NOT_FOUND, error.message()};
593  }
594  else if (error.toErrorCode() == rpcNOT_SYNCED)
595  {
596  status = {grpc::StatusCode::FAILED_PRECONDITION, error.message()};
597  }
598  else
599  {
600  status = {grpc::StatusCode::INVALID_ARGUMENT, error.message()};
601  }
602  }
603  else
604  {
605  AccountTxResult const& result = res.first;
606 
607  // account_tx always returns validated data
608  response.set_validated(true);
609  response.set_limit(result.limit);
610  response.mutable_account()->set_address(
611  context.params.account().address());
612  response.set_ledger_index_min(result.ledgerRange.min);
613  response.set_ledger_index_max(result.ledgerRange.max);
614 
615  if (auto txnsData = std::get_if<TxnsData>(&result.transactions))
616  {
617  assert(!args.binary);
618  for (auto const& [txn, txnMeta] : *txnsData)
619  {
620  if (txn)
621  {
622  auto txnProto = response.add_transactions();
623 
624  RPC::convert(
625  *txnProto->mutable_transaction(),
626  txn->getSTransaction());
627 
628  // account_tx always returns validated data
629  txnProto->set_validated(true);
630  txnProto->set_ledger_index(txn->getLedger());
631  auto& hash = txn->getID();
632  txnProto->set_hash(hash.data(), hash.size());
633  auto closeTime =
634  context.app.getLedgerMaster().getCloseTimeBySeq(
635  txn->getLedger());
636  if (closeTime)
637  txnProto->mutable_date()->set_value(
638  closeTime->time_since_epoch().count());
639  if (txnMeta)
640  {
641  RPC::convert(*txnProto->mutable_meta(), txnMeta);
642  if (!txnProto->meta().has_delivered_amount())
643  {
644  if (auto amt = getDeliveredAmount(
645  context,
646  txn->getSTransaction(),
647  *txnMeta,
648  txn->getLedger()))
649  {
650  RPC::convert(
651  *txnProto->mutable_meta()
652  ->mutable_delivered_amount(),
653  *amt);
654  }
655  }
656  }
657  }
658  }
659  }
660  else
661  {
662  assert(args.binary);
663 
664  for (auto const& binaryData :
665  std::get<TxnsDataBinary>(result.transactions))
666  {
667  auto txnProto = response.add_transactions();
668  Blob const& txnBlob = std::get<0>(binaryData);
669  txnProto->set_transaction_binary(
670  txnBlob.data(), txnBlob.size());
671 
672  Blob const& metaBlob = std::get<1>(binaryData);
673  txnProto->set_meta_binary(metaBlob.data(), metaBlob.size());
674 
675  txnProto->set_ledger_index(std::get<2>(binaryData));
676 
677  // account_tx always returns validated data
678  txnProto->set_validated(true);
679 
680  auto closeTime =
681  context.app.getLedgerMaster().getCloseTimeBySeq(
682  std::get<2>(binaryData));
683  if (closeTime)
684  txnProto->mutable_date()->set_value(
685  closeTime->time_since_epoch().count());
686  }
687  }
688 
689  if (result.marker)
690  {
691  response.mutable_marker()->set_ledger_index(
692  result.marker->ledgerSeq);
693  response.mutable_marker()->set_account_sequence(
694  result.marker->txnSeq);
695  }
696  }
697  return {response, status};
698 }
699 
703  AccountTxArgs const& args,
704  RPC::JsonContext const& context)
705 {
706  Json::Value response;
707  RPC::Status const& error = res.second;
708  if (error.toErrorCode() != rpcSUCCESS)
709  {
710  error.inject(response);
711  }
712  else
713  {
714  AccountTxResult const& result = res.first;
715  response[jss::validated] = true;
716  response[jss::limit] = result.limit;
717  response[jss::account] = context.params[jss::account].asString();
718  response[jss::ledger_index_min] = result.ledgerRange.min;
719  response[jss::ledger_index_max] = result.ledgerRange.max;
720 
721  Json::Value& jvTxns = (response[jss::transactions] = Json::arrayValue);
722 
723  if (auto txnsData = std::get_if<TxnsData>(&result.transactions))
724  {
725  assert(!args.binary);
726  for (auto const& [txn, txnMeta] : *txnsData)
727  {
728  if (txn)
729  {
730  Json::Value& jvObj = jvTxns.append(Json::objectValue);
731 
732  jvObj[jss::tx] = txn->getJson(JsonOptions::include_date);
733  if (txnMeta)
734  {
735  jvObj[jss::meta] =
736  txnMeta->getJson(JsonOptions::include_date);
737  jvObj[jss::validated] = true;
738  insertDeliveredAmount(
739  jvObj[jss::meta], context, txn, *txnMeta);
740  }
741  }
742  }
743  }
744  else
745  {
746  assert(args.binary);
747 
748  for (auto const& binaryData :
749  std::get<TxnsDataBinary>(result.transactions))
750  {
751  Json::Value& jvObj = jvTxns.append(Json::objectValue);
752 
753  jvObj[jss::tx_blob] = strHex(std::get<0>(binaryData));
754  jvObj[jss::meta] = strHex(std::get<1>(binaryData));
755  jvObj[jss::ledger_index] = std::get<2>(binaryData);
756  jvObj[jss::validated] = true;
757  }
758  }
759 
760  if (result.marker)
761  {
762  response[jss::marker] = Json::objectValue;
763  response[jss::marker][jss::ledger] = result.marker->ledgerSeq;
764  response[jss::marker][jss::seq] = result.marker->txnSeq;
765  }
766  if (context.app.config().reporting())
767  response["used_postgres"] = true;
768  }
769 
770  JLOG(context.j.debug()) << __func__ << " : finished";
771  return response;
772 }
773 
774 // {
775 // account: account,
776 // ledger_index_min: ledger_index // optional, defaults to earliest
777 // ledger_index_max: ledger_index, // optional, defaults to latest
778 // binary: boolean, // optional, defaults to false
779 // forward: boolean, // optional, defaults to false
780 // limit: integer, // optional
781 // marker: object {ledger: ledger_index, seq: txn_sequence} // optional,
782 // resume previous query
783 // }
786 {
787  if (!context.app.config().useTxTables())
788  return rpcError(rpcNOT_ENABLED);
789 
790  auto& params = context.params;
791  AccountTxArgs args;
792  Json::Value response;
793 
794  args.limit = params.isMember(jss::limit) ? params[jss::limit].asUInt() : 0;
795  args.binary = params.isMember(jss::binary) && params[jss::binary].asBool();
796  args.forward =
797  params.isMember(jss::forward) && params[jss::forward].asBool();
798 
799  if (!params.isMember(jss::account))
800  return rpcError(rpcINVALID_PARAMS);
801 
802  auto const account =
803  parseBase58<AccountID>(params[jss::account].asString());
804  if (!account)
805  return rpcError(rpcACT_MALFORMED);
806 
807  args.account = *account;
808 
809  auto parseRes = parseLedgerArgs(params);
810  if (auto jv = std::get_if<Json::Value>(&parseRes))
811  {
812  return *jv;
813  }
814  else
815  {
816  args.ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);
817  }
818 
819  if (params.isMember(jss::marker))
820  {
821  auto& token = params[jss::marker];
822  if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
823  !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
824  !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
825  {
826  RPC::Status status{
828  "invalid marker. Provide ledger index via ledger field, and "
829  "transaction sequence number via seq field"};
830  status.inject(response);
831  return response;
832  }
833  args.marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
834  }
835 
836  auto res = doAccountTxHelp(context, args);
837  JLOG(context.j.debug()) << __func__ << " populating response";
838  return populateJsonResponse(res, args, context);
839 }
840 
841 std::pair<
842  org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
843  grpc::Status>
846  context)
847 {
848  if (!context.app.config().useTxTables())
849  {
850  return {
851  {},
852  {grpc::StatusCode::UNIMPLEMENTED, "Not enabled in configuration."}};
853  }
854 
855  // return values
856  org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
857  grpc::Status status = grpc::Status::OK;
858  AccountTxArgs args;
859 
860  auto& request = context.params;
861 
862  auto const account = parseBase58<AccountID>(request.account().address());
863  if (!account)
864  {
865  return {
866  {},
867  {grpc::StatusCode::INVALID_ARGUMENT, "Could not decode account"}};
868  }
869 
870  args.account = *account;
871  args.limit = request.limit();
872  args.binary = request.binary();
873  args.forward = request.forward();
874 
875  if (request.has_marker())
876  {
877  args.marker = {
878  request.marker().ledger_index(),
879  request.marker().account_sequence()};
880  }
881 
882  auto parseRes = parseLedgerArgs(request);
883  if (auto stat = std::get_if<grpc::Status>(&parseRes))
884  {
885  return {response, *stat};
886  }
887  else
888  {
889  args.ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);
890  }
891 
892  auto res = doAccountTxHelp(context, args);
893  return populateProtoResponse(res, args, context);
894 }
895 
896 } // namespace ripple
ripple::COMMITTED
@ COMMITTED
Definition: Transaction.h:49
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:627
ripple::RPC::Status::OK
static constexpr Code OK
Definition: Status.h:46
ripple::JsonOptions::include_date
@ include_date
std::vector::resize
T resize(T... args)
ripple::RPC::JsonContext
Definition: Context.h:53
std::make_tuple
T make_tuple(T... args)
ripple::NetworkOPs::AccountTxs
std::vector< AccountTx > AccountTxs
Definition: NetworkOPs.h:264
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
std::exception
STL class.
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:444
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::Resource::feeMediumBurdenRPC
const Charge feeMediumBurdenRPC
ripple::NetworkOPs::txnMetaLedgerType
std::tuple< Blob, Blob, std::uint32_t > txnMetaLedgerType
Definition: NetworkOPs.h:286
ripple::AccountTxArgs::binary
bool binary
Definition: AccountTx.cpp:62
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
std::pair
ripple::RPC::LedgerShortcut
LedgerShortcut
Definition: RPCHelpers.h:109
ripple::TxnsData
NetworkOPs::AccountTxs TxnsData
Definition: AccountTx.cpp:68
ripple::RPC::Context::loadType
Resource::Charge & loadType
Definition: Context.h:43
ripple::AccountTxResult::marker
std::optional< AccountTxMarker > marker
Definition: AccountTx.cpp:77
std::vector
STL class.
ripple::LedgerSequence
uint32_t LedgerSequence
Definition: AccountTx.cpp:43
std::vector::size
T size(T... args)
ripple::populateProtoResponse
std::pair< org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse, grpc::Status > populateProtoResponse(std::pair< AccountTxResult, RPC::Status > const &res, AccountTxArgs const &args, RPC::GRPCContext< org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest > const &context)
Definition: AccountTx.cpp:578
ripple::AccountTxArgs::marker
std::optional< AccountTxMarker > marker
Definition: AccountTx.cpp:65
ripple::doAccountTxGrpc
std::pair< org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse, grpc::Status > doAccountTxGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest > &context)
Definition: AccountTx.cpp:844
ripple::RPC::Context::ledgerMaster
LedgerMaster & ledgerMaster
Definition: Context.h:45
ripple::AccountTxArgs::ledger
std::optional< LedgerSpecifier > ledger
Definition: AccountTx.cpp:61
ripple::DataFormat::expanded
@ expanded
std::tuple
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:92
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
ripple::RPC::Context::role
Role role
Definition: Context.h:47
ripple::rpcLGR_NOT_FOUND
@ rpcLGR_NOT_FOUND
Definition: ErrorCodes.h:72
ripple::AccountTxResult::ledgerRange
LedgerRange ledgerRange
Definition: AccountTx.cpp:75
ripple::base_uint< 256 >::size
constexpr static std::size_t size()
Definition: base_uint.h:426
ripple::doAccountTxHelp
std::pair< AccountTxResult, RPC::Status > doAccountTxHelp(RPC::Context &context, AccountTxArgs const &args)
Definition: AccountTx.cpp:528
ripple::uint256
base_uint< 256 > uint256
Definition: base_uint.h:457
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:207
std::vector::push_back
T push_back(T... args)
ripple::AccountTxResult::limit
uint32_t limit
Definition: AccountTx.cpp:76
ripple::base_uint< 256 >
ripple::DataFormat
DataFormat
Definition: AccountTx.cpp:278
ripple::RPC::convert
void convert(org::xrpl::rpc::v1::TransactionResult &to, TER from)
Definition: GRPCHelpers.cpp:961
ripple::rpcSUCCESS
@ rpcSUCCESS
Definition: ErrorCodes.h:44
ripple::doAccountTxStoredProcedure
std::pair< AccountTxResult, RPC::Status > doAccountTxStoredProcedure(AccountTxArgs const &args, RPC::Context &context)
Definition: AccountTx.cpp:416
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:267
ripple::NetworkOPs::MetaTxsList
std::vector< txnMetaLedgerType > MetaTxsList
Definition: NetworkOPs.h:287
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::Application::config
virtual Config & config()=0
ripple::RPC::GRPCContext
Definition: Context.h:70
ripple::Config::useTxTables
bool useTxTables() const
Definition: Config.h:273
std::to_string
T to_string(T... args)
ripple::RPC::Context::app
Application & app
Definition: Context.h:42
beast::Journal::error
Stream error() const
Definition: Journal.h:333
std::string::erase
T erase(T... args)
ripple::NetworkOPs::getTxsAccount
virtual AccountTxs getTxsAccount(AccountID const &account, std::int32_t minLedger, std::int32_t maxLedger, bool forward, std::optional< AccountTxMarker > &marker, int limit, bool bUnlimited)=0
ripple::rpcNOT_ENABLED
@ rpcNOT_ENABLED
Definition: ErrorCodes.h:59
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
ripple::AccountTxArgs
Definition: AccountTx.cpp:58
std::uint32_t
ripple::range
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition: RangeSet.h:53
ripple::rpcError
Json::Value rpcError(int iError, Json::Value jvResult)
Definition: RPCErr.cpp:29
ripple::NetworkOPs::AccountTxMarker
Definition: NetworkOPs.h:255
ripple::RPC::Status
Status represents the results of an operation that might fail.
Definition: Status.h:39
ripple::RPC::Context::netOps
NetworkOPs & netOps
Definition: Context.h:44
ripple::rpcINTERNAL
@ rpcINTERNAL
Definition: ErrorCodes.h:130
std::decay_t
ripple::isUnlimited
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition: Role.cpp:94
ripple::Serializer
Definition: Serializer.h:39
ripple::AccountTxResult
Definition: AccountTx.cpp:72
ripple::RPC::GRPCContext::params
RequestType params
Definition: Context.h:72
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::AccountTxArgs::limit
uint32_t limit
Definition: AccountTx.cpp:64
ripple::populateJsonResponse
Json::Value populateJsonResponse(std::pair< AccountTxResult, RPC::Status > const &res, AccountTxArgs const &args, RPC::JsonContext const &context)
Definition: AccountTx.cpp:701
ripple::RPC::isValidated
bool isValidated(LedgerMaster &ledgerMaster, ReadView const &ledger, Application &app)
Definition: RPCHelpers.cpp:464
ripple::rpcACT_MALFORMED
@ rpcACT_MALFORMED
Definition: ErrorCodes.h:90
ripple::base_uint::parseHex
bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:384
ripple::AccountTxArgs::account
AccountID account
Definition: AccountTx.cpp:60
ripple::rpcLGR_NOT_VALIDATED
@ rpcLGR_NOT_VALIDATED
Definition: ErrorCodes.h:73
ripple::doAccountTxJson
Json::Value doAccountTxJson(RPC::JsonContext &context)
Definition: AccountTx.cpp:785
ripple::base_uint< 256 >::fromVoid
static base_uint fromVoid(void const *data)
Definition: base_uint.h:223
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
ripple::LedgerRange::max
uint32_t max
Definition: AccountTx.cpp:52
ripple::RPC::Context::apiVersion
unsigned int apiVersion
Definition: Context.h:50
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:74
ripple::processAccountTxStoredProcedureResult
std::pair< AccountTxResult, RPC::Status > processAccountTxStoredProcedureResult(AccountTxArgs const &args, Json::Value &result, RPC::Context &context)
Definition: AccountTx.cpp:326
std::visit
T visit(T... args)
std::string::empty
T empty(T... args)
ripple::parseLedgerArgs
std::variant< std::optional< LedgerSpecifier >, grpc::Status > parseLedgerArgs(org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest const &params)
Definition: AccountTx.cpp:82
ripple::TxnsDataBinary
NetworkOPs::MetaTxsList TxnsDataBinary
Definition: AccountTx.cpp:69
std::optional
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::make_pair
T make_pair(T... args)
Json::Value::asInt
Int asInt() const
Definition: json_value.cpp:503
ripple::AccountTxArgs::forward
bool forward
Definition: AccountTx.cpp:63
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:45
ripple::Serializer::getData
Blob getData() const
Definition: Serializer.h:171
ripple::rpcINVALID_LGR_RANGE
@ rpcINVALID_LGR_RANGE
Definition: ErrorCodes.h:136
ripple::LedgerRange
Definition: AccountTx.cpp:49
ripple::LedgerRange::min
uint32_t min
Definition: AccountTx.cpp:51
ripple::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
ripple::DataFormat::binary
@ binary
ripple::NetworkOPs::getTxsAccountB
virtual MetaTxsList getTxsAccountB(AccountID const &account, std::int32_t minLedger, std::int32_t maxLedger, bool forward, std::optional< AccountTxMarker > &marker, int limit, bool bUnlimited)=0
std::vector::data
T data(T... args)
ripple::RPC::Context
The context of information needed to call an RPC.
Definition: Context.h:39
ripple::rpcNOT_SYNCED
@ rpcNOT_SYNCED
Definition: ErrorCodes.h:67
std::exception::what
T what(T... args)
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::AccountTxResult::transactions
std::variant< TxnsData, TxnsDataBinary > transactions
Definition: AccountTx.cpp:74
std::variant
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469
ripple::flatFetchTransactions
std::vector< std::pair< std::shared_ptr< STTx const >, std::shared_ptr< STObject const > > > flatFetchTransactions(Application &app, std::vector< uint256 > &nodestoreHashes)
Definition: Ledger.cpp:1704