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/ledger/helpers/SponsorHelpers.h>
19#include <xrpl/protocol/AccountID.h>
20#include <xrpl/protocol/Indexes.h>
21#include <xrpl/protocol/Keylet.h>
22#include <xrpl/protocol/LedgerFormats.h>
23#include <xrpl/protocol/Protocol.h>
24#include <xrpl/protocol/RippleLedgerHash.h>
25#include <xrpl/protocol/SField.h>
26#include <xrpl/protocol/STTx.h>
27#include <xrpl/protocol/SeqProxy.h>
28#include <xrpl/protocol/TER.h>
29#include <xrpl/protocol/TxFormats.h>
30#include <xrpl/protocol/Units.h>
31#include <xrpl/protocol/XRPAmount.h>
32#include <xrpl/protocol/jss.h>
33#include <xrpl/tx/apply.h>
34#include <xrpl/tx/applySteps.h>
36#include <boost/function/function_base.hpp>
60 auto const [baseFee, effectiveFeePaid] = [&view, &tx]() {
62 XRPAmount const feePaid = tx[sfFee].xrp();
66 XRPAmount const mod = [&view, &tx, baseFee]() {
70 return def.signum() == 0 ?
XRPAmount{1} : def;
72 return std::pair{baseFee + mod, feePaid + mod};
75 XRPL_ASSERT(baseFee.signum() > 0,
"xrpl::getFeeLevelPaid : positive fee");
76 if (effectiveFeePaid.signum() <= 0 || baseFee.signum() <= 0)
96 return mulDiv(level, 100 + increasePercent, 100)
110 auto const txBegin = view.
txs.
begin();
111 auto const txEnd = view.
txs.
end();
118 XRPL_ASSERT(size == feeLevels.
size(),
"xrpl::TxQ::FeeMetrics::update : fee levels size");
120 JLOG((timeLeap ?
j_.warn() :
j_.debug()))
121 <<
"Ledger " << view.
header().
seq <<
" has " << size <<
" transactions. "
122 <<
"Ledgers are processing " << (timeLeap ?
"slowly" :
"as expected")
123 <<
". Expected transactions is currently " <<
txnsExpected_ <<
" and multiplier is "
145 auto const next = [&] {
173 (feeLevels[size / 2] + feeLevels[(size - 1) / 2] +
FeeLevel64{1}) / 2;
176 JLOG(
j_.debug()) <<
"Expected transactions updated to " <<
txnsExpected_
186 auto const current = view.
txCount();
193 if (current > target)
197 return mulDiv(multiplier, current * current, target * target)
222 return {
true, (x * (x + 1) * ((2 * x) + 1)) / 6};
226static_assert(sumOfFirstSquares(1).first);
227static_assert(sumOfFirstSquares(1).second == 1);
229static_assert(sumOfFirstSquares(2).first);
230static_assert(sumOfFirstSquares(2).second == 5);
232static_assert(sumOfFirstSquares(0x1FFFFF).first);
233static_assert(sumOfFirstSquares(0x1FFFFF).second == 0x2AAAA8AAAAB00000ul);
235static_assert(!sumOfFirstSquares(0x200000).first);
251 auto const current = view.
txCount() + extraCount;
255 auto const last = current + seriesSize - 1;
262 "xrpl::TxQ::FeeMetrics::escalatedSeriesFeeLevel : current over "
277 return {sumNlast.first,
FeeLevel64{sumNlast.second}};
278 auto const totalFeeLevel =
279 mulDiv(multiplier, sumNlast.second - sumNcurrent.second, target * target);
282 totalFeeLevel.has_value(), *totalFeeLevel};
308 XRPL_ASSERT(
pfResult,
"xrpl::TxQ::MaybeTx::apply : preflight result is set");
313 JLOG(j.
debug()) <<
"Queued transaction " <<
txID
314 <<
" rules or flags have changed. Flags from " <<
pfResult->flags <<
" to "
323 return doApply(pcresult, app, view);
335TxQ::TxQAccount::TxMap::const_iterator
340 auto sameOrPrevIter =
transactions.lower_bound(seqProx);
343 return sameOrPrevIter;
350 [[maybe_unused]]
auto const* txnPtr = &txn;
352 auto result =
transactions.emplace(seqProx, std::move(txn));
353 XRPL_ASSERT(result.second,
"xrpl::TxQ::TxQAccount::add : emplace succeeded");
354 XRPL_ASSERT(&result.first->second != txnPtr,
"xrpl::TxQ::TxQAccount::add : transaction moved");
356 return result.first->second;
377template <
size_t FillPercentage>
381 static_assert(FillPercentage > 0 && FillPercentage <= 100,
"Invalid fill percentage");
391 AccountMap::iterator
const& accountIter,
421 if (lastValid && *lastValid < view.
header().
seq +
setup_.minimumLastLedgerBuffer)
434 TxQAccount const& txQAcct = accountIter->second;
441 if (txSeqProx.isTicket())
450 if (txSeqProx != nextQueuable)
470TxQ::erase(TxQ::FeeMultiSet::const_iterator_type candidateIter) -> FeeMultiSet::iterator_type
472 auto& txQAccount = byAccount_.at(candidateIter->account);
473 auto const seqProx = candidateIter->seqProxy;
474 auto const newCandidateIter = byFee_.erase(candidateIter);
478 [[maybe_unused]]
auto const found = txQAccount.remove(seqProx);
479 XRPL_ASSERT(found,
"xrpl::TxQ::erase : account removed");
481 return newCandidateIter;
486 -> FeeMultiSet::iterator_type
488 auto& txQAccount =
byAccount_.at(candidateIter->account);
489 auto const accountIter = txQAccount.transactions.find(candidateIter->seqProxy);
491 accountIter != txQAccount.transactions.end(),
"xrpl::TxQ::eraseAndAdvance : account found");
497 candidateIter->seqProxy.isTicket() || accountIter == txQAccount.transactions.begin(),
498 "xrpl::TxQ::eraseAndAdvance : ticket or sequence");
500 byFee_.iterator_to(accountIter->second) == candidateIter,
501 "xrpl::TxQ::eraseAndAdvance : found in byFee");
502 auto const accountNextIter =
std::next(accountIter);
506 auto const feeNextIter =
std::next(candidateIter);
507 bool const useAccountNext = accountNextIter != txQAccount.transactions.end() &&
508 accountNextIter->first > candidateIter->seqProxy &&
509 (feeNextIter ==
byFee_.end() ||
byFee_.value_comp()(accountNextIter->second, *feeNextIter));
511 auto const candidateNextIter =
byFee_.erase(candidateIter);
512 txQAccount.transactions.erase(accountIter);
514 return useAccountNext ?
byFee_.iterator_to(accountNextIter->second) : candidateNextIter;
520 TxQ::TxQAccount::TxMap::const_iterator begin,
521 TxQ::TxQAccount::TxMap::const_iterator end) -> TxQAccount::TxMap::iterator
523 for (
auto it = begin; it != end; ++it)
527 return txQAccount.transactions.erase(begin, end);
535 TxQ::AccountMap::iterator
const& accountIter,
536 TxQAccount::TxMap::iterator beginTxIter,
546 beginTxIter != accountIter->second.transactions.end(),
547 "xrpl::TxQ::tryClearAccountQueueUpThruTx : non-empty accounts input");
551 auto endTxIter = accountIter->second.transactions.lower_bound(tSeqProx);
554 auto const requiredTotalFeeLevel =
559 if (!requiredTotalFeeLevel.first)
563 beginTxIter, endTxIter, feeLevelPaid, [](
auto const& total,
auto const& txn) {
564 return total + txn.second.feeLevel;
568 if (totalFeeLevelPaid < requiredTotalFeeLevel.second)
573 for (
auto it = beginTxIter; it != endTxIter; ++it)
575 auto txResult = it->second.apply(app, view, j);
580 --it->second.retriesRemaining;
581 it->second.lastResult = txResult.ter;
602 if (!txResult.applied)
605 return {txResult.ter,
false};
610 auto const txResult =
doApply(
preclaim(pfResult, app, view), app, view);
612 if (txResult.applied)
616 endTxIter =
erase(accountIter->second, beginTxIter, endTxIter);
618 if (endTxIter != accountIter->second.transactions.end() && endTxIter->first == tSeqProx)
749 auto const pfResult =
preflight(app, view.
rules(), *tx, flags, j);
751 return {pfResult.ter,
false};
756 return *directApplied;
769 auto const account = (*tx)[sfAccount];
771 auto const sleAccount = view.
read(accountKey);
777 SeqProxy const txSeqProx = tx->getSeqProxy();
796 bool const accountIsInQueue = accountIter !=
byAccount_.end();
808 TxIter(TxQAccount::TxMap::iterator first, TxQAccount::TxMap::iterator end)
809 : first(first), end(end)
813 TxQAccount::TxMap::iterator first;
814 TxQAccount::TxMap::iterator end;
819 if (!accountIsInQueue)
824 auto const firstIter = acctTxs.
lower_bound(acctSeqProx);
826 if (firstIter == acctTxs.
end())
833 return {TxIter{firstIter, acctTxs.
end()}};
836 auto const acctTxCount{!txIter ? 0 :
std::distance(txIter->first, txIter->end)};
842 auto const transactionID = tx->getTransactionID();
843 if (pfResult.consequences.isBlocker())
849 JLOG(
j_.trace()) <<
"Rejecting blocker transaction " << transactionID
850 <<
". Account has other queued transactions.";
854 if (acctTxCount == 1 && (txSeqProx != txIter->first->first))
857 JLOG(
j_.trace()) <<
"Rejecting blocker transaction " << transactionID
858 <<
". Blocker does not replace lone queued transaction.";
865 auto replacedTxIter = [accountIsInQueue,
868 if (accountIsInQueue)
880 auto const metricsSnapshot =
feeMetrics_.getSnapshot();
894 if (acctTxCount == 1 && txIter->first->second.consequences().isBlocker() &&
895 (txIter->first->first != txSeqProx))
910 TxQAccount::TxMap::iterator
const& existingIter = *replacedTxIter;
911 auto requiredRetryLevel =
912 increase(existingIter->second.feeLevel,
setup_.retrySequencePercent);
913 JLOG(
j_.trace()) <<
"Found transaction in queue for account " << account <<
" with "
914 << txSeqProx <<
" new txn fee level is " << feeLevelPaid
915 <<
", old txn fee level is " << existingIter->second.feeLevel
916 <<
", new txn needs fee level of " << requiredRetryLevel;
917 if (feeLevelPaid > requiredRetryLevel)
922 JLOG(
j_.trace()) <<
"Removing transaction from queue " << existingIter->second.txID
923 <<
" in favor of " << transactionID;
928 JLOG(
j_.trace()) <<
"Ignoring transaction " << transactionID
929 <<
" in favor of queued " << existingIter->second.txID;
940 MultiTxn(
OpenView& view,
ApplyFlags flags) : applyView(&view, flags), openView(&applyView)
947 if (acctTxCount == 0)
952 if (txSeqProx.
isSeq())
954 if (acctSeqProx > txSeqProx)
956 if (acctSeqProx < txSeqProx)
965 TxQAccount const& txQAcct = accountIter->second;
967 if (acctSeqProx > txSeqProx)
976 bool requiresMultiTxn =
false;
977 if (acctTxCount > 1 || !replacedTxIter)
982 canBeHeld(*tx, flags, view, sleAccount, accountIter, replacedTxIter, lock)};
986 requiresMultiTxn =
true;
989 if (requiresMultiTxn)
1003 auto const prevIter = txQAcct.
getPrevTx(txSeqProx);
1013 XRPL_ASSERT(prevIter != txIter->end,
"xrpl::TxQ::apply : not end");
1014 if (prevIter == txIter->end || txSeqProx < prevIter->first)
1018 if (txSeqProx.
isSeq())
1020 if (txSeqProx < acctSeqProx)
1024 if (txSeqProx > acctSeqProx)
1030 else if (!replacedTxIter)
1046 for (
auto iter = txIter->first; iter != txIter->end; ++iter)
1051 if (iter->first != txSeqProx)
1053 totalFee += iter->second.consequences().fee();
1054 potentialSpend += iter->second.consequences().potentialSpend();
1056 else if (
std::next(iter) != txIter->end)
1061 totalFee += pfResult.consequences.fee();
1062 potentialSpend += pfResult.consequences.potentialSpend();
1096 auto const balance = (*sleAccount)[sfBalance].xrp();
1115 auto const base = view.
fees().
base;
1116 if (totalFee >= balance || (reserve > 10 * base && totalFee >= reserve))
1119 JLOG(
j_.trace()) <<
"Ignoring transaction " << transactionID
1120 <<
". Total fees in flight too high.";
1125 multiTxn.
emplace(view, flags);
1127 auto const sleBump = multiTxn->applyView.peek(accountKey);
1134 auto const potentialTotalSpend =
1138 (potentialTotalSpend ==
XRPAmount{0} && multiTxn->applyView.fees().base == 0),
1139 "xrpl::TxQ::apply : total spend check");
1140 sleBump->setFieldAmount(sfBalance, balance - potentialTotalSpend);
1146 sleBump->at(sfSequence) = txSeqProx.
isSeq()
1163 auto const pcresult =
preclaim(pfResult, app, multiTxn ? multiTxn->openView : view);
1164 if (!pcresult.likelyToClaimFee)
1165 return {pcresult.ter,
false};
1168 XRPL_ASSERT(feeLevelPaid >=
kBaseLevel,
"xrpl::TxQ::apply : minimum fee");
1170 JLOG(
j_.trace()) <<
"Transaction " << transactionID <<
" from account " << account
1171 <<
" has fee level of " << feeLevelPaid <<
" needs at least "
1172 << requiredFeeLevel <<
" to get in the open ledger, which has "
1173 << view.
txCount() <<
" entries.";
1194 feeLevelPaid > requiredFeeLevel && requiredFeeLevel >
kBaseLevel)
1212 sandbox.
apply(view);
1223 TER const ter{
canBeHeld(*tx, flags, view, sleAccount, accountIter, replacedTxIter, lock)};
1227 JLOG(
j_.trace()) <<
"Transaction " << transactionID <<
" cannot be held";
1228 return {ter,
false};
1235 if (!replacedTxIter &&
isFull())
1237 auto lastRIter =
byFee_.rbegin();
1238 while (lastRIter !=
byFee_.rend() && lastRIter->account == account)
1242 if (lastRIter ==
byFee_.rend())
1250 JLOG(
j_.info()) <<
"Queue is full, and transaction " << transactionID
1251 <<
" would kick a transaction from the same account (" << account
1252 <<
") out of the queue.";
1255 auto const& endAccount =
byAccount_.at(lastRIter->account);
1256 auto endEffectiveFeeLevel = [&]() {
1260 if (lastRIter->feeLevel > feeLevelPaid || endAccount.transactions.size() == 1)
1261 return lastRIter->feeLevel;
1265 endAccount.transactions.begin(),
1266 endAccount.transactions.end(),
1270 auto next = txn.second.feeLevel / endAccount.transactions.size();
1271 auto mod = txn.second.feeLevel % endAccount.transactions.size();
1272 if (total.first >= kMax - next || total.second >= kMax - mod)
1273 return {kMax, FeeLevel64{0}};
1275 return {total.first + next, total.second + mod};
1277 return endTotal.first + endTotal.second / endAccount.transactions.size();
1279 if (feeLevelPaid > endEffectiveFeeLevel)
1283 auto dropRIter = endAccount.transactions.rbegin();
1285 dropRIter->second.account == lastRIter->account,
1286 "xrpl::TxQ::apply : cheapest transaction found");
1287 JLOG(
j_.info()) <<
"Removing last item of account " << lastRIter->account
1288 <<
" from queue with average fee of " << endEffectiveFeeLevel
1289 <<
" in favor of " << transactionID <<
" with fee of " << feeLevelPaid;
1294 JLOG(
j_.info()) <<
"Queue is full, and transaction " << transactionID
1295 <<
" fee is lower than end item's account average fee";
1303 replacedTxIter = removeFromByFee(replacedTxIter, tx);
1306 if (!accountIsInQueue)
1309 [[maybe_unused]]
bool created =
false;
1310 std::tie(accountIter, created) = byAccount_.emplace(account, TxQAccount(tx));
1311 XRPL_ASSERT(created,
"xrpl::TxQ::apply : account created");
1321 auto& candidate = accountIter->second.add({tx, transactionID, feeLevelPaid, flags, pfResult});
1324 byFee_.insert(candidate);
1325 JLOG(j_.debug()) <<
"Added transaction " << candidate.txID <<
" with result "
1327 << (accountIsInQueue ?
"existing" :
"new") <<
" account " << candidate.account
1329 <<
" Flags: " << flags;
1360 for (
auto candidateIter =
byFee_.begin(); candidateIter !=
byFee_.end();)
1362 if (candidateIter->lastValid && *candidateIter->lastValid <= ledgerSeq)
1364 byAccount_.at(candidateIter->account).dropPenalty =
true;
1365 candidateIter =
erase(candidateIter);
1377 if (txQAccountIter->second.empty())
1379 txQAccountIter =
byAccount_.erase(txQAccountIter);
1426 auto ledgerChanged =
false;
1430 auto const metricsSnapshot =
feeMetrics_.getSnapshot();
1432 for (
auto candidateIter =
byFee_.begin(); candidateIter !=
byFee_.end();)
1434 auto& account =
byAccount_.at(candidateIter->account);
1435 auto const beginIter = account.transactions.begin();
1436 if (candidateIter->seqProxy.isSeq() && candidateIter->seqProxy > beginIter->first)
1441 JLOG(
j_.trace()) <<
"Skipping queued transaction " << candidateIter->txID
1442 <<
" from account " << candidateIter->account
1443 <<
" as it is not the first.";
1448 auto const feeLevelPaid = candidateIter->feeLevel;
1449 JLOG(
j_.trace()) <<
"Queued transaction " << candidateIter->txID <<
" from account "
1450 << candidateIter->account <<
" has fee level of " << feeLevelPaid
1451 <<
" needs at least " << requiredFeeLevel;
1452 if (feeLevelPaid >= requiredFeeLevel)
1454 JLOG(
j_.trace()) <<
"Applying queued transaction " << candidateIter->txID
1455 <<
" to open ledger.";
1457 auto const [txnResult, didApply, _metadata] = candidateIter->apply(app, view,
j_);
1462 JLOG(
j_.debug()) <<
"Queued transaction " << candidateIter->txID
1463 <<
" applied successfully with " <<
transToken(txnResult)
1464 <<
". Remove from queue.";
1467 ledgerChanged =
true;
1471 candidateIter->retriesRemaining <= 0)
1473 if (candidateIter->retriesRemaining <= 0)
1475 account.retryPenalty =
true;
1479 account.dropPenalty =
true;
1481 JLOG(
j_.debug()) <<
"Queued transaction " << candidateIter->txID <<
" failed with "
1482 <<
transToken(txnResult) <<
". Remove from queue.";
1487 JLOG(
j_.debug()) <<
"Queued transaction " << candidateIter->txID <<
" failed with "
1488 <<
transToken(txnResult) <<
". Leave in queue."
1489 <<
" Applied: " << didApply <<
". Flags: " << candidateIter->flags;
1490 if (account.retryPenalty && candidateIter->retriesRemaining > 2)
1492 candidateIter->retriesRemaining = 1;
1496 --candidateIter->retriesRemaining;
1498 candidateIter->lastResult = txnResult;
1499 if (account.dropPenalty && account.transactions.size() > 1 &&
isFull<95>())
1504 if (candidateIter->seqProxy.isTicket())
1509 <<
"Queue is nearly full, and transaction " << candidateIter->txID
1511 <<
". Removing ticketed tx from account " << account.account;
1520 auto dropRIter = account.transactions.rbegin();
1522 dropRIter->second.account == candidateIter->account,
1523 "xrpl::TxQ::accept : account check");
1526 <<
"Queue is nearly full, and transaction " << candidateIter->txID
1528 <<
". Removing last item from account " << account.account;
1529 auto endIter =
byFee_.iterator_to(dropRIter->second);
1530 if (endIter != candidateIter)
1554 JLOG(
j_.warn()) <<
"Parent ledger hash unchanged from " << parentHash;
1561 [[maybe_unused]]
auto const startingSize =
byFee_.size();
1576 for (
auto& [_, candidate] : account.transactions)
1578 byFee_.insert(candidate);
1581 XRPL_ASSERT(
byFee_.size() == startingSize,
"xrpl::TxQ::accept : byFee size match");
1583 return ledgerChanged;
1607 if (!sleAccount || sleAccount->getType() != ltACCOUNT_ROOT)
1613 auto const accountIter =
byAccount_.find((*sleAccount)[sfAccount]);
1614 if (accountIter ==
byAccount_.end() || accountIter->second.transactions.empty())
1624 if (txIter == acctTxs.
end() || !txIter->first.isSeq() || txIter->first != acctSeqProx)
1636 SeqProxy attempt = txIter->second.consequences().followingSeq();
1637 while (++txIter != acctTxs.
cend())
1639 if (attempt < txIter->first)
1642 attempt = txIter->second.consequences().followingSeq();
1665 auto const account = (*tx)[sfAccount];
1673 SeqProxy const txSeqProx = tx->getSeqProxy();
1677 if (txSeqProx.
isSeq() && txSeqProx != acctSeqProx)
1680 FeeLevel64 const requiredFeeLevel = [
this, &view, flags]() {
1689 if (feeLevelPaid >= requiredFeeLevel)
1692 auto const transactionID = tx->getTransactionID();
1693 JLOG(
j_.trace()) <<
"Applying transaction " << transactionID <<
" to open ledger.";
1695 auto const [txnResult, didApply, metadata] =
xrpl::apply(app, view, *tx, flags, j);
1697 JLOG(
j_.trace()) <<
"New transaction " << transactionID
1698 << (didApply ?
" applied successfully with " :
" failed with ")
1707 auto const accountIter =
byAccount_.find(account);
1718 return ApplyResult{txnResult, didApply, metadata};
1728 if (replacedTxIter && tx)
1732 auto deleteIter =
byFee_.iterator_to((*replacedTxIter)->second);
1733 XRPL_ASSERT(deleteIter !=
byFee_.end(),
"xrpl::TxQ::removeFromByFee : found in byFee");
1735 &(*replacedTxIter)->second == &*deleteIter,
1736 "xrpl::TxQ::removeFromByFee : matching transaction");
1738 deleteIter->seqProxy == tx->getSeqProxy(),
1739 "xrpl::TxQ::removeFromByFee : matching sequence");
1741 deleteIter->account == (*tx)[sfAccount],
1742 "xrpl::TxQ::removeFromByFee : matching account");
1746 return std::nullopt;
1765 result.
medFeeLevel = snapshot.escalationMultiplier;
1774 auto const account = (*tx)[sfAccount];
1784 std::uint32_t const accountSeq = sle ? (*sle)[sfSequence] : 0;
1789 .accountSeq = accountSeq,
1790 .availableSeq = availableSeq};
1800 AccountMap::const_iterator
const accountIter{
byAccount_.find(account)};
1802 if (accountIter ==
byAccount_.end() || accountIter->second.transactions.empty())
1805 result.
reserve(accountIter->second.transactions.size());
1806 for (
auto const& tx : accountIter->second.transactions)
1822 for (
auto const& tx :
byFee_)
1834 BOOST_ASSERT(
false);
1844 ret[jss::ledger_current_index] = view->header().seq;
1856 auto const baseFee = view->fees().base;
1860 auto const effectiveBaseFee = [&baseFee, &
metrics]() {
1861 if (!baseFee &&
metrics.openLedgerFeeLevel !=
metrics.referenceFeeLevel)
1867 drops[jss::base_fee] =
to_string(baseFee);
1870 metrics.minProcessingFeeLevel,
1871 metrics.txCount >=
metrics.txQMaxSize ? effectiveBaseFee : baseFee));
1872 auto openFee =
toDrops(
metrics.openLedgerFeeLevel, effectiveBaseFee);
1873 if (effectiveBaseFee &&
toFeeLevel(openFee, effectiveBaseFee) <
metrics.openLedgerFeeLevel)
1875 drops[jss::open_ledger_fee] =
to_string(openFee);
1900 "The minimum number of low-fee transactions allowed "
1901 "per ledger (minimum_txn_in_ledger) exceeds "
1902 "the maximum number of low-fee transactions allowed per "
1903 "ledger (maximum_txn_in_ledger).");
1908 "The minimum number of low-fee transactions allowed "
1909 "per ledger (minimum_txn_in_ledger_standalone) exceeds "
1910 "the maximum number of low-fee transactions allowed per "
1911 "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
TxType getTxnType() const
A type that represents either a sequence value or a ticket value.
static constexpr SeqProxy rawSequence(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, SeqProxy const &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
bool isFeeSponsored(STTx const &tx)
Whether the transaction's fee is sponsored (sfSponsor present + spfSponsorFee set).
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,...