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