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