rippled
Loading...
Searching...
No Matches
XRPEndpointStep.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/paths/Credit.h>
21#include <xrpld/app/paths/detail/AmountSpec.h>
22#include <xrpld/app/paths/detail/StepChecks.h>
23#include <xrpld/app/paths/detail/Steps.h>
24#include <xrpld/ledger/PaymentSandbox.h>
25#include <xrpl/basics/Log.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/IOUAmount.h>
28#include <xrpl/protocol/Quality.h>
29#include <xrpl/protocol/XRPAmount.h>
30
31#include <boost/container/flat_set.hpp>
32
33#include <sstream>
34
35namespace ripple {
36
37template <class TDerived>
39 : public StepImp<XRPAmount, XRPAmount, XRPEndpointStep<TDerived>>
40{
41private:
43 bool const isLast_;
45
46 // Since this step will always be an endpoint in a strand
47 // (either the first or last step) the same cache is used
48 // for cachedIn and cachedOut and only one will ever be used
50
52 cached() const
53 {
54 if (!cache_)
55 return std::nullopt;
56 return EitherAmount(*cache_);
57 }
58
59public:
61 : acc_(acc), isLast_(ctx.isLast), j_(ctx.j)
62 {
63 }
64
65 AccountID const&
66 acc() const
67 {
68 return acc_;
69 }
70
72 directStepAccts() const override
73 {
74 if (isLast_)
77 }
78
80 cachedIn() const override
81 {
82 return cached();
83 }
84
86 cachedOut() const override
87 {
88 return cached();
89 }
90
92 debtDirection(ReadView const& sb, StrandDirection dir) const override
93 {
95 }
96
99 const override;
100
103 PaymentSandbox& sb,
104 ApplyView& afView,
105 boost::container::flat_set<uint256>& ofrsToRm,
106 XRPAmount const& out);
107
110 PaymentSandbox& sb,
111 ApplyView& afView,
112 boost::container::flat_set<uint256>& ofrsToRm,
113 XRPAmount const& in);
114
117 override;
118
119 // Check for errors and violations of frozen constraints.
120 TER
121 check(StrandContext const& ctx) const;
122
123protected:
125 xrpLiquidImpl(ReadView& sb, std::int32_t reserveReduction) const
126 {
127 return ripple::xrpLiquid(sb, acc_, reserveReduction, j_);
128 }
129
131 logStringImpl(char const* name) const
132 {
134 ostr << name << ": " << "\nAcc: " << acc_;
135 return ostr.str();
136 }
137
138private:
139 template <class P>
140 friend bool
142
143 friend bool
145 {
146 return !(lhs == rhs);
147 }
148
149 bool
150 equal(Step const& rhs) const override
151 {
152 if (auto ds = dynamic_cast<XRPEndpointStep const*>(&rhs))
153 {
154 return *this == *ds;
155 }
156 return false;
157 }
158};
159
160//------------------------------------------------------------------------------
161
162// Flow is used in two different circumstances for transferring funds:
163// o Payments, and
164// o Offer crossing.
165// The rules for handling funds in these two cases are almost, but not
166// quite, the same.
167
168// Payment XRPEndpointStep class (not offer crossing).
169class XRPEndpointPaymentStep : public XRPEndpointStep<XRPEndpointPaymentStep>
170{
171public:
172 using XRPEndpointStep<XRPEndpointPaymentStep>::XRPEndpointStep;
173
176 {
177 return xrpLiquidImpl(sb, 0);
178 ;
179 }
180
182 logString() const override
183 {
184 return logStringImpl("XRPEndpointPaymentStep");
185 }
186};
187
188// Offer crossing XRPEndpointStep class (not a payment).
190 : public XRPEndpointStep<XRPEndpointOfferCrossingStep>
191{
192private:
193 // For historical reasons, offer crossing is allowed to dig further
194 // into the XRP reserve than an ordinary payment. (I believe it's
195 // because the trust line was created after the XRP was removed.)
196 // Return how much the reserve should be reduced.
197 //
198 // Note that reduced reserve only happens if the trust line does not
199 // currently exist.
200 static std::int32_t
202 {
203 if (ctx.isFirst && !ctx.view.read(keylet::line(acc, ctx.strandDeliver)))
204 return -1;
205 return 0;
206 }
207
208public:
212 {
213 }
214
217 {
219 }
220
222 logString() const override
223 {
224 return logStringImpl("XRPEndpointOfferCrossingStep");
225 }
226
227private:
229};
230
231//------------------------------------------------------------------------------
232
233template <class TDerived>
234inline bool
236 XRPEndpointStep<TDerived> const& lhs,
237 XRPEndpointStep<TDerived> const& rhs)
238{
239 return lhs.acc_ == rhs.acc_ && lhs.isLast_ == rhs.isLast_;
240}
241
242template <class TDerived>
245 ReadView const& v,
246 DebtDirection prevStepDir) const
247{
248 return {
249 Quality{STAmount::uRateOne},
250 this->debtDirection(v, StrandDirection::forward)};
251}
252
253template <class TDerived>
256 PaymentSandbox& sb,
257 ApplyView& afView,
258 boost::container::flat_set<uint256>& ofrsToRm,
259 XRPAmount const& out)
260{
261 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
262
263 auto const result = isLast_ ? out : std::min(balance, out);
264
265 auto& sender = isLast_ ? xrpAccount() : acc_;
266 auto& receiver = isLast_ ? acc_ : xrpAccount();
267 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
268 if (ter != tesSUCCESS)
269 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
270
271 cache_.emplace(result);
272 return {result, result};
273}
274
275template <class TDerived>
278 PaymentSandbox& sb,
279 ApplyView& afView,
280 boost::container::flat_set<uint256>& ofrsToRm,
281 XRPAmount const& in)
282{
283 XRPL_ASSERT(cache_, "ripple::XRPEndpointStep::fwdImp : cache is set");
284 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
285
286 auto const result = isLast_ ? in : std::min(balance, in);
287
288 auto& sender = isLast_ ? xrpAccount() : acc_;
289 auto& receiver = isLast_ ? acc_ : xrpAccount();
290 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
291 if (ter != tesSUCCESS)
292 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
293
294 cache_.emplace(result);
295 return {result, result};
296}
297
298template <class TDerived>
301 PaymentSandbox& sb,
302 ApplyView& afView,
303 EitherAmount const& in)
304{
305 if (!cache_)
306 {
307 JLOG(j_.error()) << "Expected valid cache in validFwd";
308 return {false, EitherAmount(XRPAmount(beast::zero))};
309 }
310
311 XRPL_ASSERT(in.native, "ripple::XRPEndpointStep::validFwd : input is XRP");
312
313 auto const& xrpIn = in.xrp;
314 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
315
316 if (!isLast_ && balance < xrpIn)
317 {
318 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
319 << " Insufficient balance: " << to_string(balance)
320 << " Requested: " << to_string(xrpIn);
321 return {false, EitherAmount(balance)};
322 }
323
324 if (xrpIn != *cache_)
325 {
326 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
327 << " ExpectedIn: " << to_string(*cache_)
328 << " CachedIn: " << to_string(xrpIn);
329 }
330 return {true, in};
331}
332
333template <class TDerived>
334TER
336{
337 if (!acc_)
338 {
339 JLOG(j_.debug()) << "XRPEndpointStep: specified bad account.";
340 return temBAD_PATH;
341 }
342
343 auto sleAcc = ctx.view.read(keylet::account(acc_));
344 if (!sleAcc)
345 {
346 JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
347 "non-existent account: "
348 << acc_;
349 return terNO_ACCOUNT;
350 }
351
352 if (!ctx.isFirst && !ctx.isLast)
353 {
354 return temBAD_PATH;
355 }
356
357 auto& src = isLast_ ? xrpAccount() : acc_;
358 auto& dst = isLast_ ? acc_ : xrpAccount();
359 auto ter = checkFreeze(ctx.view, src, dst, xrpCurrency());
360 if (ter != tesSUCCESS)
361 return ter;
362
363 if (ctx.view.rules().enabled(fix1781))
364 {
365 auto const issuesIndex = isLast_ ? 0 : 1;
366 if (!ctx.seenDirectIssues[issuesIndex].insert(xrpIssue()).second)
367 {
368 JLOG(j_.debug())
369 << "XRPEndpointStep: loop detected: Index: " << ctx.strandSize
370 << ' ' << *this;
371 return temBAD_PATH_LOOP;
372 }
373 }
374
375 return tesSUCCESS;
376}
377
378//------------------------------------------------------------------------------
379
380namespace test {
381// Needed for testing
382bool
383xrpEndpointStepEqual(Step const& step, AccountID const& acc)
384{
385 if (auto xs =
386 dynamic_cast<XRPEndpointStep<XRPEndpointPaymentStep> const*>(&step))
387 {
388 return xs->acc() == acc;
389 }
390 return false;
391}
392} // namespace test
393
394//------------------------------------------------------------------------------
395
398{
399 TER ter = tefINTERNAL;
401 if (ctx.offerCrossing)
402 {
403 auto offerCrossingStep =
404 std::make_unique<XRPEndpointOfferCrossingStep>(ctx, acc);
405 ter = offerCrossingStep->check(ctx);
406 r = std::move(offerCrossingStep);
407 }
408 else // payment
409 {
410 auto paymentStep = std::make_unique<XRPEndpointPaymentStep>(ctx, acc);
411 ter = paymentStep->check(ctx);
412 r = std::move(paymentStep);
413 }
414 if (ter != tesSUCCESS)
415 return {ter, nullptr};
416
417 return {tesSUCCESS, std::move(r)};
418}
419
420} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:60
Stream error() const
Definition: Journal.h:346
Stream debug() const
Definition: Journal.h:328
Stream warn() const
Definition: Journal.h:340
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:140
A wrapper which makes credits unavailable to balances.
A view into a ledger.
Definition: ReadView.h:51
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:130
static std::uint64_t const uRateOne
Definition: STAmount.h:80
A step in a payment path.
Definition: Steps.h:84
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 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:235
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:175
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:26
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:118
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:178
std::pair< TER, std::unique_ptr< Step > > make_XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
StrandDirection
Definition: Steps.h:42
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition: StepChecks.h:33
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
@ tefINTERNAL
Definition: TER.h:173
DebtDirection
Definition: Steps.h:40
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:119
@ tesSUCCESS
Definition: TER.h:242
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition: base_uint.h:585
@ terNO_ACCOUNT
Definition: TER.h:217
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Calls static accountSendIOU if saAmount represents Issue.
Definition: View.cpp:1608
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition: View.cpp:506
@ temBAD_PATH_LOOP
Definition: TER.h:97
@ temBAD_PATH
Definition: TER.h:96
T str(T... args)
Context needed to build Strand Steps and for error checking.
Definition: Steps.h:527
size_t const strandSize
Length of Strand.
Definition: Steps.h:539
ReadView const & view
Current ReadView.
Definition: Steps.h:528
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:549
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition: Steps.h:537
bool const isFirst
true if Step is first in Strand
Definition: Steps.h:533
bool const isLast
true if Step is last in Strand
Definition: Steps.h:534
Issue const strandDeliver
Issue strand delivers.
Definition: Steps.h:531