rippled
TxQ.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 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
40  STTx const& tx,
41  FeeLevel64 baseRefLevel,
42  XRPAmount refTxnCostDrops,
43  TxQ::Setup const& setup)
44 {
45  if (refTxnCostDrops == 0)
46  // If nothing is required, or the cost is 0,
47  // the level is effectively infinite.
48  return setup.zeroBaseFeeTransactionFeeLevel;
49 
50  // If the math overflows, return the clipped
51  // result blindly. This is very unlikely to ever
52  // happen.
53  return mulDiv(tx[sfFee].xrp(), baseRefLevel, refTxnCostDrops).second;
54 }
55 
56 static boost::optional<LedgerIndex>
58 {
60  return boost::none;
62 }
63 
64 static FeeLevel64
65 increase(FeeLevel64 level, std::uint32_t increasePercent)
66 {
67  return mulDiv(level, 100 + increasePercent, 100).second;
68 }
69 
71 
72 constexpr FeeLevel64 TxQ::baseLevel;
73 
76  Application& app,
77  ReadView const& view,
78  bool timeLeap,
79  TxQ::Setup const& setup)
80 {
81  std::vector<FeeLevel64> feeLevels;
82  auto const txBegin = view.txs.begin();
83  auto const txEnd = view.txs.end();
84  auto const size = std::distance(txBegin, txEnd);
85  feeLevels.reserve(size);
86  std::for_each(txBegin, txEnd, [&](auto const& tx) {
87  auto const baseFee =
88  view.fees().toDrops(calculateBaseFee(view, *tx.first)).second;
89  feeLevels.push_back(
90  getFeeLevelPaid(*tx.first, baseLevel, baseFee, setup));
91  });
92  std::sort(feeLevels.begin(), feeLevels.end());
93  assert(size == feeLevels.size());
94 
95  JLOG(j_.debug()) << "Ledger " << view.info().seq << " has " << size
96  << " transactions. "
97  << "Ledgers are processing "
98  << (timeLeap ? "slowly" : "as expected")
99  << ". Expected transactions is currently " << txnsExpected_
100  << " and multiplier is " << escalationMultiplier_;
101 
102  if (timeLeap)
103  {
104  // Ledgers are taking to long to process,
105  // so clamp down on limits.
106  auto const cutPct = 100 - setup.slowConsensusDecreasePercent;
107  // upperLimit must be >= minimumTxnCount_ or boost::clamp can give
108  // unexpected results
109  auto const upperLimit = std::max<std::uint64_t>(
110  mulDiv(txnsExpected_, cutPct, 100).second, minimumTxnCount_);
111  txnsExpected_ = boost::algorithm::clamp(
112  mulDiv(size, cutPct, 100).second, minimumTxnCount_, upperLimit);
113  recentTxnCounts_.clear();
114  }
115  else if (size > txnsExpected_ || size > targetTxnCount_)
116  {
117  recentTxnCounts_.push_back(
118  mulDiv(size, 100 + setup.normalConsensusIncreasePercent, 100)
119  .second);
120  auto const iter =
122  BOOST_ASSERT(iter != recentTxnCounts_.end());
123  auto const next = [&] {
124  // Grow quickly: If the max_element is >= the
125  // current size limit, use it.
126  if (*iter >= txnsExpected_)
127  return *iter;
128  // Shrink slowly: If the max_element is < the
129  // current size limit, use a limit that is
130  // 90% of the way from max_element to the
131  // current size limit.
132  return (txnsExpected_ * 9 + *iter) / 10;
133  }();
134  // Ledgers are processing in a timely manner,
135  // so keep the limit high, but don't let it
136  // grow without bound.
137  txnsExpected_ = std::min(next, maximumTxnCount_.value_or(next));
138  }
139 
140  if (!size)
141  {
143  }
144  else
145  {
146  // In the case of an odd number of elements, this
147  // evaluates to the middle element; for an even
148  // number of elements, it will add the two elements
149  // on either side of the "middle" and average them.
151  (feeLevels[size / 2] + feeLevels[(size - 1) / 2] + FeeLevel64{1}) /
152  2;
155  }
156  JLOG(j_.debug()) << "Expected transactions updated to " << txnsExpected_
157  << " and multiplier updated to " << escalationMultiplier_;
158 
159  return size;
160 }
161 
163 TxQ::FeeMetrics::scaleFeeLevel(Snapshot const& snapshot, OpenView const& view)
164 {
165  // Transactions in the open ledger so far
166  auto const current = view.txCount();
167 
168  auto const target = snapshot.txnsExpected;
169  auto const multiplier = snapshot.escalationMultiplier;
170 
171  // Once the open ledger bypasses the target,
172  // escalate the fee quickly.
173  if (current > target)
174  {
175  // Compute escalated fee level
176  // Don't care about the overflow flag
177  return mulDiv(multiplier, current * current, target * target).second;
178  }
179 
180  return baseLevel;
181 }
182 
183 namespace detail {
184 
187 {
188  // sum(n = 1->x) : n * n = x(x + 1)(2x + 1) / 6
189 
190  // If x is anywhere on the order of 2^^21, it's going
191  // to completely dominate the computation and is likely
192  // enough to overflow that we're just going to assume
193  // it does. If we have anywhere near 2^^21 transactions
194  // in a ledger, this is the least of our problems.
195  if (x >= (1 << 21))
197  return std::make_pair(true, (x * (x + 1) * (2 * x + 1)) / 6);
198 }
199 
200 } // namespace detail
201 
204  Snapshot const& snapshot,
205  OpenView const& view,
206  std::size_t extraCount,
207  std::size_t seriesSize)
208 {
209  /* Transactions in the open ledger so far.
210  AKA Transactions that will be in the open ledger when
211  the first tx in the series is attempted.
212  */
213  auto const current = view.txCount() + extraCount;
214  /* Transactions that will be in the open ledger when
215  the last tx in the series is attempted.
216  */
217  auto const last = current + seriesSize - 1;
218 
219  auto const target = snapshot.txnsExpected;
220  auto const multiplier = snapshot.escalationMultiplier;
221 
222  assert(current > target);
223 
224  /* Calculate (apologies for the terrible notation)
225  sum(n = current -> last) : multiplier * n * n / (target * target)
226  multiplier / (target * target) * (sum(n = current -> last) : n * n)
227  multiplier / (target * target) * ((sum(n = 1 -> last) : n * n) -
228  (sum(n = 1 -> current - 1) : n * n))
229  */
230  auto const sumNlast = detail::sumOfFirstSquares(last);
231  auto const sumNcurrent = detail::sumOfFirstSquares(current - 1);
232  // because `last` is bigger, if either sum overflowed, then
233  // `sumNlast` definitely overflowed. Also the odds of this
234  // are nearly nil.
235  if (!sumNlast.first)
236  return {sumNlast.first, FeeLevel64{sumNlast.second}};
237  auto const totalFeeLevel = mulDiv(
238  multiplier, sumNlast.second - sumNcurrent.second, target * target);
239 
240  return totalFeeLevel;
241 }
242 
244  std::shared_ptr<STTx const> const& txn_,
245  TxID const& txID_,
246  FeeLevel64 feeLevel_,
247  ApplyFlags const flags_,
248  PreflightResult const& pfresult_)
249  : txn(txn_)
250  , feeLevel(feeLevel_)
251  , txID(txID_)
252  , account(txn_->getAccountID(sfAccount))
253  , sequence(txn_->getSequence())
254  , retriesRemaining(retriesAllowed)
255  , flags(flags_)
256  , pfresult(pfresult_)
257 {
259 
260  if (txn->isFieldPresent(sfAccountTxnID))
261  priorTxID = txn->getFieldH256(sfAccountTxnID);
262 }
263 
266 {
267  // If the rules or flags change, preflight again
268  assert(pfresult);
269  if (pfresult->rules != view.rules() || pfresult->flags != flags)
270  {
271  JLOG(j.debug()) << "Queued transaction " << txID
272  << " rules or flags have changed. Flags from "
273  << pfresult->flags << " to " << flags;
274 
275  pfresult.emplace(
276  preflight(app, view.rules(), pfresult->tx, flags, pfresult->j));
277  }
278 
279  auto pcresult = preclaim(*pfresult, app, view);
280 
281  return doApply(pcresult, app, view);
282 }
283 
285  : TxQAccount(txn->getAccountID(sfAccount))
286 {
287 }
288 
289 TxQ::TxQAccount::TxQAccount(const AccountID& account_) : account(account_)
290 {
291 }
292 
293 auto
295 {
296  auto sequence = txn.sequence;
297 
298  auto result = transactions.emplace(sequence, std::move(txn));
299  assert(result.second);
300  assert(&result.first->second != &txn);
301 
302  return result.first->second;
303 }
304 
305 bool
307 {
308  return transactions.erase(sequence) != 0;
309 }
310 
312 
313 TxQ::TxQ(Setup const& setup, beast::Journal j)
314  : setup_(setup), j_(j), feeMetrics_(setup, j), maxSize_(boost::none)
315 {
316 }
317 
319 {
320  byFee_.clear();
321 }
322 
323 template <size_t fillPercentage>
324 bool
325 TxQ::isFull() const
326 {
327  static_assert(
328  fillPercentage > 0 && fillPercentage <= 100, "Invalid fill percentage");
329  return maxSize_ && byFee_.size() >= (*maxSize_ * fillPercentage / 100);
330 }
331 
332 bool
334  STTx const& tx,
335  ApplyFlags const flags,
336  OpenView const& view,
337  AccountMap::iterator accountIter,
338  boost::optional<FeeMultiSet::iterator> replacementIter)
339 {
340  // PreviousTxnID is deprecated and should never be used
341  // AccountTxnID is not supported by the transaction
342  // queue yet, but should be added in the future
343  // tapFAIL_HARD transactions are never held
345  !tx.isFieldPresent(sfAccountTxnID) && !(flags & tapFAIL_HARD);
346  if (canBeHeld)
347  {
348  /* To be queued and relayed, the transaction needs to
349  promise to stick around for long enough that it has
350  a realistic chance of getting into a ledger.
351  */
352  auto const lastValid = getLastLedgerSequence(tx);
353  canBeHeld = !lastValid ||
354  *lastValid >= view.info().seq + setup_.minimumLastLedgerBuffer;
355  }
356  if (canBeHeld)
357  {
358  /* Limit the number of transactions an individual account
359  can queue. Mitigates the lost cost of relaying should
360  an early one fail or get dropped.
361  */
362 
363  // Allow if the account is not in the queue at all
364  canBeHeld = accountIter == byAccount_.end();
365 
366  if (!canBeHeld)
367  {
368  // Allow this tx to replace another one
369  canBeHeld = replacementIter.is_initialized();
370  }
371 
372  if (!canBeHeld)
373  {
374  // Allow if there are fewer than the limit
375  canBeHeld =
376  accountIter->second.getTxnCount() < setup_.maximumTxnPerAccount;
377  }
378 
379  if (!canBeHeld)
380  {
381  // Allow if the transaction goes in front of any
382  // queued transactions. Enables recovery of open
383  // ledger transactions, and stuck transactions.
384  auto const tSeq = tx.getSequence();
385  canBeHeld = tSeq < accountIter->second.transactions.rbegin()->first;
386  }
387  }
388  return canBeHeld;
389 }
390 
391 auto
392 TxQ::erase(TxQ::FeeMultiSet::const_iterator_type candidateIter)
393  -> FeeMultiSet::iterator_type
394 {
395  auto& txQAccount = byAccount_.at(candidateIter->account);
396  auto const sequence = candidateIter->sequence;
397  auto const newCandidateIter = byFee_.erase(candidateIter);
398  // Now that the candidate has been removed from the
399  // intrusive list remove it from the TxQAccount
400  // so the memory can be freed.
401  auto const found = txQAccount.remove(sequence);
402  (void)found;
403  assert(found);
404 
405  return newCandidateIter;
406 }
407 
408 auto
409 TxQ::eraseAndAdvance(TxQ::FeeMultiSet::const_iterator_type candidateIter)
410  -> FeeMultiSet::iterator_type
411 {
412  auto& txQAccount = byAccount_.at(candidateIter->account);
413  auto const accountIter =
414  txQAccount.transactions.find(candidateIter->sequence);
415  assert(accountIter != txQAccount.transactions.end());
416  assert(accountIter == txQAccount.transactions.begin());
417  assert(byFee_.iterator_to(accountIter->second) == candidateIter);
418  auto const accountNextIter = std::next(accountIter);
419  /* Check if the next transaction for this account has the
420  next sequence number, and a higher fee level, which means
421  we skipped it earlier, and need to try it again.
422  Edge cases: If the next account tx has a lower fee level,
423  it's going to be later in the fee queue, so we haven't
424  skipped it yet.
425  If the next tx has an equal fee level, it was either
426  submitted later, so it's also going to be later in the
427  fee queue, OR the current was resubmitted to bump up
428  the fee level, and we have skipped that next tx. In
429  the latter case, continue through the fee queue anyway
430  to head off potential ordering manipulation problems.
431  */
432  auto const feeNextIter = std::next(candidateIter);
433  bool const useAccountNext =
434  accountNextIter != txQAccount.transactions.end() &&
435  accountNextIter->first == candidateIter->sequence + 1 &&
436  (feeNextIter == byFee_.end() ||
437  accountNextIter->second.feeLevel > feeNextIter->feeLevel);
438  auto const candidateNextIter = byFee_.erase(candidateIter);
439  txQAccount.transactions.erase(accountIter);
440  return useAccountNext ? byFee_.iterator_to(accountNextIter->second)
441  : candidateNextIter;
442 }
443 
444 auto
445 TxQ::erase(
446  TxQ::TxQAccount& txQAccount,
447  TxQ::TxQAccount::TxMap::const_iterator begin,
448  TxQ::TxQAccount::TxMap::const_iterator end) -> TxQAccount::TxMap::iterator
449 {
450  for (auto it = begin; it != end; ++it)
451  {
452  byFee_.erase(byFee_.iterator_to(it->second));
453  }
454  return txQAccount.transactions.erase(begin, end);
455 }
456 
459  Application& app,
460  OpenView& view,
461  STTx const& tx,
462  TxQ::AccountMap::iterator const& accountIter,
463  TxQAccount::TxMap::iterator beginTxIter,
464  FeeLevel64 feeLevelPaid,
465  PreflightResult const& pfresult,
466  std::size_t const txExtraCount,
467  ApplyFlags flags,
468  FeeMetrics::Snapshot const& metricsSnapshot,
469  beast::Journal j)
470 {
471  auto const tSeq = tx.getSequence();
472  assert(beginTxIter != accountIter->second.transactions.end());
473  auto const aSeq = beginTxIter->first;
474 
475  auto const requiredTotalFeeLevel = FeeMetrics::escalatedSeriesFeeLevel(
476  metricsSnapshot, view, txExtraCount, tSeq - aSeq + 1);
477  /* If the computation for the total manages to overflow (however extremely
478  unlikely), then there's no way we can confidently verify if the queue
479  can be cleared.
480  */
481  if (!requiredTotalFeeLevel.first)
482  return std::make_pair(telINSUF_FEE_P, false);
483 
484  // Unlike multiTx, this check is only concerned with the range
485  // from [aSeq, tSeq)
486  auto endTxIter = accountIter->second.transactions.lower_bound(tSeq);
487 
488  auto const totalFeeLevelPaid = std::accumulate(
489  beginTxIter,
490  endTxIter,
491  feeLevelPaid,
492  [](auto const& total, auto const& txn) {
493  return total + txn.second.feeLevel;
494  });
495 
496  // This transaction did not pay enough, so fall back to the normal process.
497  if (totalFeeLevelPaid < requiredTotalFeeLevel.second)
498  return std::make_pair(telINSUF_FEE_P, false);
499 
500  // This transaction paid enough to clear out the queue.
501  // Attempt to apply the queued transactions.
502  for (auto it = beginTxIter; it != endTxIter; ++it)
503  {
504  auto txResult = it->second.apply(app, view, j);
505  // Succeed or fail, use up a retry, because if the overall
506  // process fails, we want the attempt to count. If it all
507  // succeeds, the MaybeTx will be destructed, so it'll be
508  // moot.
509  --it->second.retriesRemaining;
510  it->second.lastResult = txResult.first;
511  if (!txResult.second)
512  {
513  // Transaction failed to apply. Fall back to the normal process.
514  return std::make_pair(txResult.first, false);
515  }
516  }
517  // Apply the current tx. Because the state of the view has been changed
518  // by the queued txs, we also need to preclaim again.
519  auto const txResult = doApply(preclaim(pfresult, app, view), app, view);
520 
521  if (txResult.second)
522  {
523  // All of the queued transactions applied, so remove them from the
524  // queue.
525  endTxIter = erase(accountIter->second, beginTxIter, endTxIter);
526  // If `tx` is replacing a queued tx, delete that one, too.
527  if (endTxIter != accountIter->second.transactions.end() &&
528  endTxIter->first == tSeq)
529  erase(accountIter->second, endTxIter, std::next(endTxIter));
530  }
531 
532  return txResult;
533 }
534 
535 /*
536  How the decision to apply, queue, or reject is made:
537  1. Does `preflight` indicate that the tx is valid?
538  No: Return the `TER` from `preflight`. Stop.
539  Yes: Continue to next step.
540  2. Is there already a tx for the same account with the
541  same sequence number in the queue?
542  Yes: Is `txn`'s fee `retrySequencePercent` higher than the
543  queued transaction's fee? And is this the last tx
544  in the queue for that account, or are both txs
545  non-blockers?
546  Yes: Remove the queued transaction. Continue to next
547  step.
548  No: Reject `txn` with `telCAN_NOT_QUEUE_FEE`. Stop.
549  No: Continue to next step.
550  3. Does this tx have the expected sequence number for the
551  account?
552  Yes: Continue to next step.
553  No: Are all the intervening sequence numbers also in the
554  queue?
555  No: Continue to the next step. (We expect the next
556  step to return `terPRE_SEQ`, but won't short
557  circuit that logic.)
558  Yes: Is the fee more than `multiTxnPercent` higher
559  than the previous tx?
560  No: Reject with `telINSUF_FEE_P`. Stop.
561  Yes: Are any of the prior sequence txs blockers?
562  Yes: Reject with `telCAN_NOT_QUEUE_BLOCKED`. Stop.
563  No: Are the fees in-flight of the other
564  queued txs >= than the account
565  balance or minimum account reserve?
566  Yes: Reject with `telCAN_NOT_QUEUE_BALANCE`. Stop.
567  No: Create a throwaway sandbox `View`. Modify
568  the account's sequence number to match
569  the tx (avoid `terPRE_SEQ`), and decrease
570  the account balance by the total fees and
571  maximum spend of the other in-flight txs.
572  Continue to the next step.
573  4. Does `preclaim` indicate that the account is likely to claim
574  a fee (using the throwaway sandbox `View` created above,
575  if appropriate)?
576  No: Return the `TER` from `preclaim`. Stop.
577  Yes: Continue to the next step.
578  5. Did we create a throwaway sandbox `View`?
579  Yes: Continue to the next step.
580  No: Is the `txn`s fee level >= the required fee level?
581  Yes: `txn` can be applied to the open ledger. Pass
582  it to `doApply()` and return that result.
583  No: Continue to the next step.
584  6. Can the tx be held in the queue? (See TxQ::canBeHeld).
585  No: Reject `txn` with `telCAN_NOT_QUEUE_FULL`
586  if not. Stop.
587  Yes: Continue to the next step.
588  7. Is the queue full?
589  No: Continue to the next step.
590  Yes: Is the `txn`'s fee level higher than the end /
591  lowest fee level item's fee level?
592  Yes: Remove the end item. Continue to the next step.
593  No: Reject `txn` with a low fee TER code.
594  8. Put `txn` in the queue.
595 */
598  Application& app,
599  OpenView& view,
600  std::shared_ptr<STTx const> const& tx,
601  ApplyFlags flags,
602  beast::Journal j)
603 {
604  auto const account = (*tx)[sfAccount];
605  auto const transactionID = tx->getTransactionID();
606  auto const tSeq = tx->getSequence();
607  // See if the transaction is valid, properly formed,
608  // etc. before doing potentially expensive queue
609  // replace and multi-transaction operations.
610  auto const pfresult = preflight(app, view.rules(), *tx, flags, j);
611  if (pfresult.ter != tesSUCCESS)
612  return {pfresult.ter, false};
613 
614  struct MultiTxn
615  {
616  explicit MultiTxn() = default;
617 
618  boost::optional<ApplyViewImpl> applyView;
619  boost::optional<OpenView> openView;
620 
621  TxQAccount::TxMap::iterator nextTxIter;
622 
623  XRPAmount fee = beast::zero;
624  XRPAmount potentialSpend = beast::zero;
625  bool includeCurrentFee = false;
626  };
627 
628  boost::optional<MultiTxn> multiTxn;
629  boost::optional<TxConsequences const> consequences;
630  boost::optional<FeeMultiSet::iterator> replacedItemDeleteIter;
631 
632  std::lock_guard lock(mutex_);
633 
634  auto const metricsSnapshot = feeMetrics_.getSnapshot();
635 
636  // We may need the base fee for multiple transactions
637  // or transaction replacement, so just pull it up now.
638  // TODO: Do we want to avoid doing it again during
639  // preclaim?
640  auto const baseFee =
641  view.fees().toDrops(calculateBaseFee(view, *tx)).second;
642  auto const feeLevelPaid = getFeeLevelPaid(*tx, baseLevel, baseFee, setup_);
643  auto const requiredFeeLevel = [&]() {
644  auto feeLevel = FeeMetrics::scaleFeeLevel(metricsSnapshot, view);
645  if ((flags & tapPREFER_QUEUE) && byFee_.size())
646  {
647  return std::max(feeLevel, byFee_.begin()->feeLevel);
648  }
649  return feeLevel;
650  }();
651 
652  auto accountIter = byAccount_.find(account);
653  bool const accountExists = accountIter != byAccount_.end();
654 
655  // Is there a transaction for the same account with the
656  // same sequence number already in the queue?
657  if (accountExists)
658  {
659  auto& txQAcct = accountIter->second;
660  auto existingIter = txQAcct.transactions.find(tSeq);
661  if (existingIter != txQAcct.transactions.end())
662  {
663  // Is the current transaction's fee higher than
664  // the queued transaction's fee + a percentage
665  auto requiredRetryLevel = increase(
666  existingIter->second.feeLevel, setup_.retrySequencePercent);
667  JLOG(j_.trace())
668  << "Found transaction in queue for account " << account
669  << " with sequence number " << tSeq << " new txn fee level is "
670  << feeLevelPaid << ", old txn fee level is "
671  << existingIter->second.feeLevel
672  << ", new txn needs fee level of " << requiredRetryLevel;
673  if (feeLevelPaid > requiredRetryLevel ||
674  (existingIter->second.feeLevel < requiredFeeLevel &&
675  feeLevelPaid >= requiredFeeLevel &&
676  existingIter == txQAcct.transactions.begin()))
677  {
678  /* Either the fee is high enough to retry or
679  the prior txn is the first for this account, and
680  could not get into the open ledger, but this one can.
681  */
682 
683  /* A normal tx can't be replaced by a blocker, unless it's
684  the last tx in the queue for the account.
685  */
686  if (std::next(existingIter) != txQAcct.transactions.end())
687  {
688  // Normally, only the last tx in the queue will have
689  // !consequences, but an expired transaction can be
690  // replaced, and that replacement won't have it set,
691  // and that's ok.
692  if (!existingIter->second.consequences)
693  existingIter->second.consequences.emplace(
695  *existingIter->second.pfresult));
696 
697  if (existingIter->second.consequences->category ==
699  {
700  assert(!consequences);
701  consequences.emplace(calculateConsequences(pfresult));
702  if (consequences->category == TxConsequences::blocker)
703  {
704  // Can't replace a normal transaction in the
705  // middle of the queue with a blocker.
706  JLOG(j_.trace()) << "Ignoring blocker transaction "
707  << transactionID
708  << " in favor of normal queued "
709  << existingIter->second.txID;
710  return {telCAN_NOT_QUEUE_BLOCKS, false};
711  }
712  }
713  }
714 
715  // Remove the queued transaction and continue
716  JLOG(j_.trace()) << "Removing transaction from queue "
717  << existingIter->second.txID << " in favor of "
718  << transactionID;
719  // Then save the queued tx to remove from the queue if
720  // the new tx succeeds or gets queued. DO NOT REMOVE
721  // if the new tx fails, because there may be other txs
722  // dependent on it in the queue.
723  auto deleteIter = byFee_.iterator_to(existingIter->second);
724  assert(deleteIter != byFee_.end());
725  assert(&existingIter->second == &*deleteIter);
726  assert(deleteIter->sequence == tSeq);
727  assert(deleteIter->account == txQAcct.account);
728  replacedItemDeleteIter = deleteIter;
729  }
730  else
731  {
732  // Drop the current transaction
733  JLOG(j_.trace())
734  << "Ignoring transaction " << transactionID
735  << " in favor of queued " << existingIter->second.txID;
736  return {telCAN_NOT_QUEUE_FEE, false};
737  }
738  }
739  }
740 
741  // If there are other transactions in the queue
742  // for this account, account for that before the pre-checks,
743  // so we don't get a false terPRE_SEQ.
744  if (accountExists)
745  {
746  if (auto const sle = view.read(keylet::account(account)); sle)
747  {
748  auto& txQAcct = accountIter->second;
749  auto const aSeq = (*sle)[sfSequence];
750 
751  if (aSeq < tSeq)
752  {
753  // If the transaction is queueable, create the multiTxn
754  // object to hold the info we need to adjust for
755  // prior txns. Otherwise, let preclaim fail as if
756  // we didn't have the queue at all.
757  if (canBeHeld(
758  *tx, flags, view, accountIter, replacedItemDeleteIter))
759  multiTxn.emplace();
760  }
761 
762  if (multiTxn)
763  {
764  /* See if the queue has entries for all the
765  seq's in [aSeq, tSeq). Total up all the
766  consequences while we're checking. If one
767  turns up missing or is a blocker, abort.
768  */
769  multiTxn->nextTxIter = txQAcct.transactions.find(aSeq);
770  auto workingIter = multiTxn->nextTxIter;
771  auto workingSeq = aSeq;
772  for (; workingIter != txQAcct.transactions.end();
773  ++workingIter, ++workingSeq)
774  {
775  if (workingSeq < tSeq && workingIter->first != workingSeq)
776  {
777  // If any transactions are missing before `tx`, abort.
778  multiTxn.reset();
779  break;
780  }
781  if (workingIter->first == tSeq - 1)
782  {
783  // Is the current transaction's fee higher than
784  // the previous transaction's fee + a percentage
785  auto requiredMultiLevel = increase(
786  workingIter->second.feeLevel,
788 
789  if (feeLevelPaid <= requiredMultiLevel)
790  {
791  // Drop the current transaction
792  JLOG(j_.trace())
793  << "Ignoring transaction " << transactionID
794  << ". Needs fee level of " <<
795 
796  requiredMultiLevel << ". Only paid "
797  << feeLevelPaid;
798  return {telINSUF_FEE_P, false};
799  }
800  }
801  if (workingIter->first == tSeq)
802  {
803  // If we're replacing this transaction, don't
804  // count it.
805  assert(replacedItemDeleteIter);
806  multiTxn->includeCurrentFee = std::next(workingIter) !=
807  txQAcct.transactions.end();
808  continue;
809  }
810  if (!workingIter->second.consequences)
811  workingIter->second.consequences.emplace(
813  *workingIter->second.pfresult));
814  // Don't worry about the blocker status of txs
815  // after the current.
816  if (workingIter->first < tSeq &&
817  workingIter->second.consequences->category ==
819  {
820  // Drop the current transaction, because it's
821  // blocked by workingIter.
822  JLOG(j_.trace())
823  << "Ignoring transaction " << transactionID
824  << ". A blocker-type transaction "
825  << "is in the queue.";
826  return {telCAN_NOT_QUEUE_BLOCKED, false};
827  }
828  multiTxn->fee += workingIter->second.consequences->fee;
829  multiTxn->potentialSpend +=
830  workingIter->second.consequences->potentialSpend;
831  }
832  if (workingSeq < tSeq)
833  // Transactions are missing before `tx`.
834  multiTxn.reset();
835  }
836 
837  if (multiTxn)
838  {
839  /* Check if the total fees in flight are greater
840  than the account's current balance, or the
841  minimum reserve. If it is, then there's a risk
842  that the fees won't get paid, so drop this
843  transaction with a telCAN_NOT_QUEUE_BALANCE result.
844  TODO: Decide whether to count the current txn fee
845  in this limit if it's the last transaction for
846  this account. Currently, it will not count,
847  for the same reason that it is not checked on
848  the first transaction.
849  Assume: Minimum account reserve is 20 XRP.
850  Example 1: If I have 1,000,000 XRP, I can queue
851  a transaction with a 1,000,000 XRP fee. In
852  the meantime, some other transaction may
853  lower my balance (eg. taking an offer). When
854  the transaction executes, I will either
855  spend the 1,000,000 XRP, or the transaction
856  will get stuck in the queue with a
857  `terINSUF_FEE_B`.
858  Example 2: If I have 1,000,000 XRP, and I queue
859  10 transactions with 0.1 XRP fee, I have 1 XRP
860  in flight. I can now queue another tx with a
861  999,999 XRP fee. When the first 10 execute,
862  they're guaranteed to pay their fee, because
863  nothing can eat into my reserve. The last
864  transaction, again, will either spend the
865  999,999 XRP, or get stuck in the queue.
866  Example 3: If I have 1,000,000 XRP, and I queue
867  7 transactions with 3 XRP fee, I have 21 XRP
868  in flight. I can not queue any more transactions,
869  no matter how small or large the fee.
870  Transactions stuck in the queue are mitigated by
871  LastLedgerSeq and MaybeTx::retriesRemaining.
872  */
873  auto const balance = (*sle)[sfBalance].xrp();
874  /* Get the minimum possible reserve. If fees exceed
875  this amount, the transaction can't be queued.
876  Considering that typical fees are several orders
877  of magnitude smaller than any current or expected
878  future reserve, this calculation is simpler than
879  trying to figure out the potential changes to
880  the ownerCount that may occur to the account
881  as a result of these transactions, and removes
882  any need to account for other transactions that
883  may affect the owner count while these are queued.
884  */
885  auto const reserve = view.fees().accountReserve(0);
886  auto totalFee = multiTxn->fee;
887  if (multiTxn->includeCurrentFee)
888  totalFee += (*tx)[sfFee].xrp();
889  if (totalFee >= balance || totalFee >= reserve)
890  {
891  // Drop the current transaction
892  JLOG(j_.trace()) << "Ignoring transaction " << transactionID
893  << ". Total fees in flight too high.";
894  return {telCAN_NOT_QUEUE_BALANCE, false};
895  }
896 
897  // Create the test view from the current view
898  multiTxn->applyView.emplace(&view, flags);
899  multiTxn->openView.emplace(&*multiTxn->applyView);
900 
901  auto const sleBump =
902  multiTxn->applyView->peek(keylet::account(account));
903  if (!sleBump)
904  return {tefINTERNAL, false};
905 
906  auto const potentialTotalSpend = multiTxn->fee +
907  std::min(balance - std::min(balance, reserve),
908  multiTxn->potentialSpend);
909  assert(potentialTotalSpend > XRPAmount{0});
910  sleBump->setFieldAmount(
911  sfBalance, balance - potentialTotalSpend);
912  sleBump->setFieldU32(sfSequence, tSeq);
913  }
914  }
915  }
916 
917  // See if the transaction is likely to claim a fee.
918  assert(!multiTxn || multiTxn->openView);
919  auto const pcresult =
920  preclaim(pfresult, app, multiTxn ? *multiTxn->openView : view);
921  if (!pcresult.likelyToClaimFee)
922  return {pcresult.ter, false};
923 
924  // Too low of a fee should get caught by preclaim
925  assert(feeLevelPaid >= baseLevel);
926 
927  JLOG(j_.trace()) << "Transaction " << transactionID << " from account "
928  << account << " has fee level of " << feeLevelPaid
929  << " needs at least " << requiredFeeLevel
930  << " to get in the open ledger, which has "
931  << view.txCount() << " entries.";
932 
933  /* Quick heuristic check to see if it's worth checking that this
934  tx has a high enough fee to clear all the txs in the queue.
935  1) Transaction is trying to get into the open ledger
936  2) Must be an account already in the queue.
937  3) Must be have passed the multiTxn checks (tx is not the next
938  account seq, the skipped seqs are in the queue, the reserve
939  doesn't get exhausted, etc).
940  4) The next transaction must not have previously tried and failed
941  to apply to an open ledger.
942  5) Tx must be paying more than just the required fee level to
943  get itself into the queue.
944  6) Fee level must be escalated above the default (if it's not,
945  then the first tx _must_ have failed to process in `accept`
946  for some other reason. Tx is allowed to queue in case
947  conditions change, but don't waste the effort to clear).
948  7) Tx is not a 0-fee / free transaction, regardless of fee level.
949  */
950  if (!(flags & tapPREFER_QUEUE) && accountExists &&
951  multiTxn.is_initialized() &&
952  multiTxn->nextTxIter->second.retriesRemaining ==
954  feeLevelPaid > requiredFeeLevel && requiredFeeLevel > baseLevel &&
955  baseFee != 0)
956  {
957  OpenView sandbox(open_ledger, &view, view.rules());
958 
959  auto result = tryClearAccountQueue(
960  app,
961  sandbox,
962  *tx,
963  accountIter,
964  multiTxn->nextTxIter,
965  feeLevelPaid,
966  pfresult,
967  view.txCount(),
968  flags,
969  metricsSnapshot,
970  j);
971  if (result.second)
972  {
973  sandbox.apply(view);
974  /* Can't erase(*replacedItemDeleteIter) here because success
975  implies that it has already been deleted.
976  */
977  return result;
978  }
979  }
980 
981  // Can transaction go in open ledger?
982  if (!multiTxn && feeLevelPaid >= requiredFeeLevel)
983  {
984  // Transaction fee is sufficient to go in open ledger immediately
985 
986  JLOG(j_.trace()) << "Applying transaction " << transactionID
987  << " to open ledger.";
988 
989  auto const [txnResult, didApply] = doApply(pcresult, app, view);
990 
991  JLOG(j_.trace()) << "New transaction " << transactionID
992  << (didApply ? " applied successfully with "
993  : " failed with ")
994  << transToken(txnResult);
995 
996  if (didApply && replacedItemDeleteIter)
997  erase(*replacedItemDeleteIter);
998  return {txnResult, didApply};
999  }
1000 
1001  // If `multiTxn` has a value, then `canBeHeld` has already been verified
1002  if (!multiTxn &&
1003  !canBeHeld(*tx, flags, view, accountIter, replacedItemDeleteIter))
1004  {
1005  // Bail, transaction cannot be held
1006  JLOG(j_.trace()) << "Transaction " << transactionID
1007  << " can not be held";
1008  return {telCAN_NOT_QUEUE, false};
1009  }
1010 
1011  // If the queue is full, decide whether to drop the current
1012  // transaction or the last transaction for the account with
1013  // the lowest fee.
1014  if (!replacedItemDeleteIter && isFull())
1015  {
1016  auto lastRIter = byFee_.rbegin();
1017  if (lastRIter->account == account)
1018  {
1019  JLOG(j_.warn())
1020  << "Queue is full, and transaction " << transactionID
1021  << " would kick a transaction from the same account ("
1022  << account << ") out of the queue.";
1023  return {telCAN_NOT_QUEUE_FULL, false};
1024  }
1025  auto const& endAccount = byAccount_.at(lastRIter->account);
1026  auto endEffectiveFeeLevel = [&]() {
1027  // Compute the average of all the txs for the endAccount,
1028  // but only if the last tx in the queue has a lower fee
1029  // level than this candidate tx.
1030  if (lastRIter->feeLevel > feeLevelPaid ||
1031  endAccount.transactions.size() == 1)
1032  return lastRIter->feeLevel;
1033 
1035  auto endTotal = std::accumulate(
1036  endAccount.transactions.begin(),
1037  endAccount.transactions.end(),
1039  [&](auto const& total, auto const& txn) {
1040  // Check for overflow.
1041  auto next =
1042  txn.second.feeLevel / endAccount.transactions.size();
1043  auto mod =
1044  txn.second.feeLevel % endAccount.transactions.size();
1045  if (total.first >= max - next || total.second >= max - mod)
1046  return std::make_pair(max, FeeLevel64{0});
1047  return std::make_pair(
1048  total.first + next, total.second + mod);
1049  });
1050  return endTotal.first +
1051  endTotal.second / endAccount.transactions.size();
1052  }();
1053  if (feeLevelPaid > endEffectiveFeeLevel)
1054  {
1055  // The queue is full, and this transaction is more
1056  // valuable, so kick out the cheapest transaction.
1057  auto dropRIter = endAccount.transactions.rbegin();
1058  assert(dropRIter->second.account == lastRIter->account);
1059  JLOG(j_.warn())
1060  << "Removing last item of account " << lastRIter->account
1061  << " from queue with average fee of " << endEffectiveFeeLevel
1062  << " in favor of " << transactionID << " with fee of "
1063  << feeLevelPaid;
1064  erase(byFee_.iterator_to(dropRIter->second));
1065  }
1066  else
1067  {
1068  JLOG(j_.warn())
1069  << "Queue is full, and transaction " << transactionID
1070  << " fee is lower than end item's account average fee";
1071  return {telCAN_NOT_QUEUE_FULL, false};
1072  }
1073  }
1074 
1075  // Hold the transaction in the queue.
1076  if (replacedItemDeleteIter)
1077  erase(*replacedItemDeleteIter);
1078  if (!accountExists)
1079  {
1080  // Create a new TxQAccount object and add the byAccount lookup.
1081  bool created;
1082  std::tie(accountIter, created) =
1083  byAccount_.emplace(account, TxQAccount(tx));
1084  (void)created;
1085  assert(created);
1086  }
1087  // Modify the flags for use when coming out of the queue.
1088  // These changes _may_ cause an extra `preflight`, but as long as
1089  // the `HashRouter` still knows about the transaction, the signature
1090  // will not be checked again, so the cost should be minimal.
1091 
1092  // Don't allow soft failures, which can lead to retries
1093  flags &= ~tapRETRY;
1094 
1095  // Don't queue because we're already in the queue
1096  flags &= ~tapPREFER_QUEUE;
1097 
1098  auto& candidate = accountIter->second.add(
1099  {tx, transactionID, feeLevelPaid, flags, pfresult});
1100  /* Normally we defer figuring out the consequences until
1101  something later requires us to, but if we know the
1102  consequences now, save them for later.
1103  */
1104  if (consequences)
1105  candidate.consequences.emplace(*consequences);
1106  // Then index it into the byFee lookup.
1107  byFee_.insert(candidate);
1108  JLOG(j_.debug()) << "Added transaction " << candidate.txID
1109  << " with result " << transToken(pfresult.ter) << " from "
1110  << (accountExists ? "existing" : "new") << " account "
1111  << candidate.account << " to queue."
1112  << " Flags: " << flags;
1113 
1114  return {terQUEUED, false};
1115 }
1116 
1117 /*
1118  1. Update the fee metrics based on the fee levels of the
1119  txs in the validated ledger and whether consensus is
1120  slow.
1121  2. Adjust the maximum queue size to be enough to hold
1122  `ledgersInQueue` ledgers.
1123  3. Remove any transactions from the queue for which the
1124  `LastLedgerSequence` has passed.
1125  4. Remove any account objects that have no candidates
1126  under them.
1127 
1128 */
1129 void
1130 TxQ::processClosedLedger(Application& app, ReadView const& view, bool timeLeap)
1131 {
1132  std::lock_guard lock(mutex_);
1133 
1134  feeMetrics_.update(app, view, timeLeap, setup_);
1135  auto const& snapshot = feeMetrics_.getSnapshot();
1136 
1137  auto ledgerSeq = view.info().seq;
1138 
1139  if (!timeLeap)
1140  maxSize_ = std::max(
1141  snapshot.txnsExpected * setup_.ledgersInQueue, setup_.queueSizeMin);
1142 
1143  // Remove any queued candidates whose LastLedgerSequence has gone by.
1144  for (auto candidateIter = byFee_.begin(); candidateIter != byFee_.end();)
1145  {
1146  if (candidateIter->lastValid && *candidateIter->lastValid <= ledgerSeq)
1147  {
1148  byAccount_.at(candidateIter->account).dropPenalty = true;
1149  candidateIter = erase(candidateIter);
1150  }
1151  else
1152  {
1153  ++candidateIter;
1154  }
1155  }
1156 
1157  // Remove any TxQAccounts that don't have candidates
1158  // under them
1159  for (auto txQAccountIter = byAccount_.begin();
1160  txQAccountIter != byAccount_.end();)
1161  {
1162  if (txQAccountIter->second.empty())
1163  txQAccountIter = byAccount_.erase(txQAccountIter);
1164  else
1165  ++txQAccountIter;
1166  }
1167 }
1168 
1169 /*
1170  How the txs are moved from the queue to the new open ledger.
1171 
1172  1. Iterate over the txs from highest fee level to lowest.
1173  For each tx:
1174  a) Is this the first tx in the queue for this account?
1175  No: Skip this tx. We'll come back to it later.
1176  Yes: Continue to the next sub-step.
1177  b) Is the tx fee level less than the current required
1178  fee level?
1179  Yes: Stop iterating. Continue to the next step.
1180  No: Try to apply the transaction. Did it apply?
1181  Yes: Take it out of the queue. Continue with
1182  the next appropriate candidate (see below).
1183  No: Did it get a tef, tem, or tel, or has it
1184  retried `MaybeTx::retriesAllowed`
1185  times already?
1186  Yes: Take it out of the queue. Continue
1187  with the next appropriate candidate
1188  (see below).
1189  No: Leave it in the queue, track the retries,
1190  and continue iterating.
1191  2. Return indicator of whether the open ledger was modified.
1192 
1193  "Appropriate candidate" is defined as the tx that has the
1194  highest fee level of:
1195  * the tx for the current account with the next sequence.
1196  * the next tx in the queue, simply ordered by fee.
1197 */
1198 bool
1199 TxQ::accept(Application& app, OpenView& view)
1200 {
1201  /* Move transactions from the queue from largest fee level to smallest.
1202  As we add more transactions, the required fee level will increase.
1203  Stop when the transaction fee level gets lower than the required fee
1204  level.
1205  */
1206 
1207  auto ledgerChanged = false;
1208 
1209  std::lock_guard lock(mutex_);
1210 
1211  auto const metricSnapshot = feeMetrics_.getSnapshot();
1212 
1213  for (auto candidateIter = byFee_.begin(); candidateIter != byFee_.end();)
1214  {
1215  auto& account = byAccount_.at(candidateIter->account);
1216  if (candidateIter->sequence > account.transactions.begin()->first)
1217  {
1218  // This is not the first transaction for this account, so skip it.
1219  // It can not succeed yet.
1220  JLOG(j_.trace())
1221  << "Skipping queued transaction " << candidateIter->txID
1222  << " from account " << candidateIter->account
1223  << " as it is not the first.";
1224  candidateIter++;
1225  continue;
1226  }
1227  auto const requiredFeeLevel =
1228  FeeMetrics::scaleFeeLevel(metricSnapshot, view);
1229  auto const feeLevelPaid = candidateIter->feeLevel;
1230  JLOG(j_.trace()) << "Queued transaction " << candidateIter->txID
1231  << " from account " << candidateIter->account
1232  << " has fee level of " << feeLevelPaid
1233  << " needs at least " << requiredFeeLevel;
1234  if (feeLevelPaid >= requiredFeeLevel)
1235  {
1236  auto firstTxn = candidateIter->txn;
1237 
1238  JLOG(j_.trace()) << "Applying queued transaction "
1239  << candidateIter->txID << " to open ledger.";
1240 
1241  auto const [txnResult, didApply] =
1242  candidateIter->apply(app, view, j_);
1243 
1244  if (didApply)
1245  {
1246  // Remove the candidate from the queue
1247  JLOG(j_.debug())
1248  << "Queued transaction " << candidateIter->txID
1249  << " applied successfully with " << transToken(txnResult)
1250  << ". Remove from queue.";
1251 
1252  candidateIter = eraseAndAdvance(candidateIter);
1253  ledgerChanged = true;
1254  }
1255  else if (
1256  isTefFailure(txnResult) || isTemMalformed(txnResult) ||
1257  candidateIter->retriesRemaining <= 0)
1258  {
1259  if (candidateIter->retriesRemaining <= 0)
1260  account.retryPenalty = true;
1261  else
1262  account.dropPenalty = true;
1263  JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID
1264  << " failed with " << transToken(txnResult)
1265  << ". Remove from queue.";
1266  candidateIter = eraseAndAdvance(candidateIter);
1267  }
1268  else
1269  {
1270  JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID
1271  << " failed with " << transToken(txnResult)
1272  << ". Leave in queue."
1273  << " Applied: " << didApply
1274  << ". Flags: " << candidateIter->flags;
1275  if (account.retryPenalty && candidateIter->retriesRemaining > 2)
1276  candidateIter->retriesRemaining = 1;
1277  else
1278  --candidateIter->retriesRemaining;
1279  candidateIter->lastResult = txnResult;
1280  if (account.dropPenalty && account.transactions.size() > 1 &&
1281  isFull<95>())
1282  {
1283  /* The queue is close to full, this account has multiple
1284  txs queued, and this account has had a transaction
1285  fail. Even though we're giving this transaction another
1286  chance, chances are it won't recover. So we don't make
1287  things worse, drop the _last_ transaction for this
1288  account.
1289  */
1290  auto dropRIter = account.transactions.rbegin();
1291  assert(dropRIter->second.account == candidateIter->account);
1292  JLOG(j_.warn()) << "Queue is nearly full, and transaction "
1293  << candidateIter->txID << " failed with "
1294  << transToken(txnResult)
1295  << ". Removing last item of account "
1296  << account.account;
1297  auto endIter = byFee_.iterator_to(dropRIter->second);
1298  assert(endIter != candidateIter);
1299  erase(endIter);
1300  }
1301  ++candidateIter;
1302  }
1303  }
1304  else
1305  {
1306  break;
1307  }
1308  }
1309 
1310  return ledgerChanged;
1311 }
1312 
1314 TxQ::getMetrics(OpenView const& view) const
1315 {
1316  Metrics result;
1317 
1318  std::lock_guard lock(mutex_);
1319 
1320  auto const snapshot = feeMetrics_.getSnapshot();
1321 
1322  result.txCount = byFee_.size();
1323  result.txQMaxSize = maxSize_;
1324  result.txInLedger = view.txCount();
1325  result.txPerLedger = snapshot.txnsExpected;
1326  result.referenceFeeLevel = baseLevel;
1327  result.minProcessingFeeLevel =
1328  isFull() ? byFee_.rbegin()->feeLevel + FeeLevel64{1} : baseLevel;
1329  result.medFeeLevel = snapshot.escalationMultiplier;
1330  result.openLedgerFeeLevel = FeeMetrics::scaleFeeLevel(snapshot, view);
1331 
1332  return result;
1333 }
1334 
1336 TxQ::getTxRequiredFeeAndSeq(
1337  OpenView const& view,
1338  std::shared_ptr<STTx const> const& tx) const
1339 {
1340  auto const account = (*tx)[sfAccount];
1341 
1342  std::lock_guard lock(mutex_);
1343 
1344  auto const snapshot = feeMetrics_.getSnapshot();
1345  auto const baseFee =
1346  view.fees().toDrops(calculateBaseFee(view, *tx)).second;
1347  auto const fee = FeeMetrics::scaleFeeLevel(snapshot, view);
1348 
1349  auto const accountSeq = [&view, &account]() -> std::uint32_t {
1350  auto const sle = view.read(keylet::account(account));
1351  if (sle)
1352  return (*sle)[sfSequence];
1353  return 0;
1354  }();
1355 
1356  auto availableSeq = accountSeq;
1357 
1358  if (auto iter{byAccount_.find(account)}; iter != byAccount_.end())
1359  {
1360  auto& txQAcct = iter->second;
1361  for (auto const& [seq, _] : txQAcct.transactions)
1362  {
1363  (void)_;
1364  if (seq >= availableSeq)
1365  availableSeq = seq + 1;
1366  }
1367  }
1368 
1369  return {mulDiv(fee, baseFee, baseLevel).second, accountSeq, availableSeq};
1370 }
1371 
1372 auto
1373 TxQ::getAccountTxs(AccountID const& account, ReadView const& view) const
1375 {
1376  std::lock_guard lock(mutex_);
1377 
1378  auto accountIter = byAccount_.find(account);
1379  if (accountIter == byAccount_.end() ||
1380  accountIter->second.transactions.empty())
1381  return {};
1382 
1384 
1385  for (auto const& tx : accountIter->second.transactions)
1386  {
1387  result.emplace(tx.first, [&] {
1388  AccountTxDetails resultTx;
1389  resultTx.feeLevel = tx.second.feeLevel;
1390  if (tx.second.lastValid)
1391  resultTx.lastValid.emplace(*tx.second.lastValid);
1392  if (tx.second.consequences)
1393  resultTx.consequences.emplace(*tx.second.consequences);
1394  return resultTx;
1395  }());
1396  }
1397  return result;
1398 }
1399 
1400 auto
1401 TxQ::getTxs(ReadView const& view) const -> std::vector<TxDetails>
1402 {
1403  std::lock_guard lock(mutex_);
1404 
1405  if (byFee_.empty())
1406  return {};
1407 
1408  std::vector<TxDetails> result;
1409  result.reserve(byFee_.size());
1410 
1411  for (auto const& tx : byFee_)
1412  {
1413  result.emplace_back([&] {
1414  TxDetails resultTx;
1415  resultTx.feeLevel = tx.feeLevel;
1416  if (tx.lastValid)
1417  resultTx.lastValid.emplace(*tx.lastValid);
1418  if (tx.consequences)
1419  resultTx.consequences.emplace(*tx.consequences);
1420  resultTx.account = tx.account;
1421  resultTx.txn = tx.txn;
1422  resultTx.retriesRemaining = tx.retriesRemaining;
1423  BOOST_ASSERT(tx.pfresult);
1424  resultTx.preflightResult = tx.pfresult->ter;
1425  if (tx.lastResult)
1426  resultTx.lastResult.emplace(*tx.lastResult);
1427  return resultTx;
1428  }());
1429  }
1430  return result;
1431 }
1432 
1434 TxQ::doRPC(Application& app) const
1435 {
1436  auto const view = app.openLedger().current();
1437  if (!view)
1438  {
1439  BOOST_ASSERT(false);
1440  return {};
1441  }
1442 
1443  auto const metrics = getMetrics(*view);
1444 
1446 
1447  auto& levels = ret[jss::levels] = Json::objectValue;
1448 
1449  ret[jss::ledger_current_index] = view->info().seq;
1450  ret[jss::expected_ledger_size] = std::to_string(metrics.txPerLedger);
1451  ret[jss::current_ledger_size] = std::to_string(metrics.txInLedger);
1452  ret[jss::current_queue_size] = std::to_string(metrics.txCount);
1453  if (metrics.txQMaxSize)
1454  ret[jss::max_queue_size] = std::to_string(*metrics.txQMaxSize);
1455 
1456  levels[jss::reference_level] = to_string(metrics.referenceFeeLevel);
1457  levels[jss::minimum_level] = to_string(metrics.minProcessingFeeLevel);
1458  levels[jss::median_level] = to_string(metrics.medFeeLevel);
1459  levels[jss::open_ledger_level] = to_string(metrics.openLedgerFeeLevel);
1460 
1461  auto const baseFee = view->fees().base;
1462  auto& drops = ret[jss::drops] = Json::Value();
1463 
1464  // Don't care about the overflow flags
1465  drops[jss::base_fee] =
1466  to_string(toDrops(metrics.referenceFeeLevel, baseFee).second);
1467  drops[jss::minimum_fee] =
1468  to_string(toDrops(metrics.minProcessingFeeLevel, baseFee).second);
1469  drops[jss::median_fee] =
1470  to_string(toDrops(metrics.medFeeLevel, baseFee).second);
1471  drops[jss::open_ledger_fee] = to_string(
1472  toDrops(metrics.openLedgerFeeLevel - FeeLevel64{1}, baseFee).second +
1473  1);
1474 
1475  return ret;
1476 }
1477 
1479 
1480 TxQ::Setup
1481 setup_TxQ(Config const& config)
1482 {
1483  TxQ::Setup setup;
1484  auto const& section = config.section("transaction_queue");
1485  set(setup.ledgersInQueue, "ledgers_in_queue", section);
1486  set(setup.queueSizeMin, "minimum_queue_size", section);
1487  set(setup.retrySequencePercent, "retry_sequence_percent", section);
1488  set(setup.multiTxnPercent, "multi_txn_percent", section);
1490  "minimum_escalation_multiplier",
1491  section);
1492  set(setup.minimumTxnInLedger, "minimum_txn_in_ledger", section);
1493  set(setup.minimumTxnInLedgerSA,
1494  "minimum_txn_in_ledger_standalone",
1495  section);
1496  set(setup.targetTxnInLedger, "target_txn_in_ledger", section);
1497  std::uint32_t max;
1498  if (set(max, "maximum_txn_in_ledger", section))
1499  {
1500  if (max < setup.minimumTxnInLedger)
1501  {
1502  Throw<std::runtime_error>(
1503  "The minimum number of low-fee transactions allowed "
1504  "per ledger (minimum_txn_in_ledger) exceeds "
1505  "the maximum number of low-fee transactions allowed per "
1506  "ledger (maximum_txn_in_ledger).");
1507  }
1508  if (max < setup.minimumTxnInLedgerSA)
1509  {
1510  Throw<std::runtime_error>(
1511  "The minimum number of low-fee transactions allowed "
1512  "per ledger (minimum_txn_in_ledger_standalone) exceeds "
1513  "the maximum number of low-fee transactions allowed per "
1514  "ledger (maximum_txn_in_ledger).");
1515  }
1516 
1517  setup.maximumTxnInLedger.emplace(max);
1518  }
1519 
1520  /* The math works as expected for any value up to and including
1521  MAXINT, but put a reasonable limit on this percentage so that
1522  the factor can't be configured to render escalation effectively
1523  moot. (There are other ways to do that, including
1524  minimum_txn_in_ledger.)
1525  */
1527  "normal_consensus_increase_percent",
1528  section);
1530  boost::algorithm::clamp(setup.normalConsensusIncreasePercent, 0, 1000);
1531 
1532  /* If this percentage is outside of the 0-100 range, the results
1533  are nonsensical (uint overflows happen, so the limit grows
1534  instead of shrinking). 0 is not recommended.
1535  */
1537  "slow_consensus_decrease_percent",
1538  section);
1540  boost::algorithm::clamp(setup.slowConsensusDecreasePercent, 0, 100);
1541 
1542  set(setup.maximumTxnPerAccount, "maximum_txn_per_account", section);
1543  set(setup.minimumLastLedgerBuffer, "minimum_last_ledger_buffer", section);
1545  "zero_basefee_transaction_feelevel",
1546  section);
1547 
1548  setup.standAlone = config.standalone();
1549  return setup;
1550 }
1551 
1552 } // namespace ripple
ripple::TxQ::TxDetails::lastResult
boost::optional< TER > lastResult
If the transactor attempted to apply the transaction to the open ledger from the queue and failed,...
Definition: TxQ.h:263
ripple::TxQ::setup_
const Setup setup_
Setup parameters used to control the behavior of the queue.
Definition: TxQ.h:685
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:1481
ripple::OpenView::txCount
std::size_t txCount() const
Return the number of tx inserted since creation.
Definition: OpenView.cpp:101
ripple::Application
Definition: Application.h:97
std::max_element
T max_element(T... args)
ripple::getFeeLevelPaid
static FeeLevel64 getFeeLevelPaid(STTx const &tx, FeeLevel64 baseRefLevel, XRPAmount refTxnCostDrops, TxQ::Setup const &setup)
Definition: TxQ.cpp:39
ripple::TxQ::MaybeTx::sequence
const TxSeq sequence
Transaction sequence number (sfSequence field).
Definition: TxQ.h:538
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:384
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:149
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:197
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:475
ripple::TxQ::MaybeTx::txn
std::shared_ptr< STTx const > txn
The complete transaction.
Definition: TxQ.h:520
ripple::TxQ::j_
const beast::Journal j_
Journal.
Definition: TxQ.h:687
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
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
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:597
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:617
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:409
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::sfAccount
const SF_Account sfAccount(access, STI_ACCOUNT, 1, "Account")
Definition: SField.h:480
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:188
ripple::FeeLevel64
FeeLevel< std::uint64_t > FeeLevel64
Definition: FeeUnits.h:464
ripple::TxConsequences::normal
@ normal
Moves currency around, creates offers, etc.
Definition: applySteps.h:132
ripple::TxQ::FeeMetrics::getSnapshot
Snapshot getSnapshot() const
Get the current Snapshot.
Definition: TxQ.h:453
std::map::emplace
T emplace(T... args)
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::transToken
std::string transToken(TER code)
Definition: TER.cpp:193
ripple::telCAN_NOT_QUEUE_FULL
@ telCAN_NOT_QUEUE_FULL
Definition: TER.h:63
std::lock_guard
STL class.
ripple::TxQ::TxDetails
Structure that describes a transaction in the queue waiting to be applied to the current open ledger.
Definition: TxQ.h:232
ripple::telCAN_NOT_QUEUE
@ telCAN_NOT_QUEUE
Definition: TER.h:58
std::distance
T distance(T... args)
ripple::TxQ::FeeMetrics::escalationMultiplier_
FeeLevel64 escalationMultiplier_
Based on the median fee of the LCL.
Definition: TxQ.h:394
ripple::TxQ::isFull
bool isFull() const
Is the queue at least fillPercentage full?
Definition: TxQ.cpp:325
ripple::TxQ::FeeAndSeq
Definition: TxQ.h:323
boost
Definition: IPAddress.h:117
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:88
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:409
ripple::Application::openLedger
virtual OpenLedger & openLedger()=0
ripple::TxQ::FeeMetrics::recentTxnCounts_
boost::circular_buffer< std::size_t > recentTxnCounts_
Recent history of transaction counts that exceed the targetTxnCount_.
Definition: TxQ.h:391
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:265
ripple::TxQ::TxQAccount::transactions
TxMap transactions
Sequence number will be used as the key.
Definition: TxQ.h:625
ripple::TxQ::Metrics
Structure returned by TxQ::getMetrics, expressed in reference fee level units.
Definition: TxQ.h:180
ripple::TxQ::MaybeTx::priorTxID
boost::optional< TxID > priorTxID
Prior transaction ID (sfAccountTxnID field).
Definition: TxQ.h:531
ripple::TxQ::Setup::maximumTxnPerAccount
std::uint32_t maximumTxnPerAccount
Maximum number of transactions that can be queued by one account.
Definition: TxQ.h:155
std::sort
T sort(T... args)
algorithm
ripple::sfAccountTxnID
const SF_U256 sfAccountTxnID(access, STI_HASH256, 9, "AccountTxnID")
Definition: SField.h:425
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:75
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:139
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:127
std::tie
T tie(T... args)
std::vector::push_back
T push_back(T... args)
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:57
ripple::TxQ::Setup::targetTxnInLedger
std::uint32_t targetTxnInLedger
Number of transactions per ledger that fee escalation "works towards".
Definition: TxQ.h:116
ripple::TxQ::TxQAccount::remove
bool remove(TxSeq const &sequence)
Remove the candidate with given sequence number from this account.
Definition: TxQ.cpp:306
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::TxQ::AccountTxDetails::consequences
boost::optional< TxConsequences const > consequences
Potential TxConsequences of applying the queued transaction to the open ledger, if known.
Definition: TxQ.h:224
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:243
ripple::TxQ::Setup::standAlone
bool standAlone
Use standalone mode behavior.
Definition: TxQ.h:173
ripple::doApply
std::pair< TER, bool > doApply(PreclaimResult const &preclaimResult, Application &app, OpenView &view)
Apply a prechecked transaction to an OpenView.
Definition: applySteps.cpp:493
ripple::TxQ::byFee_
FeeMultiSet byFee_
The queue itself: the collection of transactions ordered by fee level.
Definition: TxQ.h:699
ripple::PreflightResult
Describes the results of the preflight check.
Definition: applySteps.h:46
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:121
ripple::Config
Definition: Config.h:67
ripple::increase
static FeeLevel64 increase(FeeLevel64 level, std::uint32_t increasePercent)
Definition: TxQ.cpp:65
ripple::TxQ::TxDetails::account
AccountID account
The account the transaction is queued for.
Definition: TxQ.h:238
ripple::TxQ::feeMetrics_
FeeMetrics feeMetrics_
Tracks the current state of the queue.
Definition: TxQ.h:693
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:110
ripple::TxQ::Setup
Structure used to customize TxQ behavior.
Definition: TxQ.h:63
ripple::ReadView::txs_type::begin
iterator begin() const
Definition: ReadView.cpp:162
ripple::Config::standalone
bool standalone() const
Definition: Config.h:222
ripple::isTefFailure
bool isTefFailure(TER x)
Definition: TER.h:564
ripple::TxQ::AccountTxDetails::lastValid
boost::optional< LedgerIndex const > lastValid
LastValidLedger field of the queued transaction, if any.
Definition: TxQ.h:217
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::Fees::toDrops
std::pair< bool, XRPAmount > toDrops(FeeUnit64 const &fee) const
Definition: ReadView.h:71
ripple::TxQ::MaybeTx
Represents a transaction in the queue which may be applied later to the open ledger.
Definition: TxQ.h:511
ripple::TxQ::TxDetails::retriesRemaining
int retriesRemaining
Number of times the transactor can return a retry / ter result when attempting to apply this transact...
Definition: TxQ.h:246
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:153
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:58
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:163
ripple::TxQ::TxQAccount::add
MaybeTx & add(MaybeTx &&)
Add a transaction candidate to this account for queuing.
Definition: TxQ.cpp:294
ripple::tapPREFER_QUEUE
@ tapPREFER_QUEUE
Definition: ApplyView.h:44
ripple::feeunit::TaggedFee
Definition: FeeUnits.h:70
std::map
STL class.
ripple::TxQ::FeeMetrics::minimumTxnCount_
const std::size_t minimumTxnCount_
Minimum value of txnsExpected.
Definition: TxQ.h:379
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:447
ripple::TxQ::FeeMetrics::targetTxnCount_
const std::size_t targetTxnCount_
Number of transactions per ledger that fee escalation "works towards".
Definition: TxQ.h:382
ripple::TxQ::Setup::zeroBaseFeeTransactionFeeLevel
FeeLevel64 zeroBaseFeeTransactionFeeLevel
So we don't deal with "infinite" fee levels, treat any transaction with a 0 base fee (i....
Definition: TxQ.h:171
ripple::STTx::getSequence
std::uint32_t getSequence() const
Definition: STTx.h:117
ripple::TxQ::Metrics::referenceFeeLevel
FeeLevel64 referenceFeeLevel
Reference transaction fee level.
Definition: TxQ.h:194
std::min
T min(T... args)
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:429
ripple::TxQ::~TxQ
virtual ~TxQ()
Destructor.
Definition: TxQ.cpp:318
ripple::TxQ::Metrics::medFeeLevel
FeeLevel64 medFeeLevel
Median fee level of the last ledger.
Definition: TxQ.h:199
ripple::TxQ::FeeMetrics::Snapshot
Snapshot of the externally relevant FeeMetrics fields at any given time.
Definition: TxQ.h:440
ripple::TxQ::FeeMetrics::Snapshot::escalationMultiplier
const FeeLevel64 escalationMultiplier
Definition: TxQ.h:448
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:188
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:313
ripple::TxQ::maxSize_
boost::optional< size_t > maxSize_
Maximum number of transactions allowed in the queue based on the current metrics.
Definition: TxQ.h:713
ripple::sfBalance
const SF_Amount sfBalance(access, STI_AMOUNT, 2, "Balance")
Definition: SField.h:441
ripple::calculateConsequences
TxConsequences calculateConsequences(PreflightResult const &preflightResult)
Determine the XRP balance consequences if a transaction consumes the maximum XRP allowed.
Definition: applySteps.cpp:481
limits
ripple::detail::sumOfFirstSquares
static std::pair< bool, std::uint64_t > sumOfFirstSquares(std::size_t x)
Definition: TxQ.cpp:186
ripple::Fees::accountReserve
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
Definition: ReadView.h:65
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:190
ripple::toDrops
std::pair< bool, XRPAmount > toDrops(FeeLevel< T > const &level, XRPAmount const &baseFee)
Definition: TxQ.h:779
ripple::TxQ::canBeHeld
bool canBeHeld(STTx const &, ApplyFlags const, OpenView const &, AccountMap::iterator, boost::optional< FeeMultiSet::iterator >)
Checks if the indicated transaction fits the conditions for being stored in the queue.
Definition: TxQ.cpp:333
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:718
ripple::TxQ::MaybeTx::retriesAllowed
static constexpr int retriesAllowed
Starting retry count for newly queued transactions.
Definition: TxQ.h:583
ripple::TxQ::FeeMetrics::j_
const beast::Journal j_
Journal.
Definition: TxQ.h:396
ripple::TxQ::Metrics::txCount
std::size_t txCount
Number of transactions in the queue.
Definition: TxQ.h:186
ripple::tapFAIL_HARD
@ tapFAIL_HARD
Definition: ApplyView.h:35
ripple::TxQ::tryClearAccountQueue
std::pair< TER, bool > tryClearAccountQueue(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 all the queued txs for accountIter up to and including tx.
Definition: TxQ.cpp:458
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:91
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
ripple::TxQ::MaybeTx::lastValid
boost::optional< LedgerIndex > lastValid
Expiration ledger for the transaction (sfLastLedgerSequence field).
Definition: TxQ.h:536
ripple::TxQ::Setup::minimumTxnInLedgerSA
std::uint32_t minimumTxnInLedgerSA
Like minimumTxnInLedger for standalone mode.
Definition: TxQ.h:113
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
ripple::TxQ::TxDetails::preflightResult
TER preflightResult
The intermediate result returned by preflight before this transaction was queued, or after it is queu...
Definition: TxQ.h:256
std::make_pair
T make_pair(T... args)
ripple::TxQ::TxQAccount::TxQAccount
TxQAccount(std::shared_ptr< STTx const > const &txn)
Construct from a transaction.
Definition: TxQ.cpp:284
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::Setup::multiTxnPercent
std::int32_t multiTxnPercent
Extra percentage required on the fee level of a queued transaction to queue the transaction with the ...
Definition: TxQ.h:104
ripple::TxQ::Metrics::openLedgerFeeLevel
FeeLevel64 openLedgerFeeLevel
Minimum fee level to get into the current open ledger, bypassing the queue.
Definition: TxQ.h:202
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:81
ripple::TxQ::AccountTxDetails::feeLevel
FeeLevel64 feeLevel
Fee level of the queued transaction.
Definition: TxQ.h:215
ripple::TxQ::byAccount_
AccountMap byAccount_
All of the accounts which currently have any transactions in the queue.
Definition: TxQ.h:706
ripple::TxQ::FeeMetrics::txnsExpected_
std::size_t txnsExpected_
Number of transactions expected per ledger.
Definition: TxQ.h:388
ripple::TxQ::Setup::minimumEscalationMultiplier
FeeLevel64 minimumEscalationMultiplier
Minimum value of the escalation multiplier, regardless of the prior ledger's median fee level.
Definition: TxQ.h:107
ripple::TxConsequences::blocker
@ blocker
Affects the ability of subsequent transactions to claim a fee.
Definition: applySteps.h:135
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:162
ripple::telCAN_NOT_QUEUE_BALANCE
@ telCAN_NOT_QUEUE_BALANCE
Definition: TER.h:59
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:203
ripple::TxQ::Setup::ledgersInQueue
std::size_t ledgersInQueue
Number of ledgers' worth of transactions to allow in the queue.
Definition: TxQ.h:75
std::numeric_limits
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:213
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:445
ripple::sfPreviousTxnID
const SF_U256 sfPreviousTxnID(access, STI_HASH256, 5, "PreviousTxnID", SField::sMD_DeleteFinal)
Definition: SField.h:421
ripple::isTemMalformed
bool isTemMalformed(TER x)
Definition: TER.h:558
ripple::telCAN_NOT_QUEUE_BLOCKED
@ telCAN_NOT_QUEUE_BLOCKED
Definition: TER.h:61
ripple::ReadView::txs
txs_type txs
Definition: ReadView.h:386
ripple::TxQ::TxDetails::txn
std::shared_ptr< STTx const > txn
The full transaction.
Definition: TxQ.h:240
Json::Value
Represents a JSON value.
Definition: json_value.h:145
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:192