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