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