rippled
Loading...
Searching...
No Matches
XRPEndpointStep.cpp
1#include <xrpld/app/paths/Credit.h>
2#include <xrpld/app/paths/detail/AmountSpec.h>
3#include <xrpld/app/paths/detail/StepChecks.h>
4#include <xrpld/app/paths/detail/Steps.h>
5
6#include <xrpl/basics/Log.h>
7#include <xrpl/ledger/PaymentSandbox.h>
8#include <xrpl/protocol/Feature.h>
9#include <xrpl/protocol/IOUAmount.h>
10#include <xrpl/protocol/Quality.h>
11#include <xrpl/protocol/XRPAmount.h>
12
13#include <boost/container/flat_set.hpp>
14
15#include <sstream>
16
17namespace ripple {
18
19template <class TDerived>
21 : public StepImp<XRPAmount, XRPAmount, XRPEndpointStep<TDerived>>
22{
23private:
25 bool const isLast_;
27
28 // Since this step will always be an endpoint in a strand
29 // (either the first or last step) the same cache is used
30 // for cachedIn and cachedOut and only one will ever be used
32
34 cached() const
35 {
36 if (!cache_)
37 return std::nullopt;
38 return EitherAmount(*cache_);
39 }
40
41public:
43 : acc_(acc), isLast_(ctx.isLast), j_(ctx.j)
44 {
45 }
46
47 AccountID const&
48 acc() const
49 {
50 return acc_;
51 }
52
54 directStepAccts() const override
55 {
56 if (isLast_)
59 }
60
62 cachedIn() const override
63 {
64 return cached();
65 }
66
68 cachedOut() const override
69 {
70 return cached();
71 }
72
74 debtDirection(ReadView const& sb, StrandDirection dir) const override
75 {
77 }
78
81 const override;
82
86 ApplyView& afView,
87 boost::container::flat_set<uint256>& ofrsToRm,
88 XRPAmount const& out);
89
93 ApplyView& afView,
94 boost::container::flat_set<uint256>& ofrsToRm,
95 XRPAmount const& in);
96
99 override;
100
101 // Check for errors and violations of frozen constraints.
102 TER
103 check(StrandContext const& ctx) const;
104
105protected:
107 xrpLiquidImpl(ReadView& sb, std::int32_t reserveReduction) const
108 {
109 return ripple::xrpLiquid(sb, acc_, reserveReduction, j_);
110 }
111
113 logStringImpl(char const* name) const
114 {
116 ostr << name << ": "
117 << "\nAcc: " << acc_;
118 return ostr.str();
119 }
120
121private:
122 template <class P>
123 friend bool
125
126 friend bool
128 {
129 return !(lhs == rhs);
130 }
131
132 bool
133 equal(Step const& rhs) const override
134 {
135 if (auto ds = dynamic_cast<XRPEndpointStep const*>(&rhs))
136 {
137 return *this == *ds;
138 }
139 return false;
140 }
141};
142
143//------------------------------------------------------------------------------
144
145// Flow is used in two different circumstances for transferring funds:
146// o Payments, and
147// o Offer crossing.
148// The rules for handling funds in these two cases are almost, but not
149// quite, the same.
150
151// Payment XRPEndpointStep class (not offer crossing).
152class XRPEndpointPaymentStep : public XRPEndpointStep<XRPEndpointPaymentStep>
153{
154public:
155 using XRPEndpointStep<XRPEndpointPaymentStep>::XRPEndpointStep;
156
159 {
160 return xrpLiquidImpl(sb, 0);
161 ;
162 }
163
165 logString() const override
166 {
167 return logStringImpl("XRPEndpointPaymentStep");
168 }
169};
170
171// Offer crossing XRPEndpointStep class (not a payment).
173 : public XRPEndpointStep<XRPEndpointOfferCrossingStep>
174{
175private:
176 // For historical reasons, offer crossing is allowed to dig further
177 // into the XRP reserve than an ordinary payment. (I believe it's
178 // because the trust line was created after the XRP was removed.)
179 // Return how much the reserve should be reduced.
180 //
181 // Note that reduced reserve only happens if the trust line does not
182 // currently exist.
183 static std::int32_t
185 {
186 if (ctx.isFirst && !ctx.view.read(keylet::line(acc, ctx.strandDeliver)))
187 return -1;
188 return 0;
189 }
190
191public:
197
200 {
202 }
203
205 logString() const override
206 {
207 return logStringImpl("XRPEndpointOfferCrossingStep");
208 }
209
210private:
212};
213
214//------------------------------------------------------------------------------
215
216template <class TDerived>
217inline bool
219 XRPEndpointStep<TDerived> const& lhs,
220 XRPEndpointStep<TDerived> const& rhs)
221{
222 return lhs.acc_ == rhs.acc_ && lhs.isLast_ == rhs.isLast_;
223}
224
225template <class TDerived>
228 ReadView const& v,
229 DebtDirection prevStepDir) const
230{
231 return {
232 Quality{STAmount::uRateOne},
233 this->debtDirection(v, StrandDirection::forward)};
234}
235
236template <class TDerived>
239 PaymentSandbox& sb,
240 ApplyView& afView,
241 boost::container::flat_set<uint256>& ofrsToRm,
242 XRPAmount const& out)
243{
244 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
245
246 auto const result = isLast_ ? out : std::min(balance, out);
247
248 auto& sender = isLast_ ? xrpAccount() : acc_;
249 auto& receiver = isLast_ ? acc_ : xrpAccount();
250 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
251 if (ter != tesSUCCESS)
252 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
253
254 cache_.emplace(result);
255 return {result, result};
256}
257
258template <class TDerived>
261 PaymentSandbox& sb,
262 ApplyView& afView,
263 boost::container::flat_set<uint256>& ofrsToRm,
264 XRPAmount const& in)
265{
266 XRPL_ASSERT(cache_, "ripple::XRPEndpointStep::fwdImp : cache is set");
267 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
268
269 auto const result = isLast_ ? in : std::min(balance, in);
270
271 auto& sender = isLast_ ? xrpAccount() : acc_;
272 auto& receiver = isLast_ ? acc_ : xrpAccount();
273 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
274 if (ter != tesSUCCESS)
275 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
276
277 cache_.emplace(result);
278 return {result, result};
279}
280
281template <class TDerived>
284 PaymentSandbox& sb,
285 ApplyView& afView,
286 EitherAmount const& in)
287{
288 if (!cache_)
289 {
290 JLOG(j_.error()) << "Expected valid cache in validFwd";
291 return {false, EitherAmount(XRPAmount(beast::zero))};
292 }
293
294 XRPL_ASSERT(in.native, "ripple::XRPEndpointStep::validFwd : input is XRP");
295
296 auto const& xrpIn = in.xrp;
297 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
298
299 if (!isLast_ && balance < xrpIn)
300 {
301 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
302 << " Insufficient balance: " << to_string(balance)
303 << " Requested: " << to_string(xrpIn);
304 return {false, EitherAmount(balance)};
305 }
306
307 if (xrpIn != *cache_)
308 {
309 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
310 << " ExpectedIn: " << to_string(*cache_)
311 << " CachedIn: " << to_string(xrpIn);
312 }
313 return {true, in};
314}
315
316template <class TDerived>
317TER
319{
320 if (!acc_)
321 {
322 JLOG(j_.debug()) << "XRPEndpointStep: specified bad account.";
323 return temBAD_PATH;
324 }
325
326 auto sleAcc = ctx.view.read(keylet::account(acc_));
327 if (!sleAcc)
328 {
329 JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
330 "non-existent account: "
331 << acc_;
332 return terNO_ACCOUNT;
333 }
334
335 if (!ctx.isFirst && !ctx.isLast)
336 {
337 return temBAD_PATH;
338 }
339
340 auto& src = isLast_ ? xrpAccount() : acc_;
341 auto& dst = isLast_ ? acc_ : xrpAccount();
342 auto ter = checkFreeze(ctx.view, src, dst, xrpCurrency());
343 if (ter != tesSUCCESS)
344 return ter;
345
346 auto const issuesIndex = isLast_ ? 0 : 1;
347 if (!ctx.seenDirectIssues[issuesIndex].insert(xrpIssue()).second)
348 {
349 JLOG(j_.debug()) << "XRPEndpointStep: loop detected: Index: "
350 << ctx.strandSize << ' ' << *this;
351 return temBAD_PATH_LOOP;
352 }
353
354 return tesSUCCESS;
355}
356
357//------------------------------------------------------------------------------
358
359namespace test {
360// Needed for testing
361bool
362xrpEndpointStepEqual(Step const& step, AccountID const& acc)
363{
364 if (auto xs =
365 dynamic_cast<XRPEndpointStep<XRPEndpointPaymentStep> const*>(&step))
366 {
367 return xs->acc() == acc;
368 }
369 return false;
370}
371} // namespace test
372
373//------------------------------------------------------------------------------
374
377{
378 TER ter = tefINTERNAL;
380 if (ctx.offerCrossing)
381 {
382 auto offerCrossingStep =
384 ter = offerCrossingStep->check(ctx);
385 r = std::move(offerCrossingStep);
386 }
387 else // payment
388 {
389 auto paymentStep = std::make_unique<XRPEndpointPaymentStep>(ctx, acc);
390 ter = paymentStep->check(ctx);
391 r = std::move(paymentStep);
392 }
393 if (ter != tesSUCCESS)
394 return {ter, nullptr};
395
396 return {tesSUCCESS, std::move(r)};
397}
398
399} // namespace ripple
A generic endpoint for log messages.
Definition Journal.h:41
Stream error() const
Definition Journal.h:327
Stream debug() const
Definition Journal.h:309
Stream warn() const
Definition Journal.h:321
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:124
A wrapper which makes credits unavailable to balances.
A view into a ledger.
Definition ReadView.h:32
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:61
A step in a payment path.
Definition Steps.h:67
std::string logString() const override
XRPAmount xrpLiquid(ReadView &sb) const
static std::int32_t computeReserveReduction(StrandContext const &ctx, AccountID const &acc)
XRPEndpointOfferCrossingStep(StrandContext const &ctx, AccountID const &acc)
XRPAmount xrpLiquid(ReadView &sb) const
std::string logString() const override
std::optional< EitherAmount > cachedIn() const override
std::string logStringImpl(char const *name) const
beast::Journal const j_
TER check(StrandContext const &ctx) const
std::optional< XRPAmount > cache_
friend bool operator==(XRPEndpointStep< P > const &lhs, XRPEndpointStep< P > const &rhs)
std::pair< XRPAmount, XRPAmount > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, XRPAmount const &out)
std::pair< XRPAmount, XRPAmount > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, XRPAmount const &in)
friend bool operator!=(XRPEndpointStep const &lhs, XRPEndpointStep const &rhs)
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
std::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
std::optional< EitherAmount > cached() const
XRPAmount xrpLiquidImpl(ReadView &sb, std::int32_t reserveReduction) const
std::optional< EitherAmount > cachedOut() const override
XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
AccountID const & acc() const
bool equal(Step const &rhs) const override
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection prevStepDir) const override
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:225
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:6
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:96
AccountID const & xrpAccount()
Compute AccountID from public key.
std::pair< TER, std::unique_ptr< Step > > make_XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
StrandDirection
Definition Steps.h:25
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition StepChecks.h:14
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
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.
Definition View.cpp:2172
@ tefINTERNAL
Definition TER.h:154
DebtDirection
Definition Steps.h:23
Currency const & xrpCurrency()
XRP currency.
@ tesSUCCESS
Definition TER.h:226
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition base_uint.h:566
@ terNO_ACCOUNT
Definition TER.h:198
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition View.cpp:599
@ temBAD_PATH_LOOP
Definition TER.h:78
@ temBAD_PATH
Definition TER.h:77
T str(T... args)
Context needed to build Strand Steps and for error checking.
Definition Steps.h:514
size_t const strandSize
Length of Strand.
Definition Steps.h:526
ReadView const & view
Current ReadView.
Definition Steps.h:515
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:536
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition Steps.h:524
bool const isFirst
true if Step is first in Strand
Definition Steps.h:520
bool const isLast
true if Step is last in Strand
Definition Steps.h:521
Issue const strandDeliver
Issue strand delivers.
Definition Steps.h:518