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/json/json_value.h>
25 #include <ripple/ledger/ReadView.h>
26 #include <ripple/net/RPCErr.h>
27 #include <ripple/protocol/ErrorCodes.h>
28 #include <ripple/protocol/UintTypes.h>
29 #include <ripple/protocol/jss.h>
30 #include <ripple/resource/Fees.h>
31 #include <ripple/rpc/Context.h>
32 #include <ripple/rpc/DeliveredAmount.h>
33 #include <ripple/rpc/Role.h>
34 #include <ripple/rpc/impl/RPCHelpers.h>
35 #include <ripple/rpc/impl/GRPCHelpers.h>
38 #include <grpcpp/grpcpp.h>
82 org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest
const& params)
85 if (params.has_ledger_range())
87 uint32_t min = params.ledger_range().ledger_index_min();
88 uint32_t max = params.ledger_range().ledger_index_max();
91 if (min != 0 && max == 0)
98 else if (params.has_ledger_specifier())
102 auto& specifier = params.ledger_specifier();
103 using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
104 LedgerCase ledgerCase = specifier.ledger_case();
106 if (ledgerCase == LedgerCase::kShortcut)
110 if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_VALIDATED)
111 ledger = LedgerShortcut::VALIDATED;
112 else if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_CLOSED)
113 ledger = LedgerShortcut::CLOSED;
114 else if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_CURRENT)
115 ledger = LedgerShortcut::CURRENT;
119 else if (ledgerCase == LedgerCase::kSequence)
121 ledger = specifier.sequence();
123 else if (ledgerCase == LedgerCase::kHash)
127 grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT,
128 "ledger hash malformed"};
143 if (params.
isMember(jss::ledger_index_min) ||
144 params.
isMember(jss::ledger_index_max))
146 uint32_t min = params.
isMember(jss::ledger_index_min) &&
147 params[jss::ledger_index_min].
asInt() >= 0
148 ? params[jss::ledger_index_min].
asUInt()
150 uint32_t max = params.
isMember(jss::ledger_index_max) &&
151 params[jss::ledger_index_max].
asInt() >= 0
152 ? params[jss::ledger_index_max].
asUInt()
157 else if (params.
isMember(jss::ledger_hash))
159 auto& hashValue = params[jss::ledger_hash];
160 if (!hashValue.isString())
163 status.inject(response);
168 if (!hash.
SetHex(hashValue.asString()))
171 status.inject(response);
176 else if (params.
isMember(jss::ledger_index))
179 if (params[jss::ledger_index].isNumeric())
180 ledger = params[jss::ledger_index].
asInt();
185 if (ledgerStr ==
"current" || ledgerStr.
empty())
186 ledger = LedgerShortcut::CURRENT;
187 else if (ledgerStr ==
"closed")
188 ledger = LedgerShortcut::CLOSED;
189 else if (ledgerStr ==
"validated")
190 ledger = LedgerShortcut::VALIDATED;
194 "ledger_index string malformed"};
195 status.inject(response);
228 if constexpr (std::is_same_v<T, LedgerRange>)
230 if (ls.min > uValidatedMin)
234 if (ls.max < uValidatedMax)
238 if (uLedgerMax < uLedgerMin)
244 auto const status = getLedger(ledgerView, ls, context);
253 if (!validated || ledgerView->
info().
seq > uValidatedMax ||
254 ledgerView->
info().
seq < uValidatedMin)
258 uLedgerMin = uLedgerMax = ledgerView->
info().
seq;
277 if (
auto stat = std::get_if<RPC::Status>(&lgrRange))
280 return {result, *stat};
283 result.
ledgerRange = std::get<LedgerRange>(lgrRange);
315 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
321 org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest>
const& context)
323 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
324 grpc::Status status = grpc::Status::OK;
331 status = {grpc::StatusCode::NOT_FOUND, error.message()};
335 status = {grpc::StatusCode::INVALID_ARGUMENT, error.message()};
343 response.set_validated(
true);
344 response.set_limit(result.
limit);
345 response.mutable_account()->set_address(
346 context.params.account().address());
350 if (
auto txnsData = std::get_if<TxnsData>(&result.
transactions))
353 for (
auto const& [txn, txnMeta] : *txnsData)
357 auto txnProto = response.add_transactions();
360 *txnProto->mutable_transaction(),
361 txn->getSTransaction());
364 txnProto->set_validated(
true);
365 txnProto->set_ledger_index(txn->getLedger());
366 auto& hash = txn->getID();
367 txnProto->set_hash(hash.data(), hash.size());
369 context.app.getLedgerMaster().getCloseTimeBySeq(
372 txnProto->mutable_date()->set_value(
373 closeTime->time_since_epoch().count());
376 if (!txnMeta->hasDeliveredAmount())
380 txn->getSTransaction(),
385 txnMeta->setDeliveredAmount(*amount);
397 for (
auto const& binaryData :
400 auto txnProto = response.add_transactions();
401 Blob const& txnBlob = std::get<0>(binaryData);
402 txnProto->set_transaction_binary(
405 Blob const& metaBlob = std::get<1>(binaryData);
406 txnProto->set_meta_binary(metaBlob.
data(), metaBlob.
size());
408 txnProto->set_ledger_index(std::get<2>(binaryData));
411 txnProto->set_validated(
true);
414 context.app.getLedgerMaster().getCloseTimeBySeq(
415 std::get<2>(binaryData));
417 txnProto->mutable_date()->set_value(
418 closeTime->time_since_epoch().count());
424 response.mutable_marker()->set_ledger_index(
425 result.
marker->ledgerSeq);
426 response.mutable_marker()->set_account_sequence(
430 return {response, status};
443 error.inject(response);
448 response[jss::validated] =
true;
449 response[jss::limit] = result.
limit;
456 if (
auto txnsData = std::get_if<TxnsData>(&result.
transactions))
459 for (
auto const& [txn, txnMeta] : *txnsData)
470 jvObj[jss::validated] =
true;
471 insertDeliveredAmount(
472 jvObj[jss::meta], context, txn, *txnMeta);
481 for (
auto const& binaryData :
486 jvObj[jss::tx_blob] =
strHex(std::get<0>(binaryData));
487 jvObj[jss::meta] =
strHex(std::get<1>(binaryData));
488 jvObj[jss::ledger_index] = std::get<2>(binaryData);
489 jvObj[jss::validated] =
true;
496 response[jss::marker][jss::ledger] = result.
marker->ledgerSeq;
497 response[jss::marker][jss::seq] = result.
marker->txnSeq;
516 auto& params = context.
params;
520 args.
limit = params.isMember(jss::limit) ? params[jss::limit].asUInt() : 0;
521 args.
binary = params.isMember(jss::binary) && params[jss::binary].asBool();
523 params.isMember(jss::forward) && params[jss::forward].asBool();
525 if (!params.isMember(jss::account))
529 parseBase58<AccountID>(params[jss::account].asString());
536 if (
auto jv = std::get_if<Json::Value>(&parseRes))
542 args.
ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);
545 if (params.isMember(jss::marker))
547 auto& token = params[jss::marker];
548 if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
549 !token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
550 !token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
554 "invalid marker. Provide ledger index via ledger field, and "
555 "transaction sequence number via seq field"};
556 status.inject(response);
559 args.
marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
567 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
574 org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
575 grpc::Status status = grpc::Status::OK;
578 auto& request = context.
params;
580 auto const account = parseBase58<AccountID>(request.account().address());
585 {grpc::StatusCode::INVALID_ARGUMENT,
"Could not decode account"}};
589 args.
limit = request.limit();
590 args.
binary = request.binary();
591 args.
forward = request.forward();
593 if (request.has_marker())
595 args.
marker = {request.marker().ledger_index(),
596 request.marker().account_sequence()};
600 if (
auto stat = std::get_if<grpc::Status>(&parseRes))
602 return {response, *stat};
606 args.
ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);