rippled
CashDiff.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2016 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/ledger/CashDiff.h>
21 #include <ripple/ledger/detail/ApplyStateTable.h>
22 #include <ripple/protocol/st.h>
23 #include <boost/container/static_vector.hpp>
24 #include <cassert>
25 #include <cstdlib> // std::abs()
26 
27 namespace ripple {
28 namespace detail {
29 
30 // Data structure that summarize cash changes in a single ApplyStateTable.
32 {
33  explicit CashSummary() = default;
34 
35  // Sorted vectors. All of the vectors fill in for std::maps.
38 
41 
44 
48 
49  // Note that the OfferAmounts hold the amount *prior* to deletion.
53 
54  bool hasDiff () const
55  {
56  return !xrpChanges.empty()
57  || !trustChanges.empty()
58  || !trustDeletions.empty()
59  || !offerChanges.empty()
60  || !offerDeletions.empty();
61  }
62 
63  void reserve (size_t newCap)
64  {
65  xrpChanges.reserve (newCap);
66  trustChanges.reserve (newCap);
67  trustDeletions.reserve (newCap);
68  offerChanges.reserve (newCap);
69  offerDeletions.reserve (newCap);
70  }
71 
73  {
74  xrpChanges.shrink_to_fit();
75  trustChanges.shrink_to_fit();
76  trustDeletions.shrink_to_fit();
77  offerChanges.shrink_to_fit();
78  offerDeletions.shrink_to_fit();
79  }
80 
81  void sort()
82  {
83  std::sort (xrpChanges.begin(), xrpChanges.end());
84  std::sort (trustChanges.begin(), trustChanges.end());
85  std::sort (trustDeletions.begin(), trustDeletions.end());
86  std::sort (offerChanges.begin(), offerChanges.end());
87  std::sort (offerDeletions.begin(), offerDeletions.end());
88  }
89 };
90 
91 // treatZeroOfferAsDeletion()
92 //
93 // Older payment code might set an Offer's TakerPays and TakerGets to
94 // zero and let the offer be cleaned up later. A more recent version
95 // may be more proactive about removing offers. We attempt to paper
96 // over that difference here.
97 //
98 // Two conditions are checked:
99 //
100 // o A modified Offer with both TakerPays and TakerGets set to zero is
101 // added to offerDeletions (not offerChanges).
102 //
103 // o Any deleted offer that was zero before deletion is ignored. It will
104 // have been treated as deleted when the offer was first set to zero.
105 //
106 // The returned bool indicates whether the passed in data was handled.
107 // This allows the caller to avoid further handling.
108 static bool treatZeroOfferAsDeletion (CashSummary& result, bool isDelete,
109  std::shared_ptr<SLE const> const& before,
111 {
112  using beast::zero;
113 
114  if (!before)
115  return false;
116 
117  auto const& prev = *before;
118 
119  if (isDelete)
120  {
121  if (prev.getType() == ltOFFER &&
122  prev[sfTakerPays] == zero && prev[sfTakerGets] == zero)
123  {
124  // Offer was previously treated as deleted when it was zeroed.
125  return true;
126  }
127  }
128  else
129  {
130  // modify
131  if (!after)
132  return false;
133 
134  auto const& cur = *after;
135  if (cur.getType() == ltOFFER &&
136  cur[sfTakerPays] == zero && cur[sfTakerGets] == zero)
137  {
138  // Either ignore or treat as delete.
139  auto const oldTakerPays = prev[sfTakerPays];
140  auto const oldTakerGets = prev[sfTakerGets];
141  if (oldTakerPays != zero && oldTakerGets != zero)
142  {
143  result.offerDeletions.push_back (
145  std::make_tuple (prev[sfAccount], prev[sfSequence]),
146  CashDiff::OfferAmounts {{oldTakerPays, oldTakerGets}}));
147  return true;
148  }
149  }
150  }
151  return false;
152 }
153 
154 static bool getBasicCashFlow (CashSummary& result, bool isDelete,
155  std::shared_ptr<SLE const> const& before,
157 {
158  if (isDelete)
159  {
160  if (!before)
161  return false;
162 
163  auto const& prev = *before;
164  switch(prev.getType())
165  {
166  case ltACCOUNT_ROOT:
167  result.xrpChanges.push_back (
168  std::make_pair (prev[sfAccount], XRPAmount {0}));
169  return true;
170 
171  case ltRIPPLE_STATE:
172  result.trustDeletions.push_back(
175  prev[sfLowLimit].getIssuer(),
176  prev[sfHighLimit].getIssuer(),
177  prev[sfBalance].getCurrency()), false));
178  return true;
179 
180  case ltOFFER:
181  result.offerDeletions.push_back (
183  std::make_tuple (prev[sfAccount], prev[sfSequence]),
185  prev[sfTakerPays],
186  prev[sfTakerGets]}}));
187  return true;
188 
189  default:
190  return false;
191  }
192  }
193  else
194  {
195  // insert or modify
196  if (!after)
197  {
198  assert (after);
199  return false;
200  }
201 
202  auto const& cur = *after;
203  switch(cur.getType())
204  {
205  case ltACCOUNT_ROOT:
206  {
207  auto const curXrp = cur[sfBalance].xrp();
208  if (!before || (*before)[sfBalance].xrp() != curXrp)
209  result.xrpChanges.push_back (
210  std::make_pair (cur[sfAccount], curXrp));
211  return true;
212  }
213  case ltRIPPLE_STATE:
214  {
215  auto const curBalance = cur[sfBalance];
216  if (!before || (*before)[sfBalance] != curBalance)
217  result.trustChanges.push_back (
220  cur[sfLowLimit].getIssuer(),
221  cur[sfHighLimit].getIssuer(),
222  curBalance.getCurrency()),
223  curBalance));
224  return true;
225  }
226  case ltOFFER:
227  {
228  auto const curTakerPays = cur[sfTakerPays];
229  auto const curTakerGets = cur[sfTakerGets];
230  if (!before || (*before)[sfTakerGets] != curTakerGets ||
231  (*before)[sfTakerPays] != curTakerPays)
232  {
233  result.offerChanges.push_back (
235  std::make_tuple (cur[sfAccount], cur[sfSequence]),
236  CashDiff::OfferAmounts {{curTakerPays, curTakerGets}}));
237  }
238  return true;
239  }
240  default:
241  break;
242  }
243  }
244  return false;
245 }
246 
247 // Extract the final cash state from an ApplyStateTable.
248 static CashSummary
249 getCashFlow (ReadView const& view, CashFilter f, ApplyStateTable const& table)
250 {
251  CashSummary result;
252  result.reserve (table.size());
253 
254  // Make a container of filters based on the passed in filter flags.
255  using FuncType = decltype (&getBasicCashFlow);
256  boost::container::static_vector<FuncType, 2> filters;
257 
259  filters.push_back (treatZeroOfferAsDeletion);
260 
261  filters.push_back (&getBasicCashFlow);
262 
263  auto each = [&result, &filters](uint256 const& key, bool isDelete,
264  std::shared_ptr<SLE const> const& before,
266  auto discarded =
267  std::find_if (filters.begin(), filters.end(),
268  [&result, isDelete, &before, &after] (FuncType func) {
269  return func (result, isDelete, before, after);
270  });
271  (void) discarded;
272  };
273 
274  table.visit (view, each);
275  result.sort();
276  result.shrink_to_fit();
277  return result;
278 }
279 
280 } // detail
281 
282 //------------------------------------------------------------------------------
283 
284 // Holds all of the CashDiff-related data.
286 {
287 private:
288  // Note differences in destroyed XRP between two ApplyStateTables.
289  struct DropsGone
290  {
293  };
294 
295  ReadView const& view_;
296 
297  std::size_t commonKeys_ = 0; // Number of keys common to both rhs and lhs.
298  std::size_t lhsKeys_ = 0; // Number of keys in lhs but not rhs.
299  std::size_t rhsKeys_ = 0; // Number of keys in rhs but not lhs.
300  boost::optional<DropsGone> dropsGone_;
303 
304 public:
305  // Constructor.
306  Impl (ReadView const& view,
307  CashFilter lhsFilter, detail::ApplyStateTable const& lhs,
308  CashFilter rhsFilter, detail::ApplyStateTable const& rhs)
309  : view_ (view)
310  {
311  findDiffs (lhsFilter, lhs, rhsFilter, rhs);
312  }
313 
315  {
316  return commonKeys_;
317  }
318 
320  {
321  return lhsKeys_;
322  }
323 
325  {
326  return rhsKeys_;
327  }
328 
329  bool hasDiff () const
330  {
331  return dropsGone_ != boost::none
332  || lhsDiffs_.hasDiff()
333  || rhsDiffs_.hasDiff();
334  }
335 
336  int xrpRoundToZero () const;
337 
338  // Filter out differences that are small enough to be in the floating
339  // point noise.
340  bool rmDust ();
341 
342  // Remove offer deletion differences from a given side
343  bool rmLhsDeletedOffers();
344  bool rmRhsDeletedOffers();
345 
346 private:
347  void findDiffs (
348  CashFilter lhsFilter, detail::ApplyStateTable const& lhs,
349  CashFilter rhsFilter, detail::ApplyStateTable const& rhs);
350 };
351 
352 // Template function to count difference types in individual CashDiff vectors.
353 // Assumes those vectors are sorted.
354 //
355 // Returned array:
356 // [0] count of keys present in both vectors.
357 // [1] count of keys present in lhs only.
358 // [2] count of keys present in rhs only.
359 template <typename T, typename U>
361  std::vector<std::pair<T, U>> const& lhs,
362  std::vector<std::pair<T, U>> const& rhs)
363 {
364  std::array<std::size_t, 3> ret {}; // Zero initialize;
365 
366  auto lhsItr = lhs.cbegin();
367  auto rhsItr = rhs.cbegin();
368 
369  while (lhsItr != lhs.cend() || rhsItr != rhs.cend())
370  {
371  if (lhsItr == lhs.cend())
372  {
373  // rhs has an entry that is not in lhs.
374  ret[2] += 1;
375  ++rhsItr;
376  }
377  else if (rhsItr == rhs.cend())
378  {
379  // lhs has an entry that is not in rhs.
380  ret[1] += 1;
381  ++lhsItr;
382  }
383  else if (lhsItr->first < rhsItr->first)
384  {
385  // This key is only in lhs.
386  ret[1] += 1;
387  ++lhsItr;
388  }
389  else if (rhsItr->first < lhsItr->first)
390  {
391  // This key is only in rhs.
392  ret[2] += 1;
393  ++rhsItr;
394  }
395  else
396  {
397  // The equivalent key is present in both vectors.
398  ret[0] += 1;
399  ++lhsItr;
400  ++rhsItr;
401  }
402  }
403  return ret;
404 }
405 
406 // Given two CashSummary instances, count the keys. Assumes both
407 // CashSummaries have sorted entries.
408 //
409 // Returned array:
410 // [0] count of keys present in both vectors.
411 // [1] count of keys present in lhs only.
412 // [2] count of keys present in rhs only.
415 {
416  std::array<std::size_t, 3> ret {}; // Zero initialize;
417 
418  // Lambda to add in new results.
419  auto addIn = [&ret] (std::array<std::size_t, 3> const& a)
420  {
421  std::transform (a.cbegin(), a.cend(),
422  ret.cbegin(), ret.begin(), std::plus<std::size_t>());
423  };
424  addIn (countKeys(lhs.xrpChanges, rhs.xrpChanges));
425  addIn (countKeys(lhs.trustChanges, rhs.trustChanges));
426  addIn (countKeys(lhs.trustDeletions, rhs.trustDeletions));
427  addIn (countKeys(lhs.offerChanges, rhs.offerChanges));
428  addIn (countKeys(lhs.offerDeletions, rhs.offerDeletions));
429  return ret;
430 }
431 
433 {
434  // The case has one OfferChange that is present on both lhs_ and rhs_.
435  // That OfferChange should have XRP for TakerGets. There should be a 1
436  // drop difference between the TakerGets of lhsDiffs_ and rhsDiffs_.
437  if (lhsDiffs_.offerChanges.size() != 1 ||
438  rhsDiffs_.offerChanges.size() != 1)
439  return 0;
440 
441  if (! lhsDiffs_.offerChanges[0].second.takerGets().native() ||
442  ! rhsDiffs_.offerChanges[0].second.takerGets().native())
443  return 0;
444 
445  bool const lhsBigger =
446  lhsDiffs_.offerChanges[0].second.takerGets().mantissa() >
447  rhsDiffs_.offerChanges[0].second.takerGets().mantissa();
448 
449  detail::CashSummary const& bigger = lhsBigger ? lhsDiffs_ : rhsDiffs_;
450  detail::CashSummary const& smaller = lhsBigger ? rhsDiffs_ : lhsDiffs_;
451  if (bigger.offerChanges[0].second.takerGets().mantissa() -
452  smaller.offerChanges[0].second.takerGets().mantissa() != 1)
453  return 0;
454 
455  // The side with the smaller XRP balance in the OfferChange should have
456  // two XRP differences. The other side should have no XRP differences.
457  if (smaller.xrpChanges.size() != 2)
458  return 0;
459  if (! bigger.xrpChanges.empty())
460  return 0;
461 
462  // There should be no other differences.
463  if (!smaller.trustChanges.empty() || !bigger.trustChanges.empty() ||
464  !smaller.trustDeletions.empty() || !bigger.trustDeletions.empty() ||
465  !smaller.offerDeletions.empty() || !bigger.offerDeletions.empty())
466  return 0;
467 
468  // Return which side exhibited the problem.
469  return lhsBigger ? -1 : 1;
470 }
471 
472 // Function that compares two CashDiff::OfferAmounts and returns true if
473 // the difference is dust-sized.
474 static bool diffIsDust (
475  CashDiff::OfferAmounts const& lhs, CashDiff::OfferAmounts const& rhs)
476 {
477  for (auto i = 0; i < lhs.count(); ++i)
478  {
479  if (!diffIsDust (lhs[i], rhs[i]))
480  return false;
481  }
482  return true;
483 }
484 
485 // Template function to remove dust from individual CashDiff vectors.
486 template <typename T, typename U, typename L>
487 static bool
491  L&& justDust)
492 {
493  static_assert (
494  std::is_same<bool,
495  decltype (justDust (lhs[0].second, rhs[0].second))>::value,
496  "Invalid lambda passed to rmVecDust");
497 
498  bool dustWasRemoved = false;
499  auto lhsItr = lhs.begin();
500  while (lhsItr != lhs.end())
501  {
502  using value_t = std::pair<T, U>;
503  auto const found = std::equal_range (rhs.begin(), rhs.end(), *lhsItr,
504  [] (value_t const& a, value_t const& b)
505  {
506  return a.first < b.first;
507  });
508 
509  if (found.first != found.second)
510  {
511  // The same entry changed for both lhs and rhs. Check whether
512  // the differences are small enough to be removed.
513  if (justDust (lhsItr->second, found.first->second))
514  {
515  dustWasRemoved = true;
516  rhs.erase (found.first);
517  // Dodge an invalid iterator by using erase's return value.
518  lhsItr = lhs.erase (lhsItr);
519  continue;
520  }
521  }
522  ++lhsItr;
523  }
524  return dustWasRemoved;
525 }
526 
528 {
529  bool removedDust = false;
530 
531  // Four of the containers can have small (floating point style)
532  // amount differences: xrpChanges, trustChanges, offerChanges, and
533  // offerDeletions. Rifle through those containers and remove any
534  // entries that are _almost_ the same between lhs and rhs.
535 
536  // xrpChanges. We call a difference of 2 drops or less dust.
537  removedDust |= rmVecDust (lhsDiffs_.xrpChanges, rhsDiffs_.xrpChanges,
538  [](XRPAmount const& lhs, XRPAmount const& rhs)
539  {
540  return diffIsDust (lhs, rhs);
541  });
542 
543  // trustChanges.
544  removedDust |= rmVecDust (lhsDiffs_.trustChanges, rhsDiffs_.trustChanges,
545  [](STAmount const& lhs, STAmount const& rhs)
546  {
547  return diffIsDust (lhs, rhs);
548  });
549 
550  // offerChanges.
551  removedDust |= rmVecDust (lhsDiffs_.offerChanges, rhsDiffs_.offerChanges,
552  [](CashDiff::OfferAmounts const& lhs, CashDiff::OfferAmounts const& rhs)
553  {
554  return diffIsDust (lhs, rhs);
555  });
556 
557  // offerDeletions.
558  removedDust |= rmVecDust (
559  lhsDiffs_.offerDeletions, rhsDiffs_.offerDeletions,
560  [](CashDiff::OfferAmounts const& lhs, CashDiff::OfferAmounts const& rhs)
561  {
562  return diffIsDust (lhs, rhs);
563  });
564 
565  return removedDust;
566 }
567 
569 {
570  bool const ret = !lhsDiffs_.offerDeletions.empty();
571  if (ret)
572  lhsDiffs_.offerDeletions.clear();
573  return ret;
574 }
575 
577 {
578  bool const ret = !rhsDiffs_.offerDeletions.empty();
579  if (ret)
580  rhsDiffs_.offerDeletions.clear();
581  return ret;
582 }
583 
584 // Deposits differences between two sorted vectors into a destination.
585 template <typename T>
586 static void setDiff (
587  std::vector<T> const& a, std::vector<T> const& b, std::vector<T>& dest)
588 {
589  dest.clear();
590  std::set_difference (a.cbegin(), a.cend(),
591  b.cbegin(), b.cend(), std::inserter (dest, dest.end()));
592 }
593 
595  CashFilter lhsFilter, detail::ApplyStateTable const& lhs,
596  CashFilter rhsFilter, detail::ApplyStateTable const& rhs)
597 {
598  // If dropsDestroyed_ is different, note that.
599  if (lhs.dropsDestroyed() != rhs.dropsDestroyed())
600  {
601  dropsGone_ = DropsGone{lhs.dropsDestroyed(), rhs.dropsDestroyed()};
602  }
603 
604  // Extract cash flow changes from the state tables
605  auto lhsDiffs = getCashFlow (view_, lhsFilter, lhs);
606  auto rhsDiffs = getCashFlow (view_, rhsFilter, rhs);
607 
608  // Get statistics on keys.
609  auto const counts = countKeys (lhsDiffs, rhsDiffs);
610  commonKeys_ = counts[0];
611  lhsKeys_ = counts[1];
612  rhsKeys_ = counts[2];
613 
614  // Save only the differences between the results.
615  // xrpChanges:
616  setDiff (lhsDiffs.xrpChanges, rhsDiffs.xrpChanges, lhsDiffs_.xrpChanges);
617  setDiff (rhsDiffs.xrpChanges, lhsDiffs.xrpChanges, rhsDiffs_.xrpChanges);
618 
619  // trustChanges:
620  setDiff (lhsDiffs.trustChanges, rhsDiffs.trustChanges, lhsDiffs_.trustChanges);
621  setDiff (rhsDiffs.trustChanges, lhsDiffs.trustChanges, rhsDiffs_.trustChanges);
622 
623  // trustDeletions:
624  setDiff (lhsDiffs.trustDeletions, rhsDiffs.trustDeletions, lhsDiffs_.trustDeletions);
625  setDiff (rhsDiffs.trustDeletions, lhsDiffs.trustDeletions, rhsDiffs_.trustDeletions);
626 
627  // offerChanges:
628  setDiff (lhsDiffs.offerChanges, rhsDiffs.offerChanges, lhsDiffs_.offerChanges);
629  setDiff (rhsDiffs.offerChanges, lhsDiffs.offerChanges, rhsDiffs_.offerChanges);
630 
631  // offerDeletions:
632  setDiff (lhsDiffs.offerDeletions, rhsDiffs.offerDeletions, lhsDiffs_.offerDeletions);
633  setDiff (rhsDiffs.offerDeletions, lhsDiffs.offerDeletions, rhsDiffs_.offerDeletions);
634 }
635 
636 //------------------------------------------------------------------------------
637 
638 // Locates differences between two ApplyStateTables.
639 CashDiff::CashDiff (CashDiff&& other) noexcept
640 : impl_ (std::move (other.impl_))
641 {
642 }
643 
644 CashDiff::~CashDiff() = default;
645 
647  CashFilter lhsFilter, detail::ApplyStateTable const& lhs,
648  CashFilter rhsFilter, detail::ApplyStateTable const& rhs)
649 : impl_ (new Impl (view, lhsFilter, lhs, rhsFilter, rhs))
650 {
651 }
652 
654 {
655  return impl_->commonCount();
656 }
657 
659 {
660  return impl_->rhsOnlyCount();
661 }
662 
664 {
665  return impl_->lhsOnlyCount();
666 }
667 
668 bool CashDiff::hasDiff() const
669 {
670  return impl_->hasDiff();
671 }
672 
674 {
675  return impl_->xrpRoundToZero();
676 }
677 
679 {
680  return impl_->rmDust();
681 }
682 
684 {
685  return impl_->rmLhsDeletedOffers();
686 }
687 
689 {
690  return impl_->rmRhsDeletedOffers();
691 }
692 
693 //------------------------------------------------------------------------------
694 
695 // Function that compares two STAmounts and returns true if the difference
696 // is dust-sized.
697 bool diffIsDust (STAmount const& v1, STAmount const& v2, std::uint8_t e10)
698 {
699  // If one value is positive and the other negative then there's something
700  // odd afoot.
701  if (v1 != beast::zero && v2 != beast::zero && (v1.negative() != v2.negative()))
702  return false;
703 
704  // v1 and v2 must be the same Issue for their difference to make sense.
705  if (v1.native() != v2.native())
706  return false;
707 
708  if (!v1.native() && (v1.issue() != v2.issue()))
709  return false;
710 
711  // If v1 == v2 then the dust is vanishingly small.
712  if (v1 == v2)
713  return true;
714 
715  STAmount const& small = v1 < v2 ? v1 : v2;
716  STAmount const& large = v1 < v2 ? v2 : v1;
717 
718  // Handling XRP is different from IOU.
719  if (v1.native())
720  {
721  std::uint64_t const s = small.mantissa();
722  std::uint64_t const l = large.mantissa();
723 
724  // Always allow a couple of drops of noise.
725  if (l - s <= 2)
726  return true;
727 
728  static_assert (sizeof (1ULL) == sizeof (std::uint64_t), "");
729  std::uint64_t const ratio = s / (l - s);
730  static constexpr std::uint64_t e10Lookup[]
731  {
732  1ULL,
733  10ULL,
734  100ULL,
735  1'000ULL,
736  10'000ULL,
737  100'000ULL,
738  1'000'000ULL,
739  10'000'000ULL,
740  100'000'000ULL,
741  1'000'000'000ULL,
742  10'000'000'000ULL,
743  100'000'000'000ULL,
744  1'000'000'000'000ULL,
745  10'000'000'000'000ULL,
746  100'000'000'000'000ULL,
747  1'000'000'000'000'000ULL,
748  10'000'000'000'000'000ULL,
749  100'000'000'000'000'000ULL,
750  1'000'000'000'000'000'000ULL,
751  10'000'000'000'000'000'000ULL,
752  };
753  static std::size_t constexpr maxIndex =
754  sizeof (e10Lookup) / sizeof e10Lookup[0];
755 
756  // Make sure the table is big enough.
757  static_assert (
758  std::numeric_limits<std::uint64_t>::max() /
759  e10Lookup[maxIndex - 1] < 10, "Table too small");
760 
761  if (e10 >= maxIndex)
762  return false;
763 
764  return ratio >= e10Lookup[e10];
765  }
766 
767  // Non-native. Note that even though large and small may not be equal,
768  // their difference may be zero. One way that can happen is if two
769  // values are different, but their difference results in an STAmount
770  // with an exponent less than -96.
771  STAmount const diff = large - small;
772  if (diff == beast::zero)
773  return true;
774 
775  STAmount const ratio = divide (small, diff, v1.issue());
776  STAmount const one (v1.issue(), 1);
777  int const ratioExp = ratio.exponent() - one.exponent();
778 
779  return ratioExp >= e10;
780 };
781 
782 } // ripple
ripple::CashDiff::OfferAmounts
Definition: CashDiff.h:110
std::is_same
ripple::CashDiff::Impl::lhsOnlyCount
std::size_t lhsOnlyCount() const
Definition: CashDiff.cpp:319
std::make_tuple
T make_tuple(T... args)
ripple::detail::ApplyStateTable::size
std::size_t size() const
Definition: ApplyStateTable.cpp:56
ripple::detail::CashSummary::sort
void sort()
Definition: CashDiff.cpp:81
std::shared_ptr
STL class.
ripple::detail::CashSummary
Definition: CashDiff.cpp:31
ripple::CashDiff::Impl::DropsGone
Definition: CashDiff.cpp:289
ripple::CashDiff::Impl::rmLhsDeletedOffers
bool rmLhsDeletedOffers()
Definition: CashDiff.cpp:568
ripple::diffIsDust
bool diffIsDust(STAmount const &v1, STAmount const &v2, std::uint8_t e10=6)
Definition: CashDiff.cpp:697
ripple::detail::CashSummary::CashSummary
CashSummary()=default
ripple::STAmount::mantissa
std::uint64_t mantissa() const noexcept
Definition: STAmount.h:155
ripple::CashDiff::Impl::rmRhsDeletedOffers
bool rmRhsDeletedOffers()
Definition: CashDiff.cpp:576
std::pair
ripple::CashDiff::Impl::view_
ReadView const & view_
Definition: CashDiff.cpp:295
ripple::detail::getCashFlow
static CashSummary getCashFlow(ReadView const &view, CashFilter f, ApplyStateTable const &table)
Definition: CashDiff.cpp:249
ripple::detail::CashSummary::shrink_to_fit
void shrink_to_fit()
Definition: CashDiff.cpp:72
std::vector
STL class.
std::find_if
T find_if(T... args)
ripple::CashDiff
Definition: CashDiff.h:61
ripple::sfSequence
const SF_U32 sfSequence(access, STI_UINT32, 4, "Sequence")
Definition: SField.h:340
ripple::CashDiff::Impl::hasDiff
bool hasDiff() const
Definition: CashDiff.cpp:329
ripple::CashFilter::none
@ none
ripple::detail::ApplyStateTable::visit
void visit(ReadView const &base, std::function< void(uint256 const &key, bool isDelete, std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after)> const &func) const
Definition: ApplyStateTable.cpp:75
ripple::CashDiff::Impl::rhsOnlyCount
std::size_t rhsOnlyCount() const
Definition: CashDiff.cpp:324
ripple::CashDiff::Impl::findDiffs
void findDiffs(CashFilter lhsFilter, detail::ApplyStateTable const &lhs, CashFilter rhsFilter, detail::ApplyStateTable const &rhs)
Definition: CashDiff.cpp:594
ripple::CashDiff::rmLhsDeletedOffers
bool rmLhsDeletedOffers()
Definition: CashDiff.cpp:683
ripple::sfAccount
const SF_Account sfAccount(access, STI_ACCOUNT, 1, "Account")
Definition: SField.h:460
ripple::CashDiff::impl_
std::unique_ptr< Impl > impl_
Definition: CashDiff.h:133
ripple::CashDiff::Impl::Impl
Impl(ReadView const &view, CashFilter lhsFilter, detail::ApplyStateTable const &lhs, CashFilter rhsFilter, detail::ApplyStateTable const &rhs)
Definition: CashDiff.cpp:306
ripple::sfTakerPays
const SF_Amount sfTakerPays(access, STI_AMOUNT, 4, "TakerPays")
Definition: SField.h:426
ripple::detail::CashSummary::offerChanges
std::vector< std::pair< std::tuple< AccountID, std::uint32_t >, CashDiff::OfferAmounts > > offerChanges
Definition: CashDiff.cpp:47
ripple::CashDiff::CashDiff
CashDiff()=delete
std::tuple
ripple::CashDiff::Impl::DropsGone::rhs
XRPAmount rhs
Definition: CashDiff.cpp:292
ripple::CashDiff::rmRhsDeletedOffers
bool rmRhsDeletedOffers()
Definition: CashDiff.cpp:688
ripple::CashDiff::hasDiff
bool hasDiff() const
Definition: CashDiff.cpp:668
ripple::detail::CashSummary::offerDeletions
std::vector< std::pair< std::tuple< AccountID, std::uint32_t >, CashDiff::OfferAmounts > > offerDeletions
Definition: CashDiff.cpp:52
ripple::sfHighLimit
const SF_Amount sfHighLimit(access, STI_AMOUNT, 7, "HighLimit")
Definition: SField.h:429
ripple::detail::ApplyStateTable::dropsDestroyed
XRPAmount const & dropsDestroyed() const
Definition: ApplyStateTable.h:125
std::sort
T sort(T... args)
ripple::detail::CashSummary::reserve
void reserve(size_t newCap)
Definition: CashDiff.cpp:63
ripple::sfLowLimit
const SF_Amount sfLowLimit(access, STI_AMOUNT, 6, "LowLimit")
Definition: SField.h:428
std::vector::clear
T clear(T... args)
ripple::CashDiff::Impl::dropsGone_
boost::optional< DropsGone > dropsGone_
Definition: CashDiff.cpp:300
ripple::CashDiff::rhsOnlyCount
std::size_t rhsOnlyCount() const
Definition: CashDiff.cpp:658
ripple::countKeys
static std::array< std::size_t, 3 > countKeys(std::vector< std::pair< T, U >> const &lhs, std::vector< std::pair< T, U >> const &rhs)
Definition: CashDiff.cpp:360
ripple::CashDiff::Impl::DropsGone::lhs
XRPAmount lhs
Definition: CashDiff.cpp:291
ripple::detail::CashSummary::hasDiff
bool hasDiff() const
Definition: CashDiff.cpp:54
ripple::CashDiff::OfferAmounts::count
static constexpr std::size_t count()
Definition: CashDiff.h:113
ripple::CashDiff::Impl::lhsDiffs_
detail::CashSummary lhsDiffs_
Definition: CashDiff.cpp:301
ripple::base_uint
Definition: base_uint.h:65
ripple::CashDiff::Impl::commonKeys_
std::size_t commonKeys_
Definition: CashDiff.cpp:297
ripple::CashDiff::Impl::rhsKeys_
std::size_t rhsKeys_
Definition: CashDiff.cpp:299
std::plus
std::array
STL class.
ripple::STAmount
Definition: STAmount.h:42
ripple::detail::treatZeroOfferAsDeletion
static bool treatZeroOfferAsDeletion(CashSummary &result, bool isDelete, std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after)
Definition: CashDiff.cpp:108
std::uint8_t
std::transform
T transform(T... args)
ripple::CashDiff::commonCount
std::size_t commonCount() const
Definition: CashDiff.cpp:653
ripple::CashFilter::treatZeroOfferAsDeletion
@ treatZeroOfferAsDeletion
ripple::setDiff
static void setDiff(std::vector< T > const &a, std::vector< T > const &b, std::vector< T > &dest)
Definition: CashDiff.cpp:586
ripple::STAmount::native
bool native() const noexcept
Definition: STAmount.h:153
std::equal_range
T equal_range(T... args)
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:186
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::ltRIPPLE_STATE
@ ltRIPPLE_STATE
Definition: LedgerFormats.h:67
cstdlib
ripple::sfBalance
const SF_Amount sfBalance(access, STI_AMOUNT, 2, "Balance")
Definition: SField.h:424
ripple::detail::CashSummary::xrpChanges
std::vector< std::pair< AccountID, XRPAmount > > xrpChanges
Definition: CashDiff.cpp:37
ripple::detail::CashSummary::trustChanges
std::vector< std::pair< std::tuple< AccountID, AccountID, Currency >, STAmount > > trustChanges
Definition: CashDiff.cpp:40
ripple::CashDiff::lhsOnlyCount
std::size_t lhsOnlyCount() const
Definition: CashDiff.cpp:663
std::array::cbegin
T cbegin(T... args)
ripple::CashDiff::Impl
Definition: CashDiff.cpp:285
std::set_difference
T set_difference(T... args)
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:156
cassert
ripple::CashDiff::~CashDiff
~CashDiff()
ripple::STAmount::negative
bool negative() const noexcept
Definition: STAmount.h:154
ripple::sfTakerGets
const SF_Amount sfTakerGets(access, STI_AMOUNT, 5, "TakerGets")
Definition: SField.h:427
ripple::CashDiff::Impl::rhsDiffs_
detail::CashSummary rhsDiffs_
Definition: CashDiff.cpp:302
ripple::CashDiff::Impl::xrpRoundToZero
int xrpRoundToZero() const
Definition: CashDiff.cpp:432
ripple::after
static bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition: Escrow.cpp:87
std::size_t
std::make_pair
T make_pair(T... args)
std::vector::cend
T cend(T... args)
ripple::detail::getBasicCashFlow
static bool getBasicCashFlow(CashSummary &result, bool isDelete, std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after)
Definition: CashDiff.cpp:154
ripple::ltOFFER
@ ltOFFER
Definition: LedgerFormats.h:73
ripple::CashDiff::xrpRoundToZero
int xrpRoundToZero() const
Definition: CashDiff.cpp:673
ripple::CashDiff::Impl::commonCount
std::size_t commonCount() const
Definition: CashDiff.cpp:314
ripple::CashDiff::Impl::lhsKeys_
std::size_t lhsKeys_
Definition: CashDiff.cpp:298
std::inserter
T inserter(T... args)
ripple::CashDiff::rmDust
bool rmDust()
Definition: CashDiff.cpp:678
ripple::ltACCOUNT_ROOT
@ ltACCOUNT_ROOT
Definition: LedgerFormats.h:54
ripple::CashDiff::Impl::rmDust
bool rmDust()
Definition: CashDiff.cpp:527
ripple::CashFilter
CashFilter
Definition: CashDiff.h:39
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::rmVecDust
static bool rmVecDust(std::vector< std::pair< T, U >> &lhs, std::vector< std::pair< T, U >> &rhs, L &&justDust)
Definition: CashDiff.cpp:488
ripple::detail::CashSummary::trustDeletions
std::vector< std::pair< std::tuple< AccountID, AccountID, Currency >, bool > > trustDeletions
Definition: CashDiff.cpp:43
ripple::detail::ApplyStateTable
Definition: ApplyStateTable.h:36