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