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:
CJ Cobb
2020-02-04 12:31:17 -08:00
committed by Mike Ellery
parent acf4b78892
commit e7ce3909d2
57 changed files with 7498 additions and 2106 deletions

View File

@@ -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");