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