20#include <xrpld/app/ledger/OpenLedger.h> 
   21#include <xrpld/app/main/Application.h> 
   22#include <xrpld/app/misc/TxQ.h> 
   23#include <xrpld/app/tx/apply.h> 
   25#include <xrpl/basics/mulDiv.h> 
   26#include <xrpl/protocol/Feature.h> 
   27#include <xrpl/protocol/jss.h> 
   28#include <xrpl/protocol/st.h> 
   41    auto const [baseFee, effectiveFeePaid] = [&view, &tx]() {
 
   47        XRPAmount const mod = [&view, &tx, baseFee]() {
 
   51            return def.signum() == 0 ? 
XRPAmount{1} : def;
 
   53        return std::pair{baseFee + mod, feePaid + mod};
 
   56    XRPL_ASSERT(baseFee.signum() > 0, 
"ripple::getFeeLevelPaid : positive fee");
 
   57    if (effectiveFeePaid.signum() <= 0 || baseFee.signum() <= 0)
 
 
   77    return mulDiv(level, 100 + increasePercent, 100)
 
 
   91    auto const txBegin = view.
txs.
begin();
 
   92    auto const txEnd = view.
txs.
end();
 
  100        size == feeLevels.
size(),
 
  101        "ripple::TxQ::FeeMetrics::update : fee levels size");
 
  104        << 
"Ledger " << view.
info().
seq << 
" has " << size << 
" transactions. " 
  105        << 
"Ledgers are processing " << (timeLeap ? 
"slowly" : 
"as expected")
 
  133        auto const next = [&] {
 
  161            (feeLevels[size / 2] + feeLevels[(size - 1) / 2] + 
FeeLevel64{1}) /
 
 
  212    return {
true, (x * (x + 1) * (2 * x + 1)) / 6};
 
 
  216static_assert(sumOfFirstSquares(1).first == 
true);
 
  217static_assert(sumOfFirstSquares(1).second == 1);
 
  219static_assert(sumOfFirstSquares(2).first == 
true);
 
  220static_assert(sumOfFirstSquares(2).second == 5);
 
  222static_assert(sumOfFirstSquares(0x1FFFFF).first == 
true, 
"");
 
  223static_assert(sumOfFirstSquares(0x1FFFFF).second == 0x2AAAA8AAAAB00000ul, 
"");
 
  225static_assert(sumOfFirstSquares(0x200000).first == 
false, 
"");
 
  227    sumOfFirstSquares(0x200000).second ==
 
  248    auto const last = 
current + seriesSize - 1;
 
  255        "ripple::TxQ::FeeMetrics::escalatedSeriesFeeLevel : current over " 
  270        return {sumNlast.first, 
FeeLevel64{sumNlast.second}};
 
  271    auto const totalFeeLevel = 
mulDiv(
 
  272        multiplier, sumNlast.second - sumNcurrent.second, target * target);
 
  274    return {totalFeeLevel.has_value(), *totalFeeLevel};
 
 
  286    , feeLevel(feeLevel_)
 
  288    , account(txn_->getAccountID(sfAccount))
 
  290    , seqProxy(txn_->getSeqProxy())
 
  291    , retriesRemaining(retriesAllowed)
 
  293    , pfresult(pfresult_)
 
 
  302        pfresult, 
"ripple::TxQ::MaybeTx::apply : preflight result is set");
 
  305    if (pfresult->rules != view.
rules() || pfresult->flags != flags)
 
  307        JLOG(j.
debug()) << 
"Queued transaction " << txID
 
  308                        << 
" rules or flags have changed. Flags from " 
  309                        << pfresult->flags << 
" to " << flags;
 
  315    auto pcresult = 
preclaim(*pfresult, app, view);
 
  317    return doApply(pcresult, app, view);
 
 
  329TxQ::TxQAccount::TxMap::const_iterator
 
  334    auto sameOrPrevIter = transactions.lower_bound(seqProx);
 
  335    if (sameOrPrevIter != transactions.begin())
 
  337    return sameOrPrevIter;
 
 
  345    auto result = transactions.emplace(seqProx, std::move(txn));
 
  347        result.second, 
"ripple::TxQ::TxQAccount::add : emplace succeeded");
 
  349        &result.first->second != &txn,
 
  350        "ripple::TxQ::TxQAccount::add : transaction moved");
 
  352    return result.first->second;
 
 
  358    return transactions.erase(seqProx) != 0;
 
 
  373template <
size_t fillPercentage>
 
  378        fillPercentage > 0 && fillPercentage <= 100, 
"Invalid fill percentage");
 
 
  388    AccountMap::iterator 
const& accountIter,
 
  419    TxQAccount const& txQAcct = accountIter->second;
 
  426    if (txSeqProx.isTicket())
 
  433    if (txSeqProx != nextQueuable)
 
 
  449TxQ::erase(TxQ::FeeMultiSet::const_iterator_type candidateIter)
 
  450    -> FeeMultiSet::iterator_type
 
  452    auto& txQAccount = byAccount_.at(candidateIter->account);
 
  453    auto const seqProx = candidateIter->seqProxy;
 
  454    auto const newCandidateIter = byFee_.erase(candidateIter);
 
  458    [[maybe_unused]] 
