xrpld
Loading...
Searching...
No Matches
XRPEndpointStep.cpp
1#include <xrpl/basics/Log.h>
2#include <xrpl/basics/base_uint.h>
3#include <xrpl/beast/utility/Journal.h>
4#include <xrpl/beast/utility/Zero.h>
5#include <xrpl/beast/utility/instrumentation.h>
6#include <xrpl/ledger/PaymentSandbox.h>
7#include <xrpl/ledger/helpers/AccountRootHelpers.h>
8#include <xrpl/ledger/helpers/TokenHelpers.h>
9#include <xrpl/protocol/AccountID.h>
10#include <xrpl/protocol/IOUAmount.h>
11#include <xrpl/protocol/Indexes.h>
12#include <xrpl/protocol/Issue.h>
13#include <xrpl/protocol/MPTIssue.h>
14#include <xrpl/protocol/Quality.h>
15#include <xrpl/protocol/STAmount.h>
16#include <xrpl/protocol/TER.h>
17#include <xrpl/protocol/UintTypes.h>
18#include <xrpl/protocol/XRPAmount.h>
19#include <xrpl/tx/paths/detail/EitherAmount.h>
20#include <xrpl/tx/paths/detail/StepChecks.h>
21#include <xrpl/tx/paths/detail/Steps.h>
22
23#include <boost/container/flat_set.hpp>
24
25#include <cstdint>
26#include <memory>
27#include <optional>
28#include <sstream>
29#include <string>
30#include <utility>
31
32namespace xrpl {
33
34template <class TDerived>
35class XRPEndpointStep : public StepImp<XRPAmount, XRPAmount, XRPEndpointStep<TDerived>>
36{
37private:
39 bool const isLast_;
41
42 // Since this step will always be an endpoint in a strand
43 // (either the first or last step) the same cache is used
44 // for cachedIn and cachedOut and only one will ever be used
46
47 [[nodiscard]] std::optional<EitherAmount>
48 cached() const
49 {
50 if (!cache_)
51 return std::nullopt;
52 return EitherAmount(*cache_);
53 }
54
56 : acc_(acc), isLast_(ctx.isLast), j_(ctx.j)
57 {
58 }
59
60public:
61 [[nodiscard]] AccountID const&
62 acc() const
63 {
64 return acc_;
65 }
66
68 directStepAccts() const override
69 {
70 if (isLast_)
73 }
74
75 [[nodiscard]] std::optional<EitherAmount>
76 cachedIn() const override
77 {
78 return cached();
79 }
80
81 [[nodiscard]] std::optional<EitherAmount>
82 cachedOut() const override
83 {
84 return cached();
85 }
86
87 [[nodiscard]] DebtDirection
88 debtDirection(ReadView const& sb, StrandDirection dir) const override
89 {
91 }
92
94 qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const override;
95
99 ApplyView& afView,
100 boost::container::flat_set<uint256>& ofrsToRm,
101 XRPAmount const& out);
102
105 PaymentSandbox& sb,
106 ApplyView& afView,
107 boost::container::flat_set<uint256>& ofrsToRm,
108 XRPAmount const& in);
109
111 validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in) override;
112
113 // Check for errors and violations of frozen constraints.
114 [[nodiscard]] TER
115 check(StrandContext const& ctx) const;
116
117protected:
119 xrpLiquidImpl(ReadView& sb, std::int32_t reserveReduction) const
120 {
121 return xrpl::xrpLiquid(sb, acc_, reserveReduction, j_);
122 }
123
125 logStringImpl(char const* name) const
126 {
128 ostr << name << ": "
129 << "\nAcc: " << acc_;
130 return ostr.str();
131 }
132
133private:
134 template <class P>
135 friend bool
137
138 friend bool
140 {
141 return !(lhs == rhs);
142 }
143
144 [[nodiscard]] bool
145 equal(Step const& rhs) const override
146 {
147 if (auto ds = dynamic_cast<XRPEndpointStep const*>(&rhs))
148 {
149 return *this == *ds;
150 }
151 return false;
152 }
153
154 friend TDerived;
155};
156
157//------------------------------------------------------------------------------
158
159// Flow is used in two different circumstances for transferring funds:
160// o Payments, and
161// o Offer crossing.
162// The rules for handling funds in these two cases are almost, but not
163// quite, the same.
164
165// Payment XRPEndpointStep class (not offer crossing).
166class XRPEndpointPaymentStep : public XRPEndpointStep<XRPEndpointPaymentStep>
167{
168public:
173
176 {
177 return xrpLiquidImpl(sb, 0);
178 ;
179 }
180
181 [[nodiscard]] std::string
182 logString() const override
183 {
184 return logStringImpl("XRPEndpointPaymentStep");
185 }
186};
187
188// Offer crossing XRPEndpointStep class (not a payment).
189class XRPEndpointOfferCrossingStep : public XRPEndpointStep<XRPEndpointOfferCrossingStep>
190{
191private:
192 // For historical reasons, offer crossing is allowed to dig further
193 // into the XRP reserve than an ordinary payment. (I believe it's
194 // because the trust line was created after the XRP was removed.)
195 // Return how much the reserve should be reduced.
196 //
197 // Note that reduced reserve only happens if the trust line or MPT does not
198 // currently exist.
199 static std::int32_t
201 {
202 if (ctx.isFirst)
203 {
204 return ctx.strandDeliver.visit(
205 [&](Issue const& issue) {
206 if (!ctx.view.exists(keylet::trustLine(acc, issue)))
207 return -1;
208 return 0;
209 },
210 [&](MPTIssue const& issue) {
211 if (!ctx.view.exists(keylet::mptoken(issue.getMptID(), acc)))
212 return -1;
213 return 0;
214 });
215 }
216 return 0;
217 }
218
219public:
225
228 {
230 }
231
232 [[nodiscard]] std::string
233 logString() const override
234 {
235 return logStringImpl("XRPEndpointOfferCrossingStep");
236 }
237
238private:
240};
241
242//------------------------------------------------------------------------------
243
244template <class TDerived>
245inline bool
247{
248 return lhs.acc_ == rhs.acc_ && lhs.isLast_ == rhs.isLast_;
249}
250
251template <class TDerived>
257
258template <class TDerived>
261 PaymentSandbox& sb,
262 ApplyView& afView,
263 boost::container::flat_set<uint256>& ofrsToRm,
264 XRPAmount const& out)
265{
266 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
267
268 auto const result = isLast_ ? out : std::min(balance, out);
269
270 auto& sender = isLast_ ? xrpAccount() : acc_;
271 auto& receiver = isLast_ ? acc_ : xrpAccount();
272 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
273 if (!isTesSuccess(ter))
274 return {XRPAmount{beast::kZero}, XRPAmount{beast::kZero}};
275
276 cache_.emplace(result);
277 return {result, result};
278}
279
280template <class TDerived>
283 PaymentSandbox& sb,
284 ApplyView& afView,
285 boost::container::flat_set<uint256>& ofrsToRm,
286 XRPAmount const& in)
287{
288 XRPL_ASSERT(cache_, "xrpl::XRPEndpointStep::fwdImp : cache is set");
289 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
290
291 auto const result = isLast_ ? in : std::min(balance, in);
292
293 auto& sender = isLast_ ? xrpAccount() : acc_;
294 auto& receiver = isLast_ ? acc_ : xrpAccount();
295 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
296 if (!isTesSuccess(ter))
297 return {XRPAmount{beast::kZero}, XRPAmount{beast::kZero}};
298
299 cache_.emplace(result);
300 return {result, result};
301}
302
303template <class TDerived>
306{
307 if (!cache_)
308 {
309 JLOG(j_.error()) << "Expected valid cache in validFwd";
310 return {false, EitherAmount(XRPAmount(beast::kZero))};
311 }
312
313 XRPL_ASSERT(in.holds<XRPAmount>(), "xrpl::XRPEndpointStep::validFwd : input is XRP");
314
315 auto const& xrpIn = in.get<XRPAmount>();
316 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
317
318 if (!isLast_ && balance < xrpIn)
319 {
320 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
321 << " Insufficient balance: " << to_string(balance)
322 << " Requested: " << to_string(xrpIn);
323 return {false, EitherAmount(balance)};
324 }
325
326 if (xrpIn != *cache_)
327 {
328 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
329 << " ExpectedIn: " << to_string(*cache_)
330 << " CachedIn: " << to_string(xrpIn);
331 }
332 return {true, in};
333}
334
335template <class TDerived>
336TER
338{
339 if (!acc_)
340 {
341 JLOG(j_.debug()) << "XRPEndpointStep: specified bad account.";
342 return temBAD_PATH;
343 }
344
345 auto sleAcc = ctx.view.read(keylet::account(acc_));
346 if (!sleAcc)
347 {
348 JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
349 "non-existent account: "
350 << acc_;
351 return terNO_ACCOUNT;
352 }
353
354 if (!ctx.isFirst && !ctx.isLast)
355 {
356 return temBAD_PATH;
357 }
358
359 auto& src = isLast_ ? xrpAccount() : acc_;
360 auto& dst = isLast_ ? acc_ : xrpAccount();
361 auto ter = checkFreeze(ctx.view, src, dst, xrpCurrency());
362 if (!isTesSuccess(ter))
363 return ter;
364
365 auto const issuesIndex = isLast_ ? 0 : 1;
366 if (!ctx.seenDirectAssets[issuesIndex].insert(xrpIssue()).second)
367 {
368 JLOG(j_.debug()) << "XRPEndpointStep: loop detected: Index: " << ctx.strandSize << ' '
369 << *this;
370 return temBAD_PATH_LOOP;
371 }
372
373 return tesSUCCESS;
374}
375
376//------------------------------------------------------------------------------
377
378namespace test {
379// Needed for testing
380bool
381xrpEndpointStepEqual(Step const& step, AccountID const& acc)
382{
383 if (auto xs = dynamic_cast<XRPEndpointStep<XRPEndpointPaymentStep> const*>(&step))
384 {
385 return xs->acc() == acc;
386 }
387 return false;
388}
389} // namespace test
390
391//------------------------------------------------------------------------------
392
395{
396 TER ter = tefINTERNAL;
399 {
400 auto offerCrossingStep = std::make_unique<XRPEndpointOfferCrossingStep>(ctx, acc);
401 ter = offerCrossingStep->check(ctx);
402 r = std::move(offerCrossingStep);
403 }
404 else // payment
405 {
406 auto paymentStep = std::make_unique<XRPEndpointPaymentStep>(ctx, acc);
407 ter = paymentStep->check(ctx);
408 r = std::move(paymentStep);
409 }
410 if (!isTesSuccess(ter))
411 return {ter, nullptr};
412
413 return {tesSUCCESS, std::move(r)};
414}
415
416} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:118
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition Asset.h:107
A currency issued by an account.
Definition Issue.h:13
A wrapper which makes credits unavailable to balances.
Represents the logical ratio of output currency to input currency.
Definition Quality.h:91
A view into a ledger.
Definition ReadView.h:31
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
static std::uint64_t const kURateOne
Definition STAmount.h:64
A step in a payment path.
Definition Steps.h:66
static std::int32_t computeReserveReduction(StrandContext const &ctx, AccountID const &acc)
XRPAmount xrpLiquid(ReadView &sb) const
std::string logString() const override
XRPEndpointOfferCrossingStep(StrandContext const &ctx, AccountID const &acc)
XRPAmount xrpLiquid(ReadView &sb) const
XRPEndpointPaymentStep(StrandContext const &ctx, AccountID const &acc)
std::string logString() const override
std::optional< EitherAmount > cachedIn() const override
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
std::optional< EitherAmount > cached() const
XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
friend bool operator==(XRPEndpointStep< P > const &lhs, XRPEndpointStep< P > const &rhs)
std::optional< EitherAmount > cachedOut() const override
beast::Journal const j_
std::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
friend bool operator!=(XRPEndpointStep const &lhs, XRPEndpointStep const &rhs)
std::optional< XRPAmount > cache_
AccountID const & acc() const
XRPAmount xrpLiquidImpl(ReadView &sb, std::int32_t reserveReduction) const
bool equal(Step const &rhs) const override
TER check(StrandContext const &ctx) const
std::pair< XRPAmount, XRPAmount > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, XRPAmount const &out)
std::string logStringImpl(char const *name) const
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection prevStepDir) const override
std::pair< XRPAmount, XRPAmount > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, XRPAmount const &in)
T make_pair(T... args)
T make_unique(T... args)
T min(T... args)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:533
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:241
bool xrpEndpointStepEqual(Step const &step, AccountID const &acc)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::pair< TER, std::unique_ptr< Step > > makeXrpEndpointStep(StrandContext const &ctx, AccountID const &acc)
@ terNO_ACCOUNT
Definition TER.h:209
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
constexpr bool operator==(BaseUInt< Bits, Tag > const &lhs, BaseUInt< Bits, Tag > const &rhs)
Definition base_uint.h:588
@ tefINTERNAL
Definition TER.h:163
DebtDirection
Definition Steps.h:21
Currency const & xrpCurrency()
XRP currency.
Definition UintTypes.cpp:99
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
StrandDirection
Definition Steps.h:23
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition StepChecks.h:13
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
@ temBAD_PATH
Definition TER.h:82
@ temBAD_PATH_LOOP
Definition TER.h:83
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
TERSubset< CanCvtToTER > TER
Definition TER.h:634
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tesSUCCESS
Definition TER.h:240
STAmount toSTAmount(IOUAmount const &iou, Asset const &asset)
T str(T... args)
bool holds() const
T const & get() const
Context needed to build Strand Steps and for error checking.
Definition Steps.h:518
Asset const strandDeliver
Asset strand delivers.
Definition Steps.h:522
size_t const strandSize
Length of Strand.
Definition Steps.h:529
ReadView const & view
Current ReadView.
Definition Steps.h:519
bool const isFirst
true if Step is first in Strand
Definition Steps.h:524
std::array< boost::container::flat_set< Asset >, 2 > & seenDirectAssets
A strand may not include the same account node more than once in the same currency.
Definition Steps.h:539
bool const isLast
true if Step is last in Strand
Definition Steps.h:525
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition Steps.h:527