rippled
CashCheck.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2017 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/ledger/Ledger.h>
21 #include <ripple/app/paths/Flow.h>
22 #include <ripple/app/tx/impl/CashCheck.h>
23 #include <ripple/basics/Log.h>
24 #include <ripple/protocol/Feature.h>
25 #include <ripple/protocol/Indexes.h>
26 #include <ripple/protocol/STAccount.h>
27 #include <ripple/protocol/TER.h>
28 #include <ripple/protocol/TxFlags.h>
29 
30 #include <algorithm>
31 
32 namespace ripple {
33 
34 NotTEC
36 {
37  if (!ctx.rules.enabled(featureChecks))
38  return temDISABLED;
39 
40  NotTEC const ret{preflight1(ctx)};
41  if (!isTesSuccess(ret))
42  return ret;
43 
44  if (ctx.tx.getFlags() & tfUniversalMask)
45  {
46  // There are no flags (other than universal) for CashCheck yet.
47  JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set.";
48  return temINVALID_FLAG;
49  }
50 
51  // Exactly one of Amount or DeliverMin must be present.
52  auto const optAmount = ctx.tx[~sfAmount];
53  auto const optDeliverMin = ctx.tx[~sfDeliverMin];
54 
55  if (static_cast<bool>(optAmount) == static_cast<bool>(optDeliverMin))
56  {
57  JLOG(ctx.j.warn())
58  << "Malformed transaction: "
59  "does not specify exactly one of Amount and DeliverMin.";
60  return temMALFORMED;
61  }
62 
63  // Make sure the amount is valid.
64  STAmount const value{optAmount ? *optAmount : *optDeliverMin};
65  if (!isLegalNet(value) || value.signum() <= 0)
66  {
67  JLOG(ctx.j.warn()) << "Malformed transaction: bad amount: "
68  << value.getFullText();
69  return temBAD_AMOUNT;
70  }
71 
72  if (badCurrency() == value.getCurrency())
73  {
74  JLOG(ctx.j.warn()) << "Malformed transaction: Bad currency.";
75  return temBAD_CURRENCY;
76  }
77 
78  return preflight2(ctx);
79 }
80 
81 TER
83 {
84  auto const sleCheck = ctx.view.read(keylet::check(ctx.tx[sfCheckID]));
85  if (!sleCheck)
86  {
87  JLOG(ctx.j.warn()) << "Check does not exist.";
88  return tecNO_ENTRY;
89  }
90 
91  // Only cash a check with this account as the destination.
92  AccountID const dstId{(*sleCheck)[sfDestination]};
93  if (ctx.tx[sfAccount] != dstId)
94  {
95  JLOG(ctx.j.warn()) << "Cashing a check with wrong Destination.";
96  return tecNO_PERMISSION;
97  }
98  AccountID const srcId{(*sleCheck)[sfAccount]};
99  if (srcId == dstId)
100  {
101  // They wrote a check to themselves. This should be caught when
102  // the check is created, but better late than never.
103  JLOG(ctx.j.error()) << "Malformed transaction: Cashing check to self.";
104  return tecINTERNAL;
105  }
106  {
107  auto const sleSrc = ctx.view.read(keylet::account(srcId));
108  auto const sleDst = ctx.view.read(keylet::account(dstId));
109  if (!sleSrc || !sleDst)
110  {
111  // If the check exists this should never occur.
112  JLOG(ctx.j.warn())
113  << "Malformed transaction: source or destination not in ledger";
114  return tecNO_ENTRY;
115  }
116 
117  if ((sleDst->getFlags() & lsfRequireDestTag) &&
118  !sleCheck->isFieldPresent(sfDestinationTag))
119  {
120  // The tag is basically account-specific information we don't
121  // understand, but we can require someone to fill it in.
122  JLOG(ctx.j.warn())
123  << "Malformed transaction: DestinationTag required in check.";
124  return tecDST_TAG_NEEDED;
125  }
126  }
127  {
128  using duration = NetClock::duration;
129  using timepoint = NetClock::time_point;
130  auto const optExpiry = (*sleCheck)[~sfExpiration];
131 
132  // Expiration is defined in terms of the close time of the parent
133  // ledger, because we definitively know the time that it closed but
134  // we do not know the closing time of the ledger that is under
135  // construction.
136  if (optExpiry &&
137  (ctx.view.parentCloseTime() >= timepoint{duration{*optExpiry}}))
138  {
139  JLOG(ctx.j.warn()) << "Cashing a check that has already expired.";
140  return tecEXPIRED;
141  }
142  }
143  {
144  // Preflight verified exactly one of Amount or DeliverMin is present.
145  // Make sure the requested amount is reasonable.
146  STAmount const value{[](STTx const& tx) {
147  auto const optAmount = tx[~sfAmount];
148  return optAmount ? *optAmount : tx[sfDeliverMin];
149  }(ctx.tx)};
150 
151  STAmount const sendMax{(*sleCheck)[sfSendMax]};
152  Currency const currency{value.getCurrency()};
153  if (currency != sendMax.getCurrency())
154  {
155  JLOG(ctx.j.warn()) << "Check cash does not match check currency.";
156  return temMALFORMED;
157  }
158  AccountID const issuerId{value.getIssuer()};
159  if (issuerId != sendMax.getIssuer())
160  {
161  JLOG(ctx.j.warn()) << "Check cash does not match check issuer.";
162  return temMALFORMED;
163  }
164  if (value > sendMax)
165  {
166  JLOG(ctx.j.warn()) << "Check cashed for more than check sendMax.";
167  return tecPATH_PARTIAL;
168  }
169 
170  // Make sure the check owner holds at least value. If they have
171  // less than value the check cannot be cashed.
172  {
173  STAmount availableFunds{accountFunds(
174  ctx.view,
175  (*sleCheck)[sfAccount],
176  value,
178  ctx.j)};
179 
180  // Note that src will have one reserve's worth of additional XRP
181  // once the check is cashed, since the check's reserve will no
182  // longer be required. So, if we're dealing in XRP, we add one
183  // reserve's worth to the available funds.
184  if (value.native())
185  availableFunds += XRPAmount{ctx.view.fees().increment};
186 
187  if (value > availableFunds)
188  {
189  JLOG(ctx.j.warn())
190  << "Check cashed for more than owner's balance.";
191  return tecPATH_PARTIAL;
192  }
193  }
194 
195  // An issuer can always accept their own currency.
196  if (!value.native() && (value.getIssuer() != dstId))
197  {
198  auto const sleTrustLine =
199  ctx.view.read(keylet::line(dstId, issuerId, currency));
200  if (!sleTrustLine)
201  {
202  JLOG(ctx.j.warn())
203  << "Cannot cash check for IOU without trustline.";
204  return tecNO_LINE;
205  }
206 
207  auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
208  if (!sleIssuer)
209  {
210  JLOG(ctx.j.warn())
211  << "Can't receive IOUs from non-existent issuer: "
212  << to_string(issuerId);
213  return tecNO_ISSUER;
214  }
215 
216  if ((*sleIssuer)[sfFlags] & lsfRequireAuth)
217  {
218  // Entries have a canonical representation, determined by a
219  // lexicographical "greater than" comparison employing strict
220  // weak ordering. Determine which entry we need to access.
221  bool const canonical_gt(dstId > issuerId);
222 
223  bool const is_authorized(
224  (*sleTrustLine)[sfFlags] &
225  (canonical_gt ? lsfLowAuth : lsfHighAuth));
226 
227  if (!is_authorized)
228  {
229  JLOG(ctx.j.warn())
230  << "Can't receive IOUs from issuer without auth.";
231  return tecNO_AUTH;
232  }
233  }
234 
235  // The trustline from source to issuer does not need to
236  // be checked for freezing, since we already verified that the
237  // source has sufficient non-frozen funds available.
238 
239  // However, the trustline from destination to issuer may not
240  // be frozen.
241  if (isFrozen(ctx.view, dstId, currency, issuerId))
242  {
243  JLOG(ctx.j.warn()) << "Cashing a check to a frozen trustline.";
244  return tecFROZEN;
245  }
246  }
247  }
248  return tesSUCCESS;
249 }
250 
251 TER
252 CashCheck::doApply()
253 {
254  // Flow requires that we operate on a PaymentSandbox, rather than
255  // directly on a View.
256  PaymentSandbox psb(&ctx_.view());
257 
258  auto const sleCheck = psb.peek(keylet::check(ctx_.tx[sfCheckID]));
259  if (!sleCheck)
260  {
261  JLOG(j_.fatal()) << "Precheck did not verify check's existence.";
262  return tecFAILED_PROCESSING;
263  }
264 
265  AccountID const srcId{sleCheck->getAccountID(sfAccount)};
266  auto const sleSrc = psb.peek(keylet::account(srcId));
267  auto const sleDst = psb.peek(keylet::account(account_));
268 
269  if (!sleSrc || !sleDst)
270  {
271  JLOG(ctx_.journal.fatal())
272  << "Precheck did not verify source or destination's existence.";
273  return tecFAILED_PROCESSING;
274  }
275 
276  // Preclaim already checked that source has at least the requested
277  // funds.
278  //
279  // Therefore, if this is a check written to self, (and it shouldn't be)
280  // we know they have sufficient funds to pay the check. Since they are
281  // taking the funds from their own pocket and putting it back in their
282  // pocket no balance will change.
283  //
284  // If it is not a check to self (as should be the case), then there's
285  // work to do...
286  auto viewJ = ctx_.app.journal("View");
287  auto const optDeliverMin = ctx_.tx[~sfDeliverMin];
288  bool const doFix1623{ctx_.view().rules().enabled(fix1623)};
289  if (srcId != account_)
290  {
291  STAmount const sendMax{sleCheck->getFieldAmount(sfSendMax)};
292 
293  // Flow() doesn't do XRP to XRP transfers.
294  if (sendMax.native())
295  {
296  // Here we need to calculate the amount of XRP sleSrc can send.
297  // The amount they have available is their balance minus their
298  // reserve.
299  //
300  // Since (if we're successful) we're about to remove an entry
301  // from src's directory, we allow them to send that additional
302  // incremental reserve amount in the transfer. Hence the -1
303  // argument.
304  STAmount const srcLiquid{xrpLiquid(psb, srcId, -1, viewJ)};
305 
306  // Now, how much do they need in order to be successful?
307  STAmount const xrpDeliver{
308  optDeliverMin
309  ? std::max(*optDeliverMin, std::min(sendMax, srcLiquid))
310  : ctx_.tx.getFieldAmount(sfAmount)};
311 
312  if (srcLiquid < xrpDeliver)
313  {
314  // Vote no. However the transaction might succeed if applied
315  // in a different order.
316  JLOG(j_.trace()) << "Cash Check: Insufficient XRP: "
317  << srcLiquid.getFullText() << " < "
318  << xrpDeliver.getFullText();
319  return tecUNFUNDED_PAYMENT;
320  }
321 
322  if (optDeliverMin && doFix1623)
323  // Set the DeliveredAmount metadata.
324  ctx_.deliver(xrpDeliver);
325 
326  // The source account has enough XRP so make the ledger change.
327  if (TER const ter{
328  transferXRP(psb, srcId, account_, xrpDeliver, viewJ)};
329  ter != tesSUCCESS)
330  {
331  // The transfer failed. Return the error code.
332  return ter;
333  }
334  }
335  else
336  {
337  // Let flow() do the heavy lifting on a check for an IOU.
338  //
339  // Note that for DeliverMin we don't know exactly how much
340  // currency we want flow to deliver. We can't ask for the
341  // maximum possible currency because there might be a gateway
342  // transfer rate to account for. Since the transfer rate cannot
343  // exceed 200%, we use 1/2 maxValue as our limit.
344  STAmount const flowDeliver{
345  optDeliverMin
346  ? STAmount{optDeliverMin->issue(), STAmount::cMaxValue / 2, STAmount::cMaxOffset}
347  : static_cast<STAmount>(ctx_.tx[sfAmount])};
348 
349  // Call the payment engine's flow() to do the actual work.
350  auto const result = flow(
351  psb,
352  flowDeliver,
353  srcId,
354  account_,
355  STPathSet{},
356  true, // default path
357  static_cast<bool>(optDeliverMin), // partial payment
358  true, // owner pays transfer fee
359  false, // offer crossing
360  boost::none,
361  sleCheck->getFieldAmount(sfSendMax),
362  viewJ);
363 
364  if (result.result() != tesSUCCESS)
365  {
366  JLOG(ctx_.journal.warn()) << "flow failed when cashing check.";
367  return result.result();
368  }
369 
370  // Make sure that deliverMin was satisfied.
371  if (optDeliverMin)
372  {
373  if (result.actualAmountOut < *optDeliverMin)
374  {
375  JLOG(ctx_.journal.warn())
376  << "flow did not produce DeliverMin.";
377  return tecPATH_PARTIAL;
378  }
379  if (doFix1623)
380  // Set the delivered_amount metadata.
381  ctx_.deliver(result.actualAmountOut);
382  }
383  }
384  }
385 
386  // Check was cashed. If not a self send (and it shouldn't be), remove
387  // check link from destination directory.
388  if (srcId != account_)
389  {
390  std::uint64_t const page{(*sleCheck)[sfDestinationNode]};
391  if (!ctx_.view().dirRemove(
392  keylet::ownerDir(account_), page, sleCheck->key(), true))
393  {
394  JLOG(j_.fatal()) << "Unable to delete check from destination.";
395  return tefBAD_LEDGER;
396  }
397  }
398  // Remove check from check owner's directory.
399  {
400  std::uint64_t const page{(*sleCheck)[sfOwnerNode]};
401  if (!ctx_.view().dirRemove(
402  keylet::ownerDir(srcId), page, sleCheck->key(), true))
403  {
404  JLOG(j_.fatal()) << "Unable to delete check from owner.";
405  return tefBAD_LEDGER;
406  }
407  }
408  // If we succeeded, update the check owner's reserve.
409  adjustOwnerCount(psb, sleSrc, -1, viewJ);
410 
411  // Remove check from ledger.
412  psb.erase(sleCheck);
413 
414  psb.apply(ctx_.rawView());
415  return tesSUCCESS;
416 }
417 
418 } // namespace ripple
ripple::badCurrency
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
Definition: UintTypes.cpp:138
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::tecFROZEN
@ tecFROZEN
Definition: TER.h:264
ripple::preflight2
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:108
ripple::sfSendMax
const SF_AMOUNT sfSendMax
ripple::PreclaimContext::view
ReadView const & view
Definition: Transactor.h:57
ripple::fhZERO_IF_FROZEN
@ fhZERO_IF_FROZEN
Definition: View.h:53
ripple::sfOwnerNode
const SF_UINT64 sfOwnerNode
ripple::temBAD_CURRENCY
@ temBAD_CURRENCY
Definition: TER.h:85
ripple::PreclaimContext::j
const beast::Journal j
Definition: Transactor.h:61
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::sfDestination
const SF_ACCOUNT sfDestination
ripple::sfAmount
const SF_AMOUNT sfAmount
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:112
ripple::isTesSuccess
bool isTesSuccess(TER x)
Definition: TER.h:580
ripple::CashCheck::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: CashCheck.cpp:35
ripple::lsfLowAuth
@ lsfLowAuth
Definition: LedgerFormats.h:123
ripple::tecDST_TAG_NEEDED
@ tecDST_TAG_NEEDED
Definition: TER.h:270
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::isLegalNet
bool isLegalNet(STAmount const &value)
Definition: STAmount.h:385
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:42
ripple::ReadView::parentCloseTime
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition: ReadView.h:253
ripple::PreflightContext::j
const beast::Journal j
Definition: Transactor.h:39
ripple::STPathSet
Definition: STPathSet.h:309
algorithm
ripple::preflight1
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:56
ripple::lsfHighAuth
@ lsfHighAuth
Definition: LedgerFormats.h:124
ripple::sfExpiration
const SF_UINT32 sfExpiration
ripple::Keylet::key
uint256 key
Definition: Keylet.h:41
ripple::base_uint< 160, detail::AccountIDTag >
ripple::sfDeliverMin
const SF_AMOUNT sfDeliverMin
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:106
ripple::lsfRequireAuth
@ lsfRequireAuth
Definition: LedgerFormats.h:106
ripple::tefBAD_LEDGER
@ tefBAD_LEDGER
Definition: TER.h:147
ripple::adjustOwnerCount
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition: View.cpp:642
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:134
ripple::CashCheck::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: CashCheck.cpp:82
ripple::TERSubset
Definition: TER.h:326
ripple::tecFAILED_PROCESSING
@ tecFAILED_PROCESSING
Definition: TER.h:247
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:551
ripple::fix1623
const uint256 fix1623
Definition: Feature.cpp:173
ripple::STAmount
Definition: STAmount.h:42
ripple::tecUNFUNDED_PAYMENT
@ tecUNFUNDED_PAYMENT
Definition: TER.h:246
ripple::sfDestinationNode
const SF_UINT64 sfDestinationNode
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:271
ripple::xrpLiquid
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition: View.cpp:215
ripple::STObject::getFlags
std::uint32_t getFlags() const
Definition: STObject.cpp:454
ripple::STTx
Definition: STTx.h:42
ripple::temBAD_AMOUNT
@ temBAD_AMOUNT
Definition: TER.h:84
std::uint64_t
ripple::Rules::enabled
bool enabled(uint256 const &id) const
Returns true if a feature is enabled.
Definition: ReadView.cpp:103
ripple::tecPATH_PARTIAL
@ tecPATH_PARTIAL
Definition: TER.h:243
ripple::keylet::line
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:194
ripple::featureChecks
const uint256 featureChecks
Definition: Feature.cpp:170
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::PreclaimContext::tx
STTx const & tx
Definition: Transactor.h:59
ripple::accountFunds
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition: View.cpp:136
ripple::sfCheckID
const SF_HASH256 sfCheckID
ripple::transferXRP
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition: View.cpp:1450
ripple::lsfRequireDestTag
@ lsfRequireDestTag
Definition: LedgerFormats.h:104
std::min
T min(T... args)
ripple::PreclaimContext
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:53
ripple::detail::ApplyViewBase::erase
void erase(std::shared_ptr< SLE > const &sle) override
Remove a peeked SLE.
Definition: ApplyViewBase.cpp:135
ripple::Currency
base_uint< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition: UintTypes.h:56
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::tecNO_LINE
@ tecNO_LINE
Definition: TER.h:262
ripple::tecEXPIRED
@ tecEXPIRED
Definition: TER.h:275
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:109
ripple::PaymentSandbox::apply
void apply(RawView &to)
Apply changes to base view.
Definition: PaymentSandbox.cpp:255
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:197
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::tecNO_ISSUER
@ tecNO_ISSUER
Definition: TER.h:260
ripple::sfDestinationTag
const SF_UINT32 sfDestinationTag
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:266
ripple::flow
path::RippleCalc::Output flow(PaymentSandbox &view, STAmount const &deliver, AccountID const &src, AccountID const &dst, STPathSet const &paths, bool defaultPaths, bool partialPayment, bool ownerPaysTransferFee, bool offerCrossing, boost::optional< Quality > const &limitQuality, boost::optional< STAmount > const &sendMax, beast::Journal j, path::detail::FlowDebugInfo *flowDebugInfo=nullptr)
Make a payment from the src account to the dst account.
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::detail::ApplyViewBase::peek
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Definition: ApplyViewBase.cpp:129
ripple::tecNO_ENTRY
@ tecNO_ENTRY
Definition: TER.h:267
ripple::temMALFORMED
@ temMALFORMED
Definition: TER.h:82
ripple::NetClock::duration
std::chrono::duration< rep, period > duration
Definition: chrono.h:53
ripple::tfUniversalMask
const std::uint32_t tfUniversalMask
Definition: TxFlags.h:50
ripple::PreflightContext::tx
STTx const & tx
Definition: Transactor.h:36
std::max
T max(T... args)
ripple::PreflightContext
State information when preflighting a tx.
Definition: Transactor.h:32
ripple::tecNO_AUTH
@ tecNO_AUTH
Definition: TER.h:261
ripple::keylet::check
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition: Indexes.cpp:282
ripple::PreflightContext::rules
const Rules rules
Definition: Transactor.h:37
ripple::NetClock::time_point
std::chrono::time_point< NetClock > time_point
Definition: chrono.h:54
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:216
ripple::isFrozen
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:69
ripple::AccountID
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition: AccountID.h:47
ripple::NotTEC
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:511