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