1#include <xrpl/tx/transactors/check/CheckCash.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/scope.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/core/ServiceRegistry.h>
7#include <xrpl/ledger/ApplyView.h>
8#include <xrpl/ledger/PaymentSandbox.h>
9#include <xrpl/ledger/View.h>
10#include <xrpl/ledger/helpers/AccountRootHelpers.h>
11#include <xrpl/ledger/helpers/MPTokenHelpers.h>
12#include <xrpl/ledger/helpers/RippleStateHelpers.h>
13#include <xrpl/ledger/helpers/SponsorHelpers.h>
14#include <xrpl/ledger/helpers/TokenHelpers.h>
15#include <xrpl/protocol/AccountID.h>
16#include <xrpl/protocol/Asset.h>
17#include <xrpl/protocol/Feature.h>
18#include <xrpl/protocol/Indexes.h>
19#include <xrpl/protocol/Issue.h>
20#include <xrpl/protocol/Keylet.h>
21#include <xrpl/protocol/LedgerFormats.h>
22#include <xrpl/protocol/MPTAmount.h>
23#include <xrpl/protocol/MPTIssue.h>
24#include <xrpl/protocol/Quality.h>
25#include <xrpl/protocol/SField.h>
26#include <xrpl/protocol/STAmount.h>
27#include <xrpl/protocol/STLedgerEntry.h>
28#include <xrpl/protocol/STTx.h>
29#include <xrpl/protocol/TER.h>
30#include <xrpl/protocol/UintTypes.h>
31#include <xrpl/protocol/XRPAmount.h>
32#include <xrpl/tx/Transactor.h>
33#include <xrpl/tx/paths/Flow.h>
34#include <xrpl/tx/paths/detail/Steps.h>
45 auto const optAmount = ctx.
tx[~sfAmount];
46 auto const optDeliverMin = ctx.
tx[~sfDeliverMin];
49 (!(optAmount && optAmount->holds<
MPTIssue>()) &&
50 !(optDeliverMin && optDeliverMin->holds<
MPTIssue>()));
60 auto const optAmount = ctx.
tx[~sfAmount];
61 auto const optDeliverMin = ctx.
tx[~sfDeliverMin];
63 if (
static_cast<bool>(optAmount) ==
static_cast<bool>(optDeliverMin))
65 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: "
66 "does not specify exactly one of Amount and DeliverMin.";
71 STAmount const value{optAmount ? *optAmount : *optDeliverMin};
74 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: bad amount: " << value.getFullText();
80 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: Bad currency.";
93 JLOG(ctx.
j.
warn()) <<
"Check does not exist.";
98 AccountID const dstId = sleCheck->at(sfDestination);
99 if (ctx.
tx[sfAccount] != dstId)
101 JLOG(ctx.
j.
warn()) <<
"Cashing a check with wrong Destination.";
104 AccountID const srcId = sleCheck->at(sfAccount);
110 JLOG(ctx.
j.
error()) <<
"Malformed transaction: Cashing check to self.";
117 if (!sleSrc || !sleDst)
120 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: source or destination not in ledger";
124 if (sleDst->isFlag(lsfRequireDestTag) && !sleCheck->isFieldPresent(sfDestinationTag))
128 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: DestinationTag required in check.";
135 JLOG(ctx.
j.
warn()) <<
"Cashing a check that has already expired.";
143 auto const optAmount = tx[~sfAmount];
144 return optAmount ? *optAmount : tx[sfDeliverMin];
147 STAmount const sendMax = sleCheck->at(sfSendMax);
155 JLOG(ctx.
j.
warn()) <<
"Check cash does not match check currency.";
158 AccountID const issuerId{value.getIssuer()};
161 JLOG(ctx.
j.
warn()) <<
"Check cash does not match check issuer.";
166 JLOG(ctx.
j.
warn()) <<
"Check cashed for more than check sendMax.";
175 sleCheck->at(sfAccount),
185 if (value.native() && !sleCheck->isFieldPresent(sfSponsor))
188 if (value > availableFunds)
190 JLOG(ctx.
j.
warn()) <<
"Check cashed for more than owner's balance.";
196 if (!value.native() && (value.getIssuer() != dstId))
198 return value.asset().visit(
201 auto const sleTrustLine =
207 JLOG(ctx.
j.
warn()) <<
"Can't receive IOUs from "
208 "non-existent issuer: "
213 if (sleIssuer->isFlag(lsfRequireAuth))
226 bool const canonicalGt(dstId > issuerId);
228 bool const isAuthorized(
229 (sleTrustLine->at(sfFlags) &
230 (canonicalGt ? lsfLowAuth : lsfHighAuth)) != 0u);
234 JLOG(ctx.
j.
warn()) <<
"Can't receive IOUs from "
235 "issuer without auth.";
249 JLOG(ctx.
j.
warn()) <<
"Cashing a check to a frozen trustline.";
259 JLOG(ctx.
j.
warn()) <<
"Can't receive MPTs from "
260 "non-existent issuer: "
268 JLOG(ctx.
j.
warn()) <<
"Cashing a check to a MPT requiring auth.";
274 JLOG(ctx.
j.
warn()) <<
"Cashing a check to a frozen MPT.";
281 JLOG(ctx.
j.
warn()) <<
"MPT transfer is disabled.";
303 JLOG(
j_.fatal()) <<
"Precheck did not verify check's existence.";
308 AccountID const srcId{sleCheck->getAccountID(sfAccount)};
312 JLOG(
ctx_.journal.fatal()) <<
"Precheck did not verify source or destination's existence.";
329 auto viewJ =
ctx_.registry.get().getJournal(
"View");
330 auto const optDeliverMin =
ctx_.tx[~sfDeliverMin];
334 STAmount const sendMax = sleCheck->at(sfSendMax);
347 STAmount const srcLiquid{
xrpLiquid(psb, srcId, sponsorCheckSle ? 0 : -1, viewJ)};
352 :
ctx_.tx.getFieldAmount(sfAmount)};
354 if (srcLiquid < xrpDeliver)
358 JLOG(
j_.trace()) <<
"Cash Check: Insufficient XRP: " << srcLiquid.
getFullText()
366 ctx_.deliver(xrpDeliver);
384 auto const maxDeliverMin = [&]() {
385 return optDeliverMin->asset().visit(
392 auto const& issuer = issue.getIssuer();
406 optDeliverMin ? maxDeliverMin() :
ctx_.tx.getFieldAmount(sfAmount)};
411 return sponsorSle.error();
424 {.ownerCountDelta = 1},
428 JLOG(
j_.trace()) <<
"Trust line does not exist. "
429 "Insufficient reserve to create line.";
438 bool destLow =
false;
443 Issue const& trustLineIssue = issue;
448 if (!psb.
exists(*trustLineKey))
460 auto const sleDst = checkDstReserve();
461 if (sleDst ==
nullptr)
476 !sleDst->isFlag(lsfDefaultRipple),
503 auto const sleTrustLine = psb.
peek(*trustLineKey);
507 SF_AMOUNT const& tweakedLimit = destLow ? sfLowLimit : sfHighLimit;
508 savedLimit = sleTrustLine->at(tweakedLimit);
514 sleTrustLine->at(tweakedLimit) = bigAmount;
521 auto const& mptID = issue.getMptID();
524 if (!psb.
exists(mptokenKey))
526 auto sleDst = checkDstReserve();
527 if (sleDst ==
nullptr)
545 ScopeExit const fixup([&psb, &trustLineKey, destLow, &savedLimit]() {
548 SF_AMOUNT const& tweakedLimit = destLow ? sfLowLimit : sfHighLimit;
549 if (
auto const sleTrustLine = psb.
peek(*trustLineKey))
550 sleTrustLine->at(tweakedLimit) = savedLimit;
555 auto const result =
flow(
562 static_cast<bool>(optDeliverMin),
566 sleCheck->getFieldAmount(sfSendMax),
572 JLOG(
ctx_.journal.warn()) <<
"flow failed when cashing check.";
573 return result.result();
579 if (result.actualAmountOut < *optDeliverMin)
581 JLOG(
ctx_.journal.warn()) <<
"flow did not produce DeliverMin.";
584 ctx_.deliver(result.actualAmountOut);
589 ctx_.deliver(result.actualAmountOut);
602 JLOG(
j_.fatal()) <<
"Unable to delete check from destination.";
611 JLOG(
j_.fatal()) <<
"Unable to delete check from owner.";
A generic endpoint for log messages.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
A currency issued by an account.
A wrapper which makes credits unavailable to balances.
void apply(RawView &to)
Apply changes to base view.
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
constexpr TIss const & get() const
std::string getFullText() const override
bool native() const noexcept
Asset const & asset() const
AccountID const & getIssuer() const
static constexpr std::uint64_t kMaxValue
static constexpr int kMaxOffset
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
AccountID const accountID_
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
void update(SLE::ref sle) override
Indicate changes to a peeked SLE.
void erase(SLE::ref sle) override
Remove a peeked SLE.
bool exists(Keylet const &k) const override
Determine if a state item exists.
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet check(AccountID const &id, SeqProxy const &seq) noexcept
A Check.
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
void decreaseOwnerCountForObject(ApplyView &view, SLE::ref accountSle, SLE::ref objectSle, std::uint32_t count, beast::Journal j)
Decrease owner-count fields for an existing ledger object.
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Calculate liquid XRP balance for an account.
bool isLegalMPT(STAmount const &value)
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp, ExpiryComparison comparison=ExpiryComparison::Inclusive)
Determines whether the given expiration time has passed.
TER checkCreateMPT(xrpl::ApplyView &view, xrpl::MPTIssue const &mptIssue, xrpl::AccountID const &holder, SLE::ref sponsorSle, std::uint32_t flags, beast::Journal j)
bool isLegalNet(STAmount const &value)
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to, WaiveMPTCanTransfer waive=WaiveMPTCanTransfer::No, std::uint8_t depth=0)
Check whether to may receive the given MPT from from.
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uQualityIn, std::uint32_t uQualityOut, SLE::ref sponsorSle, beast::Journal j)
Create a trust line.
TypedField< STAmount > SF_AMOUNT
std::string to_string(BaseUInt< Bits, Tag > const &a)
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
std::expected< SLE::pointer, TER > getTxReserveSponsor(ApplyViewContext ctx)
Return a mutable SLE for the transaction's reserve sponsor account.
TERSubset< CanCvtToNotTEC > NotTEC
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, std::uint8_t depth=0)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
AccountID const & noAccount()
A placeholder for empty accounts.
TER checkReserve(ApplyViewContext ctx, SLE::const_ref accSle, XRPAmount accBalance, SLE::const_ref sponsorSle, Adjustment adj, beast::Journal j, TER insufReserveCode=tecINSUFFICIENT_RESERVE)
Check if an account has sufficient reserve.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, std::uint8_t depth=0)
Check if the account lacks required authorization for MPT.
@ tecNO_LINE_INSUF_RESERVE
@ tecINSUFFICIENT_RESERVE
SLE::pointer getLedgerEntryReserveSponsor(ApplyView &view, SLE::const_ref sle, SF_ACCOUNT const &field=sfSponsor)
Return a mutable SLE for the reserve sponsor recorded on a ledger entry.
BadAsset const & badAsset()
constexpr bool equalTokens(Asset const &lhs, Asset const &rhs)
Bundles the mutable ledger view and the transaction being applied.
XRPAmount increment
Additional XRP reserve required per owned ledger object.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.