xrpld
Loading...
Searching...
No Matches
PaymentSandbox.cpp
1#include <xrpl/ledger/PaymentSandbox.h>
2
3#include <xrpl/basics/base_uint.h>
4#include <xrpl/beast/utility/Zero.h>
5#include <xrpl/beast/utility/instrumentation.h>
6#include <xrpl/ledger/RawView.h>
7#include <xrpl/protocol/AccountID.h>
8#include <xrpl/protocol/Issue.h>
9#include <xrpl/protocol/MPTIssue.h>
10#include <xrpl/protocol/SField.h>
11#include <xrpl/protocol/UintTypes.h>
12#include <xrpl/protocol/XRPAmount.h>
13
14#include <algorithm>
15#include <cstdint>
16#include <map>
17#include <optional>
18#include <tuple>
19#include <utility>
20
21namespace xrpl {
22
23namespace detail {
24
25auto
27{
28 if (a1 < a2)
29 {
30 return std::make_tuple(a1, a2, c);
31 }
32
33 return std::make_tuple(a2, a1, c);
34}
35
36void
38 AccountID const& sender,
39 AccountID const& receiver,
40 STAmount const& amount,
41 STAmount const& preCreditSenderBalance)
42{
43 XRPL_ASSERT(
44 sender != receiver, "xrpl::detail::DeferredCredits::creditIOU : sender is not receiver");
45 XRPL_ASSERT(!amount.negative(), "xrpl::detail::DeferredCredits::creditIOU : positive amount");
46 XRPL_ASSERT(
47 amount.holds<Issue>(), "xrpl::detail::DeferredCredits::creditIOU : amount is for Issue");
48
49 auto const k = makeKeyIOU(sender, receiver, amount.get<Issue>().currency);
50 auto i = creditsIOU_.find(k);
51 if (i == creditsIOU_.end())
52 {
53 ValueIOU v;
54
55 if (sender < receiver)
56 {
57 v.lowAcctDebits = amount;
58 v.highAcctDebits = amount.zeroed();
59 v.lowAcctOrigBalance = preCreditSenderBalance;
60 }
61 else
62 {
63 v.lowAcctDebits = amount.zeroed();
64 v.highAcctDebits = amount;
65 v.lowAcctOrigBalance = -preCreditSenderBalance;
66 }
67
68 creditsIOU_[k] = v;
69 }
70 else
71 {
72 // only record the balance the first time, do not record it here
73 auto& v = i->second;
74 if (sender < receiver)
75 {
76 v.lowAcctDebits += amount;
77 }
78 else
79 {
80 v.highAcctDebits += amount;
81 }
82 }
83}
84
85void
87 AccountID const& sender,
88 AccountID const& receiver,
89 STAmount const& amount,
90 std::uint64_t preCreditBalanceHolder,
91 std::int64_t preCreditBalanceIssuer)
92{
93 XRPL_ASSERT(
94 amount.holds<MPTIssue>(),
95 "xrpl::detail::DeferredCredits::creditMPT : amount is for MPTIssue");
96 XRPL_ASSERT(!amount.negative(), "xrpl::detail::DeferredCredits::creditMPT : positive amount");
97 XRPL_ASSERT(
98 sender != receiver, "xrpl::detail::DeferredCredits::creditMPT : sender is not receiver");
99
100 auto const mptAmtVal = amount.mpt().value();
101 auto const& issuer = amount.getIssuer();
102 auto const& mptIssue = amount.get<MPTIssue>();
103 auto const& mptID = mptIssue.getMptID();
104 bool const isSenderIssuer = sender == issuer;
105
106 auto i = creditsMPT_.find(mptID);
107 if (i == creditsMPT_.end())
108 {
110 if (isSenderIssuer)
111 {
112 v.credit = mptAmtVal;
113 v.holders[receiver].origBalance = preCreditBalanceHolder;
114 }
115 else
116 {
117 v.holders[sender].debit = mptAmtVal;
118 v.holders[sender].origBalance = preCreditBalanceHolder;
119 }
120 v.origBalance = preCreditBalanceIssuer;
121 creditsMPT_.emplace(mptID, std::move(v));
122 }
123 else
124 {
125 // only record the balance the first time, do not record it here
126 auto& v = i->second;
127 if (isSenderIssuer)
128 {
129 v.credit += mptAmtVal;
130 if (!v.holders.contains(receiver))
131 {
132 v.holders[receiver].origBalance = preCreditBalanceHolder;
133 }
134 }
135 else
136 {
137 if (!v.holders.contains(sender))
138 {
139 v.holders[sender].debit = mptAmtVal;
140 v.holders[sender].origBalance = preCreditBalanceHolder;
141 }
142 else
143 {
144 v.holders[sender].debit += mptAmtVal;
145 }
146 }
147 }
148}
149
150void
152 MPTIssue const& issue,
153 std::uint64_t amount,
154 std::int64_t origBalance)
155{
156 auto const& mptID = issue.getMptID();
157 auto i = creditsMPT_.find(mptID);
158
159 if (i == creditsMPT_.end())
160 {
162 v.origBalance = origBalance;
163 v.selfDebit = amount;
164 creditsMPT_.emplace(mptID, std::move(v));
165 }
166 else
167 {
168 i->second.selfDebit += amount;
169 }
170}
171
172void
174{
175 auto const v = std::max(cur, next);
176 auto r = ownerCounts_.emplace(id, v);
177 if (!r.second)
178 {
179 auto& mapVal = r.first->second;
180 mapVal = std::max(v, mapVal);
181 }
182}
183
186{
187 auto i = ownerCounts_.find(id);
188 if (i != ownerCounts_.end())
189 return i->second;
190 return std::nullopt;
191}
192
193// Get the adjustments for the balance between main and other.
194auto
196 AccountID const& main,
197 AccountID const& other,
198 Currency const& currency) const -> std::optional<AdjustmentIOU>
199{
201
202 KeyIOU const k = makeKeyIOU(main, other, currency);
203 auto i = creditsIOU_.find(k);
204 if (i == creditsIOU_.end())
205 return result;
206
207 auto const& v = i->second;
208
209 if (main < other)
210 {
211 result.emplace(v.lowAcctDebits, v.highAcctDebits, v.lowAcctOrigBalance);
212 return result;
213 }
214
215 result.emplace(v.highAcctDebits, v.lowAcctDebits, -v.lowAcctOrigBalance);
216 return result;
217}
218
219auto
221{
222 auto i = creditsMPT_.find(mptID);
223 if (i == creditsMPT_.end())
224 return std::nullopt;
225 return i->second;
226}
227
228void
230{
231 for (auto const& i : creditsIOU_)
232 {
233 auto r = to.creditsIOU_.emplace(i);
234 if (!r.second)
235 {
236 auto& toVal = r.first->second;
237 auto const& fromVal = i.second;
238 toVal.lowAcctDebits += fromVal.lowAcctDebits;
239 toVal.highAcctDebits += fromVal.highAcctDebits;
240 // Do not update the orig balance, it's already correct
241 }
242 }
243
244 for (auto const& i : creditsMPT_)
245 {
246 auto r = to.creditsMPT_.emplace(i);
247 if (!r.second)
248 {
249 auto& toVal = r.first->second;
250 auto const& fromVal = i.second;
251 toVal.credit += fromVal.credit;
252 toVal.selfDebit += fromVal.selfDebit;
253 for (auto& [k, v] : fromVal.holders)
254 {
255 if (!toVal.holders.contains(k))
256 {
257 toVal.holders[k] = v;
258 }
259 else
260 {
261 toVal.holders[k].debit += v.debit;
262 }
263 }
264 // Do not update the orig balance, it's already correct
265 }
266 }
267
268 for (auto const& i : ownerCounts_)
269 {
270 auto r = to.ownerCounts_.emplace(i);
271 if (!r.second)
272 {
273 auto& toVal = r.first->second;
274 auto const& fromVal = i.second;
275 toVal = std::max(toVal, fromVal);
276 }
277 }
278}
279
280} // namespace detail
281
284 AccountID const& account,
285 AccountID const& issuer,
286 STAmount const& amount) const
287{
288 XRPL_ASSERT(amount.holds<Issue>(), "balanceHookIOU: amount is for Issue");
289
290 /*
291 There are two algorithms here. The pre-switchover algorithm takes the
292 current amount and subtracts the recorded credits. The post-switchover
293 algorithm remembers the original balance, and subtracts the debits. The
294 post-switchover algorithm should be more numerically stable. Consider a
295 large credit with a small initial balance. The pre-switchover algorithm
296 computes (B+C)-C (where B+C will the amount passed in). The
297 post-switchover algorithm returns B. When B and C differ by large
298 magnitudes, (B+C)-C may not equal B.
299 */
300
301 auto const& currency = amount.get<Issue>().currency;
302
303 auto delta = amount.zeroed();
304 auto lastBal = amount;
305 auto minBal = amount;
306 for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
307 {
308 if (auto adj = curSB->tab_.adjustmentsIOU(account, issuer, currency))
309 {
310 delta += adj->debits;
311 lastBal = adj->origBalance;
312 if (lastBal < minBal)
313 minBal = lastBal;
314 }
315 }
316
317 // The adjusted amount should never be larger than the balance. In
318 // some circumstances, it is possible for the deferred credits table
319 // to compute usable balance just slightly above what the ledger
320 // calculates (but always less than the actual balance).
321 auto adjustedAmt = std::min({amount, lastBal - delta, minBal});
322 adjustedAmt.get<Issue>().account = amount.getIssuer();
323
324 if (isXRP(issuer) && adjustedAmt < beast::kZero)
325 {
326 // A calculated negative XRP balance is not an error case. Consider a
327 // payment snippet that credits a large XRP amount and then debits the
328 // same amount. The credit can't be used, but we subtract the debit and
329 // calculate a negative value. It's not an error case.
330 adjustedAmt.clear();
331 }
332
333 return adjustedAmt;
334}
335
338 const
339{
340 auto const& issuer = issue.getIssuer();
341 bool const accountIsHolder = account != issuer;
342
343 std::int64_t delta = 0;
344 std::int64_t lastBal = amount;
345 std::int64_t minBal = amount;
346 for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
347 {
348 if (auto adj = curSB->tab_.adjustmentsMPT(issue))
349 {
350 if (accountIsHolder)
351 {
352 if (auto const i = adj->holders.find(account); i != adj->holders.end())
353 {
354 delta += i->second.debit;
355 lastBal = i->second.origBalance;
356 }
357 }
358 else
359 {
360 delta += adj->credit;
361 lastBal = adj->origBalance;
362 }
363 minBal = std::min(lastBal, minBal);
364 }
365 }
366
367 // The adjusted amount should never be larger than the balance.
368
369 auto const adjustedAmt = std::min({amount, lastBal - delta, minBal});
370
371 return adjustedAmt > 0 ? STAmount{issue, adjustedAmt} : STAmount{issue};
372}
373
376{
377 std::int64_t selfDebited = 0;
378 std::int64_t lastBal = amount;
379 for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
380 {
381 if (auto adj = curSB->tab_.adjustmentsMPT(issue))
382 {
383 selfDebited += adj->selfDebit;
384 lastBal = adj->origBalance;
385 }
386 }
387
388 if (lastBal > selfDebited)
389 return STAmount{issue, lastBal - selfDebited};
390
391 return STAmount{issue};
392}
393
396{
397 std::uint32_t result = count;
398 for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
399 {
400 if (auto adj = curSB->tab_.ownerCount(account))
401 result = std::max(result, *adj);
402 }
403 return result;
404}
405
406void
408 AccountID const& from,
409 AccountID const& to,
410 STAmount const& amount,
411 STAmount const& preCreditBalance)
412{
413 XRPL_ASSERT(amount.holds<Issue>(), "creditHookIOU: amount is for Issue");
414
415 tab_.creditIOU(from, to, amount, preCreditBalance);
416}
417
418void
420 AccountID const& from,
421 AccountID const& to,
422 STAmount const& amount,
423 std::uint64_t preCreditBalanceHolder,
424 std::int64_t preCreditBalanceIssuer)
425{
426 XRPL_ASSERT(amount.holds<MPTIssue>(), "creditHookMPT: amount is for MPTIssue");
427
428 tab_.creditMPT(from, to, amount, preCreditBalanceHolder, preCreditBalanceIssuer);
429}
430
431void
433 MPTIssue const& issue,
434 std::uint64_t amount,
435 std::int64_t origBalance)
436{
437 XRPL_ASSERT(amount > 0, "PaymentSandbox::issuerSelfDebitHookMPT: amount must be > 0");
438
439 tab_.issuerSelfDebitMPT(issue, amount, origBalance);
440}
441
442void
444 AccountID const& account,
445 std::uint32_t cur,
446 std::uint32_t next)
447{
448 tab_.ownerCount(account, cur, next);
449}
450
451void
453{
454 XRPL_ASSERT(!ps_, "xrpl::PaymentSandbox::apply : non-null sandbox");
455 items_.apply(to);
456}
457
458void
460{
461 XRPL_ASSERT(ps_ == &to, "xrpl::PaymentSandbox::apply : matching sandbox");
462 items_.apply(to);
463 tab_.apply(to.tab_);
464}
465
468{
469 return items_.dropsDestroyed();
470}
471
472} // namespace xrpl
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:122
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:33
AccountID const & getIssuer() const
Definition MPTIssue.cpp:29
void adjustOwnerCountHook(AccountID const &account, std::uint32_t cur, std::uint32_t next) override
STAmount balanceHookMPT(AccountID const &account, MPTIssue const &issue, std::int64_t amount) const override
void creditHookMPT(AccountID const &from, AccountID const &to, STAmount const &amount, std::uint64_t preCreditBalanceHolder, std::int64_t preCreditBalanceIssuer) override
STAmount balanceHookIOU(AccountID const &account, AccountID const &issuer, STAmount const &amount) const override
void creditHookIOU(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance) override
PaymentSandbox const * ps_
void issuerSelfDebitHookMPT(MPTIssue const &issue, std::uint64_t amount, std::int64_t origBalance) override
Facilitate tracking of MPT sold by an issuer owning MPT sell offer.
void apply(RawView &to)
Apply changes to base view.
detail::DeferredCredits tab_
std::uint32_t ownerCountHook(AccountID const &account, std::uint32_t count) const override
STAmount balanceHookSelfIssueMPT(MPTIssue const &issue, std::int64_t amount) const override
XRPAmount xrpDestroyed() const
Interface for ledger entry changes.
Definition RawView.h:14
constexpr bool holds() const noexcept
Definition STAmount.h:460
constexpr TIss const & get() const
bool negative() const noexcept
Definition STAmount.h:466
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
Definition STAmount.h:512
MPTAmount mpt() const
Definition STAmount.cpp:301
AccountID const & getIssuer() const
Definition STAmount.h:498
detail::ApplyStateTable items_
std::optional< AdjustmentIOU > adjustmentsIOU(AccountID const &main, AccountID const &other, Currency const &currency) const
std::map< MPTID, IssuerValueMPT > creditsMPT_
void apply(DeferredCredits &to)
static KeyIOU makeKeyIOU(AccountID const &a1, AccountID const &a2, Currency const &currency)
std::map< AccountID, std::uint32_t > ownerCounts_
void creditIOU(AccountID const &sender, AccountID const &receiver, STAmount const &amount, STAmount const &preCreditSenderBalance)
void ownerCount(AccountID const &id, std::uint32_t cur, std::uint32_t next)
std::map< KeyIOU, ValueIOU > creditsIOU_
void creditMPT(AccountID const &sender, AccountID const &receiver, STAmount const &amount, std::uint64_t preCreditBalanceHolder, std::int64_t preCreditBalanceIssuer)
std::tuple< AccountID, AccountID, Currency > KeyIOU
std::optional< AdjustmentMPT > adjustmentsMPT(MPTID const &mptID) const
void issuerSelfDebitMPT(MPTIssue const &issue, std::uint64_t amount, std::int64_t origBalance)
T emplace(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:5
bool isXRP(AccountID const &c)
Definition AccountID.h:70
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:36
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:44
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
std::map< AccountID, HolderValueMPT > holders