rippled
Loading...
Searching...
No Matches
XRPEndpointStep.cpp
1#include <xrpl/basics/Log.h>
2#include <xrpl/ledger/PaymentSandbox.h>
3#include <xrpl/ledger/helpers/AccountRootHelpers.h>
4#include <xrpl/ledger/helpers/RippleStateHelpers.h>
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/IOUAmount.h>
7#include <xrpl/protocol/Quality.h>
8#include <xrpl/protocol/XRPAmount.h>
9#include <xrpl/tx/paths/detail/AmountSpec.h>
10#include <xrpl/tx/paths/detail/StepChecks.h>
11#include <xrpl/tx/paths/detail/Steps.h>
12
13#include <boost/container/flat_set.hpp>
14
15#include <sstream>
16
17namespace xrpl {
18
19template <class TDerived>
20class XRPEndpointStep : public StepImp<XRPAmount, XRPAmount, XRPEndpointStep<TDerived>>
21{
22private:
24 bool const isLast_;
26
27 // Since this step will always be an endpoint in a strand
28 // (either the first or last step) the same cache is used
29 // for cachedIn and cachedOut and only one will ever be used
31
33 cached() const
34 {
35 if (!cache_)
36 return std::nullopt;
37 return EitherAmount(*cache_);
38 }
39
41 : acc_(acc), isLast_(ctx.isLast), j_(ctx.j)
42 {
43 }
44
45public:
46 AccountID const&
47 acc() const
48 {
49 return acc_;
50 }
51
53 directStepAccts() const override
54 {
55 if (isLast_)
58 }
59
61 cachedIn() const override
62 {
63 return cached();
64 }
65
67 cachedOut() const override
68 {
69 return cached();
70 }
71
73 debtDirection(ReadView const& sb, StrandDirection dir) const override
74 {
76 }
77
79 qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const override;
80
84 ApplyView& afView,
85 boost::container::flat_set<uint256>& ofrsToRm,
86 XRPAmount const& out);
87
91 ApplyView& afView,
92 boost::container::flat_set<uint256>& ofrsToRm,
93 XRPAmount const& in);
94
96 validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in) override;
97
98 // Check for errors and violations of frozen constraints.
99 TER
100 check(StrandContext const& ctx) const;
101
102protected:
104 xrpLiquidImpl(ReadView& sb, std::int32_t reserveReduction) const
105 {
106 return xrpl::xrpLiquid(sb, acc_, reserveReduction, j_);
107 }
108
110 logStringImpl(char const* name) const
111 {
113 ostr << name << ": "
114 << "\nAcc: " << acc_;
115 return ostr.str();
116 }
117
118private:
119 template <class P>
120 friend bool
122
123 friend bool
125 {
126 return !(lhs == rhs);
127 }
128
129 bool
130 equal(Step const& rhs) const override
131 {
132 if (auto ds = dynamic_cast<XRPEndpointStep const*>(&rhs))
133 {
134 return *this == *ds;
135 }
136 return false;
137 }
138
139 friend TDerived;
140};
141
142//------------------------------------------------------------------------------
143
144// Flow is used in two different circumstances for transferring funds:
145// o Payments, and
146// o Offer crossing.
147// The rules for handling funds in these two cases are almost, but not
148// quite, the same.
149
150// Payment XRPEndpointStep class (not offer crossing).
151class XRPEndpointPaymentStep : public XRPEndpointStep<XRPEndpointPaymentStep>
152{
153public:
158
161 {
162 return xrpLiquidImpl(sb, 0);
163 ;
164 }
165
167 logString() const override
168 {
169 return logStringImpl("XRPEndpointPaymentStep");
170 }
171};
172
173// Offer crossing XRPEndpointStep class (not a payment).
174class XRPEndpointOfferCrossingStep : public XRPEndpointStep<XRPEndpointOfferCrossingStep>
175{
176private:
177 // For historical reasons, offer crossing is allowed to dig further
178 // into the XRP reserve than an ordinary payment. (I believe it's
179 // because the trust line was created after the XRP was removed.)
180 // Return how much the reserve should be reduced.
181 //
182 // Note that reduced reserve only happens if the trust line does not
183 // currently exist.
184 static std::int32_t
186 {
187 if (ctx.isFirst && !ctx.view.read(keylet::line(acc, ctx.strandDeliver)))
188 return -1;
189 return 0;
190 }
191
192public:
198
201 {
203 }
204
206 logString() const override
207 {
208 return logStringImpl("XRPEndpointOfferCrossingStep");
209 }
210
211private:
213};
214
215//------------------------------------------------------------------------------
216
217template <class TDerived>
218inline bool
220{
221 return lhs.acc_ == rhs.acc_ && lhs.isLast_ == rhs.isLast_;
222}
223
224template <class TDerived>
227{
228 return {Quality{STAmount::uRateOne}, this->debtDirection(v, StrandDirection::forward)};
229}
230
231template <class TDerived>
234 PaymentSandbox& sb,
235 ApplyView& afView,
236 boost::container::flat_set<uint256>& ofrsToRm,
237 XRPAmount const& out)
238{
239 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
240
241 auto const result = isLast_ ? out : std::min(balance, out);
242
243 auto& sender = isLast_ ? xrpAccount() : acc_;
244 auto& receiver = isLast_ ? acc_ : xrpAccount();
245 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
246 if (!isTesSuccess(ter))
247 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
248
249 cache_.emplace(result);
250 return {result, result};
251}
252
253template <class TDerived>
256 PaymentSandbox& sb,
257 ApplyView& afView,
258 boost::container::flat_set<uint256>& ofrsToRm,
259 XRPAmount const& in)
260{
261 XRPL_ASSERT(cache_, "xrpl::XRPEndpointStep::fwdImp : cache is set");
262 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
263
264 auto const result = isLast_ ? in : std::min(balance, in);
265
266 auto& sender = isLast_ ? xrpAccount() : acc_;
267 auto& receiver = isLast_ ? acc_ : xrpAccount();
268 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
269 if (!isTesSuccess(ter))
270 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
271
272 cache_.emplace(result);
273 return {result, result};
274}
275
276template <class TDerived>
279{
280 if (!cache_)
281 {
282 JLOG(j_.error()) << "Expected valid cache in validFwd";
283 return {false, EitherAmount(XRPAmount(beast::zero))};
284 }
285
286 XRPL_ASSERT(in.native, "xrpl::XRPEndpointStep::validFwd : input is XRP");
287
288 auto const& xrpIn = in.xrp;
289 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
290
291 if (!isLast_ && balance < xrpIn)
292 {
293 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
294 << " Insufficient balance: " << to_string(balance)
295 << " Requested: " << to_string(xrpIn);
296 return {false, EitherAmount(balance)};
297 }
298
299 if (xrpIn != *cache_)
300 {
301 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
302 << " ExpectedIn: " << to_string(*cache_)
303 << " CachedIn: " << to_string(xrpIn);
304 }
305 return {true, in};
306}
307
308template <class TDerived>
309TER
311{
312 if (!acc_)
313 {
314 JLOG(j_.debug()) << "XRPEndpointStep: specified bad account.";
315 return temBAD_PATH;
316 }
317
318 auto sleAcc = ctx.view.read(keylet::account(acc_));
319 if (!sleAcc)
320 {
321 JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
322 "non-existent account: "
323 << acc_;
324 return terNO_ACCOUNT;
325 }
326
327 if (!ctx.isFirst && !ctx.isLast)
328 {
329 return temBAD_PATH;
330 }
331
332 auto& src = isLast_ ? xrpAccount() : acc_;
333 auto& dst = isLast_ ? acc_ : xrpAccount();
334 auto ter = checkFreeze(ctx.view, src, dst, xrpCurrency());
335 if (!isTesSuccess(ter))
336 return ter;
337
338 auto const issuesIndex = isLast_ ? 0 : 1;
339 if (!ctx.seenDirectIssues[issuesIndex].insert(xrpIssue()).second)
340 {
341 JLOG(j_.debug()) << "XRPEndpointStep: loop detected: Index: " << ctx.strandSize << ' '
342 << *this;
343 return temBAD_PATH_LOOP;
344 }
345
346 return tesSUCCESS;
347}
348
349//------------------------------------------------------------------------------
350
351namespace test {
352// Needed for testing
353bool
354xrpEndpointStepEqual(Step const& step, AccountID const& acc)
355{
356 if (auto xs = dynamic_cast<XRPEndpointStep<XRPEndpointPaymentStep> const*>(&step))
357 {
358 return xs->acc() == acc;
359 }
360 return false;
361}
362} // namespace test
363
364//------------------------------------------------------------------------------
365
368{
369 TER ter = tefINTERNAL;
371 if (ctx.offerCrossing != 0u)
372 {
373 auto offerCrossingStep = std::make_unique<XRPEndpointOfferCrossingStep>(ctx, acc);
374 ter = offerCrossingStep->check(ctx);
375 r = std::move(offerCrossingStep);
376 }
377 else // payment
378 {
379 auto paymentStep = std::make_unique<XRPEndpointPaymentStep>(ctx, acc);
380 ter = paymentStep->check(ctx);
381 r = std::move(paymentStep);
382 }
383 if (!isTesSuccess(ter))
384 return {ter, nullptr};
385
386 return {tesSUCCESS, std::move(r)};
387}
388
389} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:319
Stream debug() const
Definition Journal.h:301
Stream warn() const
Definition Journal.h:313
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:116
A wrapper which makes credits unavailable to balances.
A view into a ledger.
Definition ReadView.h:31
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
static std::uint64_t const uRateOne
Definition STAmount.h:62
A step in a payment path.
Definition Steps.h:65
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 is_same_v
T make_pair(T... args)
T min(T... args)
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:220
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
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
@ terNO_ACCOUNT
Definition TER.h:197
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)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
std::pair< TER, std::unique_ptr< Step > > make_XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
@ tefINTERNAL
Definition TER.h:153
DebtDirection
Definition Steps.h:21
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition base_uint.h:557
Currency const & xrpCurrency()
XRP currency.
Definition UintTypes.cpp:98
StrandDirection
Definition Steps.h:23
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition StepChecks.h:13
@ temBAD_PATH
Definition TER.h:76
@ temBAD_PATH_LOOP
Definition TER.h:77
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tesSUCCESS
Definition TER.h:225
T str(T... args)
Context needed to build Strand Steps and for error checking.
Definition Steps.h:504
size_t const strandSize
Length of Strand.
Definition Steps.h:515
ReadView const & view
Current ReadView.
Definition Steps.h:505
bool const isFirst
true if Step is first in Strand
Definition Steps.h:510
bool const isLast
true if Step is last in Strand
Definition Steps.h:511
std::array< boost::container::flat_set< Issue >, 2 > & seenDirectIssues
A strand may not include the same account node more than once in the same currency.
Definition Steps.h:525
Issue const strandDeliver
Issue strand delivers.
Definition Steps.h:508
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition Steps.h:513