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