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>
39 #include <grpcpp/grpcpp.h>
83 org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest
const& params)
86 if (params.has_ledger_range())
88 uint32_t min = params.ledger_range().ledger_index_min();
89 uint32_t max = params.ledger_range().ledger_index_max();
92 if (min != 0 && max == 0)
99 else if (params.has_ledger_specifier())
103 auto& specifier = params.ledger_specifier();
104 using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
105 LedgerCase ledgerCase = specifier.ledger_case();
107 if (ledgerCase == LedgerCase::kShortcut)
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;
120 else if (ledgerCase == LedgerCase::kSequence)
122 ledger = specifier.sequence();
124 else if (ledgerCase == LedgerCase::kHash)
128 grpc::Status errorStatus{
129 grpc::StatusCode::INVALID_ARGUMENT,
130 "ledger hash malformed"};
145 if (params.
isMember(jss::ledger_index_min) ||
146 params.
isMember(jss::ledger_index_max))
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()
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()
159 else if (params.
isMember(jss::ledger_hash))
161 auto& hashValue = params[jss::ledger_hash];
162 if (!hashValue.isString())
165 status.inject(response);
170 if (!hash.
parseHex(hashValue.asString()))
173 status.inject(response);
178 else if (params.
isMember(jss::ledger_index))
181 if (params[jss::ledger_index].isNumeric())
182 ledger = params[jss::ledger_index].
asUInt();
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;
197 status.inject(response);
232 if constexpr (std::is_same_v<T, LedgerRange>)
234 if (ls.min > uValidatedMin)
238 if (ls.max < uValidatedMax)
242 if (uLedgerMax < uLedgerMin)
252 auto const status = getLedger(ledgerView, ls, context);
261 if (!validated || ledgerView->
info().
seq > uValidatedMax ||
262 ledgerView->
info().
seq < uValidatedMin)
266 uLedgerMin = uLedgerMax = ledgerView->
info().
seq;
295 for (
size_t i = 0; i < txns.size(); ++i)
297 auto& [txn, meta] = txns[i];
300 auto& transactions = std::get<TxnsDataBinary>(ret);
311 auto& transactions = std::get<TxnsData>(ret);
314 std::make_shared<Transaction>(txn, reason, context.
app);
315 txnRet->setLedger(ledgerSequences[i]);
317 auto txMeta = std::make_shared<TxMeta>(
318 txnRet->getID(), ledgerSequences[i], *meta);
336 if (result.
isMember(
"transactions"))
340 for (
auto& t : result[
"transactions"])
342 if (t.isMember(
"ledger_seq") && t.isMember(
"nodestore_hash"))
344 uint32_t ledgerSequence = t[
"ledger_seq"].asUInt();
346 t[
"nodestore_hash"].asString();
347 nodestoreHashHex.
erase(0, 2);
349 if (!nodestoreHash.
parseHex(nodestoreHashHex))
354 ledgerSequences.
push_back(ledgerSequence);
355 nodestoreHashes.
push_back(nodestoreHash);
360 return {ret, {
rpcINTERNAL,
"nodestoreHash is zero"}};
366 return {ret, {
rpcINTERNAL,
"missing postgres fields"}};
370 assert(nodestoreHashes.
size() == ledgerSequences.
size());
377 JLOG(context.
j.
trace()) << __func__ <<
" : processed db results";
379 if (result.isMember(
"marker"))
381 auto& marker = result[
"marker"];
382 assert(marker.isMember(
"ledger"));
383 assert(marker.isMember(
"seq"));
385 marker[
"ledger"].asUInt(), marker[
"seq"].asUInt()};
387 assert(result.isMember(
"ledger_index_min"));
388 assert(result.isMember(
"ledger_index_max"));
390 result[
"ledger_index_min"].asUInt(),
391 result[
"ledger_index_max"].asUInt()};
397 << __func__ <<
" : error = " << result[
"error"].
asString();
404 return {ret, {
rpcINTERNAL,
"unexpected Postgres response"}};
409 JLOG(context.
j.
debug()) << __func__ <<
" : "
410 <<
"Caught exception : " << e.
what();
418 #ifdef RIPPLED_REPORTING
421 char const*& command = dbParams.first;
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)";
429 values[1] = args.
forward ?
"true" :
"false";
432 if (args.
limit == 0 || args.
limit > page_length)
439 if (
auto range = std::get_if<LedgerRange>(&args.
ledger.value()))
444 else if (
auto hash = std::get_if<LedgerHash>(&args.
ledger.value()))
446 values[5] = (
"\\x" +
strHex(*hash));
449 auto sequence = std::get_if<LedgerSequence>(&args.
ledger.value()))
453 else if (std::get_if<LedgerShortcut>(&args.
ledger.value()))
460 JLOG(context.
j.
error()) <<
"doAccountTxStoredProcedure - "
461 <<
"Error parsing ledger args";
471 for (
size_t i = 0; i < values.
size(); ++i)
474 << (values[i] ? values[i].value() :
"null");
477 auto res = PgQuery(context.
app.getPgPool())(dbParams);
481 << __func__ <<
" : Postgres response is null - account = "
486 else if (res.status() != PGRES_TUPLES_OK)
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()
498 << __func__ <<
" Postgres result msg : " << res.msg();
499 if (res.isNull() || res.ntuples() == 0)
502 << __func__ <<
" : No data returned from Postgres : account = "
509 char const* resultStr = res.c_str();
510 JLOG(context.
j.
trace()) << __func__ <<
" : "
511 <<
"postgres result = " << resultStr
516 bool success = reader.
parse(resultStr, resultStr + strlen(resultStr), v);
524 return {{}, {
rpcINTERNAL,
"Failed to deserialize Postgres result"}};
537 if (
auto stat = std::get_if<RPC::Status>(&lgrRange))
540 return {result, *stat};
543 result.
ledgerRange = std::get<LedgerRange>(lgrRange);
570 JLOG(context.
j.
debug()) << __func__ <<
" : finished";
576 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
582 org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest>
const& context)
584 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
585 grpc::Status status = grpc::Status::OK;
592 status = {grpc::StatusCode::NOT_FOUND, error.message()};
596 status = {grpc::StatusCode::FAILED_PRECONDITION, error.message()};
600 status = {grpc::StatusCode::INVALID_ARGUMENT, error.message()};
608 response.set_validated(
true);
609 response.set_limit(result.
limit);
610 response.mutable_account()->set_address(
611 context.params.account().address());
615 if (
auto txnsData = std::get_if<TxnsData>(&result.
transactions))
618 for (
auto const& [txn, txnMeta] : *txnsData)
622 auto txnProto = response.add_transactions();
625 *txnProto->mutable_transaction(),
626 txn->getSTransaction());
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());
634 context.app.getLedgerMaster().getCloseTimeBySeq(
637 txnProto->mutable_date()->set_value(
638 closeTime->time_since_epoch().count());
642 if (!txnProto->meta().has_delivered_amount())
644 if (
auto amt = getDeliveredAmount(
646 txn->getSTransaction(),
651 *txnProto->mutable_meta()
652 ->mutable_delivered_amount(),
664 for (
auto const& binaryData :
667 auto txnProto = response.add_transactions();
668 Blob const& txnBlob = std::get<0>(binaryData);
669 txnProto->set_transaction_binary(
672 Blob const& metaBlob = std::get<1>(binaryData);
673 txnProto->set_meta_binary(metaBlob.
data(), metaBlob.
size());
675 txnProto->set_ledger_index(std::get<2>(binaryData));
678 txnProto->set_validated(
true);
681 context.app.getLedgerMaster().getCloseTimeBySeq(
682 std::get<2>(binaryData));
684 txnProto->mutable_date()->set_value(
685 closeTime->time_since_epoch().count());
691 response.mutable_marker()->set_ledger_index(
692 result.
marker->ledgerSeq);
693 response.mutable_marker()->set_account_sequence(
697 return {response, status};
710 error.inject(response);
715 response[jss::validated] =
true;
716 response[jss::limit] = result.
limit;
723 if (
auto txnsData = std::get_if<TxnsData>(&result.
transactions))
726 for (
auto const& [txn, txnMeta] : *txnsData)
737 jvObj[jss::validated] =
true;
738 insertDeliveredAmount(
739 jvObj[jss::meta], context, txn, *txnMeta);
748 for (
auto const& binaryData :
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;
763 response[jss::marker][jss::ledger] = result.
marker->ledgerSeq;
764 response[jss::marker][jss::seq] = result.
marker->txnSeq;
767 response[
"used_postgres"] =
true;
770 JLOG(context.
j.
debug()) << __func__ <<
" : finished";
790 auto& params = context.
params;
794 args.
limit = params.isMember(jss::limit) ? params[jss::limit].asUInt() : 0;
795 args.
binary = params.isMember(jss::binary) && params[jss::binary].asBool();
797 params.isMember(jss::forward) && params[jss::forward].asBool();
799 if (!params.isMember(jss::account))
803 parseBase58<AccountID>(params[jss::account].asString());
810 if (
auto jv = std::get_if<Json::Value>(&parseRes))
816 args.
ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);
819 if (params.isMember(jss::marker))
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))
828 "invalid marker. Provide ledger index via ledger field, and "
829 "transaction sequence number via seq field"};
830 status.inject(response);
833 args.
marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
837 JLOG(context.
j.
debug()) << __func__ <<
" populating response";
842 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
852 {grpc::StatusCode::UNIMPLEMENTED,
"Not enabled in configuration."}};
856 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
857 grpc::Status status = grpc::Status::OK;
860 auto& request = context.
params;
862 auto const account = parseBase58<AccountID>(request.account().address());
867 {grpc::StatusCode::INVALID_ARGUMENT,
"Could not decode account"}};
871 args.
limit = request.limit();
872 args.
binary = request.binary();
873 args.
forward = request.forward();
875 if (request.has_marker())
878 request.marker().ledger_index(),
879 request.marker().account_sequence()};
883 if (
auto stat = std::get_if<grpc::Status>(&parseRes))
885 return {response, *stat};
889 args.
ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);