mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
gRPC support for account_tx and tx
- Add support for all transaction types and ledger object types to gRPC implementation of tx and account_tx. - Create common handlers for tx and account_tx. - Remove mutex and abort() from gRPC server. JobQueue is stopped before gRPC server, with all coroutines executed to completion, so no need for synchronization.
This commit is contained in:
@@ -29,6 +29,8 @@
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace RPC {
|
||||
|
||||
@@ -199,12 +201,10 @@ template <class T>
|
||||
Status
|
||||
ledgerFromRequest(T& ledger, JsonContext& context)
|
||||
{
|
||||
static auto const minSequenceGap = 10;
|
||||
|
||||
ledger.reset();
|
||||
|
||||
auto& params = context.params;
|
||||
auto& ledgerMaster = context.ledgerMaster;
|
||||
|
||||
auto indexValue = params[jss::ledger_index];
|
||||
auto hashValue = params[jss::ledger_hash];
|
||||
@@ -225,74 +225,32 @@ ledgerFromRequest(T& ledger, JsonContext& context)
|
||||
return {rpcINVALID_PARAMS, "ledgerHashNotString"};
|
||||
|
||||
uint256 ledgerHash;
|
||||
if (! ledgerHash.SetHex (hashValue.asString ()))
|
||||
if(!ledgerHash.SetHex (hashValue.asString ()))
|
||||
return {rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
||||
|
||||
ledger = ledgerMaster.getLedgerByHash (ledgerHash);
|
||||
if (ledger == nullptr)
|
||||
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||
return getLedger(ledger, ledgerHash, context);
|
||||
}
|
||||
else if (indexValue.isNumeric())
|
||||
{
|
||||
ledger = ledgerMaster.getLedgerBySeq (indexValue.asInt ());
|
||||
|
||||
if (ledger == nullptr)
|
||||
{
|
||||
auto cur = ledgerMaster.getCurrentLedger();
|
||||
if (cur->info().seq == indexValue.asInt())
|
||||
ledger = cur;
|
||||
}
|
||||
|
||||
if (ledger == nullptr)
|
||||
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||
|
||||
if (ledger->info().seq > ledgerMaster.getValidLedgerIndex() &&
|
||||
isValidatedOld(ledgerMaster, context.app.config().standalone()))
|
||||
{
|
||||
ledger.reset();
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
}
|
||||
return getLedger(ledger, indexValue.asInt(),context);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isValidatedOld (ledgerMaster, context.app.config().standalone()))
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
|
||||
auto const index = indexValue.asString ();
|
||||
if (index == "validated")
|
||||
{
|
||||
ledger = ledgerMaster.getValidatedLedger ();
|
||||
if (ledger == nullptr)
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
|
||||
assert (! ledger->open());
|
||||
return getLedger(ledger, LedgerShortcut::VALIDATED, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index.empty () || index == "current")
|
||||
{
|
||||
ledger = ledgerMaster.getCurrentLedger ();
|
||||
assert (ledger->open());
|
||||
}
|
||||
return getLedger(ledger, LedgerShortcut::CURRENT, context);
|
||||
else if (index == "closed")
|
||||
{
|
||||
ledger = ledgerMaster.getClosedLedger ();
|
||||
assert (! ledger->open());
|
||||
}
|
||||
return getLedger(ledger, LedgerShortcut::CLOSED, context);
|
||||
else
|
||||
{
|
||||
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||
}
|
||||
|
||||
if (ledger == nullptr)
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
|
||||
if (ledger->info().seq + minSequenceGap <
|
||||
ledgerMaster.getValidLedgerIndex ())
|
||||
{
|
||||
ledger.reset ();
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,88 +262,49 @@ template <class T>
|
||||
Status
|
||||
ledgerFromRequest(
|
||||
T& ledger,
|
||||
GRPCContext<rpc::v1::GetAccountInfoRequest>& context)
|
||||
GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>& context)
|
||||
{
|
||||
static auto const minSequenceGap = 10;
|
||||
|
||||
ledger.reset();
|
||||
|
||||
rpc::v1::GetAccountInfoRequest& request = context.params;
|
||||
auto& ledgerMaster = context.ledgerMaster;
|
||||
org::xrpl::rpc::v1::GetAccountInfoRequest& request = context.params;
|
||||
|
||||
using LedgerCase = rpc::v1::LedgerSpecifier::LedgerCase;
|
||||
using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
|
||||
LedgerCase ledgerCase = request.ledger().ledger_case();
|
||||
|
||||
if (ledgerCase == LedgerCase::kHash)
|
||||
switch (ledgerCase)
|
||||
{
|
||||
uint256 ledgerHash = uint256::fromVoid(request.ledger().hash().data());
|
||||
if (ledgerHash.size() != request.ledger().hash().size())
|
||||
return {rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
||||
|
||||
ledger = ledgerMaster.getLedgerByHash(ledgerHash);
|
||||
if (ledger == nullptr)
|
||||
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||
}
|
||||
else if (ledgerCase == LedgerCase::kSequence)
|
||||
{
|
||||
ledger = ledgerMaster.getLedgerBySeq(request.ledger().sequence());
|
||||
|
||||
if (ledger == nullptr)
|
||||
case LedgerCase::kHash:
|
||||
{
|
||||
auto cur = ledgerMaster.getCurrentLedger();
|
||||
if (cur->info().seq == request.ledger().sequence())
|
||||
ledger = cur;
|
||||
uint256 ledgerHash =
|
||||
uint256::fromVoid(request.ledger().hash().data());
|
||||
return getLedger(ledger, ledgerHash, context);
|
||||
}
|
||||
|
||||
if (ledger == nullptr)
|
||||
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||
|
||||
if (ledger->info().seq > ledgerMaster.getValidLedgerIndex() &&
|
||||
isValidatedOld(ledgerMaster, context.app.config().standalone()))
|
||||
case LedgerCase::kSequence:
|
||||
return getLedger(ledger, request.ledger().sequence(), context);
|
||||
case LedgerCase::kShortcut:
|
||||
[[fallthrough]];
|
||||
case LedgerCase::LEDGER_NOT_SET:
|
||||
{
|
||||
ledger.reset();
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
}
|
||||
}
|
||||
else if (
|
||||
ledgerCase == LedgerCase::kShortcut ||
|
||||
ledgerCase == LedgerCase::LEDGER_NOT_SET)
|
||||
{
|
||||
if (isValidatedOld(ledgerMaster, context.app.config().standalone()))
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
|
||||
auto const shortcut = request.ledger().shortcut();
|
||||
if (shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
|
||||
{
|
||||
ledger = ledgerMaster.getValidatedLedger();
|
||||
if (ledger == nullptr)
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
|
||||
assert(!ledger->open());
|
||||
}
|
||||
else
|
||||
{
|
||||
// note, if unspecified, defaults to current ledger
|
||||
if (shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED ||
|
||||
shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT)
|
||||
auto const shortcut = request.ledger().shortcut();
|
||||
if (shortcut ==
|
||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
|
||||
return getLedger(ledger, LedgerShortcut::VALIDATED, context);
|
||||
else
|
||||
{
|
||||
ledger = ledgerMaster.getCurrentLedger();
|
||||
assert(ledger->open());
|
||||
}
|
||||
else if (shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
|
||||
{
|
||||
ledger = ledgerMaster.getClosedLedger();
|
||||
assert(!ledger->open());
|
||||
}
|
||||
|
||||
if (ledger == nullptr)
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
|
||||
if (ledger->info().seq + minSequenceGap <
|
||||
ledgerMaster.getValidLedgerIndex())
|
||||
{
|
||||
ledger.reset();
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
// note, if unspecified, defaults to current ledger
|
||||
if (shortcut ==
|
||||
org::xrpl::rpc::v1::LedgerSpecifier::
|
||||
SHORTCUT_UNSPECIFIED ||
|
||||
shortcut ==
|
||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT)
|
||||
{
|
||||
return getLedger(ledger, LedgerShortcut::CURRENT, context);
|
||||
}
|
||||
else if (
|
||||
shortcut ==
|
||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
|
||||
{
|
||||
return getLedger(ledger, LedgerShortcut::CLOSED, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -397,7 +316,103 @@ ledgerFromRequest(
|
||||
template Status
|
||||
ledgerFromRequest<>(
|
||||
std::shared_ptr<ReadView const>&,
|
||||
GRPCContext<rpc::v1::GetAccountInfoRequest>&);
|
||||
GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>&);
|
||||
|
||||
Status
|
||||
getLedger(
|
||||
std::shared_ptr<ReadView const>& ledger,
|
||||
uint256 const& ledgerHash,
|
||||
Context& context)
|
||||
{
|
||||
ledger = context.ledgerMaster.getLedgerByHash(ledgerHash);
|
||||
if (ledger == nullptr)
|
||||
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Status
|
||||
getLedger(T& ledger, uint32_t ledgerIndex, Context& context)
|
||||
{
|
||||
ledger = context.ledgerMaster.getLedgerBySeq(ledgerIndex);
|
||||
if (ledger == nullptr)
|
||||
{
|
||||
auto cur = context.ledgerMaster.getCurrentLedger();
|
||||
if (cur->info().seq == ledgerIndex)
|
||||
{
|
||||
ledger = cur;
|
||||
}
|
||||
}
|
||||
|
||||
if (ledger == nullptr)
|
||||
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||
|
||||
if (ledger->info().seq > context.ledgerMaster.getValidLedgerIndex() &&
|
||||
isValidatedOld(context.ledgerMaster, context.app.config().standalone()))
|
||||
{
|
||||
ledger.reset();
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
}
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
Status
|
||||
getLedger(T& ledger, LedgerShortcut shortcut, Context& context)
|
||||
{
|
||||
if (isValidatedOld (context.ledgerMaster, context.app.config().standalone()))
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
|
||||
if (shortcut == LedgerShortcut::VALIDATED)
|
||||
{
|
||||
ledger = context.ledgerMaster.getValidatedLedger ();
|
||||
if (ledger == nullptr)
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
|
||||
assert (! ledger->open());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shortcut == LedgerShortcut::CURRENT)
|
||||
{
|
||||
ledger = context.ledgerMaster.getCurrentLedger ();
|
||||
assert (ledger->open());
|
||||
}
|
||||
else if (shortcut == LedgerShortcut::CLOSED)
|
||||
{
|
||||
ledger = context.ledgerMaster.getClosedLedger ();
|
||||
assert (! ledger->open());
|
||||
}
|
||||
else
|
||||
{
|
||||
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||
}
|
||||
|
||||
if (ledger == nullptr)
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
|
||||
static auto const minSequenceGap = 10;
|
||||
|
||||
if (ledger->info().seq + minSequenceGap <
|
||||
context.ledgerMaster.getValidLedgerIndex ())
|
||||
{
|
||||
ledger.reset ();
|
||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||
}
|
||||
}
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
//Explicit instantiaion of above three functions
|
||||
template Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&,
|
||||
uint32_t, Context&);
|
||||
|
||||
template Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&,
|
||||
LedgerShortcut shortcut, Context&);
|
||||
|
||||
bool
|
||||
isValidated(LedgerMaster& ledgerMaster, ReadView const& ledger,
|
||||
@@ -823,672 +838,6 @@ chooseLedgerEntryType(Json::Value const& params)
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
populateAccountRoot(rpc::v1::AccountRoot& proto, STObject const& obj)
|
||||
{
|
||||
if (obj.isFieldPresent(sfAccount))
|
||||
{
|
||||
AccountID account = obj.getAccountID(sfAccount);
|
||||
proto.mutable_account()->set_address(toBase58(account));
|
||||
}
|
||||
if (obj.isFieldPresent(sfBalance))
|
||||
{
|
||||
STAmount amount = obj.getFieldAmount(sfBalance);
|
||||
proto.mutable_balance()->set_drops(amount.xrp().drops());
|
||||
}
|
||||
if (obj.isFieldPresent(sfSequence))
|
||||
{
|
||||
proto.set_sequence(obj.getFieldU32(sfSequence));
|
||||
}
|
||||
if (obj.isFieldPresent(sfFlags))
|
||||
{
|
||||
proto.set_flags(obj.getFieldU32(sfFlags));
|
||||
}
|
||||
if (obj.isFieldPresent(sfOwnerCount))
|
||||
{
|
||||
proto.set_owner_count(obj.getFieldU32(sfOwnerCount));
|
||||
}
|
||||
if (obj.isFieldPresent(sfPreviousTxnID))
|
||||
{
|
||||
auto field = obj.getFieldH256(sfPreviousTxnID);
|
||||
proto.set_previous_transaction_id(field.data(), field.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfPreviousTxnLgrSeq))
|
||||
{
|
||||
proto.set_previous_transaction_ledger_sequence(
|
||||
obj.getFieldU32(sfPreviousTxnLgrSeq));
|
||||
}
|
||||
if (obj.isFieldPresent(sfAccountTxnID))
|
||||
{
|
||||
auto field = obj.getFieldH256(sfAccountTxnID);
|
||||
proto.set_account_transaction_id(field.data(), field.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfDomain))
|
||||
{
|
||||
auto field = obj.getFieldH256(sfDomain);
|
||||
proto.set_domain(field.data(), field.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfEmailHash))
|
||||
{
|
||||
auto field = obj.getFieldH128(sfEmailHash);
|
||||
proto.set_email_hash(field.data(), field.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfMessageKey))
|
||||
{
|
||||
auto field = obj.getFieldVL(sfMessageKey);
|
||||
proto.set_message_key(field.data(), field.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfRegularKey))
|
||||
{
|
||||
proto.set_regular_key(toBase58(obj.getAccountID(sfRegularKey)));
|
||||
}
|
||||
if (obj.isFieldPresent(sfTickSize))
|
||||
{
|
||||
proto.set_tick_size(obj.getFieldU8(sfTickSize));
|
||||
}
|
||||
if (obj.isFieldPresent(sfTransferRate))
|
||||
{
|
||||
proto.set_transfer_rate(obj.getFieldU32(sfTransferRate));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateRippleState(rpc::v1::RippleState& proto, STObject const& obj)
|
||||
{
|
||||
if (obj.isFieldPresent(sfBalance))
|
||||
{
|
||||
STAmount amount = obj.getFieldAmount(sfBalance);
|
||||
populateAmount(*proto.mutable_balance(), amount);
|
||||
}
|
||||
if (obj.isFieldPresent(sfFlags))
|
||||
{
|
||||
proto.set_flags(obj.getFieldU32(sfFlags));
|
||||
}
|
||||
if (obj.isFieldPresent(sfLowLimit))
|
||||
{
|
||||
STAmount amount = obj.getFieldAmount(sfLowLimit);
|
||||
populateAmount(*proto.mutable_low_limit(), amount);
|
||||
}
|
||||
if (obj.isFieldPresent(sfHighLimit))
|
||||
{
|
||||
STAmount amount = obj.getFieldAmount(sfHighLimit);
|
||||
populateAmount(*proto.mutable_high_limit(), amount);
|
||||
}
|
||||
if (obj.isFieldPresent(sfLowNode))
|
||||
{
|
||||
proto.set_low_node(obj.getFieldU64(sfLowNode));
|
||||
}
|
||||
if (obj.isFieldPresent(sfHighNode))
|
||||
{
|
||||
proto.set_high_node(obj.getFieldU64(sfHighNode));
|
||||
}
|
||||
if (obj.isFieldPresent(sfLowQualityIn))
|
||||
{
|
||||
proto.set_low_quality_in(obj.getFieldU32(sfLowQualityIn));
|
||||
}
|
||||
if (obj.isFieldPresent(sfLowQualityOut))
|
||||
{
|
||||
proto.set_low_quality_out(obj.getFieldU32(sfLowQualityOut));
|
||||
}
|
||||
if (obj.isFieldPresent(sfHighQualityIn))
|
||||
{
|
||||
proto.set_high_quality_in(obj.getFieldU32(sfHighQualityIn));
|
||||
}
|
||||
if (obj.isFieldPresent(sfHighQualityOut))
|
||||
{
|
||||
proto.set_high_quality_out(obj.getFieldU32(sfHighQualityOut));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateOffer(rpc::v1::Offer& proto, STObject const& obj)
|
||||
{
|
||||
if (obj.isFieldPresent(sfAccount))
|
||||
{
|
||||
AccountID account = obj.getAccountID(sfAccount);
|
||||
proto.set_account(toBase58(account));
|
||||
}
|
||||
if (obj.isFieldPresent(sfSequence))
|
||||
{
|
||||
proto.set_sequence(obj.getFieldU32(sfSequence));
|
||||
}
|
||||
if (obj.isFieldPresent(sfFlags))
|
||||
{
|
||||
proto.set_flags(obj.getFieldU32(sfFlags));
|
||||
}
|
||||
if (obj.isFieldPresent(sfTakerPays))
|
||||
{
|
||||
STAmount amount = obj.getFieldAmount(sfTakerPays);
|
||||
populateAmount(*proto.mutable_taker_pays(), amount);
|
||||
}
|
||||
if (obj.isFieldPresent(sfTakerGets))
|
||||
{
|
||||
STAmount amount = obj.getFieldAmount(sfTakerGets);
|
||||
populateAmount(*proto.mutable_taker_gets(), amount);
|
||||
}
|
||||
if (obj.isFieldPresent(sfBookDirectory))
|
||||
{
|
||||
auto field = obj.getFieldVL(sfBookDirectory);
|
||||
proto.set_book_directory(field.data(), field.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfBookNode))
|
||||
{
|
||||
proto.set_book_node(obj.getFieldU64(sfBookNode));
|
||||
}
|
||||
if (obj.isFieldPresent(sfExpiration))
|
||||
{
|
||||
proto.set_expiration(obj.getFieldU32(sfExpiration));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateSignerList(rpc::v1::SignerList& proto, STObject const& obj)
|
||||
{
|
||||
proto.set_flags(obj.getFieldU32(sfFlags));
|
||||
|
||||
auto prevTxnID = obj.getFieldH256(sfPreviousTxnID);
|
||||
proto.set_previous_txn_id(prevTxnID.data(), prevTxnID.size());
|
||||
|
||||
proto.set_previous_transaction_ledger_sequence(
|
||||
obj.getFieldU32(sfPreviousTxnLgrSeq));
|
||||
|
||||
proto.set_owner_node(obj.getFieldU64(sfOwnerNode));
|
||||
|
||||
proto.set_signer_list_id(obj.getFieldU32(sfSignerListID));
|
||||
|
||||
proto.set_signer_quorum(obj.getFieldU32(sfSignerQuorum));
|
||||
|
||||
STArray const& signerEntries = obj.getFieldArray(sfSignerEntries);
|
||||
|
||||
for (auto it = signerEntries.begin(); it != signerEntries.end(); ++it)
|
||||
{
|
||||
rpc::v1::SignerEntry& signerEntryProto = *proto.add_signer_entries();
|
||||
|
||||
signerEntryProto.mutable_account()->set_address(
|
||||
toBase58(it->getAccountID(sfAccount)));
|
||||
signerEntryProto.set_signer_weight(it->getFieldU16(sfSignerWeight));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateQueueData(
|
||||
rpc::v1::QueueData& proto,
|
||||
std::map<TxSeq, TxQ::AccountTxDetails const> const& txs)
|
||||
{
|
||||
if (!txs.empty())
|
||||
{
|
||||
proto.set_txn_count(txs.size());
|
||||
proto.set_lowest_sequence(txs.begin()->first);
|
||||
proto.set_highest_sequence(txs.rbegin()->first);
|
||||
|
||||
boost::optional<bool> anyAuthChanged(false);
|
||||
boost::optional<XRPAmount> totalSpend(0);
|
||||
|
||||
for (auto const& [txSeq, txDetails] : txs)
|
||||
{
|
||||
rpc::v1::QueuedTransaction& qt = *proto.add_transactions();
|
||||
|
||||
qt.set_sequence(txSeq);
|
||||
qt.set_fee_level(txDetails.feeLevel.fee());
|
||||
if (txDetails.lastValid)
|
||||
qt.set_last_ledger_sequence(*txDetails.lastValid);
|
||||
|
||||
if (txDetails.consequences)
|
||||
{
|
||||
qt.mutable_fee()->set_drops(
|
||||
txDetails.consequences->fee.drops());
|
||||
auto spend = txDetails.consequences->potentialSpend +
|
||||
txDetails.consequences->fee;
|
||||
qt.mutable_max_spend_drops()->set_drops(spend.drops());
|
||||
if (totalSpend)
|
||||
*totalSpend += spend;
|
||||
auto authChanged =
|
||||
txDetails.consequences->category == TxConsequences::blocker;
|
||||
if (authChanged)
|
||||
anyAuthChanged.emplace(authChanged);
|
||||
qt.set_auth_change(authChanged);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anyAuthChanged && !*anyAuthChanged)
|
||||
anyAuthChanged.reset();
|
||||
totalSpend.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (anyAuthChanged)
|
||||
proto.set_auth_change_queued(*anyAuthChanged);
|
||||
if (totalSpend)
|
||||
proto.mutable_max_spend_drops_total()->set_drops(
|
||||
(*totalSpend).drops());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateDirectoryNode(rpc::v1::DirectoryNode& proto, STObject const& obj)
|
||||
{
|
||||
if (obj.isFieldPresent(sfOwner))
|
||||
{
|
||||
AccountID ownerAccount = obj.getAccountID(sfAccount);
|
||||
proto.set_owner(toBase58(ownerAccount));
|
||||
}
|
||||
if (obj.isFieldPresent(sfTakerPaysCurrency))
|
||||
{
|
||||
uint160 tpCurr = obj.getFieldH160(sfTakerPaysCurrency);
|
||||
proto.mutable_taker_pays_currency()->set_code(
|
||||
tpCurr.data(), tpCurr.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfTakerPaysIssuer))
|
||||
{
|
||||
uint160 tpIss = obj.getFieldH160(sfTakerPaysIssuer);
|
||||
proto.set_taker_pays_issuer(tpIss.data(), tpIss.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfTakerGetsCurrency))
|
||||
{
|
||||
uint160 tgCurr = obj.getFieldH160(sfTakerGetsCurrency);
|
||||
proto.mutable_taker_gets_currency()->set_code(
|
||||
tgCurr.data(), tgCurr.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfTakerGetsIssuer))
|
||||
{
|
||||
uint160 tgIss = obj.getFieldH160(sfTakerGetsIssuer);
|
||||
proto.set_taker_gets_issuer(tgIss.data(), tgIss.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfIndexes))
|
||||
{
|
||||
const STVector256& vec = obj.getFieldV256(sfIndexes);
|
||||
for (size_t i = 0; i < vec.size(); ++i)
|
||||
{
|
||||
uint256 const& elt = vec[i];
|
||||
proto.add_indexes(elt.data(), elt.size());
|
||||
}
|
||||
}
|
||||
if (obj.isFieldPresent(sfRootIndex))
|
||||
{
|
||||
uint256 rootIndex = obj.getFieldH256(sfRootIndex);
|
||||
proto.set_root_index(rootIndex.data(), rootIndex.size());
|
||||
}
|
||||
if (obj.isFieldPresent(sfIndexNext))
|
||||
{
|
||||
proto.set_index_next(obj.getFieldU64(sfIndexNext));
|
||||
}
|
||||
if (obj.isFieldPresent(sfIndexPrevious))
|
||||
{
|
||||
proto.set_index_previous(obj.getFieldU64(sfIndexPrevious));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateLedgerEntryType(rpc::v1::AffectedNode& proto, std::uint16_t lgrType)
|
||||
{
|
||||
switch (lgrType)
|
||||
{
|
||||
case ltACCOUNT_ROOT:
|
||||
proto.set_ledger_entry_type(
|
||||
rpc::v1::LEDGER_ENTRY_TYPE_ACCOUNT_ROOT);
|
||||
break;
|
||||
case ltDIR_NODE:
|
||||
proto.set_ledger_entry_type(
|
||||
rpc::v1::LEDGER_ENTRY_TYPE_DIRECTORY_NODE);
|
||||
break;
|
||||
case ltRIPPLE_STATE:
|
||||
proto.set_ledger_entry_type(
|
||||
rpc::v1::LEDGER_ENTRY_TYPE_RIPPLE_STATE);
|
||||
break;
|
||||
case ltSIGNER_LIST:
|
||||
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_SIGNER_LIST);
|
||||
break;
|
||||
case ltOFFER:
|
||||
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_OFFER);
|
||||
break;
|
||||
case ltLEDGER_HASHES:
|
||||
proto.set_ledger_entry_type(
|
||||
rpc::v1::LEDGER_ENTRY_TYPE_LEDGER_HASHES);
|
||||
break;
|
||||
case ltAMENDMENTS:
|
||||
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_AMENDMENTS);
|
||||
break;
|
||||
case ltFEE_SETTINGS:
|
||||
proto.set_ledger_entry_type(
|
||||
rpc::v1::LEDGER_ENTRY_TYPE_FEE_SETTINGS);
|
||||
break;
|
||||
case ltESCROW:
|
||||
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_ESCROW);
|
||||
break;
|
||||
case ltPAYCHAN:
|
||||
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_PAY_CHANNEL);
|
||||
break;
|
||||
case ltCHECK:
|
||||
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_CHECK);
|
||||
break;
|
||||
case ltDEPOSIT_PREAUTH:
|
||||
proto.set_ledger_entry_type(
|
||||
rpc::v1::LEDGER_ENTRY_TYPE_DEPOSIT_PREAUTH);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
populateFields(T& proto, STObject const& obj, std::uint16_t type)
|
||||
{
|
||||
if (type == ltACCOUNT_ROOT)
|
||||
{
|
||||
RPC::populateAccountRoot(*proto.mutable_account_root(), obj);
|
||||
}
|
||||
else if (type == ltRIPPLE_STATE)
|
||||
{
|
||||
RPC::populateRippleState(*proto.mutable_ripple_state(), obj);
|
||||
}
|
||||
else if (type == ltOFFER)
|
||||
{
|
||||
RPC::populateOffer(*proto.mutable_offer(), obj);
|
||||
}
|
||||
else if (type == ltDIR_NODE)
|
||||
{
|
||||
RPC::populateDirectoryNode(*proto.mutable_directory_node(), obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ledger object not supported by protobuf/grpc yet
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateMeta(rpc::v1::Meta& proto, std::shared_ptr<TxMeta> txMeta)
|
||||
{
|
||||
proto.set_transaction_index(txMeta->getIndex());
|
||||
|
||||
populateTransactionResultType(
|
||||
*proto.mutable_transaction_result(), txMeta->getResultTER());
|
||||
proto.mutable_transaction_result()->set_result(
|
||||
transToken(txMeta->getResultTER()));
|
||||
|
||||
STArray& nodes = txMeta->getNodes();
|
||||
for (auto it = nodes.begin(); it != nodes.end(); ++it)
|
||||
{
|
||||
STObject& obj = *it;
|
||||
rpc::v1::AffectedNode* node = proto.add_affected_nodes();
|
||||
|
||||
// ledger index
|
||||
uint256 ledgerIndex = obj.getFieldH256(sfLedgerIndex);
|
||||
node->set_ledger_index(ledgerIndex.data(), ledgerIndex.size());
|
||||
|
||||
// ledger entry type
|
||||
std::uint16_t lgrType = obj.getFieldU16(sfLedgerEntryType);
|
||||
populateLedgerEntryType(*node, lgrType);
|
||||
|
||||
// modified node
|
||||
if (obj.getFName() == sfModifiedNode)
|
||||
{
|
||||
// final fields
|
||||
if (obj.isFieldPresent(sfFinalFields))
|
||||
{
|
||||
STObject& finalFields =
|
||||
obj.getField(sfFinalFields).downcast<STObject>();
|
||||
|
||||
rpc::v1::LedgerObject* finalFieldsProto =
|
||||
node->mutable_modified_node()->mutable_final_fields();
|
||||
|
||||
populateFields(*finalFieldsProto, finalFields, lgrType);
|
||||
}
|
||||
// previous fields
|
||||
if (obj.isFieldPresent(sfPreviousFields))
|
||||
{
|
||||
STObject& prevFields =
|
||||
obj.getField(sfPreviousFields).downcast<STObject>();
|
||||
|
||||
rpc::v1::LedgerObject* prevFieldsProto =
|
||||
node->mutable_modified_node()->mutable_previous_fields();
|
||||
|
||||
populateFields(*prevFieldsProto, prevFields, lgrType);
|
||||
}
|
||||
|
||||
// prev txn id and prev txn ledger seq
|
||||
uint256 prevTxnId = obj.getFieldH256(sfPreviousTxnID);
|
||||
node->mutable_modified_node()->set_previous_transaction_id(
|
||||
prevTxnId.data(), prevTxnId.size());
|
||||
|
||||
node->mutable_modified_node()
|
||||
->set_previous_transaction_ledger_sequence(
|
||||
obj.getFieldU32(sfPreviousTxnLgrSeq));
|
||||
}
|
||||
// created node
|
||||
else if (obj.getFName() == sfCreatedNode)
|
||||
{
|
||||
// new fields
|
||||
if (obj.isFieldPresent(sfNewFields))
|
||||
{
|
||||
STObject& newFields =
|
||||
obj.getField(sfNewFields).downcast<STObject>();
|
||||
|
||||
rpc::v1::LedgerObject* newFieldsProto =
|
||||
node->mutable_created_node()->mutable_new_fields();
|
||||
|
||||
populateFields(*newFieldsProto, newFields, lgrType);
|
||||
}
|
||||
}
|
||||
// deleted node
|
||||
else if (obj.getFName() == sfDeletedNode)
|
||||
{
|
||||
// final fields
|
||||
if (obj.isFieldPresent(sfFinalFields))
|
||||
{
|
||||
STObject& finalFields =
|
||||
obj.getField(sfFinalFields).downcast<STObject>();
|
||||
|
||||
rpc::v1::LedgerObject* finalFieldsProto =
|
||||
node->mutable_deleted_node()->mutable_final_fields();
|
||||
|
||||
populateFields(*finalFieldsProto, finalFields, lgrType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateAmount(rpc::v1::CurrencyAmount& proto, STAmount const& amount)
|
||||
{
|
||||
if (amount.native())
|
||||
{
|
||||
proto.mutable_xrp_amount()->set_drops(amount.xrp().drops());
|
||||
}
|
||||
else
|
||||
{
|
||||
rpc::v1::IssuedCurrencyAmount* issued =
|
||||
proto.mutable_issued_currency_amount();
|
||||
Issue const& issue = amount.issue();
|
||||
Currency currency = issue.currency;
|
||||
issued->mutable_currency()->set_name(to_string(issue.currency));
|
||||
issued->mutable_currency()->set_code(currency.data(), currency.size());
|
||||
issued->set_value(to_string(amount.iou()));
|
||||
issued->mutable_issuer()->set_address(toBase58(issue.account));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateTransaction(
|
||||
rpc::v1::Transaction& proto,
|
||||
std::shared_ptr<STTx const> txnSt)
|
||||
{
|
||||
AccountID account = txnSt->getAccountID(sfAccount);
|
||||
proto.mutable_account()->set_address(toBase58(account));
|
||||
|
||||
STAmount amount = txnSt->getFieldAmount(sfAmount);
|
||||
populateAmount(*proto.mutable_payment()->mutable_amount(), amount);
|
||||
|
||||
AccountID accountDest = txnSt->getAccountID(sfDestination);
|
||||
proto.mutable_payment()->mutable_destination()->set_address(
|
||||
toBase58(accountDest));
|
||||
|
||||
STAmount fee = txnSt->getFieldAmount(sfFee);
|
||||
proto.mutable_fee()->set_drops(fee.xrp().drops());
|
||||
|
||||
proto.set_sequence(txnSt->getFieldU32(sfSequence));
|
||||
|
||||
Blob signingPubKey = txnSt->getFieldVL(sfSigningPubKey);
|
||||
proto.set_signing_public_key(signingPubKey.data(), signingPubKey.size());
|
||||
|
||||
proto.set_flags(txnSt->getFieldU32(sfFlags));
|
||||
|
||||
proto.set_last_ledger_sequence(txnSt->getFieldU32(sfLastLedgerSequence));
|
||||
|
||||
Blob blob = txnSt->getFieldVL(sfTxnSignature);
|
||||
proto.set_signature(blob.data(), blob.size());
|
||||
|
||||
if (txnSt->isFieldPresent(sfSourceTag))
|
||||
{
|
||||
proto.set_source_tag(txnSt->getFieldU32(sfSourceTag));
|
||||
}
|
||||
|
||||
if (txnSt->isFieldPresent(sfAccountTxnID))
|
||||
{
|
||||
auto field = txnSt->getFieldH256(sfAccountTxnID);
|
||||
proto.set_account_transaction_id(field.data(), field.size());
|
||||
}
|
||||
|
||||
if (txnSt->isFieldPresent(sfMemos))
|
||||
{
|
||||
auto memos = txnSt->getFieldArray(sfMemos);
|
||||
for (auto it = memos.begin(); it != memos.end(); ++it)
|
||||
{
|
||||
rpc::v1::Memo* elt = proto.add_memos();
|
||||
auto memo = it->getField(sfMemo).downcast<STObject>();
|
||||
if (memo.isFieldPresent(sfMemoData))
|
||||
{
|
||||
auto memoData = memo.getFieldVL(sfMemoData);
|
||||
elt->set_memo_data(memoData.data(), memoData.size());
|
||||
}
|
||||
if (memo.isFieldPresent(sfMemoFormat))
|
||||
{
|
||||
auto memoFormat = memo.getFieldVL(sfMemoFormat);
|
||||
elt->set_memo_format(memoFormat.data(), memoFormat.size());
|
||||
}
|
||||
if (memo.isFieldPresent(sfMemoType))
|
||||
{
|
||||
auto memoType = memo.getFieldVL(sfMemoType);
|
||||
elt->set_memo_type(memoType.data(), memoType.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (txnSt->isFieldPresent(sfSigners))
|
||||
{
|
||||
auto signers = txnSt->getFieldArray(sfSigners);
|
||||
|
||||
for (auto it = signers.begin(); it != signers.end(); ++it)
|
||||
{
|
||||
rpc::v1::Signer* elt = proto.add_signers();
|
||||
auto signer = it->getField(sfSigner).downcast<STObject>();
|
||||
if (signer.isFieldPresent(sfAccount))
|
||||
{
|
||||
elt->mutable_account()->set_address(
|
||||
toBase58(signer.getAccountID(sfAccount)));
|
||||
}
|
||||
if (signer.isFieldPresent(sfTxnSignature))
|
||||
{
|
||||
auto sig = signer.getFieldVL(sfTxnSignature);
|
||||
elt->set_transaction_signature(sig.data(), sig.size());
|
||||
}
|
||||
if (signer.isFieldPresent(sfSigningPubKey))
|
||||
{
|
||||
auto pubKey = signer.getFieldVL(sfSigningPubKey);
|
||||
elt->set_signing_public_key(pubKey.data(), pubKey.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (safe_cast<TxType>(txnSt->getFieldU16(sfTransactionType)) ==
|
||||
TxType::ttPAYMENT)
|
||||
{
|
||||
if (txnSt->isFieldPresent(sfSendMax))
|
||||
{
|
||||
STAmount const& sendMax = txnSt->getFieldAmount(sfSendMax);
|
||||
populateAmount(
|
||||
*proto.mutable_payment()->mutable_send_max(), sendMax);
|
||||
}
|
||||
|
||||
if (txnSt->isFieldPresent(sfInvoiceID))
|
||||
{
|
||||
auto invoice = txnSt->getFieldH256(sfInvoiceID);
|
||||
proto.mutable_payment()->set_invoice_id(
|
||||
invoice.data(), invoice.size());
|
||||
}
|
||||
|
||||
if (txnSt->isFieldPresent(sfDestinationTag))
|
||||
{
|
||||
proto.mutable_payment()->set_destination_tag(
|
||||
txnSt->getFieldU32(sfDestinationTag));
|
||||
}
|
||||
|
||||
// populate path data
|
||||
STPathSet const& pathset = txnSt->getFieldPathSet(sfPaths);
|
||||
for (auto it = pathset.begin(); it < pathset.end(); ++it)
|
||||
{
|
||||
STPath const& path = *it;
|
||||
|
||||
rpc::v1::Path* protoPath = proto.mutable_payment()->add_paths();
|
||||
|
||||
for (auto it2 = path.begin(); it2 != path.end(); ++it2)
|
||||
{
|
||||
rpc::v1::PathElement* protoElement = protoPath->add_elements();
|
||||
STPathElement const& elt = *it2;
|
||||
|
||||
if (elt.isOffer())
|
||||
{
|
||||
if (elt.hasCurrency())
|
||||
{
|
||||
Currency const& currency = elt.getCurrency();
|
||||
protoElement->mutable_currency()->set_name(
|
||||
to_string(currency));
|
||||
}
|
||||
if (elt.hasIssuer())
|
||||
{
|
||||
AccountID const& issuer = elt.getIssuerID();
|
||||
protoElement->mutable_issuer()->set_address(
|
||||
toBase58(issuer));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AccountID const& pathAccount = elt.getAccountID();
|
||||
protoElement->mutable_account()->set_address(
|
||||
toBase58(pathAccount));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
populateTransactionResultType(rpc::v1::TransactionResult& proto, TER result)
|
||||
{
|
||||
if (isTecClaim(result))
|
||||
{
|
||||
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEC);
|
||||
}
|
||||
if (isTefFailure(result))
|
||||
{
|
||||
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEF);
|
||||
}
|
||||
if (isTelLocal(result))
|
||||
{
|
||||
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEL);
|
||||
}
|
||||
if (isTemMalformed(result))
|
||||
{
|
||||
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEM);
|
||||
}
|
||||
if (isTerRetry(result))
|
||||
{
|
||||
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TER);
|
||||
}
|
||||
if (isTesSuccess(result))
|
||||
{
|
||||
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TES);
|
||||
}
|
||||
}
|
||||
|
||||
beast::SemanticVersion const firstVersion("1.0.0");
|
||||
beast::SemanticVersion const goodVersion("1.0.0");
|
||||
beast::SemanticVersion const lastVersion("1.0.0");
|
||||
|
||||
Reference in New Issue
Block a user