//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== SETUP_LOG (PaymentTransactor) #define RIPPLE_PATHS_MAX 6 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 (!saDstAmount.isLegalNet () || !saMaxAmount.isLegalNet ()) return temBAD_AMOUNT; 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