1#include <xrpld/app/misc/TxQ.h>
3#include <xrpld/app/ledger/OpenLedger.h>
4#include <xrpld/app/main/Application.h>
6#include <xrpl/basics/Log.h>
7#include <xrpl/basics/contract.h>
8#include <xrpl/basics/mulDiv.h>
9#include <xrpl/beast/utility/Zero.h>
10#include <xrpl/beast/utility/instrumentation.h>
11#include <xrpl/config/BasicConfig.h>
12#include <xrpl/config/Constants.h>
13#include <xrpl/json/json_value.h>
14#include <xrpl/ledger/ApplyView.h>
15#include <xrpl/ledger/ApplyViewImpl.h>
16#include <xrpl/ledger/OpenView.h>
17#include <xrpl/ledger/ReadView.h>
18#include <xrpl/protocol/AccountID.h>
19#include <xrpl/protocol/Indexes.h>
20#include <xrpl/protocol/Keylet.h>
21#include <xrpl/protocol/LedgerFormats.h>
22#include <xrpl/protocol/Protocol.h>
23#include <xrpl/protocol/RippleLedgerHash.h>
24#include <xrpl/protocol/SField.h>
25#include <xrpl/protocol/STTx.h>
26#include <xrpl/protocol/SeqProxy.h>
27#include <xrpl/protocol/TER.h>
28#include <xrpl/protocol/Units.h>
29#include <xrpl/protocol/XRPAmount.h>
30#include <xrpl/protocol/jss.h>
31#include <xrpl/tx/apply.h>
32#include <xrpl/tx/applySteps.h>
34#include <boost/function/function_base.hpp>
58 auto const [baseFee, effectiveFeePaid] = [&view, &tx]() {
60 XRPAmount const feePaid = tx[sfFee].xrp();
64 XRPAmount const mod = [&view, &tx, baseFee]() {
68 return def.signum() == 0 ?
XRPAmount{1} : def;
70 return std::pair{baseFee + mod, feePaid + mod};
73 XRPL_ASSERT(baseFee.signum() > 0,
"xrpl::getFeeLevelPaid : positive fee");
74 if (effectiveFeePaid.signum() <= 0 || baseFee.signum() <= 0)
94 return mulDiv(level, 100 + increasePercent, 100)
108 auto const txBegin = view.
txs.
begin();
109 auto const txEnd = view.
txs.
end();
116 XRPL_ASSERT(size == feeLevels.
size(),
"xrpl::TxQ::FeeMetrics::update : fee levels size");
118 JLOG((timeLeap ?
j_.warn() :
j_.debug()))
119 <<
"Ledger " << view.
header().
seq <<
" has " << size <<
" transactions. "
120 <<
"Ledgers are processing " << (timeLeap ?
"slowly" :
"as expected")
121 <<
". Expected transactions is currently " <<
txnsExpected_ <<
" and multiplier is "
143 auto const next = [&] {
171 (feeLevels[size / 2] + feeLevels[(size - 1) / 2] +
FeeLevel64{1}) / 2;
174 JLOG(
j_.debug()) <<
"Expected transactions updated to " <<
txnsExpected_
184 auto const current = view.
txCount();
191 if (current > target)
195 return mulDiv(multiplier, current * current, target * target)
220 return {
true, (x * (x + 1) * ((2 * x) + 1)) / 6};
224static_assert(sumOfFirstSquares(1).first);
225static_assert(sumOfFirstSquares(1).second == 1);
227static_assert(sumOfFirstSquares(2).first);
228static_assert(sumOfFirstSquares(2).second == 5);
230static_assert(sumOfFirstSquares(0x1FFFFF).first,
"");
231static_assert(sumOfFirstSquares(0x1FFFFF).second == 0x2AAAA8AAAAB00000ul,
"");
233static_assert(!sumOfFirstSquares(0x200000).first,
"");
249 auto const current = view.
txCount() + extraCount;
253 auto const last = current + seriesSize - 1;
260 "xrpl::TxQ::FeeMetrics::escalatedSeriesFeeLevel : current over "
275 return {sumNlast.first,
FeeLevel64{sumNlast.second}};
276 auto const totalFeeLevel =
277 mulDiv(multiplier, sumNlast.second - sumNcurrent.second, target * target);
280 totalFeeLevel.has_value(), *totalFeeLevel};
306 XRPL_ASSERT(
pfResult,
"xrpl::TxQ::MaybeTx::apply : preflight result is set");
311 JLOG(j.
debug()) <<
"Queued transaction " <<
txID
312 <<
" rules or flags have changed. Flags from " <<
pfResult->flags <<
" to "
321 return doApply(pcresult, app, view);
333TxQ::TxQAccount::TxMap::const_iterator
338 auto sameOrPrevIter =
transactions.lower_bound(seqProx);
341 return sameOrPrevIter;
348 [[maybe_unused]]
auto const* txnPtr = &txn;
350 auto result =
transactions.emplace(seqProx, std::move(txn));
351 XRPL_ASSERT(result.second,
"xrpl::TxQ::TxQAccount::add : emplace succeeded");
352 XRPL_ASSERT(&result.first->second != txnPtr,
"xrpl::TxQ::TxQAccount::add : transaction moved");
354 return result.first->second;
375template <
size_t FillPercentage>
379 static_assert(FillPercentage > 0 && FillPercentage <= 100,
"Invalid fill percentage");
389 AccountMap::iterator
const& accountIter,
410 if (lastValid && *lastValid < view.
header().
seq +
setup_.minimumLastLedgerBuffer)
423 TxQAccount const& txQAcct = accountIter->second;
430 if (txSeqProx.isTicket())
439 if (txSeqProx != nextQueuable)
459TxQ::erase(TxQ::FeeMultiSet::const_iterator_type candidateIter) -> FeeMultiSet::iterator_type
461 auto& txQAccount = byAccount_.at(candidateIter->account);
462 auto const seqProx = candidateIter->seqProxy;
463 auto const newCandidateIter = byFee_.erase(candidateIter);
467 [[maybe_unused]]
auto const found = txQAccount.remove(seqProx);
468 XRPL_ASSERT(found,
"xrpl::TxQ::erase : account removed");
470 return newCandidateIter;
475 -> FeeMultiSet::iterator_type
477 auto& txQAccount =
byAccount_.at(candidateIter->account);
478 auto const accountIter = txQAccount.transactions.find(candidateIter->seqProxy);
480 accountIter != txQAccount.transactions.end(),
"xrpl::TxQ::eraseAndAdvance : account found");
486 candidateIter->seqProxy.isTicket() || accountIter == txQAccount.transactions.begin(),
487 "xrpl::TxQ::eraseAndAdvance : ticket or sequence");
489 byFee_.iterator_to(accountIter->second) == candidateIter,
490 "xrpl::TxQ::eraseAndAdvance : found in byFee");
491 auto const accountNextIter =
std::next(accountIter);
495 auto const feeNextIter =
std::next(candidateIter);
496 bool const useAccountNext = accountNextIter != txQAccount.transactions.end() &&
497 accountNextIter->first > candidateIter->seqProxy &&
498 (feeNextIter ==
byFee_.end() ||
byFee_.value_comp()(accountNextIter->second, *feeNextIter));
500 auto const candidateNextIter =
byFee_.erase(candidateIter);
501 txQAccount.transactions.erase(accountIter);
503 return useAccountNext ?
byFee_.iterator_to(accountNextIter->second) : candidateNextIter;
509 TxQ::TxQAccount::TxMap::const_iterator begin,
510 TxQ::TxQAccount::TxMap::const_iterator end) -> TxQAccount::TxMap::iterator
512 for (
auto it = begin; it != end; ++it)
516 return txQAccount.transactions.erase(begin, end);
524 TxQ::AccountMap::iterator
const& accountIter,
525 TxQAccount::TxMap::iterator beginTxIter,
535 beginTxIter != accountIter->second.transactions.end(),
536 "xrpl::TxQ::tryClearAccountQueueUpThruTx : non-empty accounts input");
540 auto endTxIter = accountIter->second.transactions.lower_bound(tSeqProx);
543 auto const requiredTotalFeeLevel =
548 if (!requiredTotalFeeLevel.first)
552 beginTxIter, endTxIter, feeLevelPaid, [](
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() && endTxIter->first == tSeqProx)
738 auto const pfResult =
preflight(app, view.
rules(), *tx, flags, j);
740 return {pfResult.ter,
false};
745 return *directApplied;
758 auto const account = (*tx)[sfAccount];
760 auto const sleAccount = view.
read(accountKey);
766 SeqProxy const txSeqProx = tx->getSeqProxy();
784 AccountMap::iterator accountIter =
byAccount_.find(account);
785 bool const accountIsInQueue = accountIter !=
byAccount_.end();
797 TxIter(TxQAccount::TxMap::iterator first, TxQAccount::TxMap::iterator end)
798 : first(first), end(end)
802 TxQAccount::TxMap::iterator first;
803 TxQAccount::TxMap::iterator end;
808 if (!accountIsInQueue)
813 TxQAccount::TxMap::iterator
const firstIter = acctTxs.
lower_bound(acctSeqProx);
815 if (firstIter == acctTxs.
end())
822 return {TxIter{firstIter, acctTxs.
end()}};
825 auto const acctTxCount{!txIter ? 0 :
std::distance(txIter->first, txIter->end)};
831 auto const transactionID = tx->getTransactionID();
832 if (pfResult.consequences.isBlocker())
838 JLOG(
j_.trace()) <<
"Rejecting blocker transaction " << transactionID
839 <<
". Account has other queued transactions.";
843 if (acctTxCount == 1 && (txSeqProx != txIter->first->first))
846 JLOG(
j_.trace()) <<
"Rejecting blocker transaction " << transactionID
847 <<
". Blocker does not replace lone queued transaction.";
854 auto replacedTxIter = [accountIsInQueue,
857 if (accountIsInQueue)
869 auto const metricsSnapshot =
feeMetrics_.getSnapshot();
883 if (acctTxCount == 1 && txIter->first->second.consequences().isBlocker() &&
884 (txIter->first->first != txSeqProx))
899 TxQAccount::TxMap::iterator
const& existingIter = *replacedTxIter;
900 auto requiredRetryLevel =
901 increase(existingIter->second.feeLevel,
setup_.retrySequencePercent);
902 JLOG(
j_.trace()) <<
"Found transaction in queue for account " << account <<
" with "
903 << txSeqProx <<
" new txn fee level is " << feeLevelPaid
904 <<
", old txn fee level is " << existingIter->second.feeLevel
905 <<
", new txn needs fee level of " << requiredRetryLevel;
906 if (feeLevelPaid > requiredRetryLevel)
911 JLOG(
j_.trace()) <<
"Removing transaction from queue " << existingIter->second.txID
912 <<
" in favor of " << transactionID;
917 JLOG(
j_.trace()) <<
"Ignoring transaction " << transactionID
918 <<
" in favor of queued " << existingIter->second.txID;
929 MultiTxn(
OpenView& view,
ApplyFlags flags) : applyView(&view, flags), openView(&applyView)
936 if (acctTxCount == 0)
941 if (txSeqProx.
isSeq())
943 if (acctSeqProx > txSeqProx)
945 if (acctSeqProx < txSeqProx)
954 TxQAccount const& txQAcct = accountIter->second;
956 if (acctSeqProx > txSeqProx)
965 bool requiresMultiTxn =
false;
966 if (acctTxCount > 1 || !replacedTxIter)
971 canBeHeld(*tx, flags, view, sleAccount, accountIter, replacedTxIter, lock)};
975 requiresMultiTxn =
true;
978 if (requiresMultiTxn)
992 TxQAccount::TxMap::const_iterator
const prevIter = txQAcct.
getPrevTx(txSeqProx);
1002 XRPL_ASSERT(prevIter != txIter->end,
"xrpl::TxQ::apply : not end");
1003 if (prevIter == txIter->end || txSeqProx < prevIter->first)
1007 if (txSeqProx.
isSeq())
1009 if (txSeqProx < acctSeqProx)
1013 if (txSeqProx > acctSeqProx)
1019 else if (!replacedTxIter)
1033 XRPAmount potentialSpend = beast::kZero;
1035 for (
auto iter = txIter->first; iter != txIter->end; ++iter)
1040 if (iter->first != txSeqProx)
1042 totalFee += iter->second.consequences().fee();
1043 potentialSpend += iter->second.consequences().potentialSpend();
1045 else if (
std::next(iter) != txIter->end)
1050 totalFee += pfResult.consequences.fee();
1051 potentialSpend += pfResult.consequences.potentialSpend();
1085 auto const balance = (*sleAccount)[sfBalance].xrp();
1104 auto const base = view.
fees().
base;
1105 if (totalFee >= balance || (reserve > 10 * base && totalFee >= reserve))
1108 JLOG(
j_.trace()) <<
"Ignoring transaction " << transactionID
1109 <<
". Total fees in flight too high.";
1114 multiTxn.
emplace(view, flags);
1116 auto const sleBump = multiTxn->applyView.peek(accountKey);
1123 auto const potentialTotalSpend =
1127 (potentialTotalSpend ==
XRPAmount{0} && multiTxn->applyView.fees().base == 0),
1128 "xrpl::TxQ::apply : total spend check");
1129 sleBump->setFieldAmount(sfBalance, balance - potentialTotalSpend);
1135 sleBump->at(sfSequence) = txSeqProx.
isSeq()
1152 auto const pcresult =
preclaim(pfResult, app, multiTxn ? multiTxn->openView : view);
1153 if (!pcresult.likelyToClaimFee)
1154 return {pcresult.ter,
false};
1157 XRPL_ASSERT(feeLevelPaid >=
kBaseLevel,
"xrpl::TxQ::apply : minimum fee");
1159 JLOG(
j_.trace()) <<
"Transaction " << transactionID <<
" from account " << account
1160 <<
" has fee level of " << feeLevelPaid <<
" needs at least "
1161 << requiredFeeLevel <<
" to get in the open ledger, which has "
1162 << view.
txCount() <<
" entries.";
1183 feeLevelPaid > requiredFeeLevel && requiredFeeLevel >
kBaseLevel)
1201 sandbox.
apply(view);
1212 TER const ter{
canBeHeld(*tx, flags, view, sleAccount, accountIter, replacedTxIter, lock)};
1216 JLOG(
j_.trace()) <<
"Transaction " << transactionID <<
" cannot be held";
1217 return {ter,
false};
1224 if (!replacedTxIter &&
isFull())
1226 auto lastRIter =
byFee_.rbegin();
1227 while (lastRIter !=
byFee_.rend() && lastRIter->account == account)
1231 if (lastRIter ==
byFee_.rend())
1239 JLOG(
j_.info()) <<
"Queue is full, and transaction " << transactionID
1240 <<
" would kick a transaction from the same account (" << account
1241 <<
") out of the queue.";
1244 auto const& endAccount =
byAccount_.at(lastRIter->account);
1245 auto endEffectiveFeeLevel = [&]() {
1249 if (lastRIter->feeLevel > feeLevelPaid || endAccount.transactions.size() == 1)
1250 return lastRIter->feeLevel;
1254 endAccount.transactions.begin(),
1255 endAccount.transactions.end(),
1259 auto next = txn.second.feeLevel / endAccount.transactions.size();
1260 auto mod = txn.second.feeLevel % endAccount.transactions.size();
1261 if (total.first >= kMax - next || total.second >= kMax - mod)
1262 return {kMax, FeeLevel64{0}};
1264 return {total.first + next, total.second + mod};
1266 return endTotal.first + endTotal.second / endAccount.transactions.size();
1268 if (feeLevelPaid > endEffectiveFeeLevel)
1272 auto dropRIter = endAccount.transactions.rbegin();
1274 dropRIter->second.account == lastRIter->account,
1275 "xrpl::TxQ::apply : cheapest transaction found");
1276 JLOG(
j_.info()) <<
"Removing last item of account " << lastRIter->account
1277 <<
" from queue with average fee of " << endEffectiveFeeLevel
1278 <<
" in favor of " << transactionID <<
" with fee of " << feeLevelPaid;
1283 JLOG(
j_.info()) <<
"Queue is full, and transaction " << transactionID
1284 <<
" fee is lower than end item's account average fee";
1292 replacedTxIter = removeFromByFee(replacedTxIter, tx);
1295 if (!accountIsInQueue)
1298 [[maybe_unused]]
bool created =
false;
1299 std::tie(accountIter, created) = byAccount_.emplace(account, TxQAccount(tx));
1300 XRPL_ASSERT(created,
"xrpl::TxQ::apply : account created");
1310 auto& candidate = accountIter->second.add({tx, transactionID, feeLevelPaid, flags, pfResult});
1313 byFee_.insert(candidate);
1314 JLOG(j_.debug()) <<
"Added transaction " << candidate.txID <<
" with result "
1316 << (accountIsInQueue ?
"existing" :
"new") <<
" account " << candidate.account
1318 <<
" Flags: " << flags;
1349 for (
auto candidateIter =
byFee_.begin(); candidateIter !=
byFee_.end();)
1351 if (candidateIter->lastValid && *candidateIter->lastValid <= ledgerSeq)
1353 byAccount_.at(candidateIter->account).dropPenalty =
true;
1354 candidateIter =
erase(candidateIter);
1366 if (txQAccountIter->second.empty())
1368 txQAccountIter =
byAccount_.erase(txQAccountIter);
1415 auto ledgerChanged =
false;
1419 auto const metricsSnapshot =
feeMetrics_.getSnapshot();
1421 for (
auto candidateIter =
byFee_.begin(); candidateIter !=
byFee_.end();)
1423 auto& account =
byAccount_.at(candidateIter->account);
1424 auto const beginIter = account.transactions.begin();
1425 if (candidateIter->seqProxy.isSeq() && candidateIter->seqProxy > beginIter->first)
1430 JLOG(
j_.trace()) <<
"Skipping queued transaction " << candidateIter->txID
1431 <<
" from account " << candidateIter->account
1432 <<
" as it is not the first.";
1437 auto const feeLevelPaid = candidateIter->feeLevel;
1438 JLOG(
j_.trace()) <<
"Queued transaction " << candidateIter->txID <<
" from account "
1439 << candidateIter->account <<
" has fee level of " << feeLevelPaid
1440 <<
" needs at least " << requiredFeeLevel;
1441 if (feeLevelPaid >= requiredFeeLevel)
1443 JLOG(
j_.trace()) <<
"Applying queued transaction " << candidateIter->txID
1444 <<
" to open ledger.";
1446 auto const [txnResult, didApply, _metadata] = candidateIter->apply(app, view,
j_);
1451 JLOG(
j_.debug()) <<
"Queued transaction " << candidateIter->txID
1452 <<
" applied successfully with " <<
transToken(txnResult)
1453 <<
". Remove from queue.";
1456 ledgerChanged =
true;
1460 candidateIter->retriesRemaining <= 0)
1462 if (candidateIter->retriesRemaining <= 0)
1464 account.retryPenalty =
true;
1468 account.dropPenalty =
true;
1470 JLOG(
j_.debug()) <<
"Queued transaction " << candidateIter->txID <<
" failed with "
1471 <<
transToken(txnResult) <<
". Remove from queue.";
1476 JLOG(
j_.debug()) <<
"Queued transaction " << candidateIter->txID <<
" failed with "
1477 <<
transToken(txnResult) <<
". Leave in queue."
1478 <<
" Applied: " << didApply <<
". Flags: " << candidateIter->flags;
1479 if (account.retryPenalty && candidateIter->retriesRemaining > 2)
1481 candidateIter->retriesRemaining = 1;
1485 --candidateIter->retriesRemaining;
1487 candidateIter->lastResult = txnResult;
1488 if (account.dropPenalty && account.transactions.size() > 1 &&
isFull<95>())
1493 if (candidateIter->seqProxy.isTicket())
1498 <<
"Queue is nearly full, and transaction " << candidateIter->txID
1500 <<
". Removing ticketed tx from account " << account.account;
1509 auto dropRIter = account.transactions.rbegin();
1511 dropRIter->second.account == candidateIter->account,
1512 "xrpl::TxQ::accept : account check");
1515 <<
"Queue is nearly full, and transaction " << candidateIter->txID
1517 <<
". Removing last item from account " << account.account;
1518 auto endIter =
byFee_.iterator_to(dropRIter->second);
1519 if (endIter != candidateIter)
1543 JLOG(
j_.warn()) <<
"Parent ledger hash unchanged from " << parentHash;
1550 [[maybe_unused]]
auto const startingSize =
byFee_.size();
1565 for (
auto& [_, candidate] : account.transactions)
1567 byFee_.insert(candidate);
1570 XRPL_ASSERT(
byFee_.size() == startingSize,
"xrpl::TxQ::accept : byFee size match");
1572 return ledgerChanged;
1596 if (!sleAccount || sleAccount->getType() != ltACCOUNT_ROOT)
1602 auto const accountIter =
byAccount_.find((*sleAccount)[sfAccount]);
1603 if (accountIter ==
byAccount_.end() || accountIter->second.transactions.empty())
1611 TxQAccount::TxMap::const_iterator txIter = acctTxs.
lower_bound(acctSeqProx);
1613 if (txIter == acctTxs.
end() || !txIter->first.isSeq() || txIter->first != acctSeqProx)
1625 SeqProxy attempt = txIter->second.consequences().followingSeq();
1626 while (++txIter != acctTxs.
cend())
1628 if (attempt < txIter->first)
1631 attempt = txIter->second.consequences().followingSeq();
1654 auto const account = (*tx)[sfAccount];
1662 SeqProxy const txSeqProx = tx->getSeqProxy();
1666 if (txSeqProx.
isSeq() && txSeqProx != acctSeqProx)
1669 FeeLevel64 const requiredFeeLevel = [
this, &view, flags]() {
1678 if (feeLevelPaid >= requiredFeeLevel)
1681 auto const transactionID = tx->getTransactionID();
1682 JLOG(
j_.trace()) <<
"Applying transaction " << transactionID <<
" to open ledger.";
1684 auto const [txnResult, didApply, metadata] =
xrpl::apply(app, view, *tx, flags, j);
1686 JLOG(
j_.trace()) <<
"New transaction " << transactionID
1687 << (didApply ?
" applied successfully with " :
" failed with ")
1696 AccountMap::iterator
const accountIter =
byAccount_.find(account);
1707 return ApplyResult{txnResult, didApply, metadata};
1717 if (replacedTxIter && tx)
1721 auto deleteIter =
byFee_.iterator_to((*replacedTxIter)->second);
1722 XRPL_ASSERT(deleteIter !=
byFee_.end(),
"xrpl::TxQ::removeFromByFee : found in byFee");
1724 &(*replacedTxIter)->second == &*deleteIter,
1725 "xrpl::TxQ::removeFromByFee : matching transaction");
1727 deleteIter->seqProxy == tx->getSeqProxy(),
1728 "xrpl::TxQ::removeFromByFee : matching sequence");
1730 deleteIter->account == (*tx)[sfAccount],
1731 "xrpl::TxQ::removeFromByFee : matching account");
1735 return std::nullopt;
1754 result.
medFeeLevel = snapshot.escalationMultiplier;
1763 auto const account = (*tx)[sfAccount];
1773 std::uint32_t const accountSeq = sle ? (*sle)[sfSequence] : 0;
1778 .accountSeq = accountSeq,
1779 .availableSeq = availableSeq};
1789 AccountMap::const_iterator
const accountIter{
byAccount_.find(account)};
1791 if (accountIter ==
byAccount_.end() || accountIter->second.transactions.empty())
1794 result.
reserve(accountIter->second.transactions.size());
1795 for (
auto const& tx : accountIter->second.transactions)
1811 for (
auto const& tx :
byFee_)
1823 BOOST_ASSERT(
false);
1833 ret[jss::ledger_current_index] = view->header().seq;
1845 auto const baseFee = view->fees().base;
1849 auto const effectiveBaseFee = [&baseFee, &
metrics]() {
1850 if (!baseFee &&
metrics.openLedgerFeeLevel !=
metrics.referenceFeeLevel)
1856 drops[jss::base_fee] =
to_string(baseFee);
1859 metrics.minProcessingFeeLevel,
1860 metrics.txCount >=
metrics.txQMaxSize ? effectiveBaseFee : baseFee));
1861 auto openFee =
toDrops(
metrics.openLedgerFeeLevel, effectiveBaseFee);
1862 if (effectiveBaseFee &&
toFeeLevel(openFee, effectiveBaseFee) <
metrics.openLedgerFeeLevel)
1864 drops[jss::open_ledger_fee] =
to_string(openFee);
1889 "The minimum number of low-fee transactions allowed "
1890 "per ledger (minimum_txn_in_ledger) exceeds "
1891 "the maximum number of low-fee transactions allowed per "
1892 "ledger (maximum_txn_in_ledger).");
1897 "The minimum number of low-fee transactions allowed "
1898 "per ledger (minimum_txn_in_ledger_standalone) exceeds "
1899 "the maximum number of low-fee transactions allowed per "
1900 "ledger (maximum_txn_in_ledger).");
A generic endpoint for log messages.
Editable, discardable view that can build metadata for one tx.
Section & section(std::string const &name)
Returns the section with the given name.
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.
Fees const & fees() const override
Returns the fees for the base ledger.
SLE::const_pointer read(Keylet const &k) const override
Return the state item associated with a key.
LedgerHeader const & header() const override
Returns information about the ledger.
void apply(TxsRawView &to) const
Apply changes.
Rules const & rules() const override
Returns the tx processing rules.
bool exists(Keylet const &k) const override
Determine if a state item exists.
virtual LedgerHeader const & header() const =0
Returns information about the ledger.
std::shared_ptr< STLedgerEntry const > const & const_ref
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 isTicket() const
constexpr std::uint32_t value() const
constexpr bool isSeq() const
virtual OpenLedger & getOpenLedger()=0
std::size_t txnsExpected_
Number of transactions expected per ledger.
std::size_t const targetTxnCount_
Number of transactions per ledger that fee escalation "workstowards".
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...
beast::Journal const j_
Journal.
std::optional< std::size_t > const maximumTxnCount_
Maximum value of txnsExpected.
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.
std::size_t const minimumTxnCount_
Minimum value of txnsExpected.
boost::circular_buffer< std::size_t > recentTxnCounts_
Recent history of transaction counts that exceed the targetTxnCount_.
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.
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.
static LedgerHash parentHashComp
The hash of the parent ledger.
std::optional< LedgerIndex > const lastValid
Expiration ledger for the transaction (sfLastLedgerSequence field).
TxID const txID
Transaction ID.
FeeLevel64 const feeLevel
Computed fee level that the transaction will pay.
MaybeTx(std::shared_ptr< STTx const > const &, TxID const &txID, FeeLevel64 feeLevel, ApplyFlags const flags, PreflightResult const &pfResult)
Constructor.
ApplyFlags const flags
Flags provided to apply.
ApplyResult apply(Application &app, OpenView &view, beast::Journal j)
Attempt to apply the queued transaction to the open ledger.
SeqProxy const seqProxy
Transaction SeqProxy number (sfSequence or sfTicketSequence field).
std::shared_ptr< STTx const > txn
The complete transaction.
static constexpr int kRetriesAllowed
Starting retry count for newly queued transactions.
AccountID const account
Account submitting the transaction.
std::optional< PreflightResult const > pfResult
Cached result of the preflight operation.
Used to represent an account to the queue, and stores the transactions queued for that account by Seq...
TxMap::const_iterator getPrevTx(SeqProxy seqProx) const
Find the entry in transactions that precedes seqProx, if one does.
TxMap transactions
Sequence number will be used as the key.
MaybeTx & add(MaybeTx &&)
Add a transaction candidate to this account for queuing.
std::size_t getTxnCount() const
Return the number of transactions currently queued for this account.
TxQAccount(std::shared_ptr< STTx const > const &txn)
Construct from a transaction.
bool remove(SeqProxy seqProx)
Remove the candidate with given SeqProxy value from this account.
AccountID const account
The account.
std::map< SeqProxy, MaybeTx > TxMap
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
json::Value doRPC(Application &app) const
Summarize current fee metrics for the fee RPC command.
TxQ(Setup const &setup, beast::Journal j)
Constructor.
SeqProxy nextQueuableSeq(SLE::const_ref sleAccount) const
Return the next sequence that would go in the TxQ for an account.
FeeMetrics feeMetrics_
Tracks the current state of the queue.
std::optional< size_t > maxSize_
Maximum number of transactions allowed in the queue based on the current metrics.
void processClosedLedger(Application &app, ReadView const &view, bool timeLeap)
Update fee metrics and clean up the queue in preparation for the next ledger.
std::vector< TxDetails > getAccountTxs(AccountID const &account) const
Returns information about the transactions currently in the queue for the account.
SeqProxy nextQueuableSeqImpl(SLE::const_ref sleAccount, std::scoped_lock< std::mutex > const &) const
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...
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.
static FeeLevel64 getRequiredFeeLevel(OpenView &view, ApplyFlags flags, FeeMetrics::Snapshot const &metricsSnapshot, std::scoped_lock< std::mutex > const &lock)
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.
FeeAndSeq getTxRequiredFeeAndSeq(OpenView const &view, std::shared_ptr< STTx const > const &tx) const
Returns minimum required fee for tx and two sequences: first valid sequence for this account in curre...
std::optional< ApplyResult > tryDirectApply(Application &app, OpenView &view, std::shared_ptr< STTx const > const &tx, ApplyFlags flags, beast::Journal j)
virtual ~TxQ()
Destructor.
bool accept(Application &app, OpenView &view)
Fill the new open ledger with transactions from the queue.
std::mutex mutex_
Most queue operations are done under the master lock, but use this mutex for the RPC "fee" command,...
std::vector< TxDetails > getTxs() const
Returns information about all transactions currently in the queue.
FeeMultiSet byFee_
The queue itself: the collection of transactions ordered by fee level.
beast::Journal const j_
Journal.
std::optional< TxQAccount::TxMap::iterator > removeFromByFee(std::optional< TxQAccount::TxMap::iterator > const &replacedTxIter, std::shared_ptr< STTx const > const &tx)
LedgerHash parentHash_
parentHash_ used for logging only
FeeMultiSet::iterator_type erase(FeeMultiSet::const_iterator_type)
Erase and return the next entry in byFee_ (lower fee level).
static constexpr FeeLevel64 kBaseLevel
Fee level for single-signed reference transaction.
TER canBeHeld(STTx const &, ApplyFlags const, OpenView const &, SLE::const_ref sleAccount, AccountMap::iterator const &, std::optional< TxQAccount::TxMap::iterator > const &, std::scoped_lock< std::mutex > const &lock)
Checks if the indicated transaction fits the conditions for being stored in the queue.
AccountMap byAccount_
All of the accounts which currently have any transactions in the queue.
Setup const setup_
Setup parameters used to control the behavior of the queue.
constexpr int signum() const noexcept
Return the sign of the amount.
T emplace_back(T... args)
@ Object
object value (collection of name/value pairs).
static constexpr std::pair< bool, std::uint64_t > sumOfFirstSquares(std::size_t xIn)
Keylet ticket(AccountID const &id, std::uint32_t ticketSeq)
A ticket belonging to an account.
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
@ telCAN_NOT_QUEUE_BLOCKED
@ telCAN_NOT_QUEUE_BALANCE
@ telCAN_NOT_QUEUE_BLOCKS
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,...
static std::optional< LedgerIndex > getLastLedgerSequence(STTx const &tx)
PreflightResult preflight(ServiceRegistry ®istry, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
std::optional< std::uint64_t > mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
Return value*mul/div accurately.
PreclaimResult preclaim(PreflightResult const &preflightResult, ServiceRegistry ®istry, OpenView const &view)
Gate a transaction based on static ledger information.
ApplyResult apply(ServiceRegistry ®istry, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
static FeeLevel64 increase(FeeLevel64 level, std::uint32_t increasePercent)
std::string transToken(TER code)
std::string to_string(BaseUInt< Bits, Tag > const &a)
static FeeLevel64 getFeeLevelPaid(ReadView const &view, STTx const &tx)
bool isTefFailure(TER x) noexcept
FeeLevel< std::uint64_t > FeeLevel64
FeeLevel64 toFeeLevel(XRPAmount const &drops, XRPAmount const &baseFee)
constexpr auto kMuldivMax
XRPAmount calculateDefaultBaseFee(ReadView const &view, STTx const &tx)
Return the minimum fee that an "ordinary" transaction would pay.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
constexpr struct xrpl::OpenLedgerT kOpenLedger
ApplyResult doApply(PreclaimResult const &preclaimResult, ServiceRegistry ®istry, OpenView &view)
Apply a prechecked transaction to an OpenView.
TxQ::Setup setupTxQ(Config const &config)
Build a TxQ::Setup object from application configuration.
bool isTemMalformed(TER x) noexcept
uint256 TxID
A transaction identifier.
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
XRPAmount reserve
Minimum XRP an account must hold to exist on the ledger.
XRPAmount base
Cost of a reference transaction in drops.
A pair of SHAMap key and LedgerEntryType.
static constexpr auto kRetrySequencePercent
static constexpr auto kMinimumEscalationMultiplier
static constexpr auto kMaximumTxnInLedger
static constexpr auto kSlowConsensusDecreasePercent
static constexpr auto kMinimumLastLedgerBuffer
static constexpr auto kMaximumTxnPerAccount
static constexpr auto kTargetTxnInLedger
static constexpr auto kMinimumQueueSize
static constexpr auto kMinimumTxnInLedger
static constexpr auto kNormalConsensusIncreasePercent
static constexpr auto kLedgersInQueue
static constexpr auto kMinimumTxnInLedgerStandalone
Describes the results of the preflight check.
static constexpr auto kTransactionQueue
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.
std::size_t txCount
Number of transactions in the queue.
std::optional< std::size_t > txQMaxSize
Max transactions currently allowed in queue.
FeeLevel64 openLedgerFeeLevel
Minimum fee level to get into the current open ledger, bypassing the queue.
std::size_t txInLedger
Number of transactions currently in the open ledger.
FeeLevel64 minProcessingFeeLevel
Minimum fee level for a transaction to be considered for the open ledger or the queue.
FeeLevel64 referenceFeeLevel
Reference transaction fee level.
FeeLevel64 medFeeLevel
Median fee level of the last ledger.
std::size_t txPerLedger
Number of transactions expected per ledger.
Structure used to customize TxQ behavior.
bool standAlone
Use standalone mode behavior.
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::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 "workstowards".
std::uint32_t minimumLastLedgerBuffer
Minimum difference between the current ledger sequence and a transaction's LastLedgerSequence for the...
std::size_t ledgersInQueue
Number of ledgers' worth of transactions to allow in the queue.
std::uint32_t retrySequencePercent
Extra percentage required on the fee level of a queued transaction to replace that transaction with a...
std::uint32_t minimumTxnInLedgerSA
Like minimumTxnInLedger for standalone mode.
std::uint32_t slowConsensusDecreasePercent
When consensus takes longer than appropriate, the expected ledger size is updated to the lesser of th...
std::size_t queueSizeMin
The smallest limit the queue is allowed.
std::uint32_t minimumTxnInLedger
Minimum number of transactions to allow into the ledger before escalation, regardless of the prior le...
std::uint32_t normalConsensusIncreasePercent
When the ledger has more transactions than "expected", and performance is humming along nicely,...