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
26#include <xrpl/basics/Log.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 << ": " << "\nAcc: " << acc_;
136 return ostr.str();
137 }
138
139private:
140 template <class P>
141 friend bool
143
144 friend bool
146 {
147 return !(lhs == rhs);
148 }
149
150 bool
151 equal(Step const& rhs) const override
152 {
153 if (auto ds = dynamic_cast<XRPEndpointStep const*>(&rhs))
154 {
155 return *this == *ds;
156 }
157 return false;
158 }
159};
160
161//------------------------------------------------------------------------------
162
163// Flow is used in two different circumstances for transferring funds:
164// o Payments, and
165// o Offer crossing.
166// The rules for handling funds in these two cases are almost, but not
167// quite, the same.
168
169// Payment XRPEndpointStep class (not offer crossing).
170class XRPEndpointPaymentStep : public XRPEndpointStep<XRPEndpointPaymentStep>
171{
172public:
173 using XRPEndpointStep<XRPEndpointPaymentStep>::XRPEndpointStep;
174
177 {
178 return xrpLiquidImpl(sb, 0);
179 ;
180 }
181
183 logString() const override
184 {
185 return logStringImpl("XRPEndpointPaymentStep");
186 }
187};
188
189// Offer crossing XRPEndpointStep class (not a payment).
191 : public XRPEndpointStep<XRPEndpointOfferCrossingStep>
192{
193private:
194 // For historical reasons, offer crossing is allowed to dig further
195 // into the XRP reserve than an ordinary payment. (I believe it's
196 // because the trust line was created after the XRP was removed.)
197 // Return how much the reserve should be reduced.
198 //
199 // Note that reduced reserve only happens if the trust line does not
200 // currently exist.
201 static std::int32_t
203 {
204 if (ctx.isFirst && !ctx.view.read(keylet::line(acc, ctx.strandDeliver)))
205 return -1;
206 return 0;
207 }
208
209public:
213 {
214 }
215
218 {
220 }
221
223 logString() const override
224 {
225 return logStringImpl("XRPEndpointOfferCrossingStep");
226 }
227
228private:
230};
231
232//------------------------------------------------------------------------------
233
234template <class TDerived>
235inline bool
237 XRPEndpointStep<TDerived> const& lhs,
238 XRPEndpointStep<TDerived> const& rhs)
239{
240 return lhs.acc_ == rhs.acc_ && lhs.isLast_ == rhs.isLast_;
241}
242
243template <class TDerived>
246 ReadView const& v,
247 DebtDirection prevStepDir) const
248{
249 return {
250 Quality{STAmount::uRateOne},
251 this->debtDirection(v, StrandDirection::forward)};
252}
253
254template <class TDerived>
257 PaymentSandbox& sb,
258 ApplyView& afView,
259 boost::container::flat_set<uint256>& ofrsToRm,
260 XRPAmount const& out)
261{
262 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
263
264 auto const result = isLast_ ? out : std::min(balance, out);
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 (ter != tesSUCCESS)
270 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
271
272 cache_.emplace(result);
273 return {result, result};
274}
275
276template <class TDerived>
279 PaymentSandbox& sb,
280 ApplyView& afView,
281 boost::container::flat_set<uint256>& ofrsToRm,
282 XRPAmount const& in)
283{
284 XRPL_ASSERT(cache_, "ripple::XRPEndpointStep::fwdImp : cache is set");
285 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
286
287 auto const result = isLast_ ? in : std::min(balance, in);
288
289 auto& sender = isLast_ ? xrpAccount() : acc_;
290 auto& receiver = isLast_ ? acc_ : xrpAccount();
291 auto ter = accountSend(sb, sender, receiver, toSTAmount(result), j_);
292 if (ter != tesSUCCESS)
293 return {XRPAmount{beast::zero}, XRPAmount{beast::zero}};
294
295 cache_.emplace(result);
296 return {result, result};
297}
298
299template <class TDerived>
302 PaymentSandbox& sb,
303 ApplyView& afView,
304 EitherAmount const& in)
305{
306 if (!cache_)
307 {
308 JLOG(j_.error()) << "Expected valid cache in validFwd";
309 return {false, EitherAmount(XRPAmount(beast::zero))};
310 }
311
312 XRPL_ASSERT(in.native, "ripple::XRPEndpointStep::validFwd : input is XRP");
313
314 auto const& xrpIn = in.xrp;
315 auto const balance = static_cast<TDerived const*>(this)->xrpLiquid(sb);
316
317 if (!isLast_ && balance < xrpIn)
318 {
319 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
320 << " Insufficient balance: " << to_string(balance)
321 << " Requested: " << to_string(xrpIn);
322 return {false, EitherAmount(balance)};
323 }
324
325 if (xrpIn != *cache_)
326 {
327 JLOG(j_.warn()) << "XRPEndpointStep: Strand re-execute check failed."
328 << " ExpectedIn: " << to_string(*cache_)
329 << " CachedIn: " << to_string(xrpIn);
330 }
331 return {true, in};
332}
333
334template <class TDerived>
335TER
337{
338 if (!acc_)
339 {
340 JLOG(j_.debug()) << "XRPEndpointStep: specified bad account.";
341 return temBAD_PATH;
342 }
343
344 auto sleAcc = ctx.view.read(keylet::account(acc_));
345 if (!sleAcc)
346 {
347 JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
348 "non-existent account: "
349 << acc_;
350 return terNO_ACCOUNT;
351 }
352
353 if (!ctx.isFirst && !ctx.isLast)
354 {
355 return temBAD_PATH;
356 }
357
358 auto& src = isLast_ ? xrpAccount() : acc_;
359 auto& dst = isLast_ ? acc_ : xrpAccount();
360 auto ter = checkFreeze(ctx.view, src, dst, xrpCurrency());
361 if (ter != tesSUCCESS)
362 return ter;
363
364 if (ctx.view.rules().enabled(fix1781))
365 {
366 auto const issuesIndex = isLast_ ? 0 : 1;
367 if (!ctx.seenDirectIssues[issuesIndex].insert(xrpIssue()).second)
368 {
369 JLOG(j_.debug())
370 << "XRPEndpointStep: loop detected: Index: " << ctx.strandSize
371 << ' ' << *this;
372 return temBAD_PATH_LOOP;
373 }
374 }
375
376 return tesSUCCESS;
377}
378
379//------------------------------------------------------------------------------
380
381namespace test {
382// Needed for testing
383bool
384xrpEndpointStepEqual(Step const& step, AccountID const& acc)
385{
386 if (auto xs =
387 dynamic_cast<XRPEndpointStep<XRPEndpointPaymentStep> const*>(&step))
388 {
389 return xs->acc() == acc;
390 }
391 return false;
392}
393} // namespace test
394
395//------------------------------------------------------------------------------
396
399{
400 TER ter = tefINTERNAL;
402 if (ctx.offerCrossing)
403 {
404 auto offerCrossingStep =
405 std::make_unique<XRPEndpointOfferCrossingStep>(ctx, acc);
406 ter = offerCrossingStep->check(ctx);
407 r = std::move(offerCrossingStep);
408 }
409 else // payment
410 {
411 auto paymentStep = std::make_unique<XRPEndpointPaymentStep>(ctx, acc);
412 ter = paymentStep->check(ctx);
413 r = std::move(paymentStep);
414 }
415 if (ter != tesSUCCESS)
416 return {ter, nullptr};
417
418 return {tesSUCCESS, std::move(r)};
419}
420
421} // 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:141
A wrapper which makes credits unavailable to balances.
A view into a ledger.
Definition: ReadView.h:52
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:85
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:236
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:176
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:43
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition: StepChecks.h:34
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
@ tefINTERNAL
Definition: TER.h:173
DebtDirection
Definition: Steps.h:41
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:1609
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition: View.cpp:507
@ 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:528
size_t const strandSize
Length of Strand.
Definition: Steps.h:540
ReadView const & view
Current ReadView.
Definition: Steps.h:529
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:550
OfferCrossing const offerCrossing
Yes/Sell if offer crossing, not payment.
Definition: Steps.h:538
bool const isFirst
true if Step is first in Strand
Definition: Steps.h:534
bool const isLast
true if Step is last in Strand
Definition: Steps.h:535
Issue const strandDeliver
Issue strand delivers.
Definition: Steps.h:532