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