1#include <xrpl/ledger/View.h>
2#include <xrpl/ledger/helpers/AccountRootHelpers.h>
3#include <xrpl/ledger/helpers/CredentialHelpers.h>
4#include <xrpl/ledger/helpers/MPTokenHelpers.h>
5#include <xrpl/ledger/helpers/TokenHelpers.h>
6#include <xrpl/protocol/Feature.h>
7#include <xrpl/protocol/Quality.h>
8#include <xrpl/protocol/Rate.h>
9#include <xrpl/protocol/TxFlags.h>
10#include <xrpl/protocol/jss.h>
11#include <xrpl/tx/paths/RippleCalc.h>
12#include <xrpl/tx/transactors/delegate/DelegateUtils.h>
13#include <xrpl/tx/transactors/dex/PermissionedDEXHelpers.h>
14#include <xrpl/tx/transactors/payment/Payment.h>
22 STAmount const maxAmount = tx.isFieldPresent(sfSendMax) ? tx[sfSendMax] : tx[sfAmount];
26 return maxAmount.
native() ? maxAmount.
xrp() : beast::zero;
51 dstAmount < beast::zero);
70 STAmount const dstAmount(tx.getFieldAmount(sfAmount));
82 STAmount const dstAmount(tx.getFieldAmount(sfAmount));
93 bool const partialPaymentAllowed = (txFlags & tfPartialPayment) != 0u;
94 bool const limitQuality = (txFlags & tfLimitQuality) != 0u;
95 bool const defaultPathsAllowed = (txFlags & tfNoRippleDirect) == 0u;
96 bool const hasPaths = tx.isFieldPresent(sfPaths);
97 bool const hasMax = tx.isFieldPresent(sfSendMax);
99 auto const deliverMin = tx[~sfDeliverMin];
101 auto const account = tx.getAccountID(sfAccount);
104 if ((mptDirect && dstAmount.
asset() != maxSourceAmount.
asset()) ||
107 JLOG(j.trace()) <<
"Malformed transaction: inconsistent issues: " << dstAmount.
getFullText()
109 << deliverMin.value_or(
STAmount{}).getFullText();
113 auto const& srcAsset = maxSourceAmount.
asset();
114 auto const& dstAsset = dstAmount.
asset();
116 bool const xrpDirect = srcAsset.
native() && dstAsset.native();
121 auto const dstAccountID = tx.getAccountID(sfDestination);
125 JLOG(j.trace()) <<
"Malformed transaction: "
126 <<
"Payment destination account not specified.";
129 if (hasMax && maxSourceAmount <= beast::zero)
131 JLOG(j.trace()) <<
"Malformed transaction: bad max amount: "
135 if (dstAmount <= beast::zero)
137 JLOG(j.trace()) <<
"Malformed transaction: bad dst amount: " << dstAmount.
getFullText();
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)
178 JLOG(j.trace()) <<
"Malformed transaction: "
179 <<
"Limit quality specified for XRP to XRP or MPT to MPT.";
182 if ((xrpDirect || mptDirect) && !defaultPathsAllowed)
185 JLOG(j.trace()) <<
"Malformed transaction: "
186 <<
"No ripple direct specified for XRP to XRP or MPT to MPT.";
192 if (!partialPaymentAllowed)
194 JLOG(j.trace()) <<
"Malformed transaction: Partial payment not "
196 << jss::DeliverMin.c_str() <<
".";
200 auto const dMin = *deliverMin;
203 JLOG(j.trace()) <<
"Malformed transaction: Invalid " << jss::DeliverMin.c_str()
204 <<
" amount. " << dMin.getFullText();
207 if (dMin.asset() != dstAmount.
asset())
209 JLOG(j.trace()) <<
"Malformed transaction: Dst issue differs "
211 << jss::DeliverMin.c_str() <<
". " << dMin.getFullText();
214 if (dMin > dstAmount)
216 JLOG(j.trace()) <<
"Malformed transaction: Dst amount less than "
217 << jss::DeliverMin.c_str() <<
". " << dMin.getFullText();
231 auto const delegate = tx[~sfDelegate];
236 auto const sle =
view.
read(delegateKey);
248 auto const& amountAsset = dstAmount.
asset();
251 if ((tx.
isFieldPresent(sfSendMax) && tx[sfSendMax].asset() != amountAsset) ||
255 if (granularPermissions.
contains(PaymentMint) && !
isXRP(amountAsset) &&
256 amountAsset.getIssuer() == tx[sfAccount])
259 if (granularPermissions.
contains(PaymentBurn) && !
isXRP(amountAsset) &&
260 amountAsset.getIssuer() == tx[sfDestination])
271 bool const partialPaymentAllowed = (txFlags & tfPartialPayment) != 0u;
273 auto const sendMax = ctx.
tx[~sfSendMax];
275 AccountID const dstAccountID(ctx.
tx[sfDestination]);
279 auto const sleDst = ctx.
view.
read(k);
286 JLOG(ctx.
j.
trace()) <<
"Delay transaction: Destination account does not exist.";
292 if (ctx.
view.
open() && partialPaymentAllowed)
296 JLOG(ctx.
j.
trace()) <<
"Delay transaction: Partial payment not "
297 "allowed to create account.";
307 JLOG(ctx.
j.
trace()) <<
"Delay transaction: Destination account does not exist. "
308 <<
"Insufficent payment to create account.";
317 ((sleDst->getFlags() & lsfRequireDestTag) != 0u) &&
325 JLOG(ctx.
j.
trace()) <<
"Malformed transaction: DestinationTag required.";
331 if ((hasPaths || sendMax || !dstAmount.
native()) && ctx.
view.
open())
337 return path.size() > MaxPathLength;
363 auto const deliverMin =
ctx_.
tx[~sfDeliverMin];
367 bool const partialPaymentAllowed = (txFlags & tfPartialPayment) != 0u;
368 bool const limitQuality = (txFlags & tfLimitQuality) != 0u;
369 bool const defaultPathsAllowed = (txFlags & tfNoRippleDirect) == 0u;
371 auto const sendMax =
ctx_.
tx[~sfSendMax];
378 JLOG(
j_.
trace()) <<
"maxSourceAmount=" << maxSourceAmount.getFullText()
389 sleDst->setAccountID(sfAccount, dstAccountID);
390 sleDst->setFieldU32(sfSequence,
view().seq());
402 bool const ripple = (hasPaths || sendMax || !dstAmount.
native()) && !mptDirect;
459 auto terResult = rc.
result();
489 auto const& issuer = mptIssue.
getIssuer();
492 Rate rate{QUALITY_ONE};
494 if (
account_ != issuer && dstAccountID != issuer)
516 if (partialPaymentAllowed && requiredMaxSourceAmount > maxSourceAmount)
518 requiredMaxSourceAmount = maxSourceAmount;
520 amountDeliver =
divide(maxSourceAmount, rate);
523 if (requiredMaxSourceAmount > maxSourceAmount ||
524 (deliverMin && amountDeliver < *deliverMin))
536 if (
view().rules().enabled(fixMPTDeliveredAmount) && amountDeliver != dstAmount)
547 XRPL_ASSERT(dstAmount.
native(),
"xrpl::Payment::doApply : amount is XRP");
557 auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
569 auto const minRequiredFunds =
577 <<
" / " <<
to_string(dstAmount.
xrp() + minRequiredFunds) <<
" ("
615 if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve)
624 sleSrc->setFieldAmount(sfBalance, sleSrc->getFieldAmount(sfBalance) - dstAmount);
625 sleDst->setFieldAmount(sfBalance, sleDst->getFieldAmount(sfBalance) + dstAmount);
628 if ((sleDst->getFlags() & lsfPasswordSpent) != 0u)
629 sleDst->clearFlag(lsfPasswordSpent);
Stream trace() const
Severity stream access functions.
std::reference_wrapper< ServiceRegistry > registry
void deliver(STAmount const &amount)
Sets the DeliveredAmount field in the metadata.
beast::Journal const journal
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 TxConsequences makeTxConsequences(PreflightContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
static std::size_t const MaxPathSize
static TER preclaim(PreclaimContext const &ctx)
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual bool open() const =0
Returns true if this reflects an open ledger.
virtual std::shared_ptr< SLE const > 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 bool holds() const noexcept
constexpr TIss const & get() const
std::string getFullText() const override
std::uint64_t mantissa() const noexcept
bool native() const noexcept
Asset const & asset() const
int exponent() const noexcept
bool isFieldPresent(SField const &field) const
AccountID getAccountID(SField const &field) const
STPathSet const & getFieldPathSet(SField const &field) const
STAmount const & getFieldAmount(SField const &field) const
std::uint32_t getFlags() const
std::vector< STPath >::const_iterator begin() const
std::vector< STPath >::size_type size() const
std::vector< STPath >::const_iterator end() const
AccountID getFeePayer() 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, ServiceRegistry ®istry, 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)
@ terNO_DELEGATE_PERMISSION
bool isTerRetry(TER x) noexcept
bool isXRP(AccountID const &c)
std::string to_string(base_uint< Bits, Tag > const &a)
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE const > const &sleDst, beast::Journal j)
STAmount getMaxSourceAmount(AccountID const &account, STAmount const &dstAmount, std::optional< STAmount > const &sendMax)
bool isLegalNet(STAmount const &value)
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
STAmount multiply(STAmount const &amount, Rate const &rate)
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
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 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.
constexpr FlagValue tfMPTPaymentMask
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
@ temBAD_SEND_XRP_PARTIAL
@ temBAD_SEND_XRP_NO_DIRECT
bool isTesSuccess(TER x) noexcept
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)
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, int depth=0)
Check if the account lacks required authorization for MPT.
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
XRPAmount reserve
Minimum XRP an account must hold to exist on the ledger.
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)