rippled
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 <ripple/app/paths/Credit.h>
21 #include <ripple/app/paths/impl/AmountSpec.h>
22 #include <ripple/app/paths/impl/Steps.h>
23 #include <ripple/app/paths/impl/StepChecks.h>
24 #include <ripple/basics/IOUAmount.h>
25 #include <ripple/basics/Log.h>
26 #include <ripple/basics/XRPAmount.h>
27 #include <ripple/ledger/PaymentSandbox.h>
28 #include <ripple/protocol/Feature.h>
29 #include <ripple/protocol/Quality.h>
30 
31 #include <boost/container/flat_set.hpp>
32 
33 #include <numeric>
34 #include <sstream>
35 
36 namespace ripple {
37 
38 template <class TDerived>
39 class XRPEndpointStep : public StepImp<
40  XRPAmount, XRPAmount, XRPEndpointStep<TDerived>>
41 {
42 private:
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
50  boost::optional<XRPAmount> cache_;
51 
52  boost::optional<EitherAmount>
53  cached () const
54  {
55  if (!cache_)
56  return boost::none;
57  return EitherAmount (*cache_);
58  }
59 
60 public:
62  StrandContext const& ctx,
63  AccountID const& acc)
64  : acc_(acc)
65  , isLast_(ctx.isLast)
66  , j_ (ctx.j) {}
67 
68  AccountID const& acc () const
69  {
70  return acc_;
71  }
72 
73  boost::optional<std::pair<AccountID,AccountID>>
74  directStepAccts () const override
75  {
76  if (isLast_)
77  return std::make_pair(xrpAccount(), acc_);
78  return std::make_pair(acc_, xrpAccount());
79  }
80 
81  boost::optional<EitherAmount>
82  cachedIn () const override
83  {
84  return cached ();
85  }
86 
87  boost::optional<EitherAmount>
88  cachedOut () const override
89  {
90  return cached ();
91  }
92 
94  debtDirection(ReadView const& sb, StrandDirection dir) const override
95  {
96  return DebtDirection::issues;
97  }
98 
100  qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const override;
101 
103  revImp (
104  PaymentSandbox& sb,
105  ApplyView& afView,
106  boost::container::flat_set<uint256>& ofrsToRm,
107  XRPAmount const& out);
108 
110  fwdImp (
111  PaymentSandbox& sb,
112  ApplyView& afView,
113  boost::container::flat_set<uint256>& ofrsToRm,
114  XRPAmount const& in);
115 
117  validFwd (
118  PaymentSandbox& sb,
119  ApplyView& afView,
120  EitherAmount const& in) override;
121 
122  // Check for errors and violations of frozen constraints.
123  TER check (StrandContext const& ctx) const;
124 
125 protected:
126  XRPAmount
127  xrpLiquidImpl (ReadView& sb, std::int32_t reserveReduction) const
128  {
129  return ripple::xrpLiquid (sb, acc_, reserveReduction, j_);
130  }
131 
132  std::string logStringImpl (char const* name) const
133  {
134  std::ostringstream ostr;
135  ostr <<
136  name << ": " <<
137  "\nAcc: " << acc_;
138  return ostr.str ();
139  }
140 
141 private:
142  template <class P>
143  friend bool operator==(
144  XRPEndpointStep<P> const& lhs,
145  XRPEndpointStep<P> const& rhs);
146 
147  friend bool operator!=(
148  XRPEndpointStep const& lhs,
149  XRPEndpointStep const& rhs)
150  {
151  return ! (lhs == rhs);
152  }
153 
154  bool equal (Step const& rhs) const override
155  {
156  if (auto ds = dynamic_cast<XRPEndpointStep const*> (&rhs))
157  {
158  return *this == *ds;
159  }
160  return false;
161  }
162 };
163 
164 //------------------------------------------------------------------------------
165 
166 // Flow is used in two different circumstances for transferring funds:
167 // o Payments, and
168 // o Offer crossing.
169 // The rules for handling funds in these two cases are almost, but not
170 // quite, the same.
171 
172 // Payment XRPEndpointStep class (not offer crossing).
173 class XRPEndpointPaymentStep : public XRPEndpointStep<XRPEndpointPaymentStep>
174 {
175 public:
177 
178  XRPAmount
179  xrpLiquid (ReadView& sb) const
180  {
181  return xrpLiquidImpl (sb, 0);;
182  }
183 
184  std::string logString () const override
185  {
186  return logStringImpl ("XRPEndpointPaymentStep");
187  }
188 };
189 
190 // Offer crossing XRPEndpointStep class (not a payment).
192  public XRPEndpointStep<XRPEndpointOfferCrossingStep>
193 {
194 private:
195 
196  // For historical reasons, offer crossing is allowed to dig further
197  // into the XRP reserve than an ordinary payment. (I believe it's
198  // because the trust line was created after the XRP was removed.)
199  // Return how much the reserve should be reduced.
200  //
201  // Note that reduced reserve only happens if the trust line does not
202  // currently exist.
204  StrandContext const& ctx, AccountID const& acc)
205  {
206  if (ctx.isFirst &&
207  !ctx.view.read (keylet::line (acc, ctx.strandDeliver)))
208  return -1;
209  return 0;
210  }
211 
212 public:
214  StrandContext const& ctx, AccountID const& acc)
217  {
218  }
219 
220  XRPAmount
221  xrpLiquid (ReadView& sb) const
222  {
223  return xrpLiquidImpl (sb, reserveReduction_);
224  }
225 
226  std::string logString () const override
227  {
228  return logStringImpl ("XRPEndpointOfferCrossingStep");
229  }
230 
231 private:
233 };
234 
235 //------------------------------------------------------------------------------
236 
237 template <class TDerived>
238 inline bool operator==(XRPEndpointStep<TDerived> const& lhs,
239  XRPEndpointStep<TDerived> const& rhs)
240 {
241  return lhs.acc_ == rhs.acc_ && lhs.isLast_ == rhs.isLast_;
242 }
243 
244 template <class TDerived>
247  ReadView const& v, DebtDirection prevStepDir) const
248 {
249  return {Quality{STAmount::uRateOne},
250  this->debtDirection(v, StrandDirection::forward)};
251 }
252 
253 template <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 
275 template <class TDerived>
278  PaymentSandbox& sb,
279  ApplyView& afView,
280  boost::container::flat_set<uint256>& ofrsToRm,
281  XRPAmount const& in)
282 {
283  assert (cache_);
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 
298 template <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  assert (in.native);
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 
333 template <class TDerived>
334 TER
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 
380 namespace test
381 {
382 // Needed for testing
383 bool xrpEndpointStepEqual (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 }
393 
394 //------------------------------------------------------------------------------
395 
398  StrandContext const& ctx,
399  AccountID const& acc)
400 {
401  TER ter = tefINTERNAL;
403  if (ctx.offerCrossing)
404  {
405  auto offerCrossingStep =
406  std::make_unique<XRPEndpointOfferCrossingStep> (ctx, acc);
407  ter = offerCrossingStep->check (ctx);
408  r = std::move (offerCrossingStep);
409  }
410  else // payment
411  {
412  auto paymentStep =
413  std::make_unique<XRPEndpointPaymentStep> (ctx, acc);
414  ter = paymentStep->check (ctx);
415  r = std::move (paymentStep);
416  }
417  if (ter != tesSUCCESS)
418  return {ter, nullptr};
419 
420  return {tesSUCCESS, std::move (r)};
421 }
422 
423 } // ripple
ripple::StrandContext
Context needed to build Strand Steps and for error checking.
Definition: Steps.h:487
ripple::StrandContext::strandSize
const size_t strandSize
Length of Strand.
Definition: Steps.h:499
ripple::XRPEndpointOfferCrossingStep::computeReserveReduction
static std::int32_t computeReserveReduction(StrandContext const &ctx, AccountID const &acc)
Definition: XRPEndpointStep.cpp:203
sstream
ripple::XRPEndpointStep::cachedOut
boost::optional< EitherAmount > cachedOut() const override
Definition: XRPEndpointStep.cpp:88
ripple::tefINTERNAL
@ tefINTERNAL
Definition: TER.h:153
std::string
STL class.
ripple::XRPEndpointOfferCrossingStep::XRPEndpointOfferCrossingStep
XRPEndpointOfferCrossingStep(StrandContext const &ctx, AccountID const &acc)
Definition: XRPEndpointStep.cpp:213
ripple::XRPEndpointStep::fwdImp
std::pair< XRPAmount, XRPAmount > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, XRPAmount const &in)
Definition: XRPEndpointStep.cpp:277
ripple::StrandContext::strandDeliver
const Issue strandDeliver
Issue strand delivers.
Definition: Steps.h:492
ripple::DebtDirection
DebtDirection
Definition: Steps.h:37
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:110
std::pair
ripple::XRPEndpointStep::acc
AccountID const & acc() const
Definition: XRPEndpointStep.cpp:68
ripple::fix1781
const uint256 fix1781
Definition: Feature.cpp:180
ripple::XRPEndpointStep::acc_
AccountID acc_
Definition: XRPEndpointStep.cpp:43
beast::Journal::warn
Stream warn() const
Definition: Journal.h:302
ripple::XRPEndpointStep::operator!=
friend bool operator!=(XRPEndpointStep const &lhs, XRPEndpointStep const &rhs)
Definition: XRPEndpointStep.cpp:147
ripple::QualityDirection::in
@ in
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
ripple::temBAD_PATH
@ temBAD_PATH
Definition: TER.h:94
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:150
ripple::XRPEndpointOfferCrossingStep::reserveReduction_
const std::int32_t reserveReduction_
Definition: XRPEndpointStep.cpp:232
ripple::StrandDirection
StrandDirection
Definition: Steps.h:39
ripple::operator==
bool operator==(Manifest const &lhs, Manifest const &rhs)
Definition: Manifest.h:148
ripple::base_uint< 160, detail::AccountIDTag >
ripple::XRPEndpointOfferCrossingStep
Definition: XRPEndpointStep.cpp:191
ripple::StrandContext::view
ReadView const & view
Current ReadView.
Definition: Steps.h:489
ripple::keylet::line
static const line_t line
Definition: Indexes.h:176
ripple::QualityDirection::out
@ out
ripple::StrandContext::offerCrossing
const bool offerCrossing
true if offer crossing, not payment
Definition: Steps.h:497
ripple::keylet::account
static const account_t account
Definition: Indexes.h:116
ripple::XRPEndpointStep::equal
bool equal(Step const &rhs) const override
Definition: XRPEndpointStep.cpp:154
ripple::XRPEndpointStep::operator==
friend bool operator==(XRPEndpointStep< P > const &lhs, XRPEndpointStep< P > const &rhs)
ripple::toSTAmount
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
Definition: AmountConversions.h:31
ripple::TERSubset< CanCvtToTER >
ripple::make_XRPEndpointStep
std::pair< TER, std::unique_ptr< Step > > make_XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
Definition: XRPEndpointStep.cpp:397
ripple::XRPEndpointStep::logStringImpl
std::string logStringImpl(char const *name) const
Definition: XRPEndpointStep.cpp:132
ripple::Step
A step in a payment path.
Definition: Steps.h:81
ripple::XRPEndpointPaymentStep::logString
std::string logString() const override
Definition: XRPEndpointStep.cpp:184
ripple::XRPEndpointOfferCrossingStep::xrpLiquid
XRPAmount xrpLiquid(ReadView &sb) const
Definition: XRPEndpointStep.cpp:221
ripple::XRPEndpointStep::cachedIn
boost::optional< EitherAmount > cachedIn() const override
Definition: XRPEndpointStep.cpp:82
ripple::accountSend
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
Definition: View.cpp:1101
ripple::XRPEndpointStep::XRPEndpointStep
XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
Definition: XRPEndpointStep.cpp:61
ripple::XRPEndpointStep::isLast_
const bool isLast_
Definition: XRPEndpointStep.cpp:44
ripple::xrpAccount
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:149
beast::Journal::error
Stream error() const
Definition: Journal.h:307
ripple::xrpLiquid
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition: View.cpp:207
ripple::XRPEndpointPaymentStep::xrpLiquid
XRPAmount xrpLiquid(ReadView &sb) const
Definition: XRPEndpointStep.cpp:179
ripple::XRPEndpointStep::xrpLiquidImpl
XRPAmount xrpLiquidImpl(ReadView &sb, std::int32_t reserveReduction) const
Definition: XRPEndpointStep.cpp:127
ripple::StrandContext::isLast
const bool isLast
true if Step is last in Strand
Definition: Steps.h:495
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:60
std::int32_t
ripple::Rules::enabled
bool enabled(uint256 const &id) const
Returns true if a feature is enabled.
Definition: ReadView.cpp:107
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::XRPEndpointStep::directStepAccts
boost::optional< std::pair< AccountID, AccountID > > directStepAccts() const override
Definition: XRPEndpointStep.cpp:74
ripple::XRPEndpointStep::cached
boost::optional< EitherAmount > cached() const
Definition: XRPEndpointStep.cpp:53
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:195
std::min
T min(T... args)
ripple::DebtDirection::issues
@ issues
std::ostringstream
STL class.
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:186
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::ReadView::rules
virtual Rules const & rules() const =0
Returns the tx processing rules.
ripple::XRPEndpointStep::check
TER check(StrandContext const &ctx) const
Definition: XRPEndpointStep.cpp:335
ripple::EitherAmount
Definition: AmountSpec.h:60
ripple::StrandContext::isFirst
const bool isFirst
true if Step is first in Strand
Definition: Steps.h:494
ripple::xrpIssue
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:97
ripple::checkFreeze
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition: StepChecks.h:33
std::ostringstream::str
T str(T... args)
ripple::XRPEndpointStep
Definition: XRPEndpointStep.cpp:39
beast::Journal::debug
Stream debug() const
Definition: Journal.h:292
ripple::XRPEndpointStep::validFwd
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
Definition: XRPEndpointStep.cpp:300
ripple::XRPEndpointStep::j_
const beast::Journal j_
Definition: XRPEndpointStep.cpp:45
std::make_pair
T make_pair(T... args)
ripple::XRPEndpointStep::qualityUpperBound
std::pair< boost::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection prevStepDir) const override
Definition: XRPEndpointStep.cpp:246
ripple::test::xrpEndpointStepEqual
bool xrpEndpointStepEqual(Step const &step, AccountID const &acc)
Definition: XRPEndpointStep.cpp:383
ripple::temBAD_PATH_LOOP
@ temBAD_PATH_LOOP
Definition: TER.h:95
ripple::XRPEndpointOfferCrossingStep::logString
std::string logString() const override
Definition: XRPEndpointStep.cpp:226
numeric
ripple::XRPEndpointStep::revImp
std::pair< XRPAmount, XRPAmount > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, XRPAmount const &out)
Definition: XRPEndpointStep.cpp:255
ripple::StrandContext::seenDirectIssues
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:509
std::unique_ptr
STL class.
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:219
ripple::XRPEndpointStep::debtDirection
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
Definition: XRPEndpointStep.cpp:94
ripple::XRPEndpointPaymentStep
Definition: XRPEndpointStep.cpp:173
ripple::xrpCurrency
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:111
ripple::XRPEndpointStep::cache_
boost::optional< XRPAmount > cache_
Definition: XRPEndpointStep.cpp:50
ripple::StrandDirection::forward
@ forward
ripple::STAmount::uRateOne
static const std::uint64_t uRateOne
Definition: STAmount.h:73
ripple::XRPAmount
Definition: XRPAmount.h:46