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