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
25#include <xrpl/basics/Log.h>
26#include <xrpl/ledger/PaymentSandbox.h>
27#include <xrpl/protocol/Feature.h>
28#include <xrpl/protocol/IOUAmount.h>
29#include <xrpl/protocol/Quality.h>
30#include <xrpl/protocol/XRPAmount.h>
31
32#include <boost/container/flat_set.hpp>
33
34#include <sstream>
35
36namespace ripple {
37
38template <class TDerived>
40 : public StepImp<XRPAmount, XRPAmount, XRPEndpointStep<TDerived>>
41{
42private:
44 bool const isLast_;
46
47 // Since this step will always be an endpoint in a strand
48 // (either the first or last step) the same cache is used
49 // for cachedIn and cachedOut and only one will ever be used
51
53 cached() const
54 {
55 if (!cache_)
56 return std::nullopt;
57 return EitherAmount(*cache_);
58 }
59
60public:
62 : acc_(acc), isLast_(ctx.isLast), j_(ctx.j)
63 {
64 }
65
66 AccountID const&
67 acc() const
68 {
69 return acc_;
70 }
71
73 directStepAccts() const override
74 {
75 if (isLast_)
78 }
79
81 cachedIn() const override
82 {
83 return cached();
84 }
85
87 cachedOut() const override
88 {
89 return cached();
90 }
91
93 debtDirection(ReadView const& sb, StrandDirection dir) const override
94 {
96 }
97
100 const override;
101
104 PaymentSandbox& sb,
105 ApplyView& afView,
106 boost::container::flat_set<uint256>& ofrsToRm,
107 XRPAmount const& out);
108
111 PaymentSandbox& sb,
112 ApplyView& afView,
113 boost::container::flat_set<uint256>& ofrsToRm,
114 XRPAmount const& in);
115
118 override;
119
120 // Check for errors and violations of frozen constraints.
121 TER
122 check(StrandContext const& ctx) const;
123
124protected:
126 xrpLiquidImpl(ReadView& sb, std::int32_t reserveReduction) const
127 {
128 return ripple::xrpLiquid(sb, acc_, reserveReduction, j_);
129 }
130
132 logStringImpl(char const* name) const
133 {
135 ostr << name << ": "
136 << "\nAcc: " << acc_;
137 return ostr.str();
138 }
139
140private:
141 template <class P>
142 friend bool
144
145 friend bool
147 {
148 return !(lhs == rhs);
149 }
150
151 bool
152 equal(Step const& rhs) const override
153 {
154 if (auto ds = dynamic_cast<XRPEndpointStep const*>(&rhs))
155 {
156 return *this == *ds;
157 }
158 return false;
159 }
160};
161
162//------------------------------------------------------------------------------
163
164// Flow is used in two different circumstances for transferring funds:
165// o Payments, and
166// o Offer crossing.
167// The rules for handling funds in these two cases are almost, but not
168// quite, the same.
169
170// Payment XRPEndpointStep class (not offer crossing).
171class XRPEndpointPaymentStep : public XRPEndpointStep<XRPEndpointPaymentStep>
172{
173public:
174 using XRPEndpointStep<XRPEndpointPaymentStep>::XRPEndpointStep;
175
178 {
179 return xrpLiquidImpl(sb, 0);
180 ;
181 }
182
184 logString() const override
185 {
186 return logStringImpl("XRPEndpointPaymentStep");
187 }
188};
189
190// Offer crossing XRPEndpointStep class (not a payment).
192 : public XRPEndpointStep<XRPEndpointOfferCrossingStep>
193{
194private:
195 // For historical reasons, offer crossing is allowed to dig further
196 // into the XRP reserve than an ordinary payment. (I believe it's
197 // because the trust line was created after the XRP was removed.)
198 // Return how much the reserve should be reduced.
199 //
200 // Note that reduced reserve only happens if the trust line does not
201 // currently exist.
202 static std::int32_t
204 {
205 if (ctx.isFirst && !ctx.view.read(keylet::line(acc, ctx.strandDeliver)))
206 return -1;
207 return 0;
208 }
209
210public:
216
219 {
221 }
222
224 logString() const override
225 {
226 return logStringImpl("XRPEndpointOfferCrossingStep");
227 }
228
229private:
231};
232
233//------------------------------------------------------------------------------
234
235template <class TDerived>
236inline bool
238 XRPEndpointStep<TDerived> const& lhs,
239 XRPEndpointStep<TDerived> const& rhs)
240{
241 return lhs.acc_ == rhs.acc_ && lhs.isLast_ == rhs.isLast_;
242}
243
244template <class TDerived>
247 ReadView const& v,
248 DebtDirection prevStepDir) const
249{
250 return {
251 Quality{STAmount::uRateOne},
252 this->debtDirection(v, StrandDirection::forward)};
253}
254
255template <class TDerived>
258 PaymentSandbox& sb,
259 ApplyView& afView,
260 boost::container::flat_set<uint256>& ofrsToRm,
261 XRPAmount const& out)
262{
263 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
264
265 auto const result = isLast_ ? out : std::min(balance, out);
266
267 auto& sender = isLast_ ? xrpAccount() : acc_;
268 auto& receiver = isLast_ ? acc_ : xrpAccount();
269 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
270 if (ter != tesSUCCESS)
271 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
272
273 cache_.emplace(result);
274 return {result, result};
275}
276
277template <class TDerived>
280 PaymentSandbox& sb,
281 ApplyView& afView,
282 boost::container::flat_set<uint256>& ofrsToRm,
283 XRPAmount const& in)
284{
285 XRPL_ASSERT(cache_, "ripple::XRPEndpointStep::fwdImp : cache is set");
286 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
287
288 auto const result = isLast_ ? in : std::min(balance, in);
289
290 auto& sender = isLast_ ? xrpAccount() : acc_;
291 auto& receiver = isLast_ ? acc_ : xrpAccount();
292 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
293 if (ter != tesSUCCESS)
294 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
295
296 cache_.emplace(result);
297 return {result, result};
298}
299
300template <class TDerived>
303 PaymentSandbox& sb,
304 ApplyView& afView,
305 EitherAmount const& in)
306{
307 if (!cache_)
308 {
309 JLOG(j_.error()) << "Expected valid cache in validFwd";
310 return {false, EitherAmount(XRPAmount(beast::zero))};
311 }
312
313 XRPL_ASSERT(in.native, "ripple::XRPEndpointStep::validFwd : input is XRP");
314
315 auto const& xrpIn = in.xrp;
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 (ter != tesSUCCESS)
363 return ter;
364
365 auto const issuesIndex = isLast_ ? 0 : 1;
366 if (!ctx.seenDirectIssues[issuesIndex].insert(xrpIssue()).second)
367 {
368 JLOG(j_.debug()) << "XRPEndpointStep: loop detected: Index: "
369 << ctx.strandSize << ' ' << *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 =
384 dynamic_cast<XRPEndpointStep<XRPEndpointPaymentStep> const*>(&step))
385 {
386 return xs->acc() == acc;
387 }
388 return false;
389}
390} // namespace test
391
392//------------------------------------------------------------------------------
393
396{
397 TER ter = tefINTERNAL;
399 if (ctx.offerCrossing)
400 {
401 auto offerCrossingStep =
403 ter = offerCrossingStep->check(ctx);
404 r = std::move(offerCrossingStep);
405 }
406 else // payment
407 {
408 auto paymentStep = std::make_unique<XRPEndpointPaymentStep>(ctx, acc);
409 ter = paymentStep->check(ctx);
410 r = std::move(paymentStep);
411 }
412 if (ter != tesSUCCESS)
413 return {ter, nullptr};
414
415 return {tesSUCCESS, std::move(r)};
416}
417
418} // 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:143
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.
static std::uint64_t const uRateOne
Definition STAmount.h:80
A step in a payment path.
Definition Steps.h:86
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:244
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
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:25
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:115
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:44
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)
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:2191
@ tefINTERNAL
Definition TER.h:173
DebtDirection
Definition Steps.h:42
Currency const & xrpCurrency()
XRP currency.
@ tesSUCCESS
Definition TER.h:245
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
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition View.cpp:618
@ 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:533
size_t const strandSize
Length of Strand.
Definition Steps.h:545
ReadView const & view
Current ReadView.
Definition Steps.h:534
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:555
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition Steps.h:543
bool const isFirst
true if Step is first in Strand
Definition Steps.h:539
bool const isLast
true if Step is last in Strand
Definition Steps.h:540
Issue const strandDeliver
Issue strand delivers.
Definition Steps.h:537