// <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed. TER RippleCalc::rippleCalculate () { JLOG (j_.trace) << "rippleCalc>" << " saMaxAmountReq_:" << saMaxAmountReq_ << " saDstAmountReq_:" << saDstAmountReq_; TER resultCode = temUNCERTAIN; permanentlyUnfundedOffers_.clear (); mumSource_.clear (); // YYY Might do basic checks on src and dst validity as per doPayment. // Incrementally search paths. if (inputFlags.defaultPathsAllowed) { if (!addPathState (STPath(), resultCode)) return resultCode; } else if (spsPaths_.empty ()) { JLOG (j_.debug) << "rippleCalc: Invalid transaction:" << "No paths and direct ripple not allowed."; return temRIPPLE_EMPTY; } // Build a default path. Use saDstAmountReq_ and saMaxAmountReq_ to imply // nodes. // XXX Might also make a XRP bridge by default. JLOG (j_.trace) << "rippleCalc: Paths in set: " << spsPaths_.size (); // Now expand the path state. for (auto const& spPath: spsPaths_) { if (!addPathState (spPath, resultCode)) return resultCode; } if (resultCode != tesSUCCESS) return (resultCode == temUNCERTAIN) ? terNO_LINE : resultCode; resultCode = temUNCERTAIN; actualAmountIn_ = saMaxAmountReq_.zeroed(); actualAmountOut_ = saDstAmountReq_.zeroed(); // When processing, we don't want to complicate directory walking with // deletion. const std::uint64_t uQualityLimit = inputFlags.limitQuality ? getRate (saDstAmountReq_, saMaxAmountReq_) : 0; // Offers that became unfunded. std::set<uint256> unfundedOffersFromBestPaths; int iPass = 0; while (resultCode == temUNCERTAIN) { int iBest = -1; int iDry = 0; // True, if ever computed multi-quality. bool multiQuality = false; // Find the best path. for (auto pathState : pathStateList_) { if (pathState->quality()) // Only do active paths. { // If computing the only non-dry path, compute multi-quality. multiQuality = ((pathStateList_.size () - iDry) == 1); // Update to current amount processed. pathState->reset (actualAmountIn_, actualAmountOut_); // Error if done, output met. PathCursor pc(*this, *pathState, multiQuality, j_); pc.nextIncrement (); // Compute increment. JLOG (j_.debug) << "rippleCalc: AFTER:" << " mIndex=" << pathState->index() << " uQuality=" << pathState->quality() << " rate=" << amountFromRate (pathState->quality()); if (!pathState->quality()) { // Path was dry. ++iDry; } else if (pathState->outPass() == zero) { // Path is not dry, but moved no funds // This should never happen. Consider the path dry JLOG (j_.warning) << "rippelCalc: Non-dry path moves no funds"; assert (false); pathState->setQuality (0); ++iDry; } else { CondLog (!pathState->inPass() || !pathState->outPass(), lsDEBUG, RippleCalc) << "rippleCalc: better:" << " uQuality=" << amountFromRate (pathState->quality()) << " inPass()=" << pathState->inPass() << " saOutPass="******"rippleCalc: better:" << " mIndex=" << pathState->index() << " uQuality=" << pathState->quality() << " rate=" << amountFromRate (pathState->quality()) << " inPass()=" << pathState->inPass() << " saOutPass="******"rippleCalc: Summary:" << " Pass: "******" Dry: " << iDry << " Paths: " << pathStateList_.size (); for (auto pathState: pathStateList_) { JLOG (j_.debug) << "rippleCalc: " << "Summary: " << pathState->index() << " rate: " << amountFromRate (pathState->quality()) << " quality:" << pathState->quality() << " best: " << (iBest == pathState->index ()); } } if (iBest >= 0) { // Apply best path. auto pathState = pathStateList_[iBest]; JLOG (j_.debug) << "rippleCalc: best:" << " uQuality=" << amountFromRate (pathState->quality()) << " inPass()=" << pathState->inPass() << " saOutPass="******"rippleCalc: TOO MUCH:" << " actualAmountOut_:" << actualAmountOut_ << " saDstAmountReq_:" << saDstAmountReq_; return tefEXCEPTION; // TEMPORARY assert (false); } else if (actualAmountIn_ != saMaxAmountReq_ && iDry != pathStateList_.size ()) { // Have not met requested amount or max send, try to do // more. Prepare for next pass. // // Merge best pass' umReverse. mumSource_.insert ( pathState->reverse().begin (), pathState->reverse().end ()); if (iPass >= PAYMENT_MAX_LOOPS) { // This payment is taking too many passes JLOG (j_.error) << "rippleCalc: pass limit"; resultCode = telFAILED_PROCESSING; } } else if (!inputFlags.partialPaymentAllowed) { // Have sent maximum allowed. Partial payment not allowed. resultCode = tecPATH_PARTIAL; } else { // Have sent maximum allowed. Partial payment allowed. Success. resultCode = tesSUCCESS; } } // Not done and ran out of paths. else if (!inputFlags.partialPaymentAllowed) { // Partial payment not allowed. resultCode = tecPATH_PARTIAL; } // Partial payment ok. else if (!actualAmountOut_) { // No payment at all. resultCode = tecPATH_DRY; } else { // Don't apply any payment increments resultCode = tesSUCCESS; } } if (resultCode == tesSUCCESS) { auto viewJ = logs_.journal ("View"); resultCode = deleteOffers(view, unfundedOffersFromBestPaths, viewJ); if (resultCode == tesSUCCESS) resultCode = deleteOffers(view, permanentlyUnfundedOffers_, viewJ); } // If isOpenLedger, then ledger is not final, can vote no. if (resultCode == telFAILED_PROCESSING && !inputFlags.isLedgerOpen) return tecFAILED_PROCESSING; return resultCode; }
// <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed. TER DivvyCalc::divvyCalculate () { assert (mActiveLedger.isValid ()); WriteLog (lsTRACE, DivvyCalc) << "divvyCalc>" << " saMaxAmountReq_:" << saMaxAmountReq_ << " saDstAmountReq_:" << saDstAmountReq_; TER resultCode = temUNCERTAIN; permanentlyUnfundedOffers_.clear (); mumSource_.clear (); // YYY Might do basic checks on src and dst validity as per doPayment. // Incrementally search paths. if (inputFlags.defaultPathsAllowed) { if (!addPathState (STPath(), resultCode)) return resultCode; } else if (spsPaths_.empty ()) { WriteLog (lsDEBUG, DivvyCalc) << "divvyCalc: Invalid transaction:" << "No paths and direct divvy not allowed."; return temRIPPLE_EMPTY; } // Build a default path. Use saDstAmountReq_ and saMaxAmountReq_ to imply // nodes. // XXX Might also make a XDV bridge by default. WriteLog (lsTRACE, DivvyCalc) << "divvyCalc: Paths in set: " << spsPaths_.size (); // Now expand the path state. for (auto const& spPath: spsPaths_) { if (!addPathState (spPath, resultCode)) return resultCode; } if (resultCode != tesSUCCESS) return (resultCode == temUNCERTAIN) ? terNO_LINE : resultCode; resultCode = temUNCERTAIN; actualAmountIn_ = saMaxAmountReq_.zeroed(); actualAmountOut_ = saDstAmountReq_.zeroed(); // When processing, we don't want to complicate directory walking with // deletion. const std::uint64_t uQualityLimit = inputFlags.limitQuality ? getRate (saDstAmountReq_, saMaxAmountReq_) : 0; // Offers that became unfunded. OfferSet unfundedOffersFromBestPaths; int iPass = 0; while (resultCode == temUNCERTAIN) { int iBest = -1; LedgerEntrySet lesCheckpoint = mActiveLedger; int iDry = 0; // True, if ever computed multi-quality. bool multiQuality = false; // Find the best path. for (auto pathState : pathStateList_) { if (pathState->quality()) // Only do active paths. { // If computing the only non-dry path, compute multi-quality. multiQuality = ((pathStateList_.size () - iDry) == 1); // Update to current amount processed. pathState->reset (actualAmountIn_, actualAmountOut_); // Error if done, output met. PathCursor pc(*this, *pathState, multiQuality); pc.nextIncrement (lesCheckpoint); // Compute increment. WriteLog (lsDEBUG, DivvyCalc) << "divvyCalc: AFTER:" << " mIndex=" << pathState->index() << " uQuality=" << pathState->quality() << " rate=" << amountFromRate (pathState->quality()); if (!pathState->quality()) { // Path was dry. ++iDry; } else if (pathState->outPass() == zero) { // Path is not dry, but moved no funds // This should never happen. Consider the path dry WriteLog (lsWARNING, DivvyCalc) << "rippelCalc: Non-dry path moves no funds"; assert (false); pathState->setQuality (0); ++iDry; } else { CondLog (!pathState->inPass() || !pathState->outPass(), lsDEBUG, DivvyCalc) << "divvyCalc: better:" << " uQuality=" << amountFromRate (pathState->quality()) << " inPass()=" << pathState->inPass() << " saOutPass="******"divvyCalc: better:" << " mIndex=" << pathState->index() << " uQuality=" << pathState->quality() << " rate=" << amountFromRate (pathState->quality()) << " inPass()=" << pathState->inPass() << " saOutPass="******"divvyCalc: Summary:" << " Pass: "******" Dry: " << iDry << " Paths: " << pathStateList_.size (); for (auto pathState: pathStateList_) { WriteLog (lsDEBUG, DivvyCalc) << "divvyCalc: " << "Summary: " << pathState->index() << " rate: " << amountFromRate (pathState->quality()) << " quality:" << pathState->quality() << " best: " << (iBest == pathState->index ()); } } if (iBest >= 0) { // Apply best path. auto pathState = pathStateList_[iBest]; WriteLog (lsDEBUG, DivvyCalc) << "divvyCalc: best:" << " uQuality=" << amountFromRate (pathState->quality()) << " inPass()=" << pathState->inPass() << " saOutPass="******"divvyCalc: TOO MUCH:" << " actualAmountOut_:" << actualAmountOut_ << " saDstAmountReq_:" << saDstAmountReq_; return tefEXCEPTION; // TEMPORARY assert (false); } else if (actualAmountIn_ != saMaxAmountReq_ && iDry != pathStateList_.size ()) { // Have not met requested amount or max send, try to do // more. Prepare for next pass. // // Merge best pass' umReverse. mumSource_.insert ( pathState->reverse().begin (), pathState->reverse().end ()); if (iPass >= PAYMENT_MAX_LOOPS) { // This payment is taking too many passes WriteLog (lsERROR, DivvyCalc) << "divvyCalc: pass limit"; resultCode = telFAILED_PROCESSING; } } else if (!inputFlags.partialPaymentAllowed) { // Have sent maximum allowed. Partial payment not allowed. resultCode = tecPATH_PARTIAL; } else { // Have sent maximum allowed. Partial payment allowed. Success. resultCode = tesSUCCESS; } } // Not done and ran out of paths. else if (!inputFlags.partialPaymentAllowed) { // Partial payment not allowed. resultCode = tecPATH_PARTIAL; } // Partial payment ok. else if (!actualAmountOut_) { // No payment at all. resultCode = tecPATH_DRY; } else { // We must restore the activeLedger from lesCheckpoint in the case // when iBest is -1 and just before the result is set to tesSUCCESS. mActiveLedger.swapWith (lesCheckpoint); resultCode = tesSUCCESS; } } if (resultCode == tesSUCCESS) { resultCode = deleteOffers(mActiveLedger, unfundedOffersFromBestPaths); if (resultCode == tesSUCCESS) resultCode = deleteOffers(mActiveLedger, permanentlyUnfundedOffers_); } // If isOpenLedger, then ledger is not final, can vote no. if (resultCode == telFAILED_PROCESSING && !inputFlags.isLedgerOpen) return tecFAILED_PROCESSING; return resultCode; }