auto const found = txQAccount.remove(seqProx);
 
  459    XRPL_ASSERT(found, 
"ripple::TxQ::erase : account removed");
 
  461    return newCandidateIter;
 
  466    -> FeeMultiSet::iterator_type
 
  468    auto& txQAccount = byAccount_.at(candidateIter->account);
 
  469    auto const accountIter =
 
  470        txQAccount.transactions.find(candidateIter->seqProxy);
 
  472        accountIter != txQAccount.transactions.end(),
 
  473        "ripple::TxQ::eraseAndAdvance : account found");
 
  479        candidateIter->seqProxy.isTicket() ||
 
  480            accountIter == txQAccount.transactions.begin(),
 
  481        "ripple::TxQ::eraseAndAdvance : ticket or sequence");
 
  483        byFee_.iterator_to(accountIter->second) == candidateIter,
 
  484        "ripple::TxQ::eraseAndAdvance : found in byFee");
 
  485    auto const accountNextIter = 
std::next(accountIter);
 
  489    auto const feeNextIter = 
std::next(candidateIter);
 
  490    bool const useAccountNext =
 
  491        accountNextIter != txQAccount.transactions.end() &&
 
  492        accountNextIter->first > candidateIter->seqProxy &&
 
  493        (feeNextIter == byFee_.end() ||
 
  494         byFee_.value_comp()(accountNextIter->second, *feeNextIter));
 
  496    auto const candidateNextIter = byFee_.erase(candidateIter);
 
  497    txQAccount.transactions.erase(accountIter);
 
  499    return useAccountNext ? byFee_.iterator_to(accountNextIter->second)
 
 
  506    TxQ::TxQAccount::TxMap::const_iterator begin,
 
  507    TxQ::TxQAccount::TxMap::const_iterator end) -> TxQAccount::TxMap::iterator
 
  509    for (
auto it = begin; it != end; ++it)
 
  511        byFee_.erase(byFee_.iterator_to(it->second));
 
  513    return txQAccount.transactions.erase(begin, end);
 
  521    TxQ::AccountMap::iterator 
const& accountIter,
 
  522    TxQAccount::TxMap::iterator beginTxIter,
 
  532        beginTxIter != accountIter->second.transactions.end(),
 
  533        "ripple::TxQ::tryClearAccountQueueUpThruTx : non-empty accounts input");
 
  537    auto endTxIter = accountIter->second.transactions.lower_bound(tSeqProx);
 
  541        metricsSnapshot, view, txExtraCount, dist + 1);
 
  545    if (!requiredTotalFeeLevel.first)
 
  552        [](
auto const& total, 
auto const& txn) {
 
  553            return total + txn.second.feeLevel;
 
  557    if (totalFeeLevelPaid < requiredTotalFeeLevel.second)
 
  562    for (
auto it = beginTxIter; it != endTxIter; ++it)
 
  564        auto txResult = it->second.apply(app, view, j);
 
  569        --it->second.retriesRemaining;
 
  570        it->second.lastResult = txResult.ter;
 
  591        if (!txResult.applied)
 
  594            return {txResult.ter, 
false};
 
  599    auto const txResult = 
doApply(
preclaim(pfresult, app, view), app, view);
 
  601    if (txResult.applied)
 
  605        endTxIter = 
erase(accountIter->second, beginTxIter, endTxIter);
 
  607        if (endTxIter != accountIter->second.transactions.end() &&
 
  608            endTxIter->first == tSeqProx)
 
 
  741    auto const pfresult = 
preflight(app, view.
rules(), *tx, flags, j);
 
  743        return {pfresult.ter, 
false};
 
  748        return *directApplied;
 
  758    auto const account = (*tx)[sfAccount];
 
  760    auto const sleAccount = view.
read(accountKey);
 
  766    SeqProxy const txSeqProx = tx->getSeqProxy();
 
  784    bool const accountIsInQueue = accountIter != 
byAccount_.
end();
 
  797            TxQAccount::TxMap::iterator first_,
 
  798            TxQAccount::TxMap::iterator end_)
 
  799            : first(first_), end(end_)
 
  803        TxQAccount::TxMap::iterator first;
 
  804        TxQAccount::TxMap::iterator end;
 
  811        if (!accountIsInQueue)
 
  816        TxQAccount::TxMap::iterator 
