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