20 #include <ripple/app/ledger/OpenLedger.h>
21 #include <ripple/app/main/Application.h>
22 #include <ripple/app/misc/LoadFeeTrack.h>
23 #include <ripple/app/misc/TxQ.h>
24 #include <ripple/app/tx/apply.h>
25 #include <ripple/basics/mulDiv.h>
26 #include <ripple/protocol/Feature.h>
27 #include <ripple/protocol/jss.h>
28 #include <ripple/protocol/st.h>
29 #include <boost/algorithm/clamp.hpp>
45 if (refTxnCostDrops == 0)
53 return mulDiv(tx[
sfFee].xrp(), baseRefLevel, refTxnCostDrops).second;
56 static boost::optional<LedgerIndex>
67 return mulDiv(level, 100 + increasePercent, 100).second;
82 auto const txBegin = view.
txs.
begin();
83 auto const txEnd = view.
txs.
end();
93 assert(size == feeLevels.
size());
95 JLOG(
j_.
debug()) <<
"Ledger " << view.
info().
seq <<
" has " << size
97 <<
"Ledgers are processing "
98 << (timeLeap ?
"slowly" :
"as expected")
109 auto const upperLimit = std::max<std::uint64_t>(
123 auto const next = [&] {
151 (feeLevels[size / 2] + feeLevels[(size - 1) / 2] +
FeeLevel64{1}) /
217 auto const last =
current + seriesSize - 1;
236 return {sumNlast.first,
FeeLevel64{sumNlast.second}};
237 auto const totalFeeLevel =
mulDiv(
238 multiplier, sumNlast.second - sumNcurrent.second, target * target);
240 return totalFeeLevel;
250 , feeLevel(feeLevel_)
253 , sequence(txn_->getSequence())
254 , retriesRemaining(retriesAllowed)
256 , pfresult(pfresult_)
269 if (pfresult->rules != view.
rules() || pfresult->flags != flags)
271 JLOG(j.
debug()) <<
"Queued transaction " << txID
272 <<
" rules or flags have changed. Flags from "
273 << pfresult->flags <<
" to " << flags;
279 auto pcresult =
preclaim(*pfresult, app, view);
281 return doApply(pcresult, app, view);
298 auto result = transactions.emplace(sequence, std::move(txn));
299 assert(result.second);
300 assert(&result.first->second != &txn);
302 return result.first->second;
308 return transactions.erase(sequence) != 0;
323 template <
size_t fillPercentage>
328 fillPercentage > 0 && fillPercentage <= 100,
"Invalid fill percentage");
337 AccountMap::iterator accountIter,
338 boost::optional<FeeMultiSet::iterator> replacementIter)
369 canBeHeld = replacementIter.is_initialized();
385 canBeHeld = tSeq < accountIter->second.transactions.rbegin()->first;
392 TxQ::erase(TxQ::FeeMultiSet::const_iterator_type candidateIter)
393 -> FeeMultiSet::iterator_type
395 auto& txQAccount = byAccount_.at(candidateIter->account);
396 auto const sequence = candidateIter->sequence;
397 auto const newCandidateIter = byFee_.erase(candidateIter);
401 auto const found = txQAccount.remove(sequence);
405 return newCandidateIter;
410 -> FeeMultiSet::iterator_type
412 auto& txQAccount = byAccount_.at(candidateIter->account);
413 auto const accountIter =
414 txQAccount.transactions.find(candidateIter->sequence);
415 assert(accountIter != txQAccount.transactions.end());
416 assert(accountIter == txQAccount.transactions.begin());
417 assert(byFee_.iterator_to(accountIter->second) == candidateIter);
418 auto const accountNextIter =
std::next(accountIter);
432 auto const feeNextIter =
std::next(candidateIter);
433 bool const useAccountNext =
434 accountNextIter != txQAccount.transactions.end() &&
435 accountNextIter->first == candidateIter->sequence + 1 &&
436 (feeNextIter == byFee_.end() ||
437 accountNextIter->second.feeLevel > feeNextIter->feeLevel);
438 auto const candidateNextIter = byFee_.erase(candidateIter);
439 txQAccount.transactions.erase(accountIter);
440 return useAccountNext ? byFee_.iterator_to(accountNextIter->second)
447 TxQ::TxQAccount::TxMap::const_iterator begin,
448 TxQ::TxQAccount::TxMap::const_iterator end) -> TxQAccount::TxMap::iterator
450 for (
auto it = begin; it != end; ++it)
452 byFee_.erase(byFee_.iterator_to(it->second));
454 return txQAccount.transactions.erase(begin, end);
462 TxQ::AccountMap::iterator
const& accountIter,
463 TxQAccount::TxMap::iterator beginTxIter,
472 assert(beginTxIter != accountIter->second.transactions.end());
473 auto const aSeq = beginTxIter->first;
476 metricsSnapshot, view, txExtraCount, tSeq - aSeq + 1);
481 if (!requiredTotalFeeLevel.first)
486 auto endTxIter = accountIter->second.transactions.lower_bound(tSeq);
492 [](
auto const& total,
auto const& txn) {
493 return total + txn.second.feeLevel;
497 if (totalFeeLevelPaid < requiredTotalFeeLevel.second)
502 for (
auto it = beginTxIter; it != endTxIter; ++it)
504 auto txResult = it->second.apply(app, view, j);
509 --it->second.retriesRemaining;
510 it->second.lastResult = txResult.first;
511 if (!txResult.second)
519 auto const txResult =
doApply(
preclaim(pfresult, app, view), app, view);
525 endTxIter =
erase(accountIter->second, beginTxIter, endTxIter);
527 if (endTxIter != accountIter->second.transactions.end() &&
528 endTxIter->first == tSeq)
606 auto const tSeq = tx->getSequence();
610 auto const pfresult =
preflight(app, view.
rules(), *tx, flags, j);
612 return {pfresult.ter,
false};
616 explicit MultiTxn() =
default;
618 boost::optional<ApplyViewImpl> applyView;
619 boost::optional<OpenView> openView;
621 TxQAccount::TxMap::iterator nextTxIter;
625 bool includeCurrentFee =
false;
628 boost::optional<MultiTxn> multiTxn;
629 boost::optional<TxConsequences const> consequences;
630 boost::optional<FeeMultiSet::iterator> replacedItemDeleteIter;
643 auto const requiredFeeLevel = [&]() {
659 auto& txQAcct = accountIter->second;
661 if (existingIter != txQAcct.transactions.end())
668 <<
"Found transaction in queue for account " << account
669 <<
" with sequence number " << tSeq <<
" new txn fee level is "
670 << feeLevelPaid <<
", old txn fee level is "
671 << existingIter->second.feeLevel
672 <<
", new txn needs fee level of " << requiredRetryLevel;
673 if (feeLevelPaid > requiredRetryLevel ||
674 (existingIter->second.feeLevel < requiredFeeLevel &&
675 feeLevelPaid >= requiredFeeLevel &&
676 existingIter == txQAcct.transactions.begin()))
686 if (
std::next(existingIter) != txQAcct.transactions.end())
692 if (!existingIter->second.consequences)
693 existingIter->second.consequences.emplace(
695 *existingIter->second.pfresult));
697 if (existingIter->second.consequences->category ==
700 assert(!consequences);
706 JLOG(
j_.
trace()) <<
"Ignoring blocker transaction "
708 <<
" in favor of normal queued "
709 << existingIter->second.txID;
716 JLOG(
j_.
trace()) <<
"Removing transaction from queue "
717 << existingIter->second.txID <<
" in favor of "
723 auto deleteIter =
byFee_.iterator_to(existingIter->second);
724 assert(deleteIter !=
byFee_.end());
725 assert(&existingIter->second == &*deleteIter);
726 assert(deleteIter->sequence == tSeq);
727 assert(deleteIter->account == txQAcct.account);
728 replacedItemDeleteIter = deleteIter;
735 <<
" in favor of queued " << existingIter->second.txID;
748 auto& txQAcct = accountIter->second;
758 *tx, flags, view, accountIter, replacedItemDeleteIter))
769 multiTxn->nextTxIter = txQAcct.transactions.find(aSeq);
770 auto workingIter = multiTxn->nextTxIter;
771 auto workingSeq = aSeq;
772 for (; workingIter != txQAcct.transactions.end();
773 ++workingIter, ++workingSeq)
775 if (workingSeq < tSeq && workingIter->first != workingSeq)
781 if (workingIter->first == tSeq - 1)
786 workingIter->second.feeLevel,
789 if (feeLevelPaid <= requiredMultiLevel)
794 <<
". Needs fee level of " <<
796 requiredMultiLevel <<
". Only paid "
801 if (workingIter->first == tSeq)
805 assert(replacedItemDeleteIter);
806 multiTxn->includeCurrentFee =
std::next(workingIter) !=
807 txQAcct.transactions.end();
810 if (!workingIter->second.consequences)
811 workingIter->second.consequences.emplace(
813 *workingIter->second.pfresult));
816 if (workingIter->first < tSeq &&
817 workingIter->second.consequences->category ==
824 <<
". A blocker-type transaction "
825 <<
"is in the queue.";
828 multiTxn->fee += workingIter->second.consequences->fee;
829 multiTxn->potentialSpend +=
830 workingIter->second.consequences->potentialSpend;
832 if (workingSeq < tSeq)
873 auto const balance = (*sle)[
sfBalance].xrp();
886 auto totalFee = multiTxn->fee;
887 if (multiTxn->includeCurrentFee)
888 totalFee += (*tx)[
sfFee].xrp();
889 if (totalFee >= balance || totalFee >= reserve)
893 <<
". Total fees in flight too high.";
898 multiTxn->applyView.emplace(&view, flags);
899 multiTxn->openView.emplace(&*multiTxn->applyView);
906 auto const potentialTotalSpend = multiTxn->fee +
908 multiTxn->potentialSpend);
909 assert(potentialTotalSpend >
XRPAmount{0});
910 sleBump->setFieldAmount(
911 sfBalance, balance - potentialTotalSpend);
918 assert(!multiTxn || multiTxn->openView);
919 auto const pcresult =
920 preclaim(pfresult, app, multiTxn ? *multiTxn->openView : view);
921 if (!pcresult.likelyToClaimFee)
922 return {pcresult.ter,
false};
928 << account <<
" has fee level of " << feeLevelPaid
929 <<
" needs at least " << requiredFeeLevel
930 <<
" to get in the open ledger, which has "
931 << view.txCount() <<
" entries.";
951 multiTxn.is_initialized() &&
952 multiTxn->nextTxIter->second.retriesRemaining ==
954 feeLevelPaid > requiredFeeLevel && requiredFeeLevel >
baseLevel &&
964 multiTxn->nextTxIter,
982 if (!multiTxn && feeLevelPaid >= requiredFeeLevel)
987 <<
" to open ledger.";
989 auto const [txnResult, didApply] =
doApply(pcresult, app, view);
992 << (didApply ?
" applied successfully with "
996 if (didApply && replacedItemDeleteIter)
997 erase(*replacedItemDeleteIter);
998 return {txnResult, didApply};
1003 !
canBeHeld(*tx, flags, view, accountIter, replacedItemDeleteIter))
1007 <<
" can not be held";
1014 if (!replacedItemDeleteIter &&
isFull())
1016 auto lastRIter =
byFee_.rbegin();
1017 if (lastRIter->account == account)
1021 <<
" would kick a transaction from the same account ("
1022 << account <<
") out of the queue.";
1025 auto const& endAccount =
byAccount_.
at(lastRIter->account);
1026 auto endEffectiveFeeLevel = [&]() {
1030 if (lastRIter->feeLevel > feeLevelPaid ||
1031 endAccount.transactions.size() == 1)
1032 return lastRIter->feeLevel;
1036 endAccount.transactions.begin(),
1037 endAccount.transactions.end(),
1039 [&](
auto const& total,
auto const& txn) {
1042 txn.second.feeLevel / endAccount.transactions.size();
1044 txn.second.feeLevel % endAccount.transactions.size();
1045 if (total.first >= max - next || total.second >= max - mod)
1046 return std::make_pair(max, FeeLevel64{0});
1048 total.first + next, total.second + mod);
1050 return endTotal.first +
1051 endTotal.second / endAccount.transactions.size();
1053 if (feeLevelPaid > endEffectiveFeeLevel)
1057 auto dropRIter = endAccount.transactions.rbegin();
1058 assert(dropRIter->second.account == lastRIter->account);
1060 <<
"Removing last item of account " << lastRIter->account
1061 <<
" from queue with average fee of " << endEffectiveFeeLevel
1064 erase(byFee_.iterator_to(dropRIter->second));
1069 <<
"Queue is full, and transaction " << transactionID
1070 <<
" fee is lower than end item's account average fee";
1076 if (replacedItemDeleteIter)
1077 erase(*replacedItemDeleteIter);
1083 byAccount_.emplace(account, TxQAccount(tx));
1098 auto& candidate = accountIter->second.add(
1105 candidate.consequences.emplace(*consequences);
1107 byFee_.insert(candidate);
1108 JLOG(j_.
debug()) <<
"Added transaction " << candidate.txID
1109 <<
" with result " <<
transToken(pfresult.ter) <<
" from "
1110 << (accountExists ?
"existing" :
"new") <<
" account "
1111 << candidate.account <<
" to queue."
1112 <<
" Flags: " << flags;
1114 return {terQUEUED,
false};
1134 feeMetrics_.update(app, view, timeLeap, setup_);
1135 auto const& snapshot = feeMetrics_.getSnapshot();
1137 auto ledgerSeq = view.
info().
seq;
1141 snapshot.txnsExpected * setup_.ledgersInQueue, setup_.queueSizeMin);
1144 for (
auto candidateIter = byFee_.begin(); candidateIter != byFee_.end();)
1146 if (candidateIter->lastValid && *candidateIter->lastValid <= ledgerSeq)
1148 byAccount_.at(candidateIter->account).dropPenalty =
true;
1149 candidateIter =
erase(candidateIter);
1159 for (
auto txQAccountIter = byAccount_.begin();
1160 txQAccountIter != byAccount_.end();)
1162 if (txQAccountIter->second.empty())
1163 txQAccountIter = byAccount_.erase(txQAccountIter);
1207 auto ledgerChanged =
false;
1211 auto const metricSnapshot = feeMetrics_.getSnapshot();
1213 for (
auto candidateIter = byFee_.begin(); candidateIter != byFee_.end();)
1215 auto& account = byAccount_.at(candidateIter->account);
1216 if (candidateIter->sequence > account.transactions.begin()->first)
1221 <<
"Skipping queued transaction " << candidateIter->txID
1222 <<
" from account " << candidateIter->account
1223 <<
" as it is not the first.";
1227 auto const requiredFeeLevel =
1228 FeeMetrics::scaleFeeLevel(metricSnapshot, view);
1229 auto const feeLevelPaid = candidateIter->feeLevel;
1230 JLOG(j_.
trace()) <<
"Queued transaction " << candidateIter->txID
1231 <<
" from account " << candidateIter->account
1232 <<
" has fee level of " << feeLevelPaid
1233 <<
" needs at least " << requiredFeeLevel;
1234 if (feeLevelPaid >= requiredFeeLevel)
1236 auto firstTxn = candidateIter->txn;
1238 JLOG(j_.
trace()) <<
"Applying queued transaction "
1239 << candidateIter->txID <<
" to open ledger.";
1241 auto const [txnResult, didApply] =
1242 candidateIter->apply(app, view, j_);
1248 <<
"Queued transaction " << candidateIter->txID
1249 <<
" applied successfully with " <<
transToken(txnResult)
1250 <<
". Remove from queue.";
1252 candidateIter = eraseAndAdvance(candidateIter);
1253 ledgerChanged =
true;
1257 candidateIter->retriesRemaining <= 0)
1259 if (candidateIter->retriesRemaining <= 0)
1260 account.retryPenalty =
true;
1262 account.dropPenalty =
true;
1263 JLOG(j_.
debug()) <<
"Queued transaction " << candidateIter->txID
1265 <<
". Remove from queue.";
1266 candidateIter = eraseAndAdvance(candidateIter);
1270 JLOG(j_.
debug()) <<
"Queued transaction " << candidateIter->txID
1272 <<
". Leave in queue."
1273 <<
" Applied: " << didApply
1274 <<
". Flags: " << candidateIter->flags;
1275 if (account.retryPenalty && candidateIter->retriesRemaining > 2)
1276 candidateIter->retriesRemaining = 1;
1278 --candidateIter->retriesRemaining;
1279 candidateIter->lastResult = txnResult;
1280 if (account.dropPenalty && account.transactions.size() > 1 &&
1290 auto dropRIter = account.transactions.rbegin();
1291 assert(dropRIter->second.account == candidateIter->account);
1292 JLOG(j_.
warn()) <<
"Queue is nearly full, and transaction "
1293 << candidateIter->txID <<
" failed with "
1295 <<
". Removing last item of account "
1297 auto endIter = byFee_.iterator_to(dropRIter->second);
1298 assert(endIter != candidateIter);
1310 return ledgerChanged;
1320 auto const snapshot = feeMetrics_.getSnapshot();
1322 result.
txCount = byFee_.size();
1328 isFull() ? byFee_.rbegin()->feeLevel +
FeeLevel64{1} : baseLevel;
1329 result.
medFeeLevel = snapshot.escalationMultiplier;
1336 TxQ::getTxRequiredFeeAndSeq(
1344 auto const snapshot = feeMetrics_.getSnapshot();
1345 auto const baseFee =
1347 auto const fee = FeeMetrics::scaleFeeLevel(snapshot, view);
1349 auto const accountSeq = [&view, &account]() ->
std::uint32_t {
1350 auto const sle = view.
read(keylet::account(account));
1356 auto availableSeq = accountSeq;
1358 if (
auto iter{byAccount_.find(account)}; iter != byAccount_.end())
1360 auto& txQAcct = iter->second;
1361 for (
auto const& [seq, _] : txQAcct.transactions)
1364 if (seq >= availableSeq)
1365 availableSeq = seq + 1;
1369 return {
mulDiv(fee, baseFee, baseLevel).second, accountSeq, availableSeq};
1378 auto accountIter = byAccount_.find(account);
1379 if (accountIter == byAccount_.end() ||
1380 accountIter->second.transactions.empty())
1385 for (
auto const& tx : accountIter->second.transactions)
1387 result.
emplace(tx.first, [&] {
1388 AccountTxDetails resultTx;
1389 resultTx.feeLevel = tx.second.feeLevel;
1390 if (tx.second.lastValid)
1391 resultTx.lastValid.emplace(*tx.second.lastValid);
1392 if (tx.second.consequences)
1393 resultTx.consequences.emplace(*tx.second.consequences);
1409 result.
reserve(byFee_.size());
1411 for (
auto const& tx : byFee_)
1417 resultTx.
lastValid.emplace(*tx.lastValid);
1418 if (tx.consequences)
1420 resultTx.
account = tx.account;
1421 resultTx.
txn = tx.txn;
1423 BOOST_ASSERT(tx.pfresult);
1439 BOOST_ASSERT(
false);
1443 auto const metrics = getMetrics(*view);
1449 ret[jss::ledger_current_index] = view->info().seq;
1450 ret[jss::expected_ledger_size] =
std::to_string(metrics.txPerLedger);
1451 ret[jss::current_ledger_size] =
std::to_string(metrics.txInLedger);
1453 if (metrics.txQMaxSize)
1456 levels[jss::reference_level] = to_string(metrics.referenceFeeLevel);
1457 levels[jss::minimum_level] = to_string(metrics.minProcessingFeeLevel);
1458 levels[jss::median_level] = to_string(metrics.medFeeLevel);
1459 levels[jss::open_ledger_level] = to_string(metrics.openLedgerFeeLevel);
1461 auto const baseFee = view->fees().base;
1465 drops[jss::base_fee] =
1466 to_string(
toDrops(metrics.referenceFeeLevel, baseFee).second);
1467 drops[jss::minimum_fee] =
1468 to_string(
toDrops(metrics.minProcessingFeeLevel, baseFee).second);
1469 drops[jss::median_fee] =
1470 to_string(
toDrops(metrics.medFeeLevel, baseFee).second);
1471 drops[jss::open_ledger_fee] = to_string(
1484 auto const& section = config.
section(
"transaction_queue");
1490 "minimum_escalation_multiplier",
1494 "minimum_txn_in_ledger_standalone",
1498 if (
set(max,
"maximum_txn_in_ledger", section))
1502 Throw<std::runtime_error>(
1503 "The minimum number of low-fee transactions allowed "
1504 "per ledger (minimum_txn_in_ledger) exceeds "
1505 "the maximum number of low-fee transactions allowed per "
1506 "ledger (maximum_txn_in_ledger).");
1510 Throw<std::runtime_error>(
1511 "The minimum number of low-fee transactions allowed "
1512 "per ledger (minimum_txn_in_ledger_standalone) exceeds "
1513 "the maximum number of low-fee transactions allowed per "
1514 "ledger (maximum_txn_in_ledger).");
1527 "normal_consensus_increase_percent",
1537 "slow_consensus_decrease_percent",
1545 "zero_basefee_transaction_feelevel",