#include "PaymentTransactor.h" #include "Config.h" #include "RippleCalc.h" #include "Application.h" #define RIPPLE_PATHS_MAX 6 SETUP_LOG(); TER PaymentTransactor::doApply() { // Ripple if source or destination is non-native or if there are paths. const uint32 uTxFlags = mTxn.getFlags(); const bool bPartialPayment = isSetBit(uTxFlags, tfPartialPayment); const bool bLimitQuality = isSetBit(uTxFlags, tfLimitQuality); const bool bNoRippleDirect = isSetBit(uTxFlags, tfNoRippleDirect); const bool bPaths = mTxn.isFieldPresent(sfPaths); const bool bMax = mTxn.isFieldPresent(sfSendMax); const uint160 uDstAccountID = mTxn.getFieldAccount160(sfDestination); const STAmount saDstAmount = mTxn.getFieldAmount(sfAmount); const STAmount saMaxAmount = bMax ? mTxn.getFieldAmount(sfSendMax) : saDstAmount.isNative() ? saDstAmount : STAmount(saDstAmount.getCurrency(), mTxnAccountID, saDstAmount.getMantissa(), saDstAmount.getExponent(), saDstAmount.isNegative()); const uint160 uSrcCurrency = saMaxAmount.getCurrency(); const uint160 uDstCurrency = saDstAmount.getCurrency(); const bool bXRPDirect = uSrcCurrency.isZero() && uDstCurrency.isZero(); WriteLog (lsINFO, PaymentTransactor) << boost::str(boost::format("Payment> saMaxAmount=%s saDstAmount=%s") % saMaxAmount.getFullText() % saDstAmount.getFullText()); if (uTxFlags & tfPaymentMask) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Invalid flags set."; return temINVALID_FLAG; } else if (!uDstAccountID) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Payment destination account not specified."; return temDST_NEEDED; } else if (bMax && !saMaxAmount.isPositive()) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: bad max amount: " << saMaxAmount.getFullText(); return temBAD_AMOUNT; } else if (!saDstAmount.isPositive()) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: bad dst amount: " << saDstAmount.getFullText(); return temBAD_AMOUNT; } else if (CURRENCY_BAD == uSrcCurrency || CURRENCY_BAD == uDstCurrency) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Bad currency."; return temBAD_CURRENCY; } else if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths) { WriteLog (lsINFO, PaymentTransactor) << boost::str(boost::format("Payment: Malformed transaction: Redundant transaction: src=%s, dst=%s, src_cur=%s, dst_cur=%s") % mTxnAccountID.ToString() % uDstAccountID.ToString() % uSrcCurrency.ToString() % uDstCurrency.ToString()); return temREDUNDANT; } else if (bMax && saMaxAmount == saDstAmount && saMaxAmount.getCurrency() == saDstAmount.getCurrency()) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Redundant SendMax."; return temREDUNDANT_SEND_MAX; } else if (bXRPDirect && bMax) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: SendMax specified for XRP to XRP."; return temBAD_SEND_XRP_MAX; } else if (bXRPDirect && bPaths) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Paths specified for XRP to XRP."; return temBAD_SEND_XRP_PATHS; } else if (bXRPDirect && bPartialPayment) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Partial payment specified for XRP to XRP."; return temBAD_SEND_XRP_PARTIAL; } else if (bXRPDirect && bLimitQuality) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Limit quality specified for XRP to XRP."; return temBAD_SEND_XRP_LIMIT; } else if (bXRPDirect && bNoRippleDirect) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: No ripple direct specified for XRP to XRP."; return temBAD_SEND_XRP_NO_DIRECT; } SLE::pointer sleDst = mEngine->entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); if (!sleDst) { // Destination account does not exist. if (!saDstAmount.isNative()) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Delay transaction: Destination account does not exist."; // Another transaction could create the account and then this transaction would succeed. return tecNO_DST; } else if (isSetBit(mParams, tapOPEN_LEDGER) && bPartialPayment) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Delay transaction: Partial payment not allowed to create account."; // Make retry work smaller, by rejecting this. // Another transaction could create the account and then this transaction would succeed. return telNO_DST_PARTIAL; } else if (saDstAmount.getNValue() < mEngine->getLedger()->getReserve(0)) // Reserve is not scaled by load. { WriteLog (lsINFO, PaymentTransactor) << "Payment: Delay transaction: Destination account does not exist. Insufficent payment to create account."; // Another transaction could create the account and then this transaction would succeed. return tecNO_DST_INSUF_XRP; } // Create the account. sleDst = mEngine->entryCreate(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); sleDst->setFieldAccount(sfAccount, uDstAccountID); sleDst->setFieldU32(sfSequence, 1); } else if ((sleDst->getFlags() & lsfRequireDestTag) && !mTxn.isFieldPresent(sfDestinationTag)) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: DestinationTag required."; return tefDST_TAG_NEEDED; } else { mEngine->entryModify(sleDst); } TER terResult; // XXX Should bMax be sufficient to imply ripple? const bool bRipple = bPaths || bMax || !saDstAmount.isNative(); if (bRipple) { // Ripple payment STPathSet spsPaths = mTxn.getFieldPathSet(sfPaths); std::vector vpsExpanded; STAmount saMaxAmountAct; STAmount saDstAmountAct; try { terResult = isSetBit(mParams, tapOPEN_LEDGER) && spsPaths.size() > RIPPLE_PATHS_MAX ? telBAD_PATH_COUNT // Too many paths for proposed ledger. : RippleCalc::rippleCalc( mEngine->getNodes(), saMaxAmountAct, saDstAmountAct, vpsExpanded, saMaxAmount, saDstAmount, uDstAccountID, mTxnAccountID, spsPaths, bPartialPayment, bLimitQuality, bNoRippleDirect, // Always compute for finalizing ledger. false, // Not standalone, delete unfundeds. isSetBit(mParams, tapOPEN_LEDGER)); } catch (const std::exception& e) { WriteLog (lsINFO, PaymentTransactor) << "Payment: Caught throw: " << e.what(); terResult = tefEXCEPTION; } } else { // Direct XRP payment. const uint32 uOwnerCount = mTxnAccount->getFieldU32(sfOwnerCount); const uint64 uReserve = mEngine->getLedger()->getReserve(uOwnerCount); // Make sure have enough reserve to send. Allow final spend to use reserve for fee. if (mPriorBalance < saDstAmount + uReserve) // Reserve is not scaled by fee. { // Vote no. However, transaction might succeed, if applied in a different order. WriteLog (lsINFO, PaymentTransactor) << ""; WriteLog (lsINFO, PaymentTransactor) << boost::str(boost::format("Payment: Delay transaction: Insufficient funds: %s / %s (%d)") % mPriorBalance.getText() % (saDstAmount + uReserve).getText() % uReserve); terResult = tecUNFUNDED_PAYMENT; } else { mTxnAccount->setFieldAmount(sfBalance, mSourceBalance - saDstAmount); sleDst->setFieldAmount(sfBalance, sleDst->getFieldAmount(sfBalance) + saDstAmount); // re-arm the password change fee if we can and need to if ((sleDst->getFlags() & lsfPasswordSpent)) sleDst->clearFlag(lsfPasswordSpent); terResult = tesSUCCESS; } } std::string strToken; std::string strHuman; if (transResultInfo(terResult, strToken, strHuman)) { WriteLog (lsINFO, PaymentTransactor) << boost::str(boost::format("Payment: %s: %s") % strToken % strHuman); } else { assert(false); } return terResult; } // vim:ts=4