20#include <xrpld/app/misc/DelegateUtils.h>
21#include <xrpld/app/misc/PermissionedDEXHelpers.h>
22#include <xrpld/app/paths/RippleCalc.h>
23#include <xrpld/app/tx/detail/Payment.h>
25#include <xrpl/basics/Log.h>
26#include <xrpl/ledger/CredentialHelpers.h>
27#include <xrpl/ledger/View.h>
28#include <xrpl/protocol/Feature.h>
29#include <xrpl/protocol/Quality.h>
30#include <xrpl/protocol/TxFlags.h>
31#include <xrpl/protocol/jss.h>
40 tx.isFieldPresent(sfSendMax) ? tx[sfSendMax] : tx[sfAmount];
44 return maxAmount.
native() ? maxAmount.
xrp() : beast::zero;
65 dstAmount < beast::zero);
85 STAmount const dstAmount(tx.getFieldAmount(sfAmount));
95 if (txFlags & paymentMask)
97 JLOG(j.trace()) <<
"Malformed transaction: Invalid flags set.";
107 bool const hasPaths = tx.isFieldPresent(sfPaths);
108 bool const hasMax = tx.isFieldPresent(sfSendMax);
110 auto const deliverMin = tx[~sfDeliverMin];
112 auto const account = tx.getAccountID(sfAccount);
116 if ((mptDirect && dstAmount.
asset() != maxSourceAmount.
asset()) ||
119 JLOG(j.trace()) <<
"Malformed transaction: inconsistent issues: "
122 << deliverMin.value_or(
STAmount{}).getFullText();
126 auto const& srcAsset = maxSourceAmount.
asset();
127 auto const& dstAsset = dstAmount.
asset();
129 bool const xrpDirect = srcAsset.
native() && dstAsset.native();
134 auto const dstAccountID = tx.getAccountID(sfDestination);
138 JLOG(j.trace()) <<
"Malformed transaction: "
139 <<
"Payment destination account not specified.";
142 if (hasMax && maxSourceAmount <= beast::zero)
144 JLOG(j.trace()) <<
"Malformed transaction: bad max amount: "
148 if (dstAmount <= beast::zero)
150 JLOG(j.trace()) <<
"Malformed transaction: bad dst amount: "
156 JLOG(j.trace()) <<
"Malformed transaction: Bad currency.";
159 if (account == dstAccountID &&
equalTokens(srcAsset, dstAsset) && !hasPaths)
163 JLOG(j.trace()) <<
"Malformed transaction: "
164 <<
"Redundant payment from " <<
to_string(account)
165 <<
" to self without path for " <<
to_string(dstAsset);
168 if (xrpDirect && hasMax)
171 JLOG(j.trace()) <<
"Malformed transaction: "
172 <<
"SendMax specified for XRP to XRP.";
175 if ((xrpDirect || mptDirect) && hasPaths)
178 JLOG(j.trace()) <<
"Malformed transaction: "
179 <<
"Paths specified for XRP to XRP or MPT to MPT.";
182 if (xrpDirect && partialPaymentAllowed)
185 JLOG(j.trace()) <<
"Malformed transaction: "
186 <<
"Partial payment specified for XRP to XRP.";
189 if ((xrpDirect || mptDirect) && limitQuality)
193 <<
"Malformed transaction: "
194 <<
"Limit quality specified for XRP to XRP or MPT to MPT.";
197 if ((xrpDirect || mptDirect) && !defaultPathsAllowed)
201 <<
"Malformed transaction: "
202 <<
"No ripple direct specified for XRP to XRP or MPT to MPT.";
208 if (!partialPaymentAllowed)
210 JLOG(j.trace()) <<
"Malformed transaction: Partial payment not "
212 << jss::DeliverMin.c_str() <<
".";
216 auto const dMin = *deliverMin;
220 <<
"Malformed transaction: Invalid " << jss::DeliverMin.c_str()
221 <<
" amount. " << dMin.getFullText();
224 if (dMin.asset() != dstAmount.
asset())
227 <<
"Malformed transaction: Dst issue differs "
229 << jss::DeliverMin.c_str() <<
". " << dMin.getFullText();
232 if (dMin > dstAmount)
235 <<
"Malformed transaction: Dst amount less than "
236 << jss::DeliverMin.c_str() <<
". " << dMin.getFullText();
251 auto const delegate = tx[~sfDelegate];
256 auto const sle =
view.
read(delegateKey);
272 auto const& amountAsset = dstAmount.
asset();
274 tx[sfSendMax].asset() != amountAsset)
277 if (granularPermissions.
contains(PaymentMint) && !
isXRP(amountAsset) &&
278 amountAsset.getIssuer() == tx[sfAccount])
281 if (granularPermissions.
contains(PaymentBurn) && !
isXRP(amountAsset) &&
282 amountAsset.getIssuer() == tx[sfDestination])
294 auto const& amountIssue = dstAmount.issue();
295 if (granularPermissions.
contains(PaymentMint) && !
isXRP(amountIssue) &&
296 amountIssue.account == tx[sfAccount])
299 if (granularPermissions.
contains(PaymentBurn) && !
isXRP(amountIssue) &&
300 amountIssue.account == tx[sfDestination])
313 auto const sendMax = ctx.
tx[~sfSendMax];
315 AccountID const dstAccountID(ctx.
tx[sfDestination]);
319 auto const sleDst = ctx.
view.
read(k);
327 <<
"Delay transaction: Destination account does not exist.";
333 else if (ctx.
view.
open() && partialPaymentAllowed)
337 JLOG(ctx.
j.
trace()) <<
"Delay transaction: Partial payment not "
338 "allowed to create account.";
349 <<
"Delay transaction: Destination account does not exist. "
350 <<
"Insufficent payment to create account.";
368 <<
"Malformed transaction: DestinationTag required.";
374 if ((hasPaths || sendMax || !dstAmount.
native()) && ctx.
view.
open())
380 return path.size() > MaxPathLength;
395 ctx.
view, ctx.
tx[sfAccount], ctx.
tx[sfDomainID]))
399 ctx.
view, ctx.
tx[sfDestination], ctx.
tx[sfDomainID]))
409 auto const deliverMin =
ctx_.
tx[~sfDeliverMin];
417 auto const sendMax =
ctx_.
tx[~sfSendMax];
425 JLOG(
j_.
trace()) <<
"maxSourceAmount=" << maxSourceAmount.getFullText()
440 sleDst->setAccountID(sfAccount, dstAccountID);
441 sleDst->setFieldU32(sfSequence, seqno);
455 bool const reqDepositAuth =
461 (hasPaths || sendMax || !dstAmount.
native()) && !mptDirect;
465 if (!depositPreauth &&
ripple && reqDepositAuth)
473 if (depositPreauth && depositAuth)
500 JLOG(
j_.
debug()) <<
"Entering RippleCalc in payment: "
528 auto terResult = rc.
result();
566 auto const& issuer = mptIssue.
getIssuer();
569 Rate rate{QUALITY_ONE};
571 if (
account_ != issuer && dstAccountID != issuer)
593 if (partialPaymentAllowed && requiredMaxSourceAmount > maxSourceAmount)
595 requiredMaxSourceAmount = maxSourceAmount;
597 amountDeliver =
divide(maxSourceAmount, rate);
600 if (requiredMaxSourceAmount > maxSourceAmount ||
601 (deliverMin && amountDeliver < *deliverMin))
614 if (
view().rules().enabled(fixMPTDeliveredAmount) &&
615 amountDeliver != dstAmount)
624 XRPL_ASSERT(dstAmount.
native(),
"ripple::Payment::doApply : amount is XRP");
634 auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
648 JLOG(
j_.
trace()) <<
"Delay transaction: Insufficient funds: "
690 if (dstAmount > dstReserve ||
691 sleDst->getFieldAmount(sfBalance) > dstReserve)
707 sleDst->setFieldAmount(
708 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 std::size_t const MaxPathSize
static TER checkPermission(ReadView const &view, STTx const &tx)
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)
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)
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
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.
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
TER checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
@ tecNO_DELEGATE_PERMISSION
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)
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
TERSubset< CanCvtToNotTEC > NotTEC
@ 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)