rippled
PaymentSandbox.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/paths/impl/AmountSpec.h>
21 #include <ripple/ledger/PaymentSandbox.h>
22 #include <ripple/ledger/View.h>
23 #include <ripple/protocol/Feature.h>
24 #include <ripple/protocol/SField.h>
25 #include <ripple/protocol/STAccount.h>
26 #include <boost/optional.hpp>
27 
28 #include <cassert>
29 
30 namespace ripple {
31 
32 namespace detail {
33 
35  AccountID const& a2,
36  Currency const& c) -> Key
37 {
38  if (a1 < a2)
39  return std::make_tuple(a1, a2, c);
40  else
41  return std::make_tuple(a2, a1, c);
42 }
43 
44 void
46  AccountID const& receiver,
47  STAmount const& amount,
48  STAmount const& preCreditSenderBalance)
49 {
50  assert (sender != receiver);
51  assert (!amount.negative());
52 
53  auto const k = makeKey (sender, receiver, amount.getCurrency ());
54  auto i = credits_.find (k);
55  if (i == credits_.end ())
56  {
57  Value v;
58 
59  if (sender < receiver)
60  {
61  v.highAcctCredits = amount;
62  v.lowAcctCredits = amount.zeroed ();
63  v.lowAcctOrigBalance = preCreditSenderBalance;
64  }
65  else
66  {
67  v.highAcctCredits = amount.zeroed ();
68  v.lowAcctCredits = amount;
69  v.lowAcctOrigBalance = -preCreditSenderBalance;
70  }
71 
72  credits_[k] = v;
73  }
74  else
75  {
76  // only record the balance the first time, do not record it here
77  auto& v = i->second;
78  if (sender < receiver)
79  v.highAcctCredits += amount;
80  else
81  v.lowAcctCredits += amount;
82  }
83 }
84 
85 void
87  std::uint32_t cur,
88  std::uint32_t next)
89 {
90  auto const v = std::max (cur, next);
91  auto r = ownerCounts_.emplace (std::make_pair (id, v));
92  if (!r.second)
93  {
94  auto& mapVal = r.first->second;
95  mapVal = std::max (v, mapVal);
96  }
97 }
98 
99 boost::optional<std::uint32_t>
101 {
102  auto i = ownerCounts_.find (id);
103  if (i != ownerCounts_.end ())
104  return i->second;
105  return boost::none;
106 }
107 
108 // Get the adjustments for the balance between main and other.
109 auto
111  AccountID const& other,
112  Currency const& currency) const -> boost::optional<Adjustment>
113 {
114  boost::optional<Adjustment> result;
115 
116  Key const k = makeKey (main, other, currency);
117  auto i = credits_.find (k);
118  if (i == credits_.end ())
119  return result;
120 
121  auto const& v = i->second;
122 
123  if (main < other)
124  {
125  result.emplace (v.highAcctCredits, v.lowAcctCredits, v.lowAcctOrigBalance);
126  return result;
127  }
128  else
129  {
130  result.emplace (v.lowAcctCredits, v.highAcctCredits, -v.lowAcctOrigBalance);
131  return result;
132  }
133 }
134 
136  DeferredCredits& to)
137 {
138  for (auto const& i : credits_)
139  {
140  auto r = to.credits_.emplace (i);
141  if (!r.second)
142  {
143  auto& toVal = r.first->second;
144  auto const& fromVal = i.second;
145  toVal.lowAcctCredits += fromVal.lowAcctCredits;
146  toVal.highAcctCredits += fromVal.highAcctCredits;
147  // Do not update the orig balance, it's already correct
148  }
149  }
150 
151  for (auto const& i : ownerCounts_)
152  {
153  auto r = to.ownerCounts_.emplace (i);
154  if (!r.second)
155  {
156  auto& toVal = r.first->second;
157  auto const& fromVal = i.second;
158  toVal = std::max (toVal, fromVal);
159  }
160  }
161 }
162 
163 } // detail
164 
165 STAmount
167  AccountID const& issuer,
168  STAmount const& amount) const
169 {
170  /*
171  There are two algorithms here. The pre-switchover algorithm takes the
172  current amount and subtracts the recorded credits. The post-switchover
173  algorithm remembers the original balance, and subtracts the debits. The
174  post-switchover algorithm should be more numerically stable. Consider a
175  large credit with a small initial balance. The pre-switchover algorithm
176  computes (B+C)-C (where B+C will the the amount passed in). The
177  post-switchover algorithm returns B. When B and C differ by large
178  magnitudes, (B+C)-C may not equal B.
179  */
180 
181  auto const currency = amount.getCurrency ();
182 
183  auto delta = amount.zeroed ();
184  auto lastBal = amount;
185  auto minBal = amount;
186  for (auto curSB = this; curSB; curSB = curSB->ps_)
187  {
188  if (auto adj = curSB->tab_.adjustments (account, issuer, currency))
189  {
190  delta += adj->debits;
191  lastBal = adj->origBalance;
192  if (lastBal < minBal)
193  minBal = lastBal;
194  }
195  }
196 
197  // The adjusted amount should never be larger than the balance. In
198  // some circumstances, it is possible for the deferred credits table
199  // to compute usable balance just slightly above what the ledger
200  // calculates (but always less than the actual balance).
201  auto adjustedAmt = std::min({amount, lastBal - delta, minBal});
202  adjustedAmt.setIssuer(amount.getIssuer());
203 
204  if (isXRP(issuer) && adjustedAmt < beast::zero)
205  // A calculated negative XRP balance is not an error case. Consider a
206  // payment snippet that credits a large XRP amount and then debits the
207  // same amount. The credit can't be used but we subtract the debit and
208  // calculate a negative value. It's not an error case.
209  adjustedAmt.clear();
210 
211  return adjustedAmt;
212 }
213 
216  std::uint32_t count) const
217 {
218  std::uint32_t result = count;
219  for (auto curSB = this; curSB; curSB = curSB->ps_)
220  {
221  if (auto adj = curSB->tab_.ownerCount (account))
222  result = std::max (result, *adj);
223  }
224  return result;
225 }
226 
227 void
229  AccountID const& to,
230  STAmount const& amount,
231  STAmount const& preCreditBalance)
232 {
233  tab_.credit (from, to, amount, preCreditBalance);
234 }
235 
236 void
238  std::uint32_t cur,
239  std::uint32_t next)
240 {
241  tab_.ownerCount (account, cur, next);
242 }
243 
244 void
246 {
247  assert(! ps_);
248  items_.apply(to);
249 }
250 
251 void
253 {
254  assert(ps_ == &to);
255  items_.apply(to);
256  tab_.apply(to.tab_);
257 }
258 
261 {
263  // Map of delta trust lines. As a special case, when both ends of the trust
264  // line are the same currency, then it's delta currency for that issuer. To
265  // get the change in XRP balance, Account == root, issuer == root, currency == XRP
267 
268  // populate a dictionary with low/high/currency/delta. This can be
269  // compared with the other versions payment code.
270  auto each = [&result](uint256 const& key, bool isDelete,
271  std::shared_ptr<SLE const> const& before,
273 
274  STAmount oldBalance;
275  STAmount newBalance;
276  AccountID lowID;
277  AccountID highID;
278 
279  // before is read from prev view
280  if (isDelete)
281  {
282  if (!before)
283  return;
284 
285  auto const bt = before->getType ();
286  switch(bt)
287  {
288  case ltACCOUNT_ROOT:
289  lowID = xrpAccount();
290  highID = (*before)[sfAccount];
291  oldBalance = (*before)[sfBalance];
292  newBalance = oldBalance.zeroed();
293  break;
294  case ltRIPPLE_STATE:
295  lowID = (*before)[sfLowLimit].getIssuer();
296  highID = (*before)[sfHighLimit].getIssuer();
297  oldBalance = (*before)[sfBalance];
298  newBalance = oldBalance.zeroed();
299  break;
300  case ltOFFER:
301  // TBD
302  break;
303  default:
304  break;
305  }
306  }
307  else if (!before)
308  {
309  // insert
310  auto const at = after->getType ();
311  switch(at)
312  {
313  case ltACCOUNT_ROOT:
314  lowID = xrpAccount();
315  highID = (*after)[sfAccount];
316  newBalance = (*after)[sfBalance];
317  oldBalance = newBalance.zeroed();
318  break;
319  case ltRIPPLE_STATE:
320  lowID = (*after)[sfLowLimit].getIssuer();
321  highID = (*after)[sfHighLimit].getIssuer();
322  newBalance = (*after)[sfBalance];
323  oldBalance = newBalance.zeroed();
324  break;
325  case ltOFFER:
326  // TBD
327  break;
328  default:
329  break;
330  }
331  }
332  else
333  {
334  // modify
335  auto const at = after->getType ();
336  assert (at == before->getType ());
337  switch(at)
338  {
339  case ltACCOUNT_ROOT:
340  lowID = xrpAccount();
341  highID = (*after)[sfAccount];
342  oldBalance = (*before)[sfBalance];
343  newBalance = (*after)[sfBalance];
344  break;
345  case ltRIPPLE_STATE:
346  lowID = (*after)[sfLowLimit].getIssuer();
347  highID = (*after)[sfHighLimit].getIssuer();
348  oldBalance = (*before)[sfBalance];
349  newBalance = (*after)[sfBalance];
350  break;
351  case ltOFFER:
352  // TBD
353  break;
354  default:
355  break;
356  }
357  }
358  // The following are now set, put them in the map
359  auto delta = newBalance - oldBalance;
360  auto const cur = newBalance.getCurrency();
361  result[std::make_tuple(lowID, highID, cur)] = delta;
362  auto r = result.emplace(std::make_tuple(lowID, lowID, cur), delta);
363  if (r.second)
364  {
365  r.first->second += delta;
366  }
367 
368  delta.negate();
369  r = result.emplace(std::make_tuple(highID, highID, cur), delta);
370  if (r.second)
371  {
372  r.first->second += delta;
373  }
374  };
375  items_.visit (view, each);
376  return result;
377 }
378 
380 {
381  return items_.dropsDestroyed ();
382 }
383 
384 } // ripple
ripple::PaymentSandbox::xrpDestroyed
XRPAmount xrpDestroyed() const
Definition: PaymentSandbox.cpp:379
std::make_tuple
T make_tuple(T... args)
std::shared_ptr
STL class.
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:110
ripple::detail::DeferredCredits::adjustments
boost::optional< Adjustment > adjustments(AccountID const &main, AccountID const &other, Currency const &currency) const
Definition: PaymentSandbox.cpp:110
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::sfAccount
const SF_Account sfAccount(access, STI_ACCOUNT, 1, "Account")
Definition: SField.h:460
std::map::emplace
T emplace(T... args)
std::tuple
ripple::STAmount::zeroed
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
Definition: STAmount.h:170
ripple::PaymentSandbox::adjustOwnerCountHook
void adjustOwnerCountHook(AccountID const &account, std::uint32_t cur, std::uint32_t next) override
Definition: PaymentSandbox.cpp:237
ripple::detail::DeferredCredits::Value
Definition: PaymentSandbox.h:75
ripple::detail::ApplyViewBase::items_
detail::ApplyStateTable items_
Definition: ApplyViewBase.h:139
ripple::PaymentSandbox::balanceChanges
std::map< std::tuple< AccountID, AccountID, Currency >, STAmount > balanceChanges(ReadView const &view) const
Definition: PaymentSandbox.cpp:260
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
ripple::detail::DeferredCredits::credits_
std::map< Key, Value > credits_
Definition: PaymentSandbox.h:90
ripple::sfLowLimit
const SF_Amount sfLowLimit(access, STI_AMOUNT, 6, "LowLimit")
Definition: SField.h:428
ripple::detail::DeferredCredits::credit
void credit(AccountID const &sender, AccountID const &receiver, STAmount const &amount, STAmount const &preCreditSenderBalance)
Definition: PaymentSandbox.cpp:45
ripple::base_uint< 160, detail::AccountIDTag >
ripple::RawView
Interface for ledger entry changes.
Definition: RawView.h:37
ripple::detail::ApplyStateTable::apply
void apply(RawView &to) const
Definition: ApplyStateTable.cpp:31
ripple::PaymentSandbox::ps_
PaymentSandbox const * ps_
Definition: PaymentSandbox.h:204
ripple::detail::DeferredCredits::Value::highAcctCredits
STAmount highAcctCredits
Definition: PaymentSandbox.h:80
ripple::STAmount
Definition: STAmount.h:42
ripple::xrpAccount
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:149
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:121
std::uint32_t
ripple::detail::DeferredCredits::apply
void apply(DeferredCredits &to)
Definition: PaymentSandbox.cpp:135
std::map
STL class.
ripple::STAmount::getCurrency
Currency const & getCurrency() const
Definition: STAmount.h:159
ripple::STAmount::getIssuer
AccountID const & getIssuer() const
Definition: STAmount.h:160
std::min
T min(T... args)
ripple::PaymentSandbox::tab_
detail::DeferredCredits tab_
Definition: PaymentSandbox.h:203
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
ripple::sfBalance
const SF_Amount sfBalance(access, STI_AMOUNT, 2, "Balance")
Definition: SField.h:424
ripple::PaymentSandbox::apply
void apply(RawView &to)
Apply changes to base view.
Definition: PaymentSandbox.cpp:245
cassert
ripple::STAmount::negative
bool negative() const noexcept
Definition: STAmount.h:154
ripple::detail::DeferredCredits::ownerCount
void ownerCount(AccountID const &id, std::uint32_t cur, std::uint32_t next)
Definition: PaymentSandbox.cpp:86
ripple::detail::DeferredCredits::Value::lowAcctCredits
STAmount lowAcctCredits
Definition: PaymentSandbox.h:79
ripple::after
static bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition: Escrow.cpp:87
std::make_pair
T make_pair(T... args)
ripple::ltOFFER
@ ltOFFER
Definition: LedgerFormats.h:73
ripple::detail::DeferredCredits::Value::lowAcctOrigBalance
STAmount lowAcctOrigBalance
Definition: PaymentSandbox.h:81
std::max
T max(T... args)
ripple::detail::DeferredCredits::makeKey
static Key makeKey(AccountID const &a1, AccountID const &a2, Currency const &c)
Definition: PaymentSandbox.cpp:34
ripple::ltACCOUNT_ROOT
@ ltACCOUNT_ROOT
Definition: LedgerFormats.h:54
ripple::PaymentSandbox::ownerCountHook
std::uint32_t ownerCountHook(AccountID const &account, std::uint32_t count) const override
Definition: PaymentSandbox.cpp:215
ripple::PaymentSandbox::creditHook
void creditHook(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance) override
Definition: PaymentSandbox.cpp:228
ripple::detail::DeferredCredits::ownerCounts_
std::map< AccountID, std::uint32_t > ownerCounts_
Definition: PaymentSandbox.h:91
ripple::PaymentSandbox::balanceHook
STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const override
Definition: PaymentSandbox.cpp:166
ripple::detail::DeferredCredits
Definition: PaymentSandbox.h:36
ripple::XRPAmount
Definition: XRPAmount.h:46