const firstIter =
 
  819        if (firstIter == acctTxs.
end())
 
  824        return {TxIter{firstIter, acctTxs.
end()}};
 
  827    auto const acctTxCount{
 
  835    if (pfresult.consequences.isBlocker())
 
  843                << 
".  Account has other queued transactions.";
 
  846        if (acctTxCount == 1 && (txSeqProx != txIter->first->first))
 
  851                << 
".  Blocker does not replace lone queued transaction.";
 
  858    auto replacedTxIter = [accountIsInQueue, &accountIter, txSeqProx]()
 
  860        if (accountIsInQueue)
 
  874    auto const requiredFeeLevel =
 
  886        if (acctTxCount == 1 &&
 
  887            txIter->first->second.consequences().isBlocker() &&
 
  888            (txIter->first->first != txSeqProx))
 
  902            TxQAccount::TxMap::iterator 
const& existingIter = *replacedTxIter;
 
  906                << 
"Found transaction in queue for account " << account
 
  907                << 
" with " << txSeqProx << 
" new txn fee level is " 
  908                << feeLevelPaid << 
", old txn fee level is " 
  909                << existingIter->second.feeLevel
 
  910                << 
", new txn needs fee level of " << requiredRetryLevel;
 
  911            if (feeLevelPaid > requiredRetryLevel)
 
  916                JLOG(
j_.
trace()) << 
"Removing transaction from queue " 
  917                                 << existingIter->second.txID << 
" in favor of " 
  925                    << 
" in favor of queued " << existingIter->second.txID;
 
  937            : applyView(&view, flags), openView(&applyView)
 
  944    if (acctTxCount == 0)
 
  949        if (txSeqProx.
isSeq())
 
  951            if (acctSeqProx > txSeqProx)
 
  953            if (acctSeqProx < txSeqProx)
 
  962        TxQAccount const& txQAcct = accountIter->second;
 
  964        if (acctSeqProx > txSeqProx)
 
  973        bool requiresMultiTxn = 
false;
 
  974        if (acctTxCount > 1 || !replacedTxIter)
 
  989            requiresMultiTxn = 
true;
 
  992        if (requiresMultiTxn)
 
 1006            TxQAccount::TxMap::const_iterator 
const prevIter =
 
 1016                prevIter != txIter->end, 
"ripple::TxQ::apply : not end");
 
 1017            if (prevIter == txIter->end || txSeqProx < prevIter->first)
 
 1021                if (txSeqProx.
isSeq())
 
 1023                    if (txSeqProx < acctSeqProx)
 
 1025                    else if (txSeqProx > acctSeqProx)
 
 1029            else if (!replacedTxIter)
 
 1036                if (txSeqProx.
isSeq() &&
 
 1046            for (
auto iter = txIter->first; iter != txIter->end; ++iter)
 
 1051                if (iter->first != txSeqProx)
 
 1053                    totalFee += iter->second.consequences().fee();
 
 1055                        iter->second.consequences().potentialSpend();
 
 1057                else if (
std::next(iter) != txIter->end)
 
 1062                    totalFee += pfresult.consequences.fee();
 
 1063                    potentialSpend += pfresult.consequences.potentialSpend();
 
 1096            auto const balance = (*sleAccount)[sfBalance].xrp();
 
 1115            auto const base = view.
fees().
base;
 
 1116            if (totalFee >= balance ||
 
 1117                (reserve > 10 * base && totalFee >= reserve))
 
 1121                                 << 
". Total fees in flight too high.";
 
 1126            multiTxn.
emplace(view, flags);
 
 1128            auto const sleBump = multiTxn->applyView.peek(accountKey);
 
 1135            auto const potentialTotalSpend = totalFee +
 
 1140                     multiTxn->applyView.fees().base == 0),
 
 1141                "ripple::TxQ::apply : total spend check");
 
 1142            sleBump->setFieldAmount(sfBalance, balance - potentialTotalSpend);
 
 1148            sleBump->at(sfSequence) = txSeqProx.
isSeq()
 
 1165    auto const pcresult =
 
 1166        preclaim(pfresult, app, multiTxn ? multiTxn->openView : view);
 
 1167    if (!pcresult.likelyToClaimFee)
 
 1168        return {pcresult.
ter, 
false};
 
 1171    XRPL_ASSERT(feeLevelPaid >= 
baseLevel, 
"ripple::TxQ::apply : minimum fee");
 
 1174                     << account << 
" has fee level of " << feeLevelPaid
 
 1175                     << 
" needs at least " << requiredFeeLevel
 
 1176                     << 
" to get in the open ledger, which has " 
 1177                     << view.
txCount() << 
" entries.";
 
 1198        feeLevelPaid > requiredFeeLevel && requiredFeeLevel > 
baseLevel)
 
 1216            sandbox.
apply(view);
 
 1228            *tx, flags, view, sleAccount, accountIter, replacedTxIter, lock)};
 
 1234            return {ter, 
false};
 
 1241    if (!replacedTxIter && 
isFull())
 
 1243        auto lastRIter = 
byFee_.rbegin();
 
 1244        while (lastRIter != 
byFee_.rend() && lastRIter->account == account)
 
 1248        if (lastRIter == 
byFee_.rend())
 
 1258                << 
" would kick a transaction from the same account (" 
 1259                << account << 
") out of the queue.";
 
 1262        auto const& endAccount = 
