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/GRPCHelpers.h>
39 #include <ripple/rpc/impl/RPCHelpers.h>
41 #include <grpcpp/grpcpp.h>
57 org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest
const& params)
60 if (params.has_ledger_range())
62 uint32_t min = params.ledger_range().ledger_index_min();
63 uint32_t max = params.ledger_range().ledger_index_max();
66 if (min != 0 && max == 0)
73 else if (params.has_ledger_specifier())
77 auto& specifier = params.ledger_specifier();
78 using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
79 LedgerCase ledgerCase = specifier.ledger_case();
81 if (ledgerCase == LedgerCase::kShortcut)
85 if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_VALIDATED)
86 ledger = LedgerShortcut::VALIDATED;
87 else if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_CLOSED)
88 ledger = LedgerShortcut::CLOSED;
89 else if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_CURRENT)
90 ledger = LedgerShortcut::CURRENT;
94 else if (ledgerCase == LedgerCase::kSequence)
96 ledger = specifier.sequence();
98 else if (ledgerCase == LedgerCase::kHash)
106 grpc::Status errorStatus{
107 grpc::StatusCode::INVALID_ARGUMENT,
108 "ledger hash malformed"};
122 if (params.
isMember(jss::ledger_index_min) ||
123 params.
isMember(jss::ledger_index_max))
125 uint32_t min = params.
isMember(jss::ledger_index_min) &&
126 params[jss::ledger_index_min].
asInt() >= 0
127 ? params[jss::ledger_index_min].
asUInt()
129 uint32_t max = params.
isMember(jss::ledger_index_max) &&
130 params[jss::ledger_index_max].
asInt() >= 0
131 ? params[jss::ledger_index_max].
asUInt()
136 else if (params.
isMember(jss::ledger_hash))
138 auto& hashValue = params[jss::ledger_hash];
139 if (!hashValue.isString())
142 status.inject(response);
147 if (!hash.
parseHex(hashValue.asString()))
150 status.inject(response);
155 else if (params.
isMember(jss::ledger_index))
158 if (params[jss::ledger_index].isNumeric())
159 ledger = params[jss::ledger_index].
asUInt();
164 if (ledgerStr ==
"current" || ledgerStr.
empty())
165 ledger = LedgerShortcut::CURRENT;
166 else if (ledgerStr ==
"closed")
167 ledger = LedgerShortcut::CLOSED;
168 else if (ledgerStr ==
"validated")
169 ledger = LedgerShortcut::VALIDATED;
174 status.inject(response);
209 if constexpr (std::is_same_v<T, LedgerRange>)
211 if (ls.min > uValidatedMin)
215 if (ls.max < uValidatedMax)
219 if (uLedgerMax < uLedgerMin)
229 auto const status = getLedger(ledgerView, ls, context);
238 if (!validated || ledgerView->
info().
seq > uValidatedMax ||
239 ledgerView->
info().
seq < uValidatedMin)
243 uLedgerMin = uLedgerMax = ledgerView->
info().
seq;
265 Throw<std::runtime_error>(
"Failed to get relational database");
267 return db->getAccountTx(args);
273 if (
auto stat = std::get_if<RPC::Status>(&lgrRange))
276 return {result, *stat};
279 result.
ledgerRange = std::get<LedgerRange>(lgrRange);
295 Throw<std::runtime_error>(
"Failed to get relational database");
301 auto [tx, marker] = db->oldestAccountTxPageB(options);
307 auto [tx, marker] = db->newestAccountTxPageB(options);
316 auto [tx, marker] = db->oldestAccountTxPage(options);
322 auto [tx, marker] = db->newestAccountTxPage(options);
329 JLOG(context.
j.
debug()) << __func__ <<
" : finished";
335 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
341 org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest>
const& context)
343 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
344 grpc::Status status = grpc::Status::OK;
351 status = {grpc::StatusCode::NOT_FOUND, error.message()};
355 status = {grpc::StatusCode::FAILED_PRECONDITION, error.message()};
359 status = {grpc::StatusCode::INVALID_ARGUMENT, error.message()};
367 response.set_validated(
true);
368 response.set_limit(result.
limit);
369 response.mutable_account()->set_address(
370 context.params.account().address());
374 if (
auto txnsData = std::get_if<TxnsData>(&result.
transactions))
377 for (
auto const& [txn, txnMeta] : *txnsData)
381 auto txnProto = response.add_transactions();
384 *txnProto->mutable_transaction(),
385 txn->getSTransaction());
388 txnProto->set_validated(
true);
389 txnProto->set_ledger_index(txn->getLedger());
390 auto& hash = txn->getID();
391 txnProto->set_hash(hash.data(), hash.size());
393 context.app.getLedgerMaster().getCloseTimeBySeq(
396 txnProto->mutable_date()->set_value(
397 closeTime->time_since_epoch().count());
401 if (!txnProto->meta().has_delivered_amount())
403 if (
auto amt = getDeliveredAmount(
405 txn->getSTransaction(),
410 *txnProto->mutable_meta()
411 ->mutable_delivered_amount(),
423 for (
auto const& binaryData :
426 auto txnProto = response.add_transactions();
427 Blob const& txnBlob = std::get<0>(binaryData);
428 txnProto->set_transaction_binary(
431 Blob const& metaBlob = std::get<1>(binaryData);
432 txnProto->set_meta_binary(metaBlob.
data(), metaBlob.
size());
434 txnProto->set_ledger_index(std::get<2>(binaryData));
437 txnProto->set_validated(
true);
440 context.app.getLedgerMaster().getCloseTimeBySeq(
441 std::get<2>(binaryData));
443 txnProto->mutable_date()->set_value(
444 closeTime->time_since_epoch().count());
450 response.mutable_marker()->set_ledger_index(
451 result.
marker->ledgerSeq);
452 response.mutable_marker()->set_account_sequence(
456 return {response, status};
469 error.inject(response);
474 response[jss::validated] =
true;
475 response[jss::limit] = result.
limit;
482 if (
auto txnsData = std::get_if<TxnsData>(&result.
transactions))
485 for (
auto const& [txn, txnMeta] : *txnsData)
496 jvObj[jss::validated] =
true;
497 insertDeliveredAmount(
498 jvObj[jss::meta], context, txn, *txnMeta);
507 for (
auto const& binaryData :
512 jvObj[jss::tx_blob] =
strHex(std::get<0>(binaryData));
513 jvObj[jss::meta] =
strHex(std::get<1>(binaryData));
514 jvObj[jss::ledger_index] = std::get<2>(binaryData);
515 jvObj[jss::validated] =
true;
522 response[jss::marker][jss::ledger] = result.
marker->ledgerSeq;
523 response[jss::marker][jss::seq] = result.
marker->txnSeq;
526 response[
"used_postgres"] =
true;
529 JLOG(context.
j.
debug()) << __func__ <<
" : finished";
549 auto& params = context.
params;
553 args.
limit = params.isMember(jss::limit) ? params[jss::limit].asUInt() : 0;
554 args.
binary = params.isMember(jss::binary) && params[jss::binary].asBool();
556 params.isMember(jss::forward) && params[jss::forward].asBool();
558 if (!params.isMember(jss::account))
562 parseBase58<AccountID>(params[jss::account].asString());
569 if (
auto jv = std::get_if<Json::Value>(&parseRes))
575 args.
ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);
578 if (params.isMember(jss::marker))
580 auto& token = params[jss::marker];
581 if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
582 !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
583 !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
587 "invalid marker. Provide ledger index via ledger field, and "
588 "transaction sequence number via seq field"};
589 status.inject(response);
592 args.
marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
596 JLOG(context.
j.
debug()) << __func__ <<
" populating response";
601 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
611 {grpc::StatusCode::UNIMPLEMENTED,
"Not enabled in configuration."}};
615 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
616 grpc::Status status = grpc::Status::OK;
619 auto& request = context.
params;
621 auto const account = parseBase58<AccountID>(request.account().address());
626 {grpc::StatusCode::INVALID_ARGUMENT,
"Could not decode account"}};
630 args.
limit = request.limit();
631 args.
binary = request.binary();
632 args.
forward = request.forward();
634 if (request.has_marker())
637 request.marker().ledger_index(),
638 request.marker().account_sequence()};
642 if (
auto stat = std::get_if<grpc::Status>(&parseRes))
644 return {response, *stat};
648 args.
ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);