rippled
TxQ.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013, 2019 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
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 <algorithm>
30 #include <limits>
31 #include <numeric>
32 
33 namespace ripple {
34 
36 
37 static FeeLevel64
38 getFeeLevelPaid(ReadView const& view, STTx const& tx)
39 {
40  auto const [baseFee, effectiveFeePaid] = [&view, &tx]() {
41  XRPAmount baseFee = calculateBaseFee(view, tx);
42  XRPAmount feePaid = tx[sfFee].xrp();
43 
44  // If baseFee is 0 then the cost of a basic transaction is free, but we
45  // need the effective fee level to be non-zero.
46  XRPAmount const mod = [&view, &tx, baseFee]() {
47  if (baseFee.signum() > 0)
48  return XRPAmount{0};
49  auto def = calculateDefaultBaseFee(view, tx);
50  return def.signum() == 0 ? XRPAmount{1} : def;
51  }();
52  return std::pair{baseFee + mod, feePaid + mod};
53  }();
54 
55  assert(baseFee.signum() > 0);
56  if (effectiveFeePaid.signum() <= 0 || baseFee.signum() <= 0)
57  {
58  return FeeLevel64(0);
59  }
60 
61  return mulDiv(effectiveFeePaid, TxQ::baseLevel, baseFee)
63 }
64 
67 {
69  return std::nullopt;
71 }
72 
73 static FeeLevel64
74 increase(FeeLevel64 level, std::uint32_t increasePercent)
75 {
76  return mulDiv(level, 100 + increasePercent, 100)
77  .value_or(static_cast<FeeLevel64>(ripple::muldiv_max));
78 }
79 
81 
84  Application& app,
85  ReadView const& view,
86  bool timeLeap,
87  TxQ::Setup const& setup)
88 {
89  std::vector<FeeLevel64> feeLevels;
90  auto const txBegin = view.txs.begin();
91  auto const txEnd = view.txs.end();
92  auto const size = std::distance(txBegin, txEnd);
93  feeLevels.reserve(size);
94  std::for_each(txBegin, txEnd, [&](auto const& tx) {
95  feeLevels.push_back(getFeeLevelPaid(view, *tx.first));
96  });
97  std::sort(feeLevels.begin(), feeLevels.end());
98  assert(size == feeLevels.size());
99 
100  JLOG((timeLeap ? j_.warn() : j_.debug()))
101  << "Ledger " << view.info().seq << " has " << size << " transactions. "
102  << "Ledgers are processing " << (timeLeap ? "slowly" : "as expected")
103  << ". Expected transactions is currently " << txnsExpected_
104  << " and multiplier is " << escalationMultiplier_;
105 
106  if (timeLeap)
107  {
108  // Ledgers are taking to long to process,
109  // so clamp down on limits.
110  auto const cutPct = 100 - setup.slowConsensusDecreasePercent;
111  // upperLimit must be >= minimumTxnCount_ or std::clamp can give
112  // unexpected results
113  auto const upperLimit = std::max<std::uint64_t>(
114  mulDiv(txnsExpected_, cutPct, 100).value_or(ripple::muldiv_max),
116  txnsExpected_ = std::clamp<std::uint64_t>(
117  mulDiv(size, cutPct, 100).value_or(ripple::muldiv_max),
119  upperLimit);
120  recentTxnCounts_.clear();
121  }
122  else if (size > txnsExpected_ || size > targetTxnCount_)
123  {
124  recentTxnCounts_.push_back(
125  mulDiv(size, 100 + setup.normalConsensusIncreasePercent, 100)
126  .value_or(ripple::muldiv_max));
127  auto const iter =
129  BOOST_ASSERT(iter != recentTxnCounts_.end());
130  auto const next = [&] {
131  // Grow quickly: If the max_element is >= the
132  // current size limit, use it.
133  if (*iter >= txnsExpected_)
134  return *iter;
135  // Shrink slowly: If the max_element is < the
136  // current size limit, use a limit that is
137  // 90% of the way from max_element to the
138  // current size limit.
139  return (txnsExpected_ * 9 + *iter) / 10;
140  }();
141  // Ledgers are processing in a timely manner,
142  // so keep the limit high, but don't let it
143  // grow without bound.
145  }
146 
147  if (!size)
148  {
150  }
151  else
152  {
153  // In the case of an odd number of elements, this
154  // evaluates to the middle element; for an even
155  // number of elements, it will add the two elements
156  // on either side of the "middle" and average them.
158  (feeLevels[size / 2] + feeLevels[(size - 1) / 2] + FeeLevel64{1}) /
159  2;
162  }
163  JLOG(j_.debug()) << "Expected transactions updated to " << txnsExpected_
164  << " and multiplier updated to " << escalationMultiplier_;
165 
166  return size;
167 }
168 
170 TxQ::FeeMetrics::scaleFeeLevel(Snapshot const& snapshot, OpenView const& view)
171 {
172  // Transactions in the open ledger so far
173  auto const current = view.txCount();
174 
175  auto const target = snapshot.txnsExpected;
176  auto const multiplier = snapshot.escalationMultiplier;
177 
178  // Once the open ledger bypasses the target,
179  // escalate the fee quickly.
180  if (current > target)
181  {
182  // Compute escalated fee level
183  // Don't care about the overflow flag
184  return mulDiv(multiplier, current * current, target * target)
185  .value_or(static_cast<FeeLevel64>(ripple::muldiv_max));
186  }
187 
188  return baseLevel;
189 }
190 
191 namespace detail {
192 
193 constexpr static std::pair<bool, std::uint64_t>
195 {
196  // sum(n = 1->x) : n * n = x(x + 1)(2x + 1) / 6
197 
198  // We expect that size_t == std::uint64_t but, just in case, guarantee
199  // we lose no bits.
200  std::uint64_t x{xIn};
201 
202  // If x is anywhere on the order of 2^^21, it's going
203  // to completely dominate the computation and is likely
204  // enough to overflow that we're just going to assume
205  // it does. If we have anywhere near 2^^21 transactions
206  // in a ledger, this is the least of our problems.
207  if (x >= (1 << 21))
208  return {false, std::numeric_limits<std::uint64_t>::max()};
209  return {true, (x * (x + 1) * (2 * x + 1)) / 6};
210 }
211 
212 // Unit tests for sumOfSquares()
213 static_assert(sumOfFirstSquares(1).first == true);
214 static_assert(sumOfFirstSquares(1).second == 1);
215 
216 static_assert(sumOfFirstSquares(2).first == true);
217 static_assert(sumOfFirstSquares(2).second == 5);
218 
219 static_assert(sumOfFirstSquares(0x1FFFFF).first == true, "");
220 static_assert(sumOfFirstSquares(0x1FFFFF).second == 0x2AAAA8AAAAB00000ul, "");
221 
222 static_assert(sumOfFirstSquares(0x200000).first == false, "");
223 static_assert(
224  sumOfFirstSquares(0x200000).second ==
226  "");
227 
228 } // namespace detail
229 
232  Snapshot const& snapshot,
233  OpenView const& view,
234  std::size_t extraCount,
235  std::size_t seriesSize)
236 {
237  /* Transactions in the open ledger so far.
238  AKA Transactions that will be in the open ledger when
239  the first tx in the series is attempted.
240  */
241  auto const current = view.txCount() + extraCount;
242  /* Transactions that will be in the open ledger when
243  the last tx in the series is attempted.
244  */
245  auto const last = current + seriesSize - 1;
246 
247  auto const target = snapshot.txnsExpected;
248  auto const multiplier = snapshot.escalationMultiplier;
249 
250  assert(current > target);
251 
252  /* Calculate (apologies for the terrible notation)
253  sum(n = current -> last) : multiplier * n * n / (target * target)
254  multiplier / (target * target) * (sum(n = current -> last) : n * n)
255  multiplier / (target * target) * ((sum(n = 1 -> last) : n * n) -
256  (sum(n = 1 -> current - 1) : n * n))
257  */
258  auto const sumNlast = detail::sumOfFirstSquares(last);
259  auto const sumNcurrent = detail::sumOfFirstSquares(current - 1);
260  // because `last` is bigger, if either sum overflowed, then
261  // `sumNlast` definitely overflowed. Also the odds of this
262  // are nearly nil.
263  if (!sumNlast.first)
264  return {sumNlast.first, FeeLevel64{sumNlast.second}};
265  auto const totalFeeLevel = mulDiv(
266  multiplier, sumNlast.second - sumNcurrent.second, target * target);
267 
268  return {totalFeeLevel.has_value(), *totalFeeLevel};
269 }
270 
272 
274  std::shared_ptr<STTx const> const& txn_,
275  TxID const& txID_,
276  FeeLevel64 feeLevel_,
277  ApplyFlags const flags_,
278  PreflightResult const& pfresult_)
279  : txn(txn_)
280  , feeLevel(feeLevel_)
281  , txID(txID_)
282  , account(txn_->getAccountID(sfAccount))
283  , lastValid(getLastLedgerSequence(*txn_))
284  , seqProxy(txn_->getSeqProxy())
285  , retriesRemaining(retriesAllowed)
286  , flags(flags_)
287  , pfresult(pfresult_)
288 {
289 }
290 
293 {
294  // If the rules or flags change, preflight again
295  assert(pfresult);
296  STAmountSO stAmountSO{view.rules().enabled(fixSTAmountCanonicalize)};
297  NumberSO stNumberSO{view.rules().enabled(fixUniversalNumber)};
298 
299  if (pfresult->rules != view.rules() || pfresult->flags != flags)
300  {
301  JLOG(j.debug()) << "Queued transaction " << txID
302  << " rules or flags have changed. Flags from "
303  << pfresult->flags << " to " << flags;
304 
305  pfresult.emplace(
306  preflight(app, view.rules(), pfresult->tx, flags, pfresult->j));
307  }
308 
309  auto pcresult = preclaim(*pfresult, app, view);
310 
311  return doApply(pcresult, app, view);
312 }
313 
315  : TxQAccount(txn->getAccountID(sfAccount))
316 {
317 }
318 
319 TxQ::TxQAccount::TxQAccount(const AccountID& account_) : account(account_)
320 {
321 }
322 
323 TxQ::TxQAccount::TxMap::const_iterator
325 {
326  // Find the entry that is greater than or equal to the new transaction,
327  // then decrement the iterator.
328  auto sameOrPrevIter = transactions.lower_bound(seqProx);
329  if (sameOrPrevIter != transactions.begin())
330  --sameOrPrevIter;
331  return sameOrPrevIter;
332 }
333 
336 {
337  auto const seqProx = txn.seqProxy;
338 
339  auto result = transactions.emplace(seqProx, std::move(txn));
340  assert(result.second);
341  assert(&result.first->second != &txn);
342 
343  return result.first->second;
344 }
345 
346 bool
348 {
349  return transactions.erase(seqProx) != 0;
350 }
351 
353 
354 TxQ::TxQ(Setup const& setup, beast::Journal j)
355  : setup_(setup), j_(j), feeMetrics_(setup, j), maxSize_(std::nullopt)
356 {
357 }
358 
360 {
361  byFee_.clear();
362 }
363 
364 template <size_t fillPercentage>
365 bool
366 TxQ::isFull() const
367 {
368  static_assert(
369  fillPercentage > 0 && fillPercentage <= 100, "Invalid fill percentage");
370  return maxSize_ && byFee_.size() >= (*maxSize_ * fillPercentage / 100);
371 }
372 
373 TER
375  STTx const& tx,
376  ApplyFlags const flags,
377  OpenView const& view,
378  std::shared_ptr<SLE const> const& sleAccount,
379  AccountMap::iterator const& accountIter,
380  std::optional<TxQAccount::TxMap::iterator> const& replacementIter,
381  std::lock_guard<std::mutex> const& lock)
382 {
383  // PreviousTxnID is deprecated and should never be used.
384  // AccountTxnID is not supported by the transaction
385  // queue yet, but should be added in the future.
386  // tapFAIL_HARD transactions are never held
388  tx.isFieldPresent(sfAccountTxnID) || (flags & tapFAIL_HARD))
389  return telCAN_NOT_QUEUE;
390 
391  {
392  // To be queued and relayed, the transaction needs to
393  // promise to stick around for long enough that it has
394  // a realistic chance of getting into a ledger.
395  auto const lastValid = getLastLedgerSequence(tx);
396  if (lastValid &&
397  *lastValid < view.info().seq + setup_.minimumLastLedgerBuffer)
398  return telCAN_NOT_QUEUE;
399  }
400 
401  // Allow if the account is not in the queue at all.
402  if (accountIter == byAccount_.end())
403  return tesSUCCESS;
404 
405  // Allow this tx to replace another one.
406  if (replacementIter)
407  return tesSUCCESS;
408 
409  // Allow if there are fewer than the limit.
410  TxQAccount const& txQAcct = accountIter->second;
411  if (txQAcct.getTxnCount() < setup_.maximumTxnPerAccount)
412  return tesSUCCESS;
413 
414  // If we get here the queue limit is exceeded. Only allow if this
415  // transaction fills the _first_ sequence hole for the account.
416  auto const txSeqProx = tx.getSeqProxy();
417  if (txSeqProx.isTicket())
418  // Tickets always follow sequence-based transactions, so a ticket
419  // cannot unblock a sequence-based transaction.
420  return telCAN_NOT_QUEUE_FULL;
421 
422  // This is the next queuable sequence-based SeqProxy for the account.
423  SeqProxy const nextQueuable = nextQueuableSeqImpl(sleAccount, lock);
424  if (txSeqProx != nextQueuable)
425  // The provided transaction does not fill the next open sequence gap.
426  return telCAN_NOT_QUEUE_FULL;
427 
428  // Make sure they are not just topping off the account's queued
429  // sequence-based transactions.
430  if (auto const nextTxIter = txQAcct.transactions.upper_bound(nextQueuable);
431  nextTxIter != txQAcct.transactions.end() && nextTxIter->first.isSeq())
432  // There is a next transaction and it is sequence based. They are
433  // filling a real gap. Allow it.
434  return tesSUCCESS;
435 
436  return telCAN_NOT_QUEUE_FULL;
437 }
438 
439 auto
440 TxQ::erase(TxQ::FeeMultiSet::const_iterator_type candidateIter)
441  -> FeeMultiSet::iterator_type
442 {
443  auto& txQAccount = byAccount_.at(candidateIter->account);
444  auto const seqProx = candidateIter->seqProxy;
445  auto const newCandidateIter = byFee_.erase(candidateIter);
446  // Now that the candidate has been removed from the
447  // intrusive list remove it from the TxQAccount
448  // so the memory can be freed.
449  auto const found = txQAccount.remove(seqProx);
450  (void)found;
451  assert(found);
452 
453  return newCandidateIter;
454 }
455 
456 auto
457 TxQ::eraseAndAdvance(TxQ::FeeMultiSet::const_iterator_type candidateIter)
458  -> FeeMultiSet::iterator_type
459 {
460  auto& txQAccount = byAccount_.at(candidateIter->account);
461  auto const accountIter =
462  txQAccount.transactions.find(candidateIter->seqProxy);
463  assert(accountIter != txQAccount.transactions.end());
464 
465  // Note that sequence-based transactions must be applied in sequence order
466  // from smallest to largest. But ticket-based transactions can be
467  // applied in any order.
468  assert(
469  candidateIter->seqProxy.isTicket() ||
470  accountIter == txQAccount.transactions.begin());
471  assert(byFee_.iterator_to(accountIter->second) == candidateIter);
472  auto const accountNextIter = std::next(accountIter);
473 
474  // Check if the next transaction for this account is earlier in the queue,
475  // which means we skipped it earlier, and need to try it again.
476  auto const feeNextIter = std::next(candidateIter);
477  bool const useAccountNext =
478  accountNextIter != txQAccount.transactions.end() &&
479  accountNextIter->first > candidateIter->seqProxy &&
480  (feeNextIter == byFee_.end() ||
481  byFee_.value_comp()(accountNextIter->second, *feeNextIter));
482 
483  auto const candidateNextIter = byFee_.erase(candidateIter);
484  txQAccount.transactions.erase(accountIter);
485 
486  return useAccountNext ? byFee_.iterator_to(accountNextIter->second)
487  : candidateNextIter;
488 }
489 
490 auto
491 TxQ::erase(
492  TxQ::TxQAccount& txQAccount,
493  TxQ::TxQAccount::TxMap::const_iterator begin,
494  TxQ::TxQAccount::TxMap::const_iterator end) -> TxQAccount::TxMap::iterator
495 {
496  for (auto it = begin; it != end; ++it)
497  {
498  byFee_.erase(byFee_.iterator_to(it->second));
499  }
500  return txQAccount.transactions.erase(begin, end);
501 }
502 
505  Application& app,
506  OpenView& view,
507  STTx const& tx,
508  TxQ::AccountMap::iterator const& accountIter,
509  TxQAccount::TxMap::iterator beginTxIter,
510  FeeLevel64 feeLevelPaid,
511  PreflightResult const& pfresult,
512  std::size_t const txExtraCount,
513  ApplyFlags flags,
514  FeeMetrics::Snapshot const& metricsSnapshot,
515  beast::Journal j)
516 {
517  SeqProxy const tSeqProx{tx.getSeqProxy()};
518  assert(beginTxIter != accountIter->second.transactions.end());
519 
520  // This check is only concerned with the range from
521  // [aSeqProxy, tSeqProxy)
522  auto endTxIter = accountIter->second.transactions.lower_bound(tSeqProx);
523  auto const dist = std::distance(beginTxIter, endTxIter);
524 
525  auto const requiredTotalFeeLevel = FeeMetrics::escalatedSeriesFeeLevel(
526  metricsSnapshot, view, txExtraCount, dist + 1);
527  // If the computation for the total manages to overflow (however extremely
528  // unlikely), then there's no way we can confidently verify if the queue
529  // can be cleared.
530  if (!requiredTotalFeeLevel.first)
531  return {telINSUF_FEE_P, false};
532 
533  auto const totalFeeLevelPaid = std::accumulate(
534  beginTxIter,
535  endTxIter,
536  feeLevelPaid,
537  [](auto const& total, auto const& txn) {
538  return total + txn.second.feeLevel;
539  });
540 
541  // This transaction did not pay enough, so fall back to the normal process.
542  if (totalFeeLevelPaid < requiredTotalFeeLevel.second)
543  return {telINSUF_FEE_P, false};
544 
545  // This transaction paid enough to clear out the queue.
546  // Attempt to apply the queued transactions.
547  for (auto it = beginTxIter; it != endTxIter; ++it)
548  {
549  auto txResult = it->second.apply(app, view, j);
550  // Succeed or fail, use up a retry, because if the overall
551  // process fails, we want the attempt to count. If it all
552  // succeeds, the MaybeTx will be destructed, so it'll be
553  // moot.
554  --it->second.retriesRemaining;
555  it->second.lastResult = txResult.first;
556 
557  // In TxQ::apply we note that it's possible for a transaction with
558  // a ticket to both be in the queue and in the ledger. And, while
559  // we're in TxQ::apply, it's too expensive to filter those out.
560  //
561  // So here in tryClearAccountQueueUpThruTx we just received a batch of
562  // queued transactions. And occasionally one of those is a ticketed
563  // transaction that is both in the queue and in the ledger. When
564  // that happens the queued transaction returns tefNO_TICKET.
565  //
566  // The transaction that returned tefNO_TICKET can never succeed
567  // and we'd like to get it out of the queue as soon as possible.
568  // The easiest way to do that from here is to treat the transaction
569  // as though it succeeded and attempt to clear the remaining
570  // transactions in the account queue. Then, if clearing the account
571  // is successful, we will have removed any ticketed transactions
572  // that can never succeed.
573  if (txResult.first == tefNO_TICKET)
574  continue;
575 
576  if (!txResult.second)
577  {
578  // Transaction failed to apply. Fall back to the normal process.
579  return {txResult.first, false};
580  }
581  }
582  // Apply the current tx. Because the state of the view has been changed
583  // by the queued txs, we also need to preclaim again.
584  auto const txResult = doApply(preclaim(pfresult, app, view), app, view);
585 
586  if (txResult.second)
587  {
588  // All of the queued transactions applied, so remove them from the
589  // queue.
590  endTxIter = erase(accountIter->second, beginTxIter, endTxIter);
591  // If `tx` is replacing a queued tx, delete that one, too.
592  if (endTxIter != accountIter->second.transactions.end() &&
593  endTxIter->first == tSeqProx)
594  erase(accountIter->second, endTxIter, std::next(endTxIter));
595  }
596 
597  return txResult;
598 }
599 
600 // Overview of considerations for when a transaction is accepted into the TxQ:
601 //
602 // These rules apply to the transactions in the queue owned by a single
603 // account. Briefly, the primary considerations are:
604 //
605 // 1. Is the new transaction blocking?
606 // 2. Is there an expiration gap in the account's sequence-based transactions?
607 // 3. Does the new transaction replace one that is already in the TxQ?
608 // 4. Is the transaction's sequence or ticket value acceptable for this account?
609 // 5. Is the transaction likely to claim a fee?
610 // 6. Is the queue full?
611 //
612 // Here are more details.
613 //
614 // 1. A blocking transaction is one that would change the validity of following
615 // transactions for the issuing account. Examples of blocking transactions
616 // include SetRegularKey and SignerListSet.
617 //
618 // A blocking transaction can only be added to the queue for an account if:
619 //
620 // a. The queue for that account is empty, or
621 //
622 // b. The blocking transaction replaces the only transaction in the
623 // account's queue.
624 //
625 // While a blocker is in the account's queue no additional transactions
626 // can be added to the queue.
627 //
628 // As a consequence, any blocker is always alone in the account's queue.
629 //
630 // 2. Transactions are given unique identifiers using either Sequence numbers
631 // or Tickets. In general, sequence numbers in the queue are expected to
632 // start with the account root sequence and increment from there. There
633 // are two exceptions:
634 //
635 // a. Sequence holes left by ticket creation. If a transaction creates
636 // more than one ticket, then the account sequence number will jump
637 // by the number of tickets created. These holes are fine.
638 //
639 // b. Sequence gaps left by transaction expiration. If transactions stay
640 // in the queue long enough they may expire. If that happens it leaves
641 // gaps in the sequence numbers held by the queue. These gaps are
642 // important because, if left in place, they will block any later
643 // sequence-based transactions in the queue from working. Remember,
644 // for any given account sequence numbers must be used consecutively
645 // (with the exception of ticket-induced holes).
646 //
647 // 3. Transactions in the queue may be replaced. If a transaction in the
648 // queue has the same SeqProxy as the incoming transaction, then the
649 // transaction in the queue will be replaced if the following conditions
650 // are met:
651 //
652 // a. The replacement must provide a fee that is at least 1.25 times the
653 // fee of the transaction it is replacing.
654 //
655 // b. If the transaction being replaced has a sequence number, then
656 // the transaction may not be after any expiration-based sequence
657 // gaps in the account's queue.
658 //
659 // c. A replacement that is a blocker is only allowed if the transaction
660 // it replaces is the only transaction in the account's queue.
661 //
662 // 4. The transaction that is not a replacement must have an acceptable
663 // sequence or ticket ID:
664 //
665 // Sequence: For a given account's queue configuration there is at most
666 // one sequence number that is acceptable to the queue for that account.
667 // The rules are:
668 //
669 // a. If there are no sequence-based transactions in the queue and the
670 // candidate transaction has a sequence number, that value must match
671 // the account root's sequence.
672 //
673 // b. If there are sequence-based transactions in the queue for that
674 // account and there are no expiration-based gaps, then the candidate's
675 // sequence number must belong at the end of the list of sequences.
676 //
677 // c. If there are expiration-based gaps in the sequence-based
678 // transactions in the account's queue, then the candidate's sequence
679 // value must go precisely at the front of the first gap.
680 //
681 // Ticket: If there are no blockers or sequence gaps in the account's
682 // queue, then there are many tickets that are acceptable to the queue
683 // for that account. The rules are:
684 //
685 // a. If there are no blockers in the account's queue and the ticket
686 // required by the transaction is in the ledger then the transaction
687 // may be added to the account's queue.
688 //
689 // b. If there is a ticket-based blocker in the account's queue then
690 // that blocker can be replaced.
691 //
692 // Note that it is not sufficient for the transaction that would create
693 // the necessary ticket to be in the account's queue. The required ticket
694 // must already be in the ledger. This avoids problems that can occur if
695 // a ticket-creating transaction enters the queue but expires out of the
696 // queue before its tickets are created.
697 //
698 // 5. The transaction must be likely to claim a fee. In general that is
699 // checked by having preclaim return a tes or tec code.
700 //
701 // Extra work is done here to account for funds that other transactions
702 // in the queue remove from the account.
703 //
704 // 6. The queue must not be full.
705 //
706 // a. Each account can queue up to a maximum of 10 transactions. Beyond
707 // that transactions are rejected. There is an exception for this case
708 // when filling expiration-based sequence gaps.
709 //
710 // b. The entire queue also has a (dynamic) maximum size. Transactions
711 // beyond that limit are rejected.
712 //
715  Application& app,
716  OpenView& view,
717  std::shared_ptr<STTx const> const& tx,
718  ApplyFlags flags,
719  beast::Journal j)
720 {
721  STAmountSO stAmountSO{view.rules().enabled(fixSTAmountCanonicalize)};
722  NumberSO stNumberSO{view.rules().enabled(fixUniversalNumber)};
723 
724  // See if the transaction paid a high enough fee that it can go straight
725  // into the ledger.
726  if (auto directApplied = tryDirectApply(app, view, tx, flags, j))
727  return *directApplied;
728 
729  // If we get past tryDirectApply() without returning then we expect
730  // one of the following to occur:
731  //
732  // o We will decide the transaction is unlikely to claim a fee.
733  // o The transaction paid a high enough fee that fee averaging will apply.
734  // o The transaction will be queued.
735 
736  // See if the transaction is valid, properly formed,
737  // etc. before doing potentially expensive queue
738  // replace and multi-transaction operations.
739  auto const pfresult = preflight(app, view.rules(), *tx, flags, j);
740  if (pfresult.ter != tesSUCCESS)
741  return {pfresult.ter, false};
742 
743  // If the account is not currently in the ledger, don't queue its tx.
744  auto const account = (*tx)[sfAccount];
745  Keylet const accountKey{keylet::account(account)};
746  auto const sleAccount = view.read(accountKey);
747  if (!sleAccount)
748  return {terNO_ACCOUNT, false};
749 
750  // If the transaction needs a Ticket is that Ticket in the ledger?
751  SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
752  SeqProxy const txSeqProx = tx->getSeqProxy();
753  if (txSeqProx.isTicket() &&
754  !view.exists(keylet::ticket(account, txSeqProx)))
755  {
756  if (txSeqProx.value() < acctSeqProx.value())
757  // The ticket number is low enough that it should already be
758  // in the ledger if it were ever going to exist.
759  return {tefNO_TICKET, false};
760 
761  // We don't queue transactions that use Tickets unless
762  // we can find the Ticket in the ledger.
763  return {terPRE_TICKET, false};
764  }
765 
766  std::lock_guard lock(mutex_);
767 
768  // accountIter is not const because it may be updated further down.
769  AccountMap::iterator accountIter = byAccount_.find(account);
770  bool const accountIsInQueue = accountIter != byAccount_.end();
771 
772  // _If_ the account is in the queue, then ignore any sequence-based
773  // queued transactions that slipped into the ledger while we were not
774  // watching. This does actually happen in the wild, but it's uncommon.
775  //
776  // Note that we _don't_ ignore queued ticket-based transactions that
777  // slipped into the ledger while we were not watching. It would be
778  // desirable to do so, but the measured cost was too high since we have
779  // to individually check each queued ticket against the ledger.
780  struct TxIter
781  {
782  TxIter(
783  TxQAccount::TxMap::iterator first_,
784  TxQAccount::TxMap::iterator end_)
785  : first(first_), end(end_)
786  {
787  }
788 
789  TxQAccount::TxMap::iterator first;
790  TxQAccount::TxMap::iterator end;
791  };
792 
793  std::optional<TxIter> const txIter =
794  [accountIter,
795  accountIsInQueue,
796  acctSeqProx]() -> std::optional<TxIter> {
797  if (!accountIsInQueue)
798  return {};
799 
800  // Find the first transaction in the queue that we might apply.
801  TxQAccount::TxMap& acctTxs = accountIter->second.transactions;
802  TxQAccount::TxMap::iterator const firstIter =
803  acctTxs.lower_bound(acctSeqProx);
804 
805  if (firstIter == acctTxs.end())
806  // Even though there may be transactions in the queue, there are
807  // none that we should pay attention to.
808  return {};
809 
810  return {TxIter{firstIter, acctTxs.end()}};
811  }();
812 
813  auto const acctTxCount{
814  !txIter ? 0 : std::distance(txIter->first, txIter->end)};
815 
816  // Is tx a blocker? If so there are very limited conditions when it
817  // is allowed in the TxQ:
818  // 1. If the account's queue is empty or
819  // 2. If the blocker replaces the only entry in the account's queue.
820  auto const transactionID = tx->getTransactionID();
821  if (pfresult.consequences.isBlocker())
822  {
823  if (acctTxCount > 1)
824  {
825  // A blocker may not be co-resident with other transactions in
826  // the account's queue.
827  JLOG(j_.trace())
828  << "Rejecting blocker transaction " << transactionID
829  << ". Account has other queued transactions.";
830  return {telCAN_NOT_QUEUE_BLOCKS, false};
831  }
832  if (acctTxCount == 1 && (txSeqProx != txIter->first->first))
833  {
834  // The blocker is not replacing the lone queued transaction.
835  JLOG(j_.trace())
836  << "Rejecting blocker transaction " << transactionID
837  << ". Blocker does not replace lone queued transaction.";
838  return {telCAN_NOT_QUEUE_BLOCKS, false};
839  }
840  }
841 
842  // If the transaction is intending to replace a transaction in the queue
843  // identify the one that might be replaced.
844  auto replacedTxIter = [accountIsInQueue, &accountIter, txSeqProx]()
846  if (accountIsInQueue)
847  {
848  TxQAccount& txQAcct = accountIter->second;
849  if (auto const existingIter = txQAcct.transactions.find(txSeqProx);
850  existingIter != txQAcct.transactions.end())
851  return existingIter;
852  }
853  return {};
854  }();
855 
856  // We may need the base fee for multiple transactions or transaction
857  // replacement, so just pull it up now.
858  auto const metricsSnapshot = feeMetrics_.getSnapshot();
859  auto const feeLevelPaid = getFeeLevelPaid(view, *tx);
860  auto const requiredFeeLevel =
861  getRequiredFeeLevel(view, flags, metricsSnapshot, lock);
862 
863  // Is there a blocker already in the account's queue? If so, don't
864  // allow additional transactions in the queue.
865  if (acctTxCount > 0)
866  {
867  // Allow tx to replace a blocker. Otherwise, if there's a
868  // blocker, we can't queue tx.
869  //
870  // We only need to check if txIter->first is a blocker because we
871  // require that a blocker be alone in the account's queue.
872  if (acctTxCount == 1 &&
873  txIter->first->second.consequences().isBlocker() &&
874  (txIter->first->first != txSeqProx))
875  {
876  return {telCAN_NOT_QUEUE_BLOCKED, false};
877  }
878 
879  // Is there a transaction for the same account with the same
880  // SeqProxy already in the queue? If so we may replace the
881  // existing entry with this new transaction.
882  if (replacedTxIter)
883  {
884  // We are attempting to replace a transaction in the queue.
885  //
886  // Is the current transaction's fee higher than
887  // the queued transaction's fee + a percentage
888  TxQAccount::TxMap::iterator const& existingIter = *replacedTxIter;
889  auto requiredRetryLevel = increase(
890  existingIter->second.feeLevel, setup_.retrySequencePercent);
891  JLOG(j_.trace())
892  << "Found transaction in queue for account " << account
893  << " with " << txSeqProx << " new txn fee level is "
894  << feeLevelPaid << ", old txn fee level is "
895  << existingIter->second.feeLevel
896  << ", new txn needs fee level of " << requiredRetryLevel;
897  if (feeLevelPaid > requiredRetryLevel)
898  {
899  // Continue, leaving the queued transaction marked for removal.
900  // DO NOT REMOVE if the new tx fails, because there may
901  // be other txs dependent on it in the queue.
902  JLOG(j_.trace()) << "Removing transaction from queue "
903  << existingIter->second.txID << " in favor of "
904  << transactionID;
905  }
906  else
907  {
908  // Drop the current transaction
909  JLOG(j_.trace())
910  << "Ignoring transaction " << transactionID
911  << " in favor of queued " << existingIter->second.txID;
912  return {telCAN_NOT_QUEUE_FEE, false};
913  }
914  }
915  }
916 
917  struct MultiTxn
918  {
919  ApplyViewImpl applyView;
920  OpenView openView;
921 
922  MultiTxn(OpenView& view, ApplyFlags flags)
923  : applyView(&view, flags), openView(&applyView)
924  {
925  }
926  };
927 
928  std::optional<MultiTxn> multiTxn;
929 
930  if (acctTxCount == 0)
931  {
932  // There are no queued transactions for this account. If the
933  // transaction has a sequence make sure it's valid (tickets
934  // are checked elsewhere).
935  if (txSeqProx.isSeq())
936  {
937  if (acctSeqProx > txSeqProx)
938  return {tefPAST_SEQ, false};
939  if (acctSeqProx < txSeqProx)
940  return {terPRE_SEQ, false};
941  }
942  }
943  else
944  {
945  // There are probably other transactions in the queue for this
946  // account. Make sure the new transaction can work with the others
947  // in the queue.
948  TxQAccount const& txQAcct = accountIter->second;
949 
950  if (acctSeqProx > txSeqProx)
951  return {tefPAST_SEQ, false};
952 
953  // Determine if we need a multiTxn object. Assuming the account
954  // is in the queue, there are two situations where we need to
955  // build multiTx:
956  // 1. If there are two or more transactions in the account's queue, or
957  // 2. If the account has a single queue entry, we may still need
958  // multiTxn, but only if that lone entry will not be replaced by tx.
959  bool requiresMultiTxn = false;
960  if (acctTxCount > 1 || !replacedTxIter)
961  {
962  // If the transaction is queueable, create the multiTxn
963  // object to hold the info we need to adjust for prior txns.
964  TER const ter{canBeHeld(
965  *tx,
966  flags,
967  view,
968  sleAccount,
969  accountIter,
970  replacedTxIter,
971  lock)};
972  if (!isTesSuccess(ter))
973  return {ter, false};
974 
975  requiresMultiTxn = true;
976  }
977 
978  if (requiresMultiTxn)
979  {
980  // See if adding this entry to the queue makes sense.
981  //
982  // o Transactions with sequences should start with the
983  // account's Sequence.
984  //
985  // o Additional transactions with Sequences should
986  // follow preceding sequence-based transactions with no
987  // gaps (except for those required by CreateTicket
988  // transactions).
989 
990  // Find the entry in the queue that precedes the new
991  // transaction, if one does.
992  TxQAccount::TxMap::const_iterator const prevIter =
993  txQAcct.getPrevTx(txSeqProx);
994 
995  // Does the new transaction go to the front of the queue?
996  // This can happen if:
997  // o A transaction in the queue with a Sequence expired, or
998  // o The current first thing in the queue has a Ticket and
999  // * The tx has a Ticket that precedes it or
1000  // * txSeqProx == acctSeqProx.
1001  assert(prevIter != txIter->end);
1002  if (prevIter == txIter->end || txSeqProx < prevIter->first)
1003  {
1004  // The first Sequence number in the queue must be the
1005  // account's sequence.
1006  if (txSeqProx.isSeq())
1007  {
1008  if (txSeqProx < acctSeqProx)
1009  return {tefPAST_SEQ, false};
1010  else if (txSeqProx > acctSeqProx)
1011  return {terPRE_SEQ, false};
1012  }
1013  }
1014  else if (!replacedTxIter)
1015  {
1016  // The current transaction is not replacing a transaction
1017  // in the queue. So apparently there's a transaction in
1018  // front of this one in the queue. Make sure the current
1019  // transaction fits in proper sequence order with the
1020  // previous transaction or is a ticket.
1021  if (txSeqProx.isSeq() &&
1022  nextQueuableSeqImpl(sleAccount, lock) != txSeqProx)
1023  return {telCAN_NOT_QUEUE, false};
1024  }
1025 
1026  // Sum fees and spending for all of the queued transactions
1027  // so we know how much to remove from the account balance
1028  // for the trial preclaim.
1029  XRPAmount potentialSpend = beast::zero;
1030  XRPAmount totalFee = beast::zero;
1031  for (auto iter = txIter->first; iter != txIter->end; ++iter)
1032  {
1033  // If we're replacing this transaction don't include
1034  // the replaced transaction's XRP spend. Otherwise add
1035  // it to potentialSpend.
1036  if (iter->first != txSeqProx)
1037  {
1038  totalFee += iter->second.consequences().fee();
1039  potentialSpend +=
1040  iter->second.consequences().potentialSpend();
1041  }
1042  else if (std::next(iter) != txIter->end)
1043  {
1044  // The fee for the candidate transaction _should_ be
1045  // counted if it's replacing a transaction in the middle
1046  // of the queue.
1047  totalFee += pfresult.consequences.fee();
1048  potentialSpend += pfresult.consequences.potentialSpend();
1049  }
1050  }
1051 
1052  /* Check if the total fees in flight are greater
1053  than the account's current balance, or the
1054  minimum reserve. If it is, then there's a risk
1055  that the fees won't get paid, so drop this
1056  transaction with a telCAN_NOT_QUEUE_BALANCE result.
1057  Assume: Minimum account reserve is 20 XRP.
1058  Example 1: If I have 1,000,000 XRP, I can queue
1059  a transaction with a 1,000,000 XRP fee. In
1060  the meantime, some other transaction may
1061  lower my balance (eg. taking an offer). When
1062  the transaction executes, I will either
1063  spend the 1,000,000 XRP, or the transaction
1064  will get stuck in the queue with a
1065  `terINSUF_FEE_B`.
1066  Example 2: If I have 1,000,000 XRP, and I queue
1067  10 transactions with 0.1 XRP fee, I have 1 XRP
1068  in flight. I can now queue another tx with a
1069  999,999 XRP fee. When the first 10 execute,
1070  they're guaranteed to pay their fee, because
1071  nothing can eat into my reserve. The last
1072  transaction, again, will either spend the
1073  999,999 XRP, or get stuck in the queue.
1074  Example 3: If I have 1,000,000 XRP, and I queue
1075  7 transactions with 3 XRP fee, I have 21 XRP
1076  in flight. I can not queue any more transactions,
1077  no matter how small or large the fee.
1078  Transactions stuck in the queue are mitigated by
1079  LastLedgerSeq and MaybeTx::retriesRemaining.
1080  */
1081  auto const balance = (*sleAccount)[sfBalance].xrp();
1082  /* Get the minimum possible account reserve. If it
1083  is at least 10 * the base fee, and fees exceed
1084  this amount, the transaction can't be queued.
1085 
1086  Currently typical fees are several orders
1087  of magnitude smaller than any current or expected
1088  future reserve. This calculation is simpler than
1089  trying to figure out the potential changes to
1090  the ownerCount that may occur to the account
1091  as a result of these transactions, and removes
1092  any need to account for other transactions that
1093  may affect the owner count while these are queued.
1094 
1095  However, in case the account reserve is on a
1096  comparable scale to the base fee, ignore the
1097  reserve. Only check the account balance.
1098  */
1099  auto const reserve = view.fees().accountReserve(0);
1100  auto const base = view.fees().base;
1101  if (totalFee >= balance ||
1102  (reserve > 10 * base && totalFee >= reserve))
1103  {
1104  // Drop the current transaction
1105  JLOG(j_.trace()) << "Ignoring transaction " << transactionID
1106  << ". Total fees in flight too high.";
1107  return {telCAN_NOT_QUEUE_BALANCE, false};
1108  }
1109 
1110  // Create the test view from the current view.
1111  multiTxn.emplace(view, flags);
1112 
1113  auto const sleBump = multiTxn->applyView.peek(accountKey);
1114  if (!sleBump)
1115  return {tefINTERNAL, false};
1116 
1117  // Subtract the fees and XRP spend from all of the other
1118  // transactions in the queue. That prevents a transaction
1119  // inserted in the middle from fouling up later transactions.
1120  auto const potentialTotalSpend = totalFee +
1121  std::min(balance - std::min(balance, reserve), potentialSpend);
1122  assert(
1123  potentialTotalSpend > XRPAmount{0} ||
1124  (potentialTotalSpend == XRPAmount{0} &&
1125  multiTxn->applyView.fees().base == 0));
1126  sleBump->setFieldAmount(sfBalance, balance - potentialTotalSpend);
1127  // The transaction's sequence/ticket will be valid when the other
1128  // transactions in the queue have been processed. If the tx has a
1129  // sequence, set the account to match it. If it has a ticket, use
1130  // the next queueable sequence, which is the closest approximation
1131  // to the most successful case.
1132  sleBump->at(sfSequence) = txSeqProx.isSeq()
1133  ? txSeqProx.value()
1134  : nextQueuableSeqImpl(sleAccount, lock).value();
1135  }
1136  }
1137 
1138  // See if the transaction is likely to claim a fee.
1139  //
1140  // We assume that if the transaction survives preclaim(), then it
1141  // is likely to claim a fee. However we can't allow preclaim to
1142  // check the sequence/ticket. Transactions in the queue may be
1143  // responsible for increasing the sequence, and mocking those up
1144  // is non-trivially expensive.
1145  //
1146  // Note that earlier code has already verified that the sequence/ticket
1147  // is valid. So we use a special entry point that runs all of the
1148  // preclaim checks with the exception of the sequence check.
1149  auto const pcresult =
1150  preclaim(pfresult, app, multiTxn ? multiTxn->openView : view);
1151  if (!pcresult.likelyToClaimFee)
1152  return {pcresult.ter, false};
1153 
1154  // Too low of a fee should get caught by preclaim
1155  assert(feeLevelPaid >= baseLevel);
1156 
1157  JLOG(j_.trace()) << "Transaction " << transactionID << " from account "
1158  << account << " has fee level of " << feeLevelPaid
1159  << " needs at least " << requiredFeeLevel
1160  << " to get in the open ledger, which has "
1161  << view.txCount() << " entries.";
1162 
1163  /* Quick heuristic check to see if it's worth checking that this tx has
1164  a high enough fee to clear all the txs in front of it in the queue.
1165  1) Transaction is trying to get into the open ledger.
1166  2) Transaction must be Sequence-based.
1167  3) Must be an account already in the queue.
1168  4) Must be have passed the multiTxn checks (tx is not the next
1169  account seq, the skipped seqs are in the queue, the reserve
1170  doesn't get exhausted, etc).
1171  5) The next transaction must not have previously tried and failed
1172  to apply to an open ledger.
1173  6) Tx must be paying more than just the required fee level to
1174  get itself into the queue.
1175  7) Fee level must be escalated above the default (if it's not,
1176  then the first tx _must_ have failed to process in `accept`
1177  for some other reason. Tx is allowed to queue in case
1178  conditions change, but don't waste the effort to clear).
1179  */
1180  if (txSeqProx.isSeq() && txIter && multiTxn.has_value() &&
1181  txIter->first->second.retriesRemaining == MaybeTx::retriesAllowed &&
1182  feeLevelPaid > requiredFeeLevel && requiredFeeLevel > baseLevel)
1183  {
1184  OpenView sandbox(open_ledger, &view, view.rules());
1185 
1186  auto result = tryClearAccountQueueUpThruTx(
1187  app,
1188  sandbox,
1189  *tx,
1190  accountIter,
1191  txIter->first,
1192  feeLevelPaid,
1193  pfresult,
1194  view.txCount(),
1195  flags,
1196  metricsSnapshot,
1197  j);
1198  if (result.second)
1199  {
1200  sandbox.apply(view);
1201  /* Can't erase (*replacedTxIter) here because success
1202  implies that it has already been deleted.
1203  */
1204  return result;
1205  }
1206  }
1207 
1208  // If `multiTxn` has a value, then `canBeHeld` has already been verified
1209  if (!multiTxn)
1210  {
1211  TER const ter{canBeHeld(
1212  *tx, flags, view, sleAccount, accountIter, replacedTxIter, lock)};
1213  if (!isTesSuccess(ter))
1214  {
1215  // Bail, transaction cannot be held
1216  JLOG(j_.trace())
1217  << "Transaction " << transactionID << " cannot be held";
1218  return {ter, false};
1219  }
1220  }
1221 
1222  // If the queue is full, decide whether to drop the current
1223  // transaction or the last transaction for the account with
1224  // the lowest fee.
1225  if (!replacedTxIter && isFull())
1226  {
1227  auto lastRIter = byFee_.rbegin();
1228  while (lastRIter != byFee_.rend() && lastRIter->account == account)
1229  {
1230  ++lastRIter;
1231  }
1232  if (lastRIter == byFee_.rend())
1233  {
1234  // The only way this condition can happen is if the entire
1235  // queue is filled with transactions from this account. This
1236  // is impossible with default settings - minimum queue size
1237  // is 2000, and an account can only have 10 transactions
1238  // queued. However, it can occur if settings are changed,
1239  // and there is unit test coverage.
1240  JLOG(j_.info())
1241  << "Queue is full, and transaction " << transactionID
1242  << " would kick a transaction from the same account ("
1243  << account << ") out of the queue.";
1244  return {telCAN_NOT_QUEUE_FULL, false};
1245  }
1246  auto const& endAccount = byAccount_.at(lastRIter->account);
1247  auto endEffectiveFeeLevel = [&]() {
1248  // Compute the average of all the txs for the endAccount,
1249  // but only if the last tx in the queue has a lower fee
1250  // level than this candidate tx.
1251  if (lastRIter->feeLevel > feeLevelPaid ||
1252  endAccount.transactions.size() == 1)
1253  return lastRIter->feeLevel;
1254 
1256  auto endTotal = std::accumulate(
1257  endAccount.transactions.begin(),
1258  endAccount.transactions.end(),
1260  [&](auto const& total,
1261  auto const& txn) -> std::pair<FeeLevel64, FeeLevel64> {
1262  // Check for overflow.
1263  auto next =
1264  txn.second.feeLevel / endAccount.transactions.size();
1265  auto mod =
1266  txn.second.feeLevel % endAccount.transactions.size();
1267  if (total.first >= max - next || total.second >= max - mod)
1268  return {max, FeeLevel64{0}};
1269 
1270  return {total.first + next, total.second + mod};
1271  });
1272  return endTotal.first +
1273  endTotal.second / endAccount.transactions.size();
1274  }();
1275  if (feeLevelPaid > endEffectiveFeeLevel)
1276  {
1277  // The queue is full, and this transaction is more
1278  // valuable, so kick out the cheapest transaction.
1279  auto dropRIter = endAccount.transactions.rbegin();
1280  assert(dropRIter->second.account == lastRIter->account);
1281  JLOG(j_.info())
1282  << "Removing last item of account " << lastRIter->account
1283  << " from queue with average fee of " << endEffectiveFeeLevel
1284  << " in favor of " << transactionID << " with fee of "
1285  << feeLevelPaid;
1286  erase(byFee_.iterator_to(dropRIter->second));
1287  }
1288  else
1289  {
1290  JLOG(j_.info())
1291  << "Queue is full, and transaction " << transactionID
1292  << " fee is lower than end item's account average fee";
1293  return {telCAN_NOT_QUEUE_FULL, false};
1294  }
1295  }
1296 
1297  // Hold the transaction in the queue.
1298  if (replacedTxIter)
1299  {
1300  replacedTxIter = removeFromByFee(replacedTxIter, tx);
1301  }
1302 
1303  if (!accountIsInQueue)
1304  {
1305  // Create a new TxQAccount object and add the byAccount lookup.
1306  bool created;
1307  std::tie(accountIter, created) =
1308  byAccount_.emplace(account, TxQAccount(tx));
1309  (void)created;
1310  assert(created);
1311  }
1312  // Modify the flags for use when coming out of the queue.
1313  // These changes _may_ cause an extra `preflight`, but as long as
1314  // the `HashRouter` still knows about the transaction, the signature
1315  // will not be checked again, so the cost should be minimal.
1316 
1317  // Don't allow soft failures, which can lead to retries
1318  flags &= ~tapRETRY;
1319 
1320  auto& candidate = accountIter->second.add(
1321  {tx, transactionID, feeLevelPaid, flags, pfresult});
1322 
1323  // Then index it into the byFee lookup.
1324  byFee_.insert(candidate);
1325  JLOG(j_.debug()) << "Added transaction " << candidate.txID
1326  << " with result " << transToken(pfresult.ter) << " from "
1327  << (accountIsInQueue ? "existing" : "new") << " account "
1328  << candidate.account << " to queue."
1329  << " Flags: " << flags;
1330 
1331  return {terQUEUED, false};
1332 }
1333 
1334 /*
1335  1. Update the fee metrics based on the fee levels of the
1336  txs in the validated ledger and whether consensus is
1337  slow.
1338  2. Adjust the maximum queue size to be enough to hold
1339  `ledgersInQueue` ledgers.
1340  3. Remove any transactions from the queue for which the
1341  `LastLedgerSequence` has passed.
1342  4. Remove any account objects that have no candidates
1343  under them.
1344 
1345 */
1346 void
1347 TxQ::processClosedLedger(Application& app, ReadView const& view, bool timeLeap)
1348 {
1349  std::lock_guard lock(mutex_);
1350 
1351  feeMetrics_.update(app, view, timeLeap, setup_);
1352  auto const& snapshot = feeMetrics_.getSnapshot();
1353 
1354  auto ledgerSeq = view.info().seq;
1355 
1356  if (!timeLeap)
1357  maxSize_ = std::max(
1358  snapshot.txnsExpected * setup_.ledgersInQueue, setup_.queueSizeMin);
1359 
1360  // Remove any queued candidates whose LastLedgerSequence has gone by.
1361  for (auto candidateIter = byFee_.begin(); candidateIter != byFee_.end();)
1362  {
1363  if (candidateIter->lastValid && *candidateIter->lastValid <= ledgerSeq)
1364  {
1365  byAccount_.at(candidateIter->account).dropPenalty = true;
1366  candidateIter = erase(candidateIter);
1367  }
1368  else
1369  {
1370  ++candidateIter;
1371  }
1372  }
1373 
1374  // Remove any TxQAccounts that don't have candidates
1375  // under them
1376  for (auto txQAccountIter = byAccount_.begin();
1377  txQAccountIter != byAccount_.end();)
1378  {
1379  if (txQAccountIter->second.empty())
1380  txQAccountIter = byAccount_.erase(txQAccountIter);
1381  else
1382  ++txQAccountIter;
1383  }
1384 }
1385 
1386 /*
1387  How the txs are moved from the queue to the new open ledger.
1388 
1389  1. Iterate over the txs from highest fee level to lowest.
1390  For each tx:
1391  a) Is this the first tx in the queue for this account?
1392  No: Skip this tx. We'll come back to it later.
1393  Yes: Continue to the next sub-step.
1394  b) Is the tx fee level less than the current required
1395  fee level?
1396  Yes: Stop iterating. Continue to the next step.
1397  No: Try to apply the transaction. Did it apply?
1398  Yes: Take it out of the queue. Continue with
1399  the next appropriate candidate (see below).
1400  No: Did it get a tef, tem, or tel, or has it
1401  retried `MaybeTx::retriesAllowed`
1402  times already?
1403  Yes: Take it out of the queue. Continue
1404  with the next appropriate candidate
1405  (see below).
1406  No: Leave it in the queue, track the retries,
1407  and continue iterating.
1408  2. Return indicator of whether the open ledger was modified.
1409 
1410  "Appropriate candidate" is defined as the tx that has the
1411  highest fee level of:
1412  * the tx for the current account with the next sequence.
1413  * the next tx in the queue, simply ordered by fee.
1414 */
1415 bool
1416 TxQ::accept(Application& app, OpenView& view)
1417 {
1418  /* Move transactions from the queue from largest fee level to smallest.
1419  As we add more transactions, the required fee level will increase.
1420  Stop when the transaction fee level gets lower than the required fee
1421  level.
1422  */
1423 
1424  auto ledgerChanged = false;
1425 
1426  std::lock_guard lock(mutex_);
1427 
1428  auto const metricsSnapshot = feeMetrics_.getSnapshot();
1429 
1430  for (auto candidateIter = byFee_.begin(); candidateIter != byFee_.end();)
1431  {
1432  auto& account = byAccount_.at(candidateIter->account);
1433  auto const beginIter = account.transactions.begin();
1434  if (candidateIter->seqProxy.isSeq() &&
1435  candidateIter->seqProxy > beginIter->first)
1436  {
1437  // There is a sequence transaction at the front of the queue and
1438  // candidate has a later sequence, so skip this candidate. We
1439  // need to process sequence-based transactions in sequence order.
1440  JLOG(j_.trace())
1441  << "Skipping queued transaction " << candidateIter->txID
1442  << " from account " << candidateIter->account
1443  << " as it is not the first.";
1444  candidateIter++;
1445  continue;
1446  }
1447  auto const requiredFeeLevel =
1448  getRequiredFeeLevel(view, tapNONE, metricsSnapshot, lock);
1449  auto const feeLevelPaid = candidateIter->feeLevel;
1450  JLOG(j_.trace()) << "Queued transaction " << candidateIter->txID
1451  << " from account " << candidateIter->account
1452  << " has fee level of " << feeLevelPaid
1453  << " needs at least " << requiredFeeLevel;
1454  if (feeLevelPaid >= requiredFeeLevel)
1455  {
1456  JLOG(j_.trace()) << "Applying queued transaction "
1457  << candidateIter->txID << " to open ledger.";
1458 
1459  auto const [txnResult, didApply] =
1460  candidateIter->apply(app, view, j_);
1461 
1462  if (didApply)
1463  {
1464  // Remove the candidate from the queue
1465  JLOG(j_.debug())
1466  << "Queued transaction " << candidateIter->txID
1467  << " applied successfully with " << transToken(txnResult)
1468  << ". Remove from queue.";
1469 
1470  candidateIter = eraseAndAdvance(candidateIter);
1471  ledgerChanged = true;
1472  }
1473  else if (
1474  isTefFailure(txnResult) || isTemMalformed(txnResult) ||
1475  candidateIter->retriesRemaining <= 0)
1476  {
1477  if (candidateIter->retriesRemaining <= 0)
1478  account.retryPenalty = true;
1479  else
1480  account.dropPenalty = true;
1481  JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID
1482  << " failed with " << transToken(txnResult)
1483  << ". Remove from queue.";
1484  candidateIter = eraseAndAdvance(candidateIter);
1485  }
1486  else
1487  {
1488  JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID
1489  << " failed with " << transToken(txnResult)
1490  << ". Leave in queue."
1491  << " Applied: " << didApply
1492  << ". Flags: " << candidateIter->flags;
1493  if (account.retryPenalty && candidateIter->retriesRemaining > 2)
1494  candidateIter->retriesRemaining = 1;
1495  else
1496  --candidateIter->retriesRemaining;
1497  candidateIter->lastResult = txnResult;
1498  if (account.dropPenalty && account.transactions.size() > 1 &&
1499  isFull<95>())
1500  {
1501  // The queue is close to full, this account has multiple
1502  // txs queued, and this account has had a transaction
1503  // fail.
1504  if (candidateIter->seqProxy.isTicket())
1505  {
1506  // Since the failed transaction has a ticket, order
1507  // doesn't matter. Drop this one.
1508  JLOG(j_.info())
1509  << "Queue is nearly full, and transaction "
1510  << candidateIter->txID << " failed with "
1511  << transToken(txnResult)
1512  << ". Removing ticketed tx from account "
1513  << account.account;
1514  candidateIter = eraseAndAdvance(candidateIter);
1515  }
1516  else
1517  {
1518  // Even though we're giving this transaction another
1519  // chance, chances are it won't recover. To avoid
1520  // making things worse, drop the _last_ transaction for
1521  // this account.
1522  auto dropRIter = account.transactions.rbegin();
1523  assert(
1524  dropRIter->second.account ==
1525  candidateIter->account);
1526 
1527  JLOG(j_.info())
1528  << "Queue is nearly full, and transaction "
1529  << candidateIter->txID << " failed with "
1530  << transToken(txnResult)
1531  << ". Removing last item from account "
1532  << account.account;
1533  auto endIter = byFee_.iterator_to(dropRIter->second);
1534  if (endIter != candidateIter)
1535  erase(endIter);
1536  ++candidateIter;
1537  }
1538  }
1539  else
1540  ++candidateIter;
1541  }
1542  }
1543  else
1544  {
1545  break;
1546  }
1547  }
1548 
1549  // All transactions that can be moved out of the queue into the open
1550  // ledger have been. Rebuild the queue using the open ledger's
1551  // parent hash, so that transactions paying the same fee are
1552  // reordered.
1553  LedgerHash const& parentHash = view.info().parentHash;
1554 #if !NDEBUG
1555  auto const startingSize = byFee_.size();
1556  assert(parentHash != parentHash_);
1557  parentHash_ = parentHash;
1558 #endif
1559  // byFee_ doesn't "own" the candidate objects inside it, so it's
1560  // perfectly safe to wipe it and start over, repopulating from
1561  // byAccount_.
1562  //
1563  // In the absence of a "re-sort the list in place" function, this
1564  // was the fastest method tried to repopulate the list.
1565  // Other methods included: create a new list and moving items over one at a
1566  // time, create a new list and merge the old list into it.
1567  byFee_.clear();
1568 
1569  MaybeTx::parentHashComp = parentHash;
1570 
1571  for (auto& [_, account] : byAccount_)
1572  {
1573  for (auto& [_, candidate] : account.transactions)
1574  {
1575  byFee_.insert(candidate);
1576  }
1577  }
1578  assert(byFee_.size() == startingSize);
1579 
1580  return ledgerChanged;
1581 }
1582 
1583 // Public entry point for nextQueuableSeq().
1584 //
1585 // Acquires a lock and calls the implementation.
1586 SeqProxy
1587 TxQ::nextQueuableSeq(std::shared_ptr<SLE const> const& sleAccount) const
1588 {
1589  std::lock_guard<std::mutex> lock(mutex_);
1590  return nextQueuableSeqImpl(sleAccount, lock);
1591 }
1592 
1593 // The goal is to return a SeqProxy for a sequence that will fill the next
1594 // available hole in the queue for the passed in account.
1595 //
1596 // If there are queued transactions for the account then the first viable
1597 // sequence number, that is not used by a transaction in the queue, must
1598 // be found and returned.
1599 SeqProxy
1600 TxQ::nextQueuableSeqImpl(
1601  std::shared_ptr<SLE const> const& sleAccount,
1602  std::lock_guard<std::mutex> const&) const
1603 {
1604  // If the account is not in the ledger or a non-account was passed
1605  // then return zero. We have no idea.
1606  if (!sleAccount || sleAccount->getType() != ltACCOUNT_ROOT)
1607  return SeqProxy::sequence(0);
1608 
1609  SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
1610 
1611  // If the account is not in the queue then acctSeqProx is good enough.
1612  auto const accountIter = byAccount_.find((*sleAccount)[sfAccount]);
1613  if (accountIter == byAccount_.end() ||
1614  accountIter->second.transactions.empty())
1615  return acctSeqProx;
1616 
1617  TxQAccount::TxMap const& acctTxs = accountIter->second.transactions;
1618 
1619  // Ignore any sequence-based queued transactions that slipped into the
1620  // ledger while we were not watching. This does actually happen in the
1621  // wild, but it's uncommon.
1622  TxQAccount::TxMap::const_iterator txIter = acctTxs.lower_bound(acctSeqProx);
1623 
1624  if (txIter == acctTxs.end() || !txIter->first.isSeq() ||
1625  txIter->first != acctSeqProx)
1626  // Either...
1627  // o There are no queued sequence-based transactions equal to or
1628  // following acctSeqProx or
1629  // o acctSeqProx is not currently in the queue.
1630  // So acctSeqProx is as good as it gets.
1631  return acctSeqProx;
1632 
1633  // There are sequence-based transactions queued that follow acctSeqProx.
1634  // Locate the first opening to put a transaction into.
1635  SeqProxy attempt = txIter->second.consequences().followingSeq();
1636  while (++txIter != acctTxs.cend())
1637  {
1638  if (attempt < txIter->first)
1639  break;
1640 
1641  attempt = txIter->second.consequences().followingSeq();
1642  }
1643  return attempt;
1644 }
1645 
1646 FeeLevel64
1647 TxQ::getRequiredFeeLevel(
1648  OpenView& view,
1649  ApplyFlags flags,
1650  FeeMetrics::Snapshot const& metricsSnapshot,
1651  std::lock_guard<std::mutex> const& lock) const
1652 {
1653  return FeeMetrics::scaleFeeLevel(metricsSnapshot, view);
1654 }
1655 
1657 TxQ::tryDirectApply(
1658  Application& app,
1659  OpenView& view,
1660  std::shared_ptr<STTx const> const& tx,
1661  ApplyFlags flags,
1662  beast::Journal j)
1663 {
1664  auto const account = (*tx)[sfAccount];
1665  auto const sleAccount = view.read(keylet::account(account));
1666 
1667  // Don't attempt to direct apply if the account is not in the ledger.
1668  if (!sleAccount)
1669  return {};
1670 
1671  SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
1672  SeqProxy const txSeqProx = tx->getSeqProxy();
1673 
1674  // Can only directly apply if the transaction sequence matches the account
1675  // sequence or if the transaction uses a ticket.
1676  if (txSeqProx.isSeq() && txSeqProx != acctSeqProx)
1677  return {};
1678 
1679  FeeLevel64 const requiredFeeLevel = [this, &view, flags]() {
1680  std::lock_guard lock(mutex_);
1681  return getRequiredFeeLevel(
1682  view, flags, feeMetrics_.getSnapshot(), lock);
1683  }();
1684 
1685  // If the transaction's fee is high enough we may be able to put the
1686  // transaction straight into the ledger.
1687  FeeLevel64 const feeLevelPaid = getFeeLevelPaid(view, *tx);
1688 
1689  if (feeLevelPaid >= requiredFeeLevel)
1690  {
1691  // Attempt to apply the transaction directly.
1692  auto const transactionID = tx->getTransactionID();
1693  JLOG(j_.trace()) << "Applying transaction " << transactionID
1694  << " to open ledger.";
1695 
1696  auto const [txnResult, didApply] =
1697  ripple::apply(app, view, *tx, flags, j);
1698 
1699  JLOG(j_.trace()) << "New transaction " << transactionID
1700  << (didApply ? " applied successfully with "
1701  : " failed with ")
1702  << transToken(txnResult);
1703 
1704  if (didApply)
1705  {
1706  // If the applied transaction replaced a transaction in the
1707  // queue then remove the replaced transaction.
1708  std::lock_guard lock(mutex_);
1709 
1710  AccountMap::iterator accountIter = byAccount_.find(account);
1711  if (accountIter != byAccount_.end())
1712  {
1713  TxQAccount& txQAcct = accountIter->second;
1714  if (auto const existingIter =
1715  txQAcct.transactions.find(txSeqProx);
1716  existingIter != txQAcct.transactions.end())
1717  {
1718  removeFromByFee(existingIter, tx);
1719  }
1720  }
1721  }
1722  return {std::pair(txnResult, didApply)};
1723  }
1724  return {};
1725 }
1726 
1728 TxQ::removeFromByFee(
1729  std::optional<TxQAccount::TxMap::iterator> const& replacedTxIter,
1730  std::shared_ptr<STTx const> const& tx)
1731 {
1732  if (replacedTxIter && tx)
1733  {
1734  // If the transaction we're holding replaces a transaction in the
1735  // queue, remove the transaction that is being replaced.
1736  auto deleteIter = byFee_.iterator_to((*replacedTxIter)->second);
1737  assert(deleteIter != byFee_.end());
1738  assert(&(*replacedTxIter)->second == &*deleteIter);
1739  assert(deleteIter->seqProxy == tx->getSeqProxy());
1740  assert(deleteIter->account == (*tx)[sfAccount]);
1741 
1742  erase(deleteIter);
1743  }
1744  return std::nullopt;
1745 }
1746 
1748 TxQ::getMetrics(OpenView const& view) const
1749 {
1750  Metrics result;
1751 
1752  std::lock_guard lock(mutex_);
1753 
1754  auto const snapshot = feeMetrics_.getSnapshot();
1755 
1756  result.txCount = byFee_.size();
1757  result.txQMaxSize = maxSize_;
1758  result.txInLedger = view.txCount();
1759  result.txPerLedger = snapshot.txnsExpected;
1760  result.referenceFeeLevel = baseLevel;
1761  result.minProcessingFeeLevel =
1762  isFull() ? byFee_.rbegin()->feeLevel + FeeLevel64{1} : baseLevel;
1763  result.medFeeLevel = snapshot.escalationMultiplier;
1764  result.openLedgerFeeLevel = FeeMetrics::scaleFeeLevel(snapshot, view);
1765 
1766  return result;
1767 }
1768 
1770 TxQ::getTxRequiredFeeAndSeq(
1771  OpenView const& view,
1772  std::shared_ptr<STTx const> const& tx) const
1773 {
1774  auto const account = (*tx)[sfAccount];
1775 
1776  std::lock_guard lock(mutex_);
1777 
1778  auto const snapshot = feeMetrics_.getSnapshot();
1779  auto const baseFee = calculateBaseFee(view, *tx);
1780  auto const fee = FeeMetrics::scaleFeeLevel(snapshot, view);
1781 
1782  auto const sle = view.read(keylet::account(account));
1783 
1784  std::uint32_t const accountSeq = sle ? (*sle)[sfSequence] : 0;
1785  std::uint32_t const availableSeq = nextQueuableSeqImpl(sle, lock).value();
1786  return {
1787  mulDiv(fee, baseFee, baseLevel)
1789  accountSeq,
1790  availableSeq};
1791 }
1792 
1794 TxQ::getAccountTxs(AccountID const& account) const
1795 {
1796  std::vector<TxDetails> result;
1797 
1798  std::lock_guard lock(mutex_);
1799 
1800  AccountMap::const_iterator const accountIter{byAccount_.find(account)};
1801 
1802  if (accountIter == byAccount_.end() ||
1803  accountIter->second.transactions.empty())
1804  return result;
1805 
1806  result.reserve(accountIter->second.transactions.size());
1807  for (auto const& tx : accountIter->second.transactions)
1808  {
1809  result.emplace_back(tx.second.getTxDetails());
1810  }
1811  return result;
1812 }
1813 
1815 TxQ::getTxs() const
1816 {
1817  std::vector<TxDetails> result;
1818 
1819  std::lock_guard lock(mutex_);
1820 
1821  result.reserve(byFee_.size());
1822 
1823  for (auto const& tx : byFee_)
1824  result.emplace_back(tx.getTxDetails());
1825 
1826  return result;
1827 }
1828 
1830 TxQ::doRPC(Application& app) const
1831 {
1832  auto const view = app.openLedger().current();
1833  if (!view)
1834  {
1835  BOOST_ASSERT(false);
1836  return {};
1837  }
1838 
1839  auto const metrics = getMetrics(*view);
1840 
1842 
1843  auto& levels = ret[jss::levels] = Json::objectValue;
1844 
1845  ret[jss::ledger_current_index] = view->info().seq;
1846  ret[jss::expected_ledger_size] = std::to_string(metrics.txPerLedger);
1847  ret[jss::current_ledger_size] = std::to_string(metrics.txInLedger);
1848  ret[jss::current_queue_size] = std::to_string(metrics.txCount);
1849  if (metrics.txQMaxSize)
1850  ret[jss::max_queue_size] = std::to_string(*metrics.txQMaxSize);
1851 
1852  levels[jss::reference_level] = to_string(metrics.referenceFeeLevel);
1853  levels[jss::minimum_level] = to_string(metrics.minProcessingFeeLevel);
1854  levels[jss::median_level] = to_string(metrics.medFeeLevel);
1855  levels[jss::open_ledger_level] = to_string(metrics.openLedgerFeeLevel);
1856 
1857  auto const baseFee = view->fees().base;
1858  // If the base fee is 0 drops, but escalation has kicked in, treat the
1859  // base fee as if it is 1 drop, which makes the rest of the math
1860  // work.
1861  auto const effectiveBaseFee = [&baseFee, &metrics]() {
1862  if (!baseFee && metrics.openLedgerFeeLevel != metrics.referenceFeeLevel)
1863  return XRPAmount{1};
1864  return baseFee;
1865  }();
1866  auto& drops = ret[jss::drops] = Json::Value();
1867 
1868  drops[jss::base_fee] = to_string(baseFee);
1869  drops[jss::median_fee] = to_string(toDrops(metrics.medFeeLevel, baseFee));
1870  drops[jss::minimum_fee] = to_string(toDrops(
1871  metrics.minProcessingFeeLevel,
1872  metrics.txCount >= metrics.txQMaxSize ? effectiveBaseFee : baseFee));
1873  auto openFee = toDrops(metrics.openLedgerFeeLevel, effectiveBaseFee);
1874  if (effectiveBaseFee &&
1875  toFeeLevel(openFee, effectiveBaseFee) < metrics.openLedgerFeeLevel)
1876  openFee += 1;
1877  drops[jss::open_ledger_fee] = to_string(openFee);
1878 
1879  return ret;
1880 }
1881 
1883 
1884 TxQ::Setup
1885 setup_TxQ(Config const& config)
1886 {
1887  TxQ::Setup setup;
1888  auto const& section = config.section("transaction_queue");
1889  set(setup.ledgersInQueue, "ledgers_in_queue", section);
1890  set(setup.queueSizeMin, "minimum_queue_size", section);
1891  set(setup.retrySequencePercent, "retry_sequence_percent", section);
1893  "minimum_escalation_multiplier",
1894  section);
1895  set(setup.minimumTxnInLedger, "minimum_txn_in_ledger", section);
1896  set(setup.minimumTxnInLedgerSA,
1897  "minimum_txn_in_ledger_standalone",
1898  section);
1899  set(setup.targetTxnInLedger, "target_txn_in_ledger", section);
1900  std::uint32_t max;
1901  if (set(max, "maximum_txn_in_ledger", section))
1902  {
1903  if (max < setup.minimumTxnInLedger)
1904  {
1905  Throw<std::runtime_error>(
1906  "The minimum number of low-fee transactions allowed "
1907  "per ledger (minimum_txn_in_ledger) exceeds "
1908  "the maximum number of low-fee transactions allowed per "
1909  "ledger (maximum_txn_in_ledger).");
1910  }
1911  if (max < setup.minimumTxnInLedgerSA)
1912  {
1913  Throw<std::runtime_error>(
1914  "The minimum number of low-fee transactions allowed "
1915  "per ledger (minimum_txn_in_ledger_standalone) exceeds "
1916  "the maximum number of low-fee transactions allowed per "
1917  "ledger (maximum_txn_in_ledger).");
1918  }
1919 
1920  setup.maximumTxnInLedger.emplace(max);
1921  }
1922 
1923  /* The math works as expected for any value up to and including
1924  MAXINT, but put a reasonable limit on this percentage so that
1925  the factor can't be configured to render escalation effectively
1926  moot. (There are other ways to do that, including
1927  minimum_txn_in_ledger.)
1928  */
1930  "normal_consensus_increase_percent",
1931  section);
1933  std::clamp(setup.normalConsensusIncreasePercent, 0u, 1000u);
1934 
1935  /* If this percentage is outside of the 0-100 range, the results
1936  are nonsensical (uint overflows happen, so the limit grows
1937  instead of shrinking). 0 is not recommended.
1938  */
1940  "slow_consensus_decrease_percent",
1941  section);
1943  std::clamp(setup.slowConsensusDecreasePercent, 0u, 100u);
1944 
1945  set(setup.maximumTxnPerAccount, "maximum_txn_per_account", section);
1946  set(setup.minimumLastLedgerBuffer, "minimum_last_ledger_buffer", section);
1947 
1948  setup.standAlone = config.standalone();
1949  return setup;
1950 }
1951 
1952 } // namespace ripple
ripple::TxQ::setup_
const Setup setup_
Setup parameters used to control the behavior of the queue.
Definition: TxQ.h:758
ripple::ReadView::info
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
ripple::setup_TxQ
TxQ::Setup setup_TxQ(Config const &config)
Build a TxQ::Setup object from application configuration.
Definition: TxQ.cpp:1885
ripple::TxQ::tryClearAccountQueueUpThruTx
std::pair< TER, bool > 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.
Definition: TxQ.cpp:504
ripple::OpenView::txCount
std::size_t txCount() const
Return the number of tx inserted since creation.
Definition: OpenView.cpp:124
ripple::tefNO_TICKET
@ tefNO_TICKET
Definition: TER.h:179
ripple::Application
Definition: Application.h:116
std::max_element
T max_element(T... args)
ripple::terPRE_TICKET
@ terPRE_TICKET
Definition: TER.h:219
std::optional::has_value
T has_value(T... args)
ripple::STAmountSO
RAII class to set and restore the STAmount canonicalize switchover.
Definition: STAmount.h:569
std::for_each
T for_each(T... args)
ripple::Keylet
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:38
ripple::mulDiv
std::optional< Dest > mulDiv(Source1 value, Dest mul, Source2 div)
Definition: FeeUnits.h:468
ripple::OpenLedger::current
std::shared_ptr< OpenView const > current() const
Returns a view to the current open ledger.
Definition: OpenLedger.cpp:50
ripple::tefINTERNAL
@ tefINTERNAL
Definition: TER.h:167
ripple::TxQ::Metrics::minProcessingFeeLevel
FeeLevel64 minProcessingFeeLevel
Minimum fee level for a transaction to be considered for the open ledger or the queue.
Definition: TxQ.h:178
ripple::Rules::enabled
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:94
std::shared_ptr
STL class.
ripple::OpenView::info
LedgerInfo const & info() const override
Returns information about the ledger.
Definition: OpenView.cpp:140
ripple::TxQ::j_
const beast::Journal j_
Journal.
Definition: TxQ.h:760
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:308
ripple::apply
std::pair< TER, bool > apply(Application &app, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
Definition: apply.cpp:109
ripple::open_ledger
const open_ledger_t open_ledger
Definition: OpenView.cpp:25
ripple::OpenView::apply
void apply(TxsRawView &to) const
Apply changes.
Definition: OpenView.cpp:130
ripple::isTesSuccess
bool isTesSuccess(TER x)
Definition: TER.h:637
std::pair
ripple::TxQ::apply
std::pair< TER, bool > 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.
Definition: TxQ.cpp:714
std::vector::reserve
T reserve(T... args)
ripple::sfSequence
const SF_UINT32 sfSequence
ripple::telCAN_NOT_QUEUE_FEE
@ telCAN_NOT_QUEUE_FEE
Definition: TER.h:63
ripple::OpenView
Writable ledger view that accumulates state and tx changes.
Definition: OpenView.h:55
std::vector
STL class.
ripple::TxQ::TxQAccount
Used to represent an account to the queue, and stores the transactions queued for that account by Seq...
Definition: TxQ.h:662
std::map::find
T find(T... args)
std::vector::size
T size(T... args)
ripple::TxQ::eraseAndAdvance
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...
Definition: TxQ.cpp:457
ripple::NumberSO
RAII class to set and restore the Number switchover.
Definition: IOUAmount.h:201
ripple::LedgerHeader::parentHash
uint256 parentHash
Definition: LedgerHeader.h:52
ripple::fixSTAmountCanonicalize
const uint256 fixSTAmountCanonicalize
std::optional::value_or
T value_or(T... args)
ripple::TxQ::erase
FeeMultiSet::iterator_type erase(FeeMultiSet::const_iterator_type)
Erase and return the next entry in byFee_ (lower fee level)
ripple::OpenView::exists
bool exists(Keylet const &k) const override
Determine if a state item exists.
Definition: OpenView.cpp:158
ripple::ApplyFlags
ApplyFlags
Definition: ApplyView.h:29
ripple::FeeLevel64
FeeLevel< std::uint64_t > FeeLevel64
Definition: FeeUnits.h:459
ripple::LedgerHeader::seq
LedgerIndex seq
Definition: LedgerHeader.h:41
ripple::muldiv_max
constexpr auto muldiv_max
Definition: mulDiv.h:29
ripple::TxQ::FeeMetrics::getSnapshot
Snapshot getSnapshot() const
Get the current Snapshot.
Definition: TxQ.h:452
std::optional::emplace
T emplace(T... args)
ripple::SeqProxy::sequence
static constexpr SeqProxy sequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition: SeqProxy.h:76
beast::Journal::warn
Stream warn() const
Definition: Journal.h:326
ripple::transToken
std::string transToken(TER code)
Definition: TER.cpp:241
ripple::telCAN_NOT_QUEUE_FULL
@ telCAN_NOT_QUEUE_FULL
Definition: TER.h:64
std::lock_guard
STL class.
ripple::telCAN_NOT_QUEUE
@ telCAN_NOT_QUEUE
Definition: TER.h:59
ripple::TxQ::nextQueuableSeqImpl
SeqProxy nextQueuableSeqImpl(std::shared_ptr< SLE const > const &sleAccount, std::lock_guard< std::mutex > const &) const
Definition: TxQ.cpp:1600
std::distance
T distance(T... args)
ripple::TxQ::TxQAccount::remove
bool remove(SeqProxy seqProx)
Remove the candidate with given SeqProxy value from this account.
Definition: TxQ.cpp:347
ripple::STTx::getSeqProxy
SeqProxy getSeqProxy() const
Definition: STTx.cpp:183
ripple::TxQ::FeeMetrics::escalationMultiplier_
FeeLevel64 escalationMultiplier_
Based on the median fee of the LCL.
Definition: TxQ.h:393
ripple::keylet::ticket
static const ticket_t ticket
Definition: Indexes.h:168
ripple::TxQ::isFull
bool isFull() const
Is the queue at least fillPercentage full?
Definition: TxQ.cpp:366
ripple::TxQ::FeeAndSeq
Definition: TxQ.h:316
ripple::ApplyViewImpl
Editable, discardable view that can build metadata for one tx.
Definition: ApplyViewImpl.h:36
ripple::getLastLedgerSequence
static std::optional< LedgerIndex > getLastLedgerSequence(STTx const &tx)
Definition: TxQ.cpp:66
ripple::preflight
PreflightResult preflight(Application &app, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
Definition: applySteps.cpp:355
ripple::Application::openLedger
virtual OpenLedger & openLedger()=0
ripple::TxQ::canBeHeld
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.
Definition: TxQ.cpp:374
ripple::toFeeLevel
FeeLevel64 toFeeLevel(XRPAmount const &drops, XRPAmount const &baseFee)
Definition: TxQ.h:870
ripple::TxQ::FeeMetrics::maximumTxnCount_
const std::optional< std::size_t > maximumTxnCount_
Maximum value of txnsExpected.
Definition: TxQ.h:383
ripple::tapNONE
@ tapNONE
Definition: ApplyView.h:30
ripple::TxQ::FeeMetrics::recentTxnCounts_
boost::circular_buffer< std::size_t > recentTxnCounts_
Recent history of transaction counts that exceed the targetTxnCount_.
Definition: TxQ.h:390
ripple::TxQ::MaybeTx::apply
std::pair< TER, bool > apply(Application &app, OpenView &view, beast::Journal j)
Attempt to apply the queued transaction to the open ledger.
Definition: TxQ.cpp:292
ripple::TxQ::TxQAccount::transactions
TxMap transactions
Sequence number will be used as the key.
Definition: TxQ.h:670
ripple::TxQ::Metrics
Structure returned by TxQ::getMetrics, expressed in reference fee level units.
Definition: TxQ.h:161
ripple::TxQ::Setup::maximumTxnPerAccount
std::uint32_t maximumTxnPerAccount
Maximum number of transactions that can be queued by one account.
Definition: TxQ.h:145
std::sort
T sort(T... args)
algorithm
ripple::TxQ::FeeMetrics::update
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.
Definition: TxQ.cpp:83
ripple::TxQ::Setup::normalConsensusIncreasePercent
std::uint32_t normalConsensusIncreasePercent
When the ledger has more transactions than "expected", and performance is humming along nicely,...
Definition: TxQ.h:129
ripple::base_uint::size
constexpr static std::size_t size()
Definition: base_uint.h:519
ripple::SeqProxy::isTicket
constexpr bool isTicket() const
Definition: SeqProxy.h:94
std::tie
T tie(T... args)
std::vector::push_back
T push_back(T... args)
ripple::SeqProxy::isSeq
constexpr bool isSeq() const
Definition: SeqProxy.h:88
ripple::erase
void erase(STObject &st, TypedField< U > const &f)
Remove a field in an STObject.
Definition: STExchange.h:171
ripple::base_uint< 256 >
ripple::TxQ::Setup::targetTxnInLedger
std::uint32_t targetTxnInLedger
Number of transactions per ledger that fee escalation "works towards".
Definition: TxQ.h:106
ripple::OpenView::fees
Fees const & fees() const override
Returns the fees for the base ledger.
Definition: OpenView.cpp:146
ripple::TxQ::MaybeTx::MaybeTx
MaybeTx(std::shared_ptr< STTx const > const &, TxID const &txID, FeeLevel64 feeLevel, ApplyFlags const flags, PreflightResult const &pfresult)
Constructor.
Definition: TxQ.cpp:273
ripple::TxQ::Setup::standAlone
bool standAlone
Use standalone mode behavior.
Definition: TxQ.h:154
ripple::doApply
std::pair< TER, bool > doApply(PreclaimResult const &preclaimResult, Application &app, OpenView &view)
Apply a prechecked transaction to an OpenView.
Definition: applySteps.cpp:433
ripple::TxQ::byFee_
FeeMultiSet byFee_
The queue itself: the collection of transactions ordered by fee level.
Definition: TxQ.h:772
ripple::PreflightResult
Describes the results of the preflight check.
Definition: applySteps.h:150
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
std::map::at
T at(T... args)
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:142
ripple::Config
Definition: Config.h:92
ripple::increase
static FeeLevel64 increase(FeeLevel64 level, std::uint32_t increasePercent)
Definition: TxQ.cpp:74
ripple::TxQ::feeMetrics_
FeeMetrics feeMetrics_
Tracks the current state of the queue.
Definition: TxQ.h:766
ripple::TxQ::Setup::minimumTxnInLedger
std::uint32_t minimumTxnInLedger
Minimum number of transactions to allow into the ledger before escalation, regardless of the prior le...
Definition: TxQ.h:100
ripple::TxQ::Setup
Structure used to customize TxQ behavior.
Definition: TxQ.h:66
ripple::TxQ::TxQAccount::getPrevTx
TxMap::const_iterator getPrevTx(SeqProxy seqProx) const
Find the entry in transactions that precedes seqProx, if one does.
Definition: TxQ.cpp:324
ripple::TERSubset< CanCvtToTER >
ripple::ReadView::txs_type::begin
iterator begin() const
Definition: ReadView.cpp:57
ripple::Config::standalone
bool standalone() const
Definition: Config.h:346
ripple::isTefFailure
bool isTefFailure(TER x)
Definition: TER.h:625
ripple::TxQ::TxQAccount::add
MaybeTx & add(MaybeTx &&)
Add a transaction candidate to this account for queuing.
Definition: TxQ.cpp:335
std::to_string
T to_string(T... args)
ripple::telINSUF_FEE_P
@ telINSUF_FEE_P
Definition: TER.h:57
ripple::set
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Definition: BasicConfig.h:313
ripple::sfAccountTxnID
const SF_UINT256 sfAccountTxnID
beast::Journal::info
Stream info() const
Definition: Journal.h:320
ripple::TxQ::MaybeTx
Represents a transaction in the queue which may be applied later to the open ledger.
Definition: TxQ.h:510
ripple::STTx
Definition: STTx.h:45
ripple::TxQ::Setup::slowConsensusDecreasePercent
std::uint32_t slowConsensusDecreasePercent
When consensus takes longer than appropriate, the expected ledger size is updated to the lesser of th...
Definition: TxQ.h:143
ripple::ValStatus::current
@ current
This was a new validation and was added.
std::accumulate
T accumulate(T... args)
ripple::HashPrefix::transactionID
@ transactionID
transaction plus signature to give transaction ID
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::sfPreviousTxnID
const SF_UINT256 sfPreviousTxnID
ripple::TxQ::baseLevel
static constexpr FeeLevel64 baseLevel
Fee level for single-signed reference transaction.
Definition: TxQ.h:61
std::uint32_t
ripple::TxQ::FeeMetrics::scaleFeeLevel
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...
Definition: TxQ.cpp:170
ripple::feeunit::TaggedFee
Definition: FeeUnits.h:70
std::map< SeqProxy, MaybeTx >
ripple::TxQ::FeeMetrics::minimumTxnCount_
const std::size_t minimumTxnCount_
Minimum value of txnsExpected.
Definition: TxQ.h:378
std::map::upper_bound
T upper_bound(T... args)
ripple::telCAN_NOT_QUEUE_BLOCKS
@ telCAN_NOT_QUEUE_BLOCKS
Definition: TER.h:61
ripple::ReadView::txs_type::end
iterator end() const
Definition: ReadView.cpp:63
ripple::TxQ::MaybeTx::seqProxy
const SeqProxy seqProxy
Transaction SeqProxy number (sfSequence or sfTicketSequence field).
Definition: TxQ.h:532
ripple::TxQ::FeeMetrics::targetTxnCount_
const std::size_t targetTxnCount_
Number of transactions per ledger that fee escalation "works towards".
Definition: TxQ.h:381
ripple::detail::sumOfFirstSquares
constexpr static std::pair< bool, std::uint64_t > sumOfFirstSquares(std::size_t xIn)
Definition: TxQ.cpp:194
ripple::TxQ::Metrics::referenceFeeLevel
FeeLevel64 referenceFeeLevel
Reference transaction fee level.
Definition: TxQ.h:175
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:210
std::min
T min(T... args)
ripple::calculateDefaultBaseFee
XRPAmount calculateDefaultBaseFee(ReadView const &view, STTx const &tx)
Return the minimum fee that an "ordinary" transaction would pay.
Definition: applySteps.cpp:427
ripple::tapRETRY
@ tapRETRY
Definition: ApplyView.h:38
ripple::preclaim
PreclaimResult preclaim(PreflightResult const &preflightResult, Application &app, OpenView const &view)
Gate a transaction based on static ledger information.
Definition: applySteps.cpp:375
ripple::TxQ::~TxQ
virtual ~TxQ()
Destructor.
Definition: TxQ.cpp:359
ripple::TxQ::Metrics::medFeeLevel
FeeLevel64 medFeeLevel
Median fee level of the last ledger.
Definition: TxQ.h:180
ripple::TxQ::FeeMetrics::Snapshot
Snapshot of the externally relevant FeeMetrics fields at any given time.
Definition: TxQ.h:439
ripple::TxQ::FeeMetrics::Snapshot::escalationMultiplier
const FeeLevel64 escalationMultiplier
Definition: TxQ.h:447
ripple::SeqProxy::value
constexpr std::uint32_t value() const
Definition: SeqProxy.h:82
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:54
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::TxQ::TxQ
TxQ(Setup const &setup, beast::Journal j)
Constructor.
Definition: TxQ.cpp:354
ripple::TxQ::TxQAccount::getTxnCount
std::size_t getTxnCount() const
Return the number of transactions currently queued for this account.
Definition: TxQ.h:694
std::map::lower_bound
T lower_bound(T... args)
ripple::tefPAST_SEQ
@ tefPAST_SEQ
Definition: TER.h:169
std::clamp
T clamp(T... args)
ripple::TxQ::getRequiredFeeLevel
FeeLevel64 getRequiredFeeLevel(OpenView &view, ApplyFlags flags, FeeMetrics::Snapshot const &metricsSnapshot, std::lock_guard< std::mutex > const &lock) const
Definition: TxQ.cpp:1647
limits
ripple::Fees::accountReserve
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
Definition: protocol/Fees.h:49
std::vector::begin
T begin(T... args)
std
STL namespace.
ripple::ltACCOUNT_ROOT
@ ltACCOUNT_ROOT
A ledger object which describes an account.
Definition: LedgerFormats.h:59
ripple::STObject::isFieldPresent
bool isFieldPresent(SField const &field) const
Definition: STObject.cpp:428
ripple::TxQ::Metrics::txInLedger
std::size_t txInLedger
Number of transactions currently in the open ledger.
Definition: TxQ.h:171
ripple::fixUniversalNumber
const uint256 fixUniversalNumber
ripple::TxQ::mutex_
std::mutex mutex_
Most queue operations are done under the master lock, but use this mutex for the RPC "fee" command,...
Definition: TxQ.h:799
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::TxQ::maxSize_
std::optional< size_t > maxSize_
Maximum number of transactions allowed in the queue based on the current metrics.
Definition: TxQ.h:786
ripple::SeqProxy
A type that represents either a sequence value or a ticket value.
Definition: SeqProxy.h:55
ripple::TxQ::MaybeTx::parentHashComp
static LedgerHash parentHashComp
The hash of the parent ledger.
Definition: TxQ.h:587
ripple::TxQ::MaybeTx::retriesAllowed
static constexpr int retriesAllowed
Starting retry count for newly queued transactions.
Definition: TxQ.h:577
ripple::TxQ::FeeMetrics::j_
const beast::Journal j_
Journal.
Definition: TxQ.h:395
ripple::TxQ::Metrics::txCount
std::size_t txCount
Number of transactions in the queue.
Definition: TxQ.h:167
ripple::tapFAIL_HARD
@ tapFAIL_HARD
Definition: ApplyView.h:34
ripple::calculateBaseFee
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
Definition: applySteps.cpp:421
ripple::terPRE_SEQ
@ terPRE_SEQ
Definition: TER.h:214
ripple::TxQ::Setup::retrySequencePercent
std::uint32_t retrySequencePercent
Extra percentage required on the fee level of a queued transaction to replace that transaction with a...
Definition: TxQ.h:94
ripple::OpenView::read
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition: OpenView.cpp:171
std::optional< LedgerIndex >
ripple::TxQ::Setup::minimumTxnInLedgerSA
std::uint32_t minimumTxnInLedgerSA
Like minimumTxnInLedger for standalone mode.
Definition: TxQ.h:103
beast::Journal::debug
Stream debug() const
Definition: Journal.h:314
std::size_t
ripple::sfFee
const SF_AMOUNT sfFee
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::TxQ::TxQAccount::TxQAccount
TxQAccount(std::shared_ptr< STTx const > const &txn)
Construct from a transaction.
Definition: TxQ.cpp:314
std::vector::end
T end(T... args)
ripple::TxQ::tryDirectApply
std::optional< std::pair< TER, bool > > tryDirectApply(Application &app, OpenView &view, std::shared_ptr< STTx const > const &tx, ApplyFlags flags, beast::Journal j)
Definition: TxQ.cpp:1657
ripple::TxQ::Setup::maximumTxnInLedger
std::optional< std::uint32_t > maximumTxnInLedger
Optional maximum allowed value of transactions per ledger before fee escalation kicks in.
Definition: TxQ.h:117
ripple::TxQ::Metrics::openLedgerFeeLevel
FeeLevel64 openLedgerFeeLevel
Minimum fee level to get into the current open ledger, bypassing the queue.
Definition: TxQ.h:183
ripple::TxQ::Metrics::txQMaxSize
std::optional< std::size_t > txQMaxSize
Max transactions currently allowed in queue.
Definition: TxQ.h:169
numeric
std::max
T max(T... args)
ripple::STObject::getFieldU32
std::uint32_t getFieldU32(SField const &field) const
Definition: STObject.cpp:559
ripple::TxQ::Setup::queueSizeMin
std::size_t queueSizeMin
The smallest limit the queue is allowed.
Definition: TxQ.h:84
ripple::TxQ::byAccount_
AccountMap byAccount_
All of the accounts which currently have any transactions in the queue.
Definition: TxQ.h:779
ripple::TxQ::FeeMetrics::txnsExpected_
std::size_t txnsExpected_
Number of transactions expected per ledger.
Definition: TxQ.h:387
ripple::sfLastLedgerSequence
const SF_UINT32 sfLastLedgerSequence
ripple::TxQ::Setup::minimumEscalationMultiplier
FeeLevel64 minimumEscalationMultiplier
Minimum value of the escalation multiplier, regardless of the prior ledger's median fee level.
Definition: TxQ.h:97
ripple::TxQ::Setup::minimumLastLedgerBuffer
std::uint32_t minimumLastLedgerBuffer
Minimum difference between the current ledger sequence and a transaction's LastLedgerSequence for the...
Definition: TxQ.h:152
ripple::telCAN_NOT_QUEUE_BALANCE
@ telCAN_NOT_QUEUE_BALANCE
Definition: TER.h:60
ripple::getFeeLevelPaid
static FeeLevel64 getFeeLevelPaid(ReadView const &view, STTx const &tx)
Definition: TxQ.cpp:38
ripple::TxQ::FeeMetrics::escalatedSeriesFeeLevel
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.
Definition: TxQ.cpp:231
ripple::TxQ::Setup::ledgersInQueue
std::size_t ledgersInQueue
Number of ledgers' worth of transactions to allow in the queue.
Definition: TxQ.h:78
std::numeric_limits
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:236
ripple::OpenView::rules
Rules const & rules() const override
Returns the tx processing rules.
Definition: OpenView.cpp:152
ripple::toDrops
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
Definition: TxQ.h:863
ripple::TxQ::FeeMetrics::Snapshot::txnsExpected
const std::size_t txnsExpected
Definition: TxQ.h:444
ripple::isTemMalformed
bool isTemMalformed(TER x)
Definition: TER.h:619
ripple::Fees::base
XRPAmount base
Definition: protocol/Fees.h:34
ripple::telCAN_NOT_QUEUE_BLOCKED
@ telCAN_NOT_QUEUE_BLOCKED
Definition: TER.h:62
ripple::ReadView::txs
txs_type txs
Definition: ReadView.h:252
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::XRPAmount::signum
constexpr int signum() const noexcept
Return the sign of the amount.
Definition: XRPAmount.h:165
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::BasicConfig::section
Section & section(std::string const &name)
Returns the section with the given name.
Definition: BasicConfig.cpp:127
std::next
T next(T... args)
ripple::TxQ::Metrics::txPerLedger
std::size_t txPerLedger
Number of transactions expected per ledger.
Definition: TxQ.h:173