byAccount_.
at(lastRIter->account);
 
 1263        auto endEffectiveFeeLevel = [&]() {
 
 1267            if (lastRIter->feeLevel > feeLevelPaid ||
 
 1268                endAccount.transactions.size() == 1)
 
 1269                return lastRIter->feeLevel;
 
 1273                endAccount.transactions.begin(),
 
 1274                endAccount.transactions.end(),
 
 1276                [&](
auto const& total,
 
 1280                        txn.second.feeLevel / endAccount.transactions.size();
 
 1282                        txn.second.feeLevel % endAccount.transactions.size();
 
 1283                    if (total.first >= max - next || total.second >= max - mod)
 
 1284                        return {max, FeeLevel64{0}};
 
 1286                    return {total.first + next, total.second + mod};
 
 1288            return endTotal.first +
 
 1289                endTotal.second / endAccount.transactions.size();
 
 1291        if (feeLevelPaid > endEffectiveFeeLevel)
 
 1295            auto dropRIter = endAccount.transactions.rbegin();
 
 1297                dropRIter->second.account == lastRIter->account,
 
 1298                "ripple::TxQ::apply : cheapest transaction found");
 
 1300                << 
"Removing last item of account " << lastRIter->account
 
 1301                << 
" from queue with average fee of " << endEffectiveFeeLevel
 
 1310                << 
" fee is lower than end item's account average fee";
 
 1318        replacedTxIter = removeFromByFee(replacedTxIter, tx);
 
 1321    if (!accountIsInQueue)
 
 1324        [[maybe_unused]] 
bool created = 
false;
 
 1326            byAccount_.emplace(account, TxQAccount(tx));
 
 1327        XRPL_ASSERT(created, 
"ripple::TxQ::apply : account created");
 
 1337    auto& candidate = accountIter->second.add(
 
 1341    byFee_.insert(candidate);
 
 1342    JLOG(j_.
debug()) << 
"Added transaction " << candidate.txID
 
 1343                     << 
" with result " << 
transToken(pfresult.ter) << 
" from " 
 1344                     << (accountIsInQueue ? 
"existing" : 
"new") << 
" account " 
 1345                     << candidate.account << 
" to queue." 
 1346                     << 
" Flags: " << flags;
 
 
 1368    feeMetrics_.update(app, view, timeLeap, setup_);
 
 1369    auto const& snapshot = feeMetrics_.getSnapshot();
 
 1371    auto ledgerSeq = view.
info().
seq;
 
 1375            snapshot.txnsExpected * setup_.ledgersInQueue, setup_.queueSizeMin);
 
 1378    for (
auto candidateIter = byFee_.begin(); candidateIter != byFee_.end();)
 
 1380        if (candidateIter->lastValid && *candidateIter->lastValid <= ledgerSeq)
 
 1382            byAccount_.at(candidateIter->account).dropPenalty = 
true;
 
 1383            candidateIter = 
erase(candidateIter);
 
 1393    for (
auto txQAccountIter = byAccount_.begin();
 
 1394         txQAccountIter != byAccount_.end();)
 
 1396        if (txQAccountIter->second.empty())
 
 1397            txQAccountIter = byAccount_.erase(txQAccountIter);
 
 
 1441    auto ledgerChanged = 
false;
 
 1445    auto const metricsSnapshot = feeMetrics_.getSnapshot();
 
 1447    for (
auto candidateIter = byFee_.begin(); candidateIter != byFee_.end();)
 
 1449        auto& account = byAccount_.at(candidateIter->account);
 
 1450        auto const beginIter = account.transactions.begin();
 
 1451        if (candidateIter->seqProxy.isSeq() &&
 
 1452            candidateIter->seqProxy > beginIter->first)
 
 1458                << 
"Skipping queued transaction " << candidateIter->txID
 
 1459                << 
" from account " << candidateIter->account
 
 1460                << 
" as it is not the first.";
 
 1464        auto const requiredFeeLevel =
 
 1465            getRequiredFeeLevel(view, 
tapNONE, metricsSnapshot, lock);
 
 1466        auto const feeLevelPaid = candidateIter->feeLevel;
 
 1467        JLOG(j_.
trace()) << 
"Queued transaction " << candidateIter->txID
 
 1468                         << 
" from account " << candidateIter->account
 
 1469                         << 
" has fee level of " << feeLevelPaid
 
 1470                         << 
" needs at least " << requiredFeeLevel;
 
 1471        if (feeLevelPaid >= requiredFeeLevel)
 
 1473            JLOG(j_.
trace()) << 
"Applying queued transaction " 
 1474                             << candidateIter->txID << 
" to open ledger.";
 
 1476            auto const [txnResult, didApply, _metadata] =
 
 1477                candidateIter->apply(app, view, j_);
 
 1483                    << 
"Queued transaction " << candidateIter->txID
 
 1484                    << 
" applied successfully with " << 
transToken(txnResult)
 
 1485                    << 
". Remove from queue.";
 
 1487                candidateIter = eraseAndAdvance(candidateIter);
 
 1488                ledgerChanged = 
true;
 
 1492                candidateIter->retriesRemaining <= 0)
 
 1494                if (candidateIter->retriesRemaining <= 0)
 
 1495                    account.retryPenalty = 
true;
 
 1497                    account.dropPenalty = 
true;
 
 1498                JLOG(j_.
debug()) << 
"Queued transaction " << candidateIter->txID
 
 1500                                 << 
". Remove from queue.";
 
 1501                candidateIter = eraseAndAdvance(candidateIter);
 
 1505                JLOG(j_.
debug()) << 
"Queued transaction " << candidateIter->txID
 
 1507                                 << 
". Leave in queue." 
 1508                                 << 
" Applied: " << didApply
 
 1509                                 << 
". Flags: " << candidateIter->flags;
 
 1510                if (account.retryPenalty && candidateIter->retriesRemaining > 2)
 
 1511                    candidateIter->retriesRemaining = 1;
 
 1513                    --candidateIter->retriesRemaining;
 
 1514                candidateIter->lastResult = txnResult;
 
 1515                if (account.dropPenalty && account.transactions.size() > 1 &&
 
 1521                    if (candidateIter->seqProxy.isTicket())
 
 1526                            << 
"Queue is nearly full, and transaction " 
 1527                            << candidateIter->txID << 
" failed with " 
 1529                            << 
". Removing ticketed tx from account " 
 1531                        candidateIter = eraseAndAdvance(candidateIter);
 
 1539                        auto dropRIter = account.transactions.rbegin();
 
 1541                            dropRIter->second.account == candidateIter->account,
 
 1542                            "ripple::TxQ::accept : account check");
 
 1545                            << 
"Queue is nearly full, and transaction " 
 1546                            << candidateIter->txID << 
" failed with " 
 1548                            << 
". Removing last item from account " 
 1550                        auto endIter = byFee_.iterator_to(dropRIter->second);
 
 1551                        if (endIter != candidateIter)
 
 1571    if (parentHash == parentHash_)
 
 1572        JLOG(j_.
warn()) << 
"Parent ledger hash unchanged from " << parentHash;
 
 1574        parentHash_ = parentHash;
 
 1576    [[maybe_unused]] 
auto const startingSize = byFee_.
size();
 
 1587    MaybeTx::parentHashComp = parentHash;
 
 1589    for (
auto& [_, account] : byAccount_)
 
 1591        for (
auto& [_, candidate] : account.transactions)
 
 1593            byFee_.insert(candidate);
 
 1597        byFee_.size() == startingSize,
 
 1598        "ripple::TxQ::accept : byFee size match");
 
 1600    return ledgerChanged;
 
 
 1610    return nextQueuableSeqImpl(sleAccount, lock);
 
 
 1620TxQ::nextQueuableSeqImpl(
 
 1626    if (!sleAccount || sleAccount->getType() != ltACCOUNT_ROOT)
 
 1627        return SeqProxy::sequence(0);
 
 1629    SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
 
 1632    auto const accountIter = byAccount_.find((*sleAccount)[sfAccount]);
 
 1633    if (accountIter == byAccount_.end() ||
 
 1634        accountIter->second.transactions.empty())
 
 1642    TxQAccount::TxMap::const_iterator txIter = acctTxs.
lower_bound(acctSeqProx);
 
 1644    if (txIter == acctTxs.
end() || !txIter->first.isSeq() ||
 
 1645        txIter->first != acctSeqProx)
 
 1655    SeqProxy attempt = txIter->second.consequences().followingSeq();
 
 1656    while (++txIter != acctTxs.
cend())
 
 1658        if (attempt < txIter->first)
 
 1661        attempt = txIter->second.consequences().followingSeq();
 
 
 1667TxQ::getRequiredFeeLevel(
 
 1673    return FeeMetrics::scaleFeeLevel(metricsSnapshot, view);
 
 
 1684    auto const account = (*tx)[sfAccount];
 
 1685    auto const sleAccount = view.
read(keylet::account(account));
 
 1691    SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
 
 1692    SeqProxy const txSeqProx = tx->getSeqProxy();
 
 1696    if (txSeqProx.
isSeq() && txSeqProx != acctSeqProx)
 
 1699    FeeLevel64 const requiredFeeLevel = [
this, &view, flags]() {
 
 1701        return getRequiredFeeLevel(
 
 1702            view, flags, feeMetrics_.getSnapshot(), lock);
 
 1709    if (feeLevelPaid >= requiredFeeLevel)
 
 1714                         << 
" to open ledger.";
 
 1716        auto const [txnResult, didApply, metadata] =
 
 1720                         << (didApply ? 
" applied successfully with " 
 1730            AccountMap::iterator accountIter = byAccount_.find(account);
 
 1731            if (accountIter != byAccount_.end())
 
 1734                if (
auto const existingIter =
 
 1738                    removeFromByFee(existingIter, tx);
 
 1742        return ApplyResult{txnResult, didApply, metadata};
 
 
 1748TxQ::removeFromByFee(
 
 1752    if (replacedTxIter && tx)
 
 1756        auto deleteIter = byFee_.iterator_to((*replacedTxIter)->second);
 
 1758            deleteIter != byFee_.end(),
 
 1759            "ripple::TxQ::removeFromByFee : found in byFee");
 
 1761            &(*replacedTxIter)->second == &*deleteIter,
 
 1762            "ripple::TxQ::removeFromByFee : matching transaction");
 
 1764            deleteIter->seqProxy == tx->getSeqProxy(),
 
 1765            "ripple::TxQ::removeFromByFee : matching sequence");
 
 1767            deleteIter->account == (*tx)[sfAccount],
 
 1768            "ripple::TxQ::removeFromByFee : matching account");
 
 
 1782    auto const snapshot = feeMetrics_.getSnapshot();
 
 1784    result.
txCount = byFee_.size();
 
 1790        isFull() ? byFee_.rbegin()->feeLevel + 
FeeLevel64{1} : baseLevel;
 
 1791    result.
medFeeLevel = snapshot.escalationMultiplier;
 
 
 1798TxQ::getTxRequiredFeeAndSeq(
 
 1802    auto const account = (*tx)[sfAccount];
 
 1806    auto const snapshot = feeMetrics_.getSnapshot();
 
 1808    auto const fee = FeeMetrics::scaleFeeLevel(snapshot, view);
 
 1810    auto const sle = view.
read(keylet::account(account));
 
 1812    std::uint32_t const accountSeq = sle ? (*sle)[sfSequence] : 0;
 
 1813    std::uint32_t const availableSeq = nextQueuableSeqImpl(sle, lock).value();
 
 1815        mulDiv(fee, baseFee, baseLevel)
 
 
 1828    AccountMap::const_iterator 
const accountIter{byAccount_.find(account)};
 
 1830    if (accountIter == byAccount_.end() ||
 
 1831        accountIter->second.transactions.empty())
 
 1834    result.
reserve(accountIter->second.transactions.size());
 
 1835    for (
auto const& tx : accountIter->second.transactions)
 
 
 1849    result.
reserve(byFee_.size());
 
 1851    for (
auto const& tx : byFee_)
 
 
 1863        BOOST_ASSERT(
false);
 
 1867    auto const metrics = getMetrics(*view);
 
 1873    ret[jss::ledger_current_index] = view->info().seq;
 
 1874    ret[jss::expected_ledger_size] = 
std::to_string(metrics.txPerLedger);
 
 1875    ret[jss::current_ledger_size] = 
std::to_string(metrics.txInLedger);
 
 1877    if (metrics.txQMaxSize)
 
 1880    levels[jss::reference_level] = to_string(metrics.referenceFeeLevel);
 
 1881    levels[jss::minimum_level] = to_string(metrics.minProcessingFeeLevel);
 
 1882    levels[jss::median_level] = to_string(metrics.medFeeLevel);
 
 1883    levels[jss::open_ledger_level] = to_string(metrics.openLedgerFeeLevel);
 
 1885    auto const baseFee = view->fees().base;
 
 1889    auto const effectiveBaseFee = [&baseFee, &metrics]() {
 
 1890        if (!baseFee && metrics.openLedgerFeeLevel != metrics.referenceFeeLevel)
 
 1896    drops[jss::base_fee] = to_string(baseFee);
 
 1897    drops[jss::median_fee] = to_string(
toDrops(metrics.medFeeLevel, baseFee));
 
 1898    drops[jss::minimum_fee] = to_string(
toDrops(
 
 1899        metrics.minProcessingFeeLevel,
 
 1900        metrics.txCount >= metrics.txQMaxSize ? effectiveBaseFee : baseFee));
 
 1901    auto openFee = 
toDrops(metrics.openLedgerFeeLevel, effectiveBaseFee);
 
 1902    if (effectiveBaseFee &&
 
 1903        toFeeLevel(openFee, effectiveBaseFee) < metrics.openLedgerFeeLevel)
 
 1905    drops[jss::open_ledger_fee] = to_string(openFee);
 
 
 1916    auto const& section = config.
section(
"transaction_queue");
 
 1921        "minimum_escalation_multiplier",
 
 1925        "minimum_txn_in_ledger_standalone",
 
 1929    if (
set(max, 
"maximum_txn_in_ledger", section))
 
 1933            Throw<std::runtime_error>(
 
 1934                "The minimum number of low-fee transactions allowed " 
 1935                "per ledger (minimum_txn_in_ledger) exceeds " 
 1936                "the maximum number of low-fee transactions allowed per " 
 1937                "ledger (maximum_txn_in_ledger).");
 
 1941            Throw<std::runtime_error>(
 
 1942                "The minimum number of low-fee transactions allowed " 
 1943                "per ledger (minimum_txn_in_ledger_standalone) exceeds " 
 1944                "the maximum number of low-fee transactions allowed per " 
 1945                "ledger (maximum_txn_in_ledger).");
 
 1958        "normal_consensus_increase_percent",
 
 1968        "slow_consensus_decrease_percent",
 
 
A generic endpoint for log messages.
 
Stream trace() const
Severity stream access functions.
 
virtual OpenLedger & openLedger()=0
 
Editable, discardable view that can build metadata for one tx.
 
Section & section(std::string const &name)
Returns the section with the given name.
 
RAII class to set and restore the Number switchover.
 
std::shared_ptr< OpenView const > current() const
Returns a view to the current open ledger.
 
Writable ledger view that accumulates state and tx changes.
 
std::size_t txCount() const
Return the number of tx inserted since creation.
 
LedgerInfo const & info() const override
Returns information about the ledger.
 
bool exists(Keylet const &k) const override
Determine if a state item exists.
 
Rules const & rules() const override
Returns the tx processing rules.
 
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
 
Fees const & fees() const override
Returns the fees for the base ledger.
 
void apply(TxsRawView &to) const
Apply changes.
 
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
 
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
 
std::uint32_t getFieldU32(SField const &field) const
 
bool isFieldPresent(SField const &field) const
 
SeqProxy getSeqProxy() const
 
A type that represents either a sequence value or a ticket value.
 
static constexpr SeqProxy sequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
 
constexpr bool isSeq() const
 
constexpr std::uint32_t value() const
 
constexpr bool isTicket() const
 
std::size_t txnsExpected_
Number of transactions expected per ledger.
 
beast::Journal const j_
Journal.
 
static FeeLevel64 scaleFeeLevel(Snapshot const &snapshot, OpenView const &view)
Use the number of transactions in the current open ledger to compute the fee level a transaction must...
 
std::size_t const minimumTxnCount_
Minimum value of txnsExpected.
 
static std::pair< bool, FeeLevel64 > escalatedSeriesFeeLevel(Snapshot const &snapshot, OpenView const &view, std::size_t extraCount, std::size_t seriesSize)
Computes the total fee level for all transactions in a series.
 
Snapshot getSnapshot() const
Get the current Snapshot.
 
std::optional< std::size_t > const maximumTxnCount_
Maximum value of txnsExpected.
 
std::size_t const targetTxnCount_
Number of transactions per ledger that fee escalation "works towards".
 
boost::circular_buffer< std::size_t > recentTxnCounts_
Recent history of transaction counts that exceed the targetTxnCount_.
 
std::size_t update(Application &app, ReadView const &view, bool timeLeap, TxQ::Setup const &setup)
Updates fee metrics based on the transactions in the ReadView for use in fee escalation calculations.
 
FeeLevel64 escalationMultiplier_
Based on the median fee of the LCL.
 
Represents a transaction in the queue which may be applied later to the open ledger.
 
SeqProxy const seqProxy
Transaction SeqProxy number (sfSequence or sfTicketSequence field).
 
ApplyResult apply(Application &app, OpenView &view, beast::Journal j)
Attempt to apply the queued transaction to the open ledger.
 
MaybeTx(std::shared_ptr< STTx const > const &, TxID const &txID, FeeLevel64 feeLevel, ApplyFlags const flags, PreflightResult const &pfresult)
Constructor.
 
static constexpr int retriesAllowed
Starting retry count for newly queued transactions.
 
static LedgerHash parentHashComp
The hash of the parent ledger.
 
Used to represent an account to the queue, and stores the transactions queued for that account by Seq...
 
TxQAccount(std::shared_ptr< STTx const > const &txn)
Construct from a transaction.
 
TxMap transactions
Sequence number will be used as the key.
 
std::size_t getTxnCount() const
Return the number of transactions currently queued for this account.
 
TxMap::const_iterator getPrevTx(SeqProxy seqProx) const
Find the entry in transactions that precedes seqProx, if one does.
 
bool remove(SeqProxy seqProx)
Remove the candidate with given SeqProxy value from this account.
 
MaybeTx & add(MaybeTx &&)
Add a transaction candidate to this account for queuing.
 
std::optional< size_t > maxSize_
Maximum number of transactions allowed in the queue based on the current metrics.
 
FeeMultiSet::iterator_type erase(FeeMultiSet::const_iterator_type)
Erase and return the next entry in byFee_ (lower fee level)
 
FeeMultiSet byFee_
The queue itself: the collection of transactions ordered by fee level.
 
beast::Journal const j_
Journal.
 
TER canBeHeld(STTx const &, ApplyFlags const, OpenView const &, std::shared_ptr< SLE const > const &sleAccount, AccountMap::iterator const &, std::optional< TxQAccount::TxMap::iterator > const &, std::lock_guard< std::mutex > const &lock)
Checks if the indicated transaction fits the conditions for being stored in the queue.
 
std::mutex mutex_
Most queue operations are done under the master lock, but use this mutex for the RPC "fee" command,...
 
AccountMap byAccount_
All of the accounts which currently have any transactions in the queue.
 
SeqProxy nextQueuableSeqImpl(std::shared_ptr< SLE const > const &sleAccount, std::lock_guard< std::mutex > const &) const
 
ApplyResult tryClearAccountQueueUpThruTx(Application &app, OpenView &view, STTx const &tx, AccountMap::iterator const &accountIter, TxQAccount::TxMap::iterator, FeeLevel64 feeLevelPaid, PreflightResult const &pfresult, std::size_t const txExtraCount, ApplyFlags flags, FeeMetrics::Snapshot const &metricsSnapshot, beast::Journal j)
All-or-nothing attempt to try to apply the queued txs for accountIter up to and including tx.
 
bool isFull() const
Is the queue at least fillPercentage full?
 
FeeMultiSet::iterator_type eraseAndAdvance(FeeMultiSet::const_iterator_type)
Erase and return the next entry for the account (if fee level is higher), or next entry in byFee_ (lo...
 
FeeMetrics feeMetrics_
Tracks the current state of the queue.
 
virtual ~TxQ()
Destructor.
 
FeeLevel64 getRequiredFeeLevel(OpenView &view, ApplyFlags flags, FeeMetrics::Snapshot const &metricsSnapshot, std::lock_guard< std::mutex > const &lock) const
 
TxQ(Setup const &setup, beast::Journal j)
Constructor.
 
static constexpr FeeLevel64 baseLevel
Fee level for single-signed reference transaction.
 
Setup const setup_
Setup parameters used to control the behavior of the queue.
 
std::optional< ApplyResult > tryDirectApply(Application &app, OpenView &view, std::shared_ptr< STTx const > const &tx, ApplyFlags flags, beast::Journal j)
 
ApplyResult apply(Application &app, OpenView &view, std::shared_ptr< STTx const > const &tx, ApplyFlags flags, beast::Journal j)
Add a new transaction to the open ledger, hold it in the queue, or reject it.
 
constexpr int signum() const noexcept
Return the sign of the amount.
 
static constexpr std::size_t size()
 
T emplace_back(T... args)
 
@ objectValue
object value (collection of name/value pairs).
 
static constexpr std::pair< bool, std::uint64_t > sumOfFirstSquares(std::size_t xIn)
 
Keylet account(AccountID const &id) noexcept
AccountID root.
 
static ticket_t const ticket
 
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
 
constexpr struct ripple::open_ledger_t open_ledger
 
TxQ::Setup setup_TxQ(Config const &config)
Build a TxQ::Setup object from application configuration.
 
PreflightResult preflight(Application &app, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
 
static FeeLevel64 increase(FeeLevel64 level, std::uint32_t increasePercent)
 
@ telCAN_NOT_QUEUE_BLOCKED
 
@ telCAN_NOT_QUEUE_BALANCE
 
@ telCAN_NOT_QUEUE_BLOCKS
 
ApplyResult doApply(PreclaimResult const &preclaimResult, Application &app, OpenView &view)
Apply a prechecked transaction to an OpenView.
 
auto constexpr muldiv_max
 
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
 
PreclaimResult preclaim(PreflightResult const &preflightResult, Application &app, OpenView const &view)
Gate a transaction based on static ledger information.
 
bool set(T &target, std::string const &name, Section const §ion)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
 
@ current
This was a new validation and was added.
 
static FeeLevel64 getFeeLevelPaid(ReadView const &view, STTx const &tx)
 
bool isTefFailure(TER x) noexcept
 
std::string transToken(TER code)
 
FeeLevel64 toFeeLevel(XRPAmount const &drops, XRPAmount const &baseFee)
 
void erase(STObject &st, TypedField< U > const &f)
Remove a field in an STObject.
 
FeeLevel< std::uint64_t > FeeLevel64
 
bool isTesSuccess(TER x) noexcept
 
ApplyResult apply(Application &app, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
 
bool isTemMalformed(TER x) noexcept
 
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
 
std::optional< std::uint64_t > mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
Return value*mul/div accurately.
 
static std::optional< LedgerIndex > getLastLedgerSequence(STTx const &tx)
 
@ transactionID
transaction plus signature to give transaction ID
 
XRPAmount calculateDefaultBaseFee(ReadView const &view, STTx const &tx)
Return the minimum fee that an "ordinary" transaction would pay.
 
A pair of SHAMap key and LedgerEntryType.
 
TER const ter
Intermediate transaction result.
 
Describes the results of the preflight check.
 
Snapshot of the externally relevant FeeMetrics fields at any given time.
 
std::size_t const txnsExpected
 
FeeLevel64 const escalationMultiplier
 
Structure returned by TxQ::getMetrics, expressed in reference fee level units.
 
FeeLevel64 minProcessingFeeLevel
Minimum fee level for a transaction to be considered for the open ledger or the queue.
 
FeeLevel64 openLedgerFeeLevel
Minimum fee level to get into the current open ledger, bypassing the queue.
 
std::size_t txPerLedger
Number of transactions expected per ledger.
 
std::optional< std::size_t > txQMaxSize
Max transactions currently allowed in queue.
 
FeeLevel64 referenceFeeLevel
Reference transaction fee level.
 
std::size_t txInLedger
Number of transactions currently in the open ledger.
 
std::size_t txCount
Number of transactions in the queue.
 
FeeLevel64 medFeeLevel
Median fee level of the last ledger.
 
Structure used to customize TxQ behavior.
 
std::uint32_t slowConsensusDecreasePercent
When consensus takes longer than appropriate, the expected ledger size is updated to the lesser of th...
 
std::uint32_t minimumTxnInLedger
Minimum number of transactions to allow into the ledger before escalation, regardless of the prior le...
 
std::uint32_t maximumTxnPerAccount
Maximum number of transactions that can be queued by one account.
 
FeeLevel64 minimumEscalationMultiplier
Minimum value of the escalation multiplier, regardless of the prior ledger's median fee level.
 
std::size_t queueSizeMin
The smallest limit the queue is allowed.
 
std::optional< std::uint32_t > maximumTxnInLedger
Optional maximum allowed value of transactions per ledger before fee escalation kicks in.
 
std::uint32_t targetTxnInLedger
Number of transactions per ledger that fee escalation "works towards".
 
std::uint32_t retrySequencePercent
Extra percentage required on the fee level of a queued transaction to replace that transaction with a...
 
std::uint32_t minimumLastLedgerBuffer
Minimum difference between the current ledger sequence and a transaction's LastLedgerSequence for the...
 
std::uint32_t minimumTxnInLedgerSA
Like minimumTxnInLedger for standalone mode.
 
std::size_t ledgersInQueue
Number of ledgers' worth of transactions to allow in the queue.
 
bool standAlone
Use standalone mode behavior.
 
std::uint32_t normalConsensusIncreasePercent
When the ledger has more transactions than "expected", and performance is humming along nicely,...