1#include <xrpl/tx/transactors/check/CheckCash.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/scope.h>
5#include <xrpl/core/ServiceRegistry.h>
6#include <xrpl/ledger/PaymentSandbox.h>
7#include <xrpl/ledger/View.h>
8#include <xrpl/ledger/helpers/AccountRootHelpers.h>
9#include <xrpl/ledger/helpers/MPTokenHelpers.h>
10#include <xrpl/ledger/helpers/RippleStateHelpers.h>
11#include <xrpl/ledger/helpers/TokenHelpers.h>
12#include <xrpl/protocol/AccountID.h>
13#include <xrpl/protocol/Asset.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/Indexes.h>
16#include <xrpl/protocol/Issue.h>
17#include <xrpl/protocol/Keylet.h>
18#include <xrpl/protocol/LedgerFormats.h>
19#include <xrpl/protocol/MPTIssue.h>
20#include <xrpl/protocol/Protocol.h>
21#include <xrpl/protocol/SField.h>
22#include <xrpl/protocol/STAmount.h>
23#include <xrpl/protocol/STLedgerEntry.h>
24#include <xrpl/protocol/STTx.h>
25#include <xrpl/protocol/TER.h>
26#include <xrpl/protocol/UintTypes.h>
27#include <xrpl/protocol/XRPAmount.h>
28#include <xrpl/tx/Transactor.h>
29#include <xrpl/tx/paths/Flow.h>
30#include <xrpl/tx/paths/detail/Steps.h>
41 auto const optAmount = ctx.
tx[~sfAmount];
42 auto const optDeliverMin = ctx.
tx[~sfDeliverMin];
45 (!(optAmount && optAmount->holds<
MPTIssue>()) &&
46 !(optDeliverMin && optDeliverMin->holds<
MPTIssue>()));
53 auto const optAmount = ctx.
tx[~sfAmount];
54 auto const optDeliverMin = ctx.
tx[~sfDeliverMin];
56 if (
static_cast<bool>(optAmount) ==
static_cast<bool>(optDeliverMin))
58 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: "
59 "does not specify exactly one of Amount and DeliverMin.";
64 STAmount const value{optAmount ? *optAmount : *optDeliverMin};
67 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: bad amount: " << value.getFullText();
73 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: Bad currency.";
86 JLOG(ctx.
j.
warn()) <<
"Check does not exist.";
91 AccountID const dstId = sleCheck->at(sfDestination);
92 if (ctx.
tx[sfAccount] != dstId)
94 JLOG(ctx.
j.
warn()) <<
"Cashing a check with wrong Destination.";
97 AccountID const srcId = sleCheck->at(sfAccount);
103 JLOG(ctx.
j.
error()) <<
"Malformed transaction: Cashing check to self.";
110 if (!sleSrc || !sleDst)
113 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: source or destination not in ledger";
117 if (sleDst->isFlag(lsfRequireDestTag) && !sleCheck->isFieldPresent(sfDestinationTag))
121 JLOG(ctx.
j.
warn()) <<
"Malformed transaction: DestinationTag required in check.";
128 JLOG(ctx.
j.
warn()) <<
"Cashing a check that has already expired.";
136 auto const optAmount = tx[~sfAmount];
137 return optAmount ? *optAmount : tx[sfDeliverMin];
140 STAmount const sendMax = sleCheck->at(sfSendMax);
148 JLOG(ctx.
j.
warn()) <<
"Check cash does not match check currency.";
151 AccountID const issuerId{value.getIssuer()};
154 JLOG(ctx.
j.
warn()) <<
"Check cash does not match check issuer.";
159 JLOG(ctx.
j.
warn()) <<
"Check cashed for more than check sendMax.";
168 sleCheck->at(sfAccount),
181 if (value > availableFunds)
183 JLOG(ctx.
j.
warn()) <<
"Check cashed for more than owner's balance.";
189 if (!value.native() && (value.getIssuer() != dstId))
191 return value.asset().visit(
194 auto const sleTrustLine =
200 JLOG(ctx.
j.
warn()) <<
"Can't receive IOUs from "
201 "non-existent issuer: "
206 if (sleIssuer->isFlag(lsfRequireAuth))
219 bool const canonicalGt(dstId > issuerId);
221 bool const isAuthorized(
222 (sleTrustLine->at(sfFlags) &
223 (canonicalGt ? lsfLowAuth : lsfHighAuth)) != 0u);
227 JLOG(ctx.
j.
warn()) <<
"Can't receive IOUs from "
228 "issuer without auth.";
242 JLOG(ctx.
j.
warn()) <<
"Cashing a check to a frozen trustline.";
252 JLOG(ctx.
j.
warn()) <<
"Can't receive MPTs from "
253 "non-existent issuer: "
261 JLOG(ctx.
j.
warn()) <<
"Cashing a check to a MPT requiring auth.";
267 JLOG(ctx.
j.
warn()) <<
"Cashing a check to a frozen MPT.";
274 JLOG(ctx.
j.
warn()) <<
"MPT transfer is disabled.";
296 JLOG(
j_.fatal()) <<
"Precheck did not verify check's existence.";
301 AccountID const srcId{sleCheck->getAccountID(sfAccount)};
305 JLOG(
ctx_.journal.fatal()) <<
"Precheck did not verify source or destination's existence.";
320 auto viewJ =
ctx_.registry.get().getJournal(
"View");
321 auto const optDeliverMin =
ctx_.tx[~sfDeliverMin];
325 STAmount const sendMax = sleCheck->at(sfSendMax);
343 :
ctx_.tx.getFieldAmount(sfAmount)};
345 if (srcLiquid < xrpDeliver)
349 JLOG(
j_.trace()) <<
"Cash Check: Insufficient XRP: " << srcLiquid.
getFullText()
357 ctx_.deliver(xrpDeliver);
375 auto const maxDeliverMin = [&]() {
376 return optDeliverMin->asset().visit(
386 optDeliverMin ? maxDeliverMin() :
ctx_.tx.getFieldAmount(sfAmount)};
394 if (
std::uint32_t const ownerCount = {sleDst->at(sfOwnerCount)};
397 JLOG(
j_.trace()) <<
"Trust line does not exist. "
398 "Insufficient reserve to create line.";
407 bool destLow =
false;
412 Issue const& trustLineIssue = issue;
417 if (!psb.
exists(*trustLineKey))
429 auto const sleDst = checkReserve();
430 if (sleDst ==
nullptr)
445 !sleDst->isFlag(lsfDefaultRipple),
471 auto const sleTrustLine = psb.
peek(*trustLineKey);
475 SF_AMOUNT const& tweakedLimit = destLow ? sfLowLimit : sfHighLimit;
476 savedLimit = sleTrustLine->at(tweakedLimit);
482 sleTrustLine->at(tweakedLimit) = bigAmount;
489 auto const& mptID = issue.getMptID();
492 if (!psb.
exists(mptokenKey))
494 auto sleDst = checkReserve();
495 if (sleDst ==
nullptr)
512 ScopeExit const fixup([&psb, &trustLineKey, destLow, &savedLimit]() {
515 SF_AMOUNT const& tweakedLimit = destLow ? sfLowLimit : sfHighLimit;
516 if (
auto const sleTrustLine = psb.
peek(*trustLineKey))
517 sleTrustLine->at(tweakedLimit) = savedLimit;
522 auto const result =
flow(
529 static_cast<bool>(optDeliverMin),
533 sleCheck->getFieldAmount(sfSendMax),
539 JLOG(
ctx_.journal.warn()) <<
"flow failed when cashing check.";
540 return result.result();
546 if (result.actualAmountOut < *optDeliverMin)
548 JLOG(
ctx_.journal.warn()) <<
"flow did not produce DeliverMin.";
551 ctx_.deliver(result.actualAmountOut);
556 ctx_.deliver(result.actualAmountOut);
569 JLOG(
j_.fatal()) <<
"Unable to delete check from destination.";
578 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.
Fees const & fees() const override
Returns the fees for the base ledger.
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, std::uint32_t 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.
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
bool isLegalMPT(STAmount const &value)
TER checkCreateMPT(xrpl::ApplyView &view, xrpl::MPTIssue const &mptIssue, xrpl::AccountID const &holder, beast::Journal j)
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, beast::Journal j)
Create a trust line.
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
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.
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)
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.
void adjustOwnerCount(ApplyView &view, SLE::ref sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
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.
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
BadAsset const & badAsset()
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
constexpr bool equalTokens(Asset const &lhs, Asset const &rhs)
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.