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