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