1#include <xrpld/app/misc/DelegateUtils.h>
2#include <xrpld/app/misc/PermissionedDEXHelpers.h>
3#include <xrpld/app/paths/RippleCalc.h>
4#include <xrpld/app/tx/detail/Payment.h>
6#include <xrpl/basics/Log.h>
7#include <xrpl/ledger/CredentialHelpers.h>
8#include <xrpl/ledger/View.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Quality.h>
11#include <xrpl/protocol/TxFlags.h>
12#include <xrpl/protocol/jss.h>
21 tx.isFieldPresent(sfSendMax) ? tx[sfSendMax] : tx[sfAmount];
25 return maxAmount.
native() ? maxAmount.
xrp() : beast::zero;
46 dstAmount < beast::zero);
67 STAmount const dstAmount(tx.getFieldAmount(sfAmount));
79 STAmount const dstAmount(tx.getFieldAmount(sfAmount));
93 bool const hasPaths = tx.isFieldPresent(sfPaths);
94 bool const hasMax = tx.isFieldPresent(sfSendMax);
96 auto const deliverMin = tx[~sfDeliverMin];
98 auto const account = tx.getAccountID(sfAccount);
102 if ((mptDirect && dstAmount.
asset() != maxSourceAmount.
asset()) ||
105 JLOG(j.trace()) <<
"Malformed transaction: inconsistent issues: "
108 << deliverMin.value_or(
STAmount{}).getFullText();
112 auto const& srcAsset = maxSourceAmount.
asset();
113 auto const& dstAsset = dstAmount.
asset();
115 bool const xrpDirect = srcAsset.
native() && dstAsset.native();
120 auto const dstAccountID = tx.getAccountID(sfDestination);
124 JLOG(j.trace()) <<
"Malformed transaction: "
125 <<
"Payment destination account not specified.";
128 if (hasMax && maxSourceAmount <= beast::zero)
130 JLOG(j.trace()) <<
"Malformed transaction: bad max amount: "
134 if (dstAmount <= beast::zero)
136 JLOG(j.trace()) <<
"Malformed transaction: bad dst amount: "
142 JLOG(j.trace()) <<
"Malformed transaction: Bad currency.";
145 if (account == dstAccountID &&
equalTokens(srcAsset, dstAsset) && !hasPaths)
149 JLOG(j.trace()) <<
"Malformed transaction: "
150 <<
"Redundant payment from " <<
to_string(account)
151 <<
" to self without path for " <<
to_string(dstAsset);
154 if (xrpDirect && hasMax)
157 JLOG(j.trace()) <<
"Malformed transaction: "
158 <<
"SendMax specified for XRP to XRP.";
161 if ((xrpDirect || mptDirect) && hasPaths)
164 JLOG(j.trace()) <<
"Malformed transaction: "
165 <<
"Paths specified for XRP to XRP or MPT to MPT.";
168 if (xrpDirect && partialPaymentAllowed)
171 JLOG(j.trace()) <<
"Malformed transaction: "
172 <<
"Partial payment specified for XRP to XRP.";
175 if ((xrpDirect || mptDirect) && limitQuality)
179 <<
"Malformed transaction: "
180 <<
"Limit quality specified for XRP to XRP or MPT to MPT.";
183 if ((xrpDirect || mptDirect) && !defaultPathsAllowed)
187 <<
"Malformed transaction: "
188 <<
"No ripple direct specified for XRP to XRP or MPT to MPT.";
194 if (!partialPaymentAllowed)
196 JLOG(j.trace()) <<
"Malformed transaction: Partial payment not "
198 << jss::DeliverMin.c_str() <<
".";
202 auto const dMin = *deliverMin;
206 <<
"Malformed transaction: Invalid " << jss::DeliverMin.c_str()
207 <<
" amount. " << dMin.getFullText();
210 if (dMin.asset() != dstAmount.
asset())
213 <<
"Malformed transaction: Dst issue differs "
215 << jss::DeliverMin.c_str() <<
". " << dMin.getFullText();
218 if (dMin > dstAmount)
221 <<
"Malformed transaction: Dst amount less than "
222 << jss::DeliverMin.c_str() <<
". " << dMin.getFullText();
237 auto const delegate = tx[~sfDelegate];
242 auto const sle =
view.
read(delegateKey);
254 auto const& amountAsset = dstAmount.
asset();
258 tx[sfSendMax].asset() != amountAsset) ||
262 if (granularPermissions.
contains(PaymentMint) && !
isXRP(amountAsset) &&
263 amountAsset.getIssuer() == tx[sfAccount])
266 if (granularPermissions.
contains(PaymentBurn) && !
isXRP(amountAsset) &&
267 amountAsset.getIssuer() == tx[sfDestination])
280 auto const sendMax = ctx.
tx[~sfSendMax];
282 AccountID const dstAccountID(ctx.
tx[sfDestination]);
286 auto const sleDst = ctx.
view.
read(k);
294 <<
"Delay transaction: Destination account does not exist.";
300 else if (ctx.
view.
open() && partialPaymentAllowed)
304 JLOG(ctx.
j.
trace()) <<
"Delay transaction: Partial payment not "
305 "allowed to create account.";
316 <<
"Delay transaction: Destination account does not exist. "
317 <<
"Insufficent payment to create account.";
335 <<
"Malformed transaction: DestinationTag required.";
341 if ((hasPaths || sendMax || !dstAmount.
native()) && ctx.
view.
open())
347 return path.size() > MaxPathLength;
362 ctx.
view, ctx.
tx[sfAccount], ctx.
tx[sfDomainID]))
366 ctx.
view, ctx.
tx[sfDestination], ctx.
tx[sfDomainID]))
376 auto const deliverMin =
ctx_.
tx[~sfDeliverMin];
384 auto const sendMax =
ctx_.
tx[~sfSendMax];
392 JLOG(
j_.
trace()) <<
"maxSourceAmount=" << maxSourceAmount.getFullText()
407 sleDst->setAccountID(sfAccount, dstAccountID);
408 sleDst->setFieldU32(sfSequence, seqno);
421 (hasPaths || sendMax || !dstAmount.
native()) && !mptDirect;
452 JLOG(
j_.
debug()) <<
"Entering RippleCalc in payment: "
480 auto terResult = rc.
result();
518 auto const& issuer = mptIssue.
getIssuer();
521 Rate rate{QUALITY_ONE};
523 if (
account_ != issuer && dstAccountID != issuer)
545 if (partialPaymentAllowed && requiredMaxSourceAmount > maxSourceAmount)
547 requiredMaxSourceAmount = maxSourceAmount;
549 amountDeliver =
divide(maxSourceAmount, rate);
552 if (requiredMaxSourceAmount > maxSourceAmount ||
553 (deliverMin && amountDeliver < *deliverMin))
566 if (
view().rules().enabled(fixMPTDeliveredAmount) &&
567 amountDeliver != dstAmount)
576 XRPL_ASSERT(dstAmount.
native(),
"ripple::Payment::doApply : amount is XRP");
586 auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
600 JLOG(
j_.
trace()) <<
"Delay transaction: Insufficient funds: "
640 if (dstAmount > dstReserve ||
641 sleDst->getFieldAmount(sfBalance) > dstReserve)
656 sleDst->setFieldAmount(
657 sfBalance, sleDst->getFieldAmount(sfBalance) + dstAmount);
Stream trace() const
Severity stream access functions.
beast::Journal const journal
void deliver(STAmount const &amount)
Sets the DeliveredAmount field in the metadata.
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
A currency issued by an account.
AccountID const & getIssuer() const
A wrapper which makes credits unavailable to balances.
void apply(RawView &to)
Apply changes to base view.
static NotTEC checkPermission(ReadView const &view, STTx const &tx)
static std::size_t const MaxPathSize
static bool checkExtraFeatures(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual bool open() const =0
Returns true if this reflects an open ledger.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
constexpr bool holds() const noexcept
int exponent() const noexcept
Asset const & asset() const
constexpr TIss const & get() const
std::uint64_t mantissa() const noexcept
std::string getFullText() const override
bool native() const noexcept
STPathSet const & getFieldPathSet(SField const &field) const
AccountID getAccountID(SField const &field) const
STAmount const & getFieldAmount(SField const &field) const
bool isFieldPresent(SField const &field) const
std::uint32_t getFlags() const
std::vector< STPath >::const_iterator end() const
std::vector< STPath >::const_iterator begin() const
std::vector< STPath >::size_type size() const
uint256 getTransactionID() const
Class describing the consequences to the account of applying a transaction if the transaction consume...
static Output rippleCalculate(PaymentSandbox &view, STAmount const &saMaxAmountReq, STAmount const &saDstAmountReq, AccountID const &uDstAccountID, AccountID const &uSrcAccountID, STPathSet const &spsPaths, std::optional< uint256 > const &domainID, Logs &l, Input const *const pInputs=nullptr)
NotTEC checkFields(STTx const &tx, beast::Journal j)
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Keylet account(AccountID const &id) noexcept
AccountID root.
bool accountInDomain(ReadView const &view, AccountID const &account, Domain const &domainID)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
STAmount divide(STAmount const &amount, Rate const &rate)
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
constexpr bool equalTokens(Asset const &lhs, Asset const &rhs)
bool isXRP(AccountID const &c)
bool isLegalNet(STAmount const &value)
constexpr std::uint32_t tfMPTPaymentMask
STAmount getMaxSourceAmount(AccountID const &account, STAmount const &dstAmount, std::optional< STAmount > const &sendMax)
STAmount multiply(STAmount const &amount, Rate const &rate)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst, beast::Journal j)
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
constexpr std::uint32_t tfPartialPayment
void loadGranularPermission(std::shared_ptr< SLE const > const &delegate, TxType const &type, std::unordered_set< GranularPermissionType > &granularPermissions)
Load the granular permissions granted to the delegate account for the specified transaction type.
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
bool isTerRetry(TER x) noexcept
constexpr std::uint32_t tfNoRippleDirect
bool isTesSuccess(TER x) noexcept
constexpr std::uint32_t tfPaymentMask
constexpr std::uint32_t tfLimitQuality
std::string to_string(base_uint< Bits, Tag > const &a)
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
@ terNO_DELEGATE_PERMISSION
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
@ temBAD_SEND_XRP_PARTIAL
@ temBAD_SEND_XRP_NO_DIRECT
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.
Represents a transfer rate.
void setResult(TER const value)