rippled
Loading...
Searching...
No Matches
XRPEndpointStep.cpp
1#include <xrpld/app/paths/detail/AmountSpec.h>
2#include <xrpld/app/paths/detail/StepChecks.h>
3#include <xrpld/app/paths/detail/Steps.h>
4
5#include <xrpl/basics/Log.h>
6#include <xrpl/ledger/Credit.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 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
40public:
41 XRPEndpointStep(StrandContext const& ctx, AccountID const& acc) : acc_(acc), isLast_(ctx.isLast), j_(ctx.j)
42 {
43 }
44
45 AccountID const&
46 acc() const
47 {
48 return acc_;
49 }
50
52 directStepAccts() const override
53 {
54 if (isLast_)
57 }
58
60 cachedIn() const override
61 {
62 return cached();
63 }
64
66 cachedOut() const override
67 {
68 return cached();
69 }
70
72 debtDirection(ReadView const& sb, StrandDirection dir) const override
73 {
75 }
76
78 qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const override;
79
81 revImp(PaymentSandbox& sb, ApplyView& afView, boost::container::flat_set<uint256>& ofrsToRm, XRPAmount const& out);
82
84 fwdImp(PaymentSandbox& sb, ApplyView& afView, boost::container::flat_set<uint256>& ofrsToRm, XRPAmount const& in);
85
87 validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in) override;
88
89 // Check for errors and violations of frozen constraints.
90 TER
91 check(StrandContext const& ctx) const;
92
93protected:
95 xrpLiquidImpl(ReadView& sb, std::int32_t reserveReduction) const
96 {
97 return xrpl::xrpLiquid(sb, acc_, reserveReduction, j_);
98 }
99
101 logStringImpl(char const* name) const
102 {
104 ostr << name << ": "
105 << "\nAcc: " << acc_;
106 return ostr.str();
107 }
108
109private:
110 template <class P>
111 friend bool
113
114 friend bool
116 {
117 return !(lhs == rhs);
118 }
119
120 bool
121 equal(Step const& rhs) const override
122 {
123 if (auto ds = dynamic_cast<XRPEndpointStep const*>(&rhs))
124 {
125 return *this == *ds;
126 }
127 return false;
128 }
129};
130
131//------------------------------------------------------------------------------
132
133// Flow is used in two different circumstances for transferring funds:
134// o Payments, and
135// o Offer crossing.
136// The rules for handling funds in these two cases are almost, but not
137// quite, the same.
138
139// Payment XRPEndpointStep class (not offer crossing).
140class XRPEndpointPaymentStep : public XRPEndpointStep<XRPEndpointPaymentStep>
141{
142public:
143 using XRPEndpointStep<XRPEndpointPaymentStep>::XRPEndpointStep;
144
147 {
148 return xrpLiquidImpl(sb, 0);
149 ;
150 }
151
153 logString() const override
154 {
155 return logStringImpl("XRPEndpointPaymentStep");
156 }
157};
158
159// Offer crossing XRPEndpointStep class (not a payment).
160class XRPEndpointOfferCrossingStep : public XRPEndpointStep<XRPEndpointOfferCrossingStep>
161{
162private:
163 // For historical reasons, offer crossing is allowed to dig further
164 // into the XRP reserve than an ordinary payment. (I believe it's
165 // because the trust line was created after the XRP was removed.)
166 // Return how much the reserve should be reduced.
167 //
168 // Note that reduced reserve only happens if the trust line does not
169 // currently exist.
170 static std::int32_t
172 {
173 if (ctx.isFirst && !ctx.view.read(keylet::line(acc, ctx.strandDeliver)))
174 return -1;
175 return 0;
176 }
177
178public:
183
186 {
188 }
189
191 logString() const override
192 {
193 return logStringImpl("XRPEndpointOfferCrossingStep");
194 }
195
196private:
198};
199
200//------------------------------------------------------------------------------
201
202template <class TDerived>
203inline bool
205{
206 return lhs.acc_ == rhs.acc_ && lhs.isLast_ == rhs.isLast_;
207}
208
209template <class TDerived>
212{
213 return {Quality{STAmount::uRateOne}, this->debtDirection(v, StrandDirection::forward)};
214}
215
216template <class TDerived>
219 PaymentSandbox& sb,
220 ApplyView& afView,
221 boost::container::flat_set<uint256>& ofrsToRm,
222 XRPAmount const& out)
223{
224 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
225
226 auto const result = isLast_ ? out : std::min(balance, out);
227
228 auto& sender = isLast_ ? xrpAccount() : acc_;
229 auto& receiver = isLast_ ? acc_ : xrpAccount();
230 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
231 if (ter != tesSUCCESS)
232 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
233
234 cache_.emplace(result);
235 return {result, result};
236}
237
238template <class TDerived>
241 PaymentSandbox& sb,
242 ApplyView& afView,
243 boost::container::flat_set<uint256>& ofrsToRm,
244 XRPAmount const& in)
245{
246 XRPL_ASSERT(cache_, "xrpl::XRPEndpointStep::fwdImp : cache is set");
247 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
248
249 auto const result = isLast_ ? in : std::min(balance, in);
250
251 auto& sender = isLast_ ? xrpAccount() : acc_;
252 auto& receiver = isLast_ ? acc_ : xrpAccount();
253 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
254 if (ter != tesSUCCESS)
255 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
256
257 cache_.emplace(result);
258 return {result, result};
259}
260
261template <class TDerived>
264{
265 if (!cache_)
266 {
267 JLOG(j_.error()) << "Expected valid cache in validFwd";
268 return {false, EitherAmount(XRPAmount(beast::zero))};
269 }
270
271 XRPL_ASSERT(in.native, "xrpl::XRPEndpointStep::validFwd : input is XRP");
272
273 auto const& xrpIn = in.xrp;
274 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
275
276 if (!isLast_ && balance < xrpIn)
277 {
278 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
279 << " Insufficient balance: " << to_string(balance) << " Requested: " << to_string(xrpIn);
280 return {false, EitherAmount(balance)};
281 }
282
283 if (xrpIn != *cache_)
284 {
285 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
286 << " ExpectedIn: " << to_string(*cache_) << " CachedIn: " << to_string(xrpIn);
287 }
288 return {true, in};
289}
290
291template <class TDerived>
292TER
294{
295 if (!acc_)
296 {
297 JLOG(j_.debug()) << "XRPEndpointStep: specified bad account.";
298 return temBAD_PATH;
299 }
300
301 auto sleAcc = ctx.view.read(keylet::account(acc_));
302 if (!sleAcc)
303 {
304 JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
305 "non-existent account: "
306 << acc_;
307 return terNO_ACCOUNT;
308 }
309
310 if (!ctx.isFirst && !ctx.isLast)
311 {
312 return temBAD_PATH;
313 }
314
315 auto& src = isLast_ ? xrpAccount() : acc_;
316 auto& dst = isLast_ ? acc_ : xrpAccount();
317 auto ter = checkFreeze(ctx.view, src, dst, xrpCurrency());
318 if (ter != tesSUCCESS)
319 return ter;
320
321 auto const issuesIndex = isLast_ ? 0 : 1;
322 if (!ctx.seenDirectIssues[issuesIndex].insert(xrpIssue()).second)
323 {
324 JLOG(j_.debug()) << "XRPEndpointStep: loop detected: Index: " << ctx.strandSize << ' ' << *this;
325 return temBAD_PATH_LOOP;
326 }
327
328 return tesSUCCESS;
329}
330
331//------------------------------------------------------------------------------
332
333namespace test {
334// Needed for testing
335bool
336xrpEndpointStepEqual(Step const& step, AccountID const& acc)
337{
338 if (auto xs = dynamic_cast<XRPEndpointStep<XRPEndpointPaymentStep> const*>(&step))
339 {
340 return xs->acc() == acc;
341 }
342 return false;
343}
344} // namespace test
345
346//------------------------------------------------------------------------------
347
350{
351 TER ter = tefINTERNAL;
353 if (ctx.offerCrossing)
354 {
355 auto offerCrossingStep = std::make_unique<XRPEndpointOfferCrossingStep>(ctx, acc);
356 ter = offerCrossingStep->check(ctx);
357 r = std::move(offerCrossingStep);
358 }
359 else // payment
360 {
361 auto paymentStep = std::make_unique<XRPEndpointPaymentStep>(ctx, acc);
362 ter = paymentStep->check(ctx);
363 r = std::move(paymentStep);
364 }
365 if (ter != tesSUCCESS)
366 return {ter, nullptr};
367
368 return {tesSUCCESS, std::move(r)};
369}
370
371} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:114
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: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
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:214
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
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)
Definition View.cpp:571
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
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:22
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition base_uint.h:552
Currency const & xrpCurrency()
XRP currency.
Definition UintTypes.cpp:96
StrandDirection
Definition Steps.h:24
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:2446
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
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:508
size_t const strandSize
Length of Strand.
Definition Steps.h:519
ReadView const & view
Current ReadView.
Definition Steps.h:509
bool const isFirst
true if Step is first in Strand
Definition Steps.h:514
bool const isLast
true if Step is last in Strand
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:529
Issue const strandDeliver
Issue strand delivers.
Definition Steps.h:512
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition Steps.h:517