mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
TxQ full queue RPC info (RIPD-1404):
* RPC `ledger` command returns all queue entries in "queue_data" when requesting open ledger, and including boolean "queue: true". * Includes queue state. e.g.: fee_level, retries, last_result, tx. * Respects "expand" and "binary" parameters for the txs. * Remove some unused code.
This commit is contained in:
committed by
Scott Schurr
parent
846723d771
commit
7265729446
@@ -21,6 +21,7 @@
|
||||
#define RIPPLE_APP_LEDGER_LEDGERTOJSON_H_INCLUDED
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/STTx.h>
|
||||
@@ -36,12 +37,27 @@ struct LedgerFill
|
||||
{
|
||||
}
|
||||
|
||||
LedgerFill(ReadView const& l, int o,
|
||||
std::vector<TxQ::TxDetails> q)
|
||||
: ledger(l)
|
||||
, options(o)
|
||||
, txQueue(q)
|
||||
{
|
||||
}
|
||||
|
||||
enum Options {
|
||||
dumpTxrp = 1, dumpState = 2, expand = 4, full = 8, binary = 16,
|
||||
ownerFunds = 32};
|
||||
dumpTxrp = 1,
|
||||
dumpState = 2,
|
||||
expand = 4,
|
||||
full = 8,
|
||||
binary = 16,
|
||||
ownerFunds = 32,
|
||||
dumpQueue = 64
|
||||
};
|
||||
|
||||
ReadView const& ledger;
|
||||
int options;
|
||||
std::vector<TxQ::TxDetails> txQueue;
|
||||
};
|
||||
|
||||
/** Given a Ledger and options, fill a Json::Object or Json::Value with a
|
||||
@@ -49,7 +65,6 @@ struct LedgerFill
|
||||
*/
|
||||
|
||||
void addJson(Json::Value&, LedgerFill const&);
|
||||
void addJson(Json::Object&, LedgerFill const&);
|
||||
|
||||
/** Return a new Json::Value representing the ledger with given options.*/
|
||||
Json::Value getJson (LedgerFill const&);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/LedgerToJson.h>
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/basics/base_uint.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -50,7 +51,7 @@ void fillJson(Object& json, bool closed, LedgerInfo const& info, bool bFull)
|
||||
{
|
||||
json[jss::closed] = true;
|
||||
}
|
||||
else if (!bFull)
|
||||
else if (! bFull)
|
||||
{
|
||||
json[jss::closed] = false;
|
||||
return;
|
||||
@@ -95,6 +96,50 @@ void fillJsonBinary(Object& json, bool closed, LedgerInfo const& info)
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value fillJsonTx (LedgerFill const& fill,
|
||||
bool bBinary, bool bExpanded,
|
||||
std::pair<std::shared_ptr<STTx const>,
|
||||
std::shared_ptr<STObject const>> const i)
|
||||
{
|
||||
if (! bExpanded)
|
||||
{
|
||||
return to_string(i.first->getTransactionID());
|
||||
}
|
||||
else
|
||||
{
|
||||
Json::Value txJson{ Json::objectValue };
|
||||
if (bBinary)
|
||||
{
|
||||
txJson[jss::tx_blob] = serializeHex(*i.first);
|
||||
if (i.second)
|
||||
txJson[jss::meta] = serializeHex(*i.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
copyFrom(txJson, i.first->getJson(0));
|
||||
if (i.second)
|
||||
txJson[jss::metaData] = i.second->getJson(0);
|
||||
}
|
||||
|
||||
if ((fill.options & LedgerFill::ownerFunds) &&
|
||||
i.first->getTxnType() == ttOFFER_CREATE)
|
||||
{
|
||||
auto const account = i.first->getAccountID(sfAccount);
|
||||
auto const amount = i.first->getFieldAmount(sfTakerGets);
|
||||
|
||||
// If the offer create is not self funded then add the
|
||||
// owner balance
|
||||
if (account != amount.getIssuer())
|
||||
{
|
||||
auto const ownerFunds = accountFunds(fill.ledger,
|
||||
account, amount, fhIGNORE_FREEZE, beast::Journal());
|
||||
txJson[jss::owner_funds] = ownerFunds.getText ();
|
||||
}
|
||||
}
|
||||
return txJson;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void fillJsonTx (Object& json, LedgerFill const& fill)
|
||||
{
|
||||
@@ -106,42 +151,7 @@ void fillJsonTx (Object& json, LedgerFill const& fill)
|
||||
{
|
||||
for (auto& i: fill.ledger.txs)
|
||||
{
|
||||
if (! bExpanded)
|
||||
{
|
||||
txns.append(to_string(i.first->getTransactionID()));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto&& txJson = appendObject(txns);
|
||||
if (bBinary)
|
||||
{
|
||||
txJson[jss::tx_blob] = serializeHex(*i.first);
|
||||
if (i.second)
|
||||
txJson[jss::meta] = serializeHex(*i.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
copyFrom(txJson, i.first->getJson(0));
|
||||
if (i.second)
|
||||
txJson[jss::metaData] = i.second->getJson(0);
|
||||
}
|
||||
|
||||
if ((fill.options & LedgerFill::ownerFunds) &&
|
||||
i.first->getTxnType() == ttOFFER_CREATE)
|
||||
{
|
||||
auto const account = i.first->getAccountID(sfAccount);
|
||||
auto const amount = i.first->getFieldAmount(sfTakerGets);
|
||||
|
||||
// If the offer create is not self funded then add the
|
||||
// owner balance
|
||||
if (account != amount.getIssuer())
|
||||
{
|
||||
auto const ownerFunds = accountFunds(fill.ledger,
|
||||
account, amount, fhIGNORE_FREEZE, beast::Journal());
|
||||
txJson[jss::owner_funds] = ownerFunds.getText ();
|
||||
}
|
||||
}
|
||||
}
|
||||
txns.append(fillJsonTx(fill, bBinary, bExpanded, i));
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
@@ -173,6 +183,42 @@ void fillJsonState(Object& json, LedgerFill const& fill)
|
||||
}
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void fillJsonQueue(Object& json, LedgerFill const& fill)
|
||||
{
|
||||
auto&& queueData = Json::setArray(json, jss::queue_data);
|
||||
auto bBinary = isBinary(fill);
|
||||
auto bExpanded = isExpanded(fill);
|
||||
|
||||
for (auto const& tx : fill.txQueue)
|
||||
{
|
||||
auto&& txJson = appendObject(queueData);
|
||||
txJson[jss::fee_level] = to_string(tx.feeLevel);
|
||||
if (tx.lastValid)
|
||||
txJson[jss::LastLedgerSequence] = *tx.lastValid;
|
||||
if (tx.consequences)
|
||||
{
|
||||
txJson[jss::fee] = to_string(
|
||||
tx.consequences->fee);
|
||||
auto spend = tx.consequences->potentialSpend +
|
||||
tx.consequences->fee;
|
||||
txJson[jss::max_spend_drops] = to_string(spend);
|
||||
auto authChanged = tx.consequences->category ==
|
||||
TxConsequences::blocker;
|
||||
txJson[jss::auth_change] = authChanged;
|
||||
}
|
||||
|
||||
txJson[jss::account] = to_string(tx.account);
|
||||
txJson["retries_remaining"] = tx.retriesRemaining;
|
||||
txJson["preflight_result"] = transToken(tx.preflightResult);
|
||||
if (tx.lastResult)
|
||||
txJson["last_result"] = transToken(*tx.lastResult);
|
||||
|
||||
txJson[jss::tx] = fillJsonTx(fill, bBinary, bExpanded,
|
||||
std::make_pair(tx.txn, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
void fillJson (Object& json, LedgerFill const& fill)
|
||||
{
|
||||
@@ -180,9 +226,9 @@ void fillJson (Object& json, LedgerFill const& fill)
|
||||
// Is there a way to report this back?
|
||||
auto bFull = isFull(fill);
|
||||
if (isBinary(fill))
|
||||
fillJsonBinary(json, !fill.ledger.open(), fill.ledger.info());
|
||||
fillJsonBinary(json, ! fill.ledger.open(), fill.ledger.info());
|
||||
else
|
||||
fillJson(json, !fill.ledger.open(), fill.ledger.info(), bFull);
|
||||
fillJson(json, ! fill.ledger.open(), fill.ledger.info(), bFull);
|
||||
|
||||
if (bFull || fill.options & LedgerFill::dumpTxrp)
|
||||
fillJsonTx(json, fill);
|
||||
@@ -193,17 +239,13 @@ void fillJson (Object& json, LedgerFill const& fill)
|
||||
|
||||
} // namespace
|
||||
|
||||
void addJson (Json::Object& json, LedgerFill const& fill)
|
||||
{
|
||||
auto&& object = Json::addObject (json, jss::ledger);
|
||||
fillJson (object, fill);
|
||||
}
|
||||
|
||||
void addJson (Json::Value& json, LedgerFill const& fill)
|
||||
{
|
||||
auto&& object = Json::addObject (json, jss::ledger);
|
||||
fillJson (object, fill);
|
||||
|
||||
if ((fill.options & LedgerFill::dumpQueue) && !fill.txQueue.empty())
|
||||
fillJsonQueue(json, fill);
|
||||
}
|
||||
|
||||
Json::Value getJson (LedgerFill const& fill)
|
||||
|
||||
@@ -96,6 +96,15 @@ public:
|
||||
boost::optional<TxConsequences const> consequences;
|
||||
};
|
||||
|
||||
struct TxDetails : AccountTxDetails
|
||||
{
|
||||
AccountID account;
|
||||
std::shared_ptr<STTx const> txn;
|
||||
int retriesRemaining;
|
||||
TER preflightResult;
|
||||
boost::optional<TER> lastResult;
|
||||
};
|
||||
|
||||
TxQ(Setup const& setup,
|
||||
beast::Journal j);
|
||||
|
||||
@@ -149,9 +158,19 @@ public:
|
||||
amendment is not enabled, OR if the account has no transactions
|
||||
in the queue.
|
||||
*/
|
||||
boost::optional<std::map<TxSeq, AccountTxDetails>>
|
||||
std::map<TxSeq, AccountTxDetails const>
|
||||
getAccountTxs(AccountID const& account, ReadView const& view) const;
|
||||
|
||||
/** Returns information about all transactions currently
|
||||
in the queue.
|
||||
|
||||
@returns Uninitialized @ref optional if the FeeEscalation
|
||||
amendment is not enabled, OR if there are no transactions
|
||||
in the queue.
|
||||
*/
|
||||
std::vector<TxDetails>
|
||||
getTxs(ReadView const& view) const;
|
||||
|
||||
/** Packages up fee metrics for the `fee` RPC command.
|
||||
*/
|
||||
Json::Value
|
||||
@@ -267,6 +286,7 @@ private:
|
||||
int retriesRemaining;
|
||||
TxSeq const sequence;
|
||||
ApplyFlags const flags;
|
||||
boost::optional<TER> lastResult;
|
||||
// Invariant: pfresult is never allowed to be empty. The
|
||||
// boost::optional is leveraged to allow `emplace`d
|
||||
// construction and replacement without a copy
|
||||
|
||||
@@ -492,6 +492,7 @@ TxQ::tryClearAccountQueue(Application& app, OpenView& view,
|
||||
// succeeds, the MaybeTx will be destructed, so it'll be
|
||||
// moot.
|
||||
--it->second.retriesRemaining;
|
||||
it->second.lastResult = txResult.first;
|
||||
if (!txResult.second)
|
||||
{
|
||||
// Transaction failed to apply. Fall back to the normal process.
|
||||
@@ -1268,6 +1269,7 @@ TxQ::accept(Application& app,
|
||||
candidateIter->retriesRemaining = 1;
|
||||
else
|
||||
--candidateIter->retriesRemaining;
|
||||
candidateIter->lastResult = txnResult;
|
||||
if (account.dropPenalty &&
|
||||
account.transactions.size() > 1 && isFull<95>())
|
||||
{
|
||||
@@ -1331,34 +1333,79 @@ TxQ::getMetrics(OpenView const& view, std::uint32_t txCountPadding) const
|
||||
|
||||
auto
|
||||
TxQ::getAccountTxs(AccountID const& account, ReadView const& view) const
|
||||
-> boost::optional<std::map<TxSeq, AccountTxDetails>>
|
||||
-> std::map<TxSeq, AccountTxDetails const>
|
||||
{
|
||||
auto const allowEscalation =
|
||||
(view.rules().enabled(featureFeeEscalation));
|
||||
if (!allowEscalation)
|
||||
return boost::none;
|
||||
return {};
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto accountIter = byAccount_.find(account);
|
||||
if (accountIter == byAccount_.end() ||
|
||||
accountIter->second.transactions.empty())
|
||||
return boost::none;
|
||||
return {};
|
||||
|
||||
std::map<TxSeq, AccountTxDetails> result;
|
||||
std::map<TxSeq, AccountTxDetails const> result;
|
||||
|
||||
for (auto const& tx : accountIter->second.transactions)
|
||||
{
|
||||
auto& resultTx = result[tx.first];
|
||||
resultTx.feeLevel = tx.second.feeLevel;
|
||||
if(tx.second.lastValid)
|
||||
resultTx.lastValid.emplace(*tx.second.lastValid);
|
||||
if(tx.second.consequences)
|
||||
resultTx.consequences.emplace(*tx.second.consequences);
|
||||
result.emplace(tx.first, [&]
|
||||
{
|
||||
AccountTxDetails resultTx;
|
||||
resultTx.feeLevel = tx.second.feeLevel;
|
||||
if (tx.second.lastValid)
|
||||
resultTx.lastValid.emplace(*tx.second.lastValid);
|
||||
if (tx.second.consequences)
|
||||
resultTx.consequences.emplace(*tx.second.consequences);
|
||||
return resultTx;
|
||||
}());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto
|
||||
TxQ::getTxs(ReadView const& view) const
|
||||
-> std::vector<TxDetails>
|
||||
{
|
||||
auto const allowEscalation =
|
||||
(view.rules().enabled(featureFeeEscalation));
|
||||
if (!allowEscalation)
|
||||
return {};
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (byFee_.empty())
|
||||
return {};
|
||||
|
||||
std::vector<TxDetails> result;
|
||||
result.reserve(byFee_.size());
|
||||
|
||||
for (auto const& tx : byFee_)
|
||||
{
|
||||
result.emplace_back([&]
|
||||
{
|
||||
TxDetails resultTx;
|
||||
resultTx.feeLevel = tx.feeLevel;
|
||||
if (tx.lastValid)
|
||||
resultTx.lastValid.emplace(*tx.lastValid);
|
||||
if (tx.consequences)
|
||||
resultTx.consequences.emplace(*tx.consequences);
|
||||
resultTx.account = tx.account;
|
||||
resultTx.txn = tx.txn;
|
||||
resultTx.retriesRemaining = tx.retriesRemaining;
|
||||
BOOST_ASSERT(tx.pfresult);
|
||||
resultTx.preflightResult = tx.pfresult->ter;
|
||||
if (tx.lastResult)
|
||||
resultTx.lastResult.emplace(*tx.lastResult);
|
||||
return resultTx;
|
||||
}());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Json::Value
|
||||
TxQ::doRPC(Application& app) const
|
||||
{
|
||||
|
||||
@@ -33,9 +33,6 @@ struct Context;
|
||||
/** Execute an RPC command and store the results in a Json::Value. */
|
||||
Status doCommand (RPC::Context&, Json::Value&);
|
||||
|
||||
/** Execute an RPC command and store the results in an std::string. */
|
||||
void executeRPC (RPC::Context&, std::string&);
|
||||
|
||||
Role roleRequired (std::string const& method );
|
||||
|
||||
} // RPC
|
||||
|
||||
@@ -117,11 +117,11 @@ Json::Value doAccountInfo (RPC::Context& context)
|
||||
|
||||
auto const txs = context.app.getTxQ().getAccountTxs(
|
||||
accountID, *ledger);
|
||||
if (txs && !txs->empty())
|
||||
if (!txs.empty())
|
||||
{
|
||||
jvQueueData[jss::txn_count] = static_cast<Json::UInt>(txs->size());
|
||||
jvQueueData[jss::lowest_sequence] = txs->begin()->first;
|
||||
jvQueueData[jss::highest_sequence] = txs->rbegin()->first;
|
||||
jvQueueData[jss::txn_count] = static_cast<Json::UInt>(txs.size());
|
||||
jvQueueData[jss::lowest_sequence] = txs.begin()->first;
|
||||
jvQueueData[jss::highest_sequence] = txs.rbegin()->first;
|
||||
|
||||
auto& jvQueueTx = jvQueueData[jss::transactions];
|
||||
jvQueueTx = Json::arrayValue;
|
||||
@@ -129,7 +129,7 @@ Json::Value doAccountInfo (RPC::Context& context)
|
||||
boost::optional<bool> anyAuthChanged(false);
|
||||
boost::optional<XRPAmount> totalSpend(0);
|
||||
|
||||
for (auto const& tx : *txs)
|
||||
for (auto const& tx : txs)
|
||||
{
|
||||
Json::Value jvTx = Json::objectValue;
|
||||
|
||||
|
||||
@@ -42,27 +42,29 @@ Status LedgerHandler::check()
|
||||
bool needsLedger = params.isMember (jss::ledger) ||
|
||||
params.isMember (jss::ledger_hash) ||
|
||||
params.isMember (jss::ledger_index);
|
||||
if (!needsLedger)
|
||||
if (! needsLedger)
|
||||
return Status::OK;
|
||||
|
||||
if (auto s = lookupLedger (ledger_, context_, result_))
|
||||
return s;
|
||||
|
||||
bool bFull = params[jss::full].asBool();
|
||||
bool bTransactions = params[jss::transactions].asBool();
|
||||
bool bAccounts = params[jss::accounts].asBool();
|
||||
bool bExpand = params[jss::expand].asBool();
|
||||
bool bBinary = params[jss::binary].asBool();
|
||||
bool const full = params[jss::full].asBool();
|
||||
bool const transactions = params[jss::transactions].asBool();
|
||||
bool const accounts = params[jss::accounts].asBool();
|
||||
bool const expand = params[jss::expand].asBool();
|
||||
bool const binary = params[jss::binary].asBool();
|
||||
bool const owner_funds = params[jss::owner_funds].asBool();
|
||||
bool const queue = params[jss::queue].asBool();
|
||||
|
||||
options_ = (bFull ? LedgerFill::full : 0)
|
||||
| (bExpand ? LedgerFill::expand : 0)
|
||||
| (bTransactions ? LedgerFill::dumpTxrp : 0)
|
||||
| (bAccounts ? LedgerFill::dumpState : 0)
|
||||
| (bBinary ? LedgerFill::binary : 0)
|
||||
| (owner_funds ? LedgerFill::ownerFunds : 0);
|
||||
options_ = (full ? LedgerFill::full : 0)
|
||||
| (expand ? LedgerFill::expand : 0)
|
||||
| (transactions ? LedgerFill::dumpTxrp : 0)
|
||||
| (accounts ? LedgerFill::dumpState : 0)
|
||||
| (binary ? LedgerFill::binary : 0)
|
||||
| (owner_funds ? LedgerFill::ownerFunds : 0)
|
||||
| (queue ? LedgerFill::dumpQueue : 0);
|
||||
|
||||
if (bFull || bAccounts)
|
||||
if (full || accounts)
|
||||
{
|
||||
// Until some sane way to get full ledgers has been implemented,
|
||||
// disallow retrieving all state nodes.
|
||||
@@ -70,13 +72,24 @@ Status LedgerHandler::check()
|
||||
return rpcNO_PERMISSION;
|
||||
|
||||
if (context_.app.getFeeTrack().isLoadedLocal() &&
|
||||
!isUnlimited (context_.role))
|
||||
! isUnlimited (context_.role))
|
||||
{
|
||||
return rpcTOO_BUSY;
|
||||
}
|
||||
context_.loadType = bBinary ? Resource::feeMediumBurdenRPC :
|
||||
context_.loadType = binary ? Resource::feeMediumBurdenRPC :
|
||||
Resource::feeHighBurdenRPC;
|
||||
}
|
||||
if (queue)
|
||||
{
|
||||
if (! ledger_ || ! ledger_->open())
|
||||
{
|
||||
// It doesn't make sense to request the queue
|
||||
// with a non-existant or closed/validated ledger.
|
||||
return rpcINVALID_PARAMS;
|
||||
}
|
||||
|
||||
queueTxs_ = context_.app.getTxQ().getTxs(*ledger_);
|
||||
}
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ public:
|
||||
private:
|
||||
Context& context_;
|
||||
std::shared_ptr<ReadView const> ledger_;
|
||||
std::vector<TxQ::TxDetails> queueTxs_;
|
||||
Json::Value result_;
|
||||
int options_ = 0;
|
||||
};
|
||||
@@ -88,7 +89,7 @@ void LedgerHandler::writeResult (Object& value)
|
||||
if (ledger_)
|
||||
{
|
||||
Json::copyFrom (value, result_);
|
||||
addJson (value, {*ledger_, options_});
|
||||
addJson (value, {*ledger_, options_, queueTxs_});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -92,7 +92,6 @@ class HandlerTable {
|
||||
h.valueMethod_ = &handle<Json::Value, HandlerImpl>;
|
||||
h.role_ = HandlerImpl::role();
|
||||
h.condition_ = HandlerImpl::condition();
|
||||
h.objectMethod_ = &handle<Json::Object, HandlerImpl>;
|
||||
|
||||
table_[HandlerImpl::name()] = h;
|
||||
};
|
||||
|
||||
@@ -48,7 +48,6 @@ struct Handler
|
||||
Method<Json::Value> valueMethod_;
|
||||
Role role_;
|
||||
RPC::Condition condition_;
|
||||
Method<Json::Object> objectMethod_;
|
||||
};
|
||||
|
||||
const Handler* getHandler (std::string const&);
|
||||
|
||||
@@ -265,36 +265,6 @@ Status doCommand (
|
||||
return rpcUNKNOWN_COMMAND;
|
||||
}
|
||||
|
||||
/** Execute an RPC command and store the results in a string. */
|
||||
void executeRPC (
|
||||
RPC::Context& context, std::string& output)
|
||||
{
|
||||
boost::optional <Handler const&> handler;
|
||||
if (auto error = fillHandler (context, handler))
|
||||
{
|
||||
auto wo = Json::stringWriterObject (output);
|
||||
auto&& sub = Json::addObject (*wo, jss::result);
|
||||
inject_error (error, sub);
|
||||
}
|
||||
else if (auto method = handler->objectMethod_)
|
||||
{
|
||||
auto wo = Json::stringWriterObject (output);
|
||||
getResult (context, method, *wo, handler->name_);
|
||||
}
|
||||
else if (auto method = handler->valueMethod_)
|
||||
{
|
||||
auto object = Json::Value (Json::objectValue);
|
||||
getResult (context, method, object, handler->name_);
|
||||
output = to_string (object);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Can't ever get here.
|
||||
assert (false);
|
||||
Throw<std::logic_error> ("RPC handler with no method");
|
||||
}
|
||||
}
|
||||
|
||||
Role roleRequired (std::string const& method)
|
||||
{
|
||||
auto handler = RPC::getHandler(method);
|
||||
|
||||
@@ -434,14 +434,13 @@ transactionPreProcessImpl (
|
||||
*ledger);
|
||||
// If the account has any txs in the TxQ, skip those sequence
|
||||
// numbers (accounting for possible gaps).
|
||||
if(queued)
|
||||
for(auto const& tx : *queued)
|
||||
{
|
||||
if (tx.first == seq)
|
||||
++seq;
|
||||
else if (tx.first > seq)
|
||||
break;
|
||||
}
|
||||
for(auto const& tx : queued)
|
||||
{
|
||||
if (tx.first == seq)
|
||||
++seq;
|
||||
else if (tx.first > seq)
|
||||
break;
|
||||
}
|
||||
tx_json[jss::Sequence] = seq;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user