diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index 8522baa7d8..835772aee9 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -457,25 +457,12 @@ else () src/ripple/app/paths/AccountCurrencies.cpp src/ripple/app/paths/Credit.cpp src/ripple/app/paths/Flow.cpp - src/ripple/app/paths/Node.cpp src/ripple/app/paths/PathRequest.cpp src/ripple/app/paths/PathRequests.cpp - src/ripple/app/paths/PathState.cpp src/ripple/app/paths/Pathfinder.cpp src/ripple/app/paths/RippleCalc.cpp src/ripple/app/paths/RippleLineCache.cpp src/ripple/app/paths/RippleState.cpp - src/ripple/app/paths/cursor/AdvanceNode.cpp - src/ripple/app/paths/cursor/DeliverNodeForward.cpp - src/ripple/app/paths/cursor/DeliverNodeReverse.cpp - src/ripple/app/paths/cursor/EffectiveRate.cpp - src/ripple/app/paths/cursor/ForwardLiquidity.cpp - src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp - src/ripple/app/paths/cursor/Liquidity.cpp - src/ripple/app/paths/cursor/NextIncrement.cpp - src/ripple/app/paths/cursor/ReverseLiquidity.cpp - src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp - src/ripple/app/paths/cursor/RippleLiquidity.cpp src/ripple/app/paths/impl/BookStep.cpp src/ripple/app/paths/impl/DirectStep.cpp src/ripple/app/paths/impl/PaySteps.cpp diff --git a/src/ripple/app/paths/Node.cpp b/src/ripple/app/paths/Node.cpp deleted file mode 100644 index cb215fb6cb..0000000000 --- a/src/ripple/app/paths/Node.cpp +++ /dev/null @@ -1,98 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { -namespace path { - -// Compare the non-calculated fields. -bool Node::operator== (const Node& other) const -{ - return other.uFlags == uFlags - && other.account_ == account_ - && other.issue_ == issue_; -} - -// This is for debugging not end users. Output names can be changed without -// warning. -Json::Value Node::getJson () const -{ - Json::Value jvNode (Json::objectValue); - Json::Value jvFlags (Json::arrayValue); - - jvNode[jss::type] = uFlags; - - bool const hasCurrency = !isXRP (issue_.currency); - bool const hasAccount = !isXRP (account_); - bool const hasIssuer = !isXRP (issue_.account); - - if (isAccount() || hasAccount) - jvFlags.append (!isAccount() == hasAccount ? "account" : "-account"); - - if (uFlags & STPathElement::typeCurrency || hasCurrency) - { - jvFlags.append ((uFlags & STPathElement::typeCurrency) && hasCurrency - ? "currency" - : "-currency"); - } - - if (uFlags & STPathElement::typeIssuer || hasIssuer) - { - jvFlags.append ((uFlags & STPathElement::typeIssuer) && hasIssuer - ? "issuer" - : "-issuer"); - } - - jvNode["flags"] = jvFlags; - - if (!isXRP (account_)) - jvNode[jss::account] = to_string (account_); - - if (!isXRP (issue_.currency)) - jvNode[jss::currency] = to_string (issue_.currency); - - if (!isXRP (issue_.account)) - jvNode[jss::issuer] = to_string (issue_.account); - - if (saRevRedeem) - jvNode["rev_redeem"] = saRevRedeem.getFullText (); - - if (saRevIssue) - jvNode["rev_issue"] = saRevIssue.getFullText (); - - if (saRevDeliver) - jvNode["rev_deliver"] = saRevDeliver.getFullText (); - - if (saFwdRedeem) - jvNode["fwd_redeem"] = saFwdRedeem.getFullText (); - - if (saFwdIssue) - jvNode["fwd_issue"] = saFwdIssue.getFullText (); - - if (saFwdDeliver) - jvNode["fwd_deliver"] = saFwdDeliver.getFullText (); - - return jvNode; -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/Node.h b/src/ripple/app/paths/Node.h deleted file mode 100644 index 50e08fa82f..0000000000 --- a/src/ripple/app/paths/Node.h +++ /dev/null @@ -1,114 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_APP_PATHS_NODE_H_INCLUDED -#define RIPPLE_APP_PATHS_NODE_H_INCLUDED - -#include -#include -#include -#include -#include - -namespace ripple { -namespace path { - -struct Node -{ - explicit Node() = default; - - using List = std::vector; - - inline bool isAccount() const - { - return (uFlags & STPathElement::typeAccount); - } - - Json::Value getJson () const; - - bool operator == (Node const&) const; - - std::uint16_t uFlags; // --> From path. - - AccountID account_; // --> Accounts: Receiving/sending account. - - Issue issue_; // --> Accounts: Receive and send, Offers: send. - // --- For offer's next has currency out. - - boost::optional transferRate_; // Transfer rate for issuer. - - // Computed by Reverse. - STAmount saRevRedeem; // <-- Amount to redeem to next. - STAmount saRevIssue; // <-- Amount to issue to next, limited by - // credit and outstanding IOUs. Issue - // isn't used by offers. - STAmount saRevDeliver; // <-- Amount to deliver to next regardless of - // fee. - - // Computed by forward. - STAmount saFwdRedeem; // <-- Amount node will redeem to next. - STAmount saFwdIssue; // <-- Amount node will issue to next. - // Issue isn't used by offers. - STAmount saFwdDeliver; // <-- Amount to deliver to next regardless of - // fee. - - // For offers: - boost::optional rateMax; - - // The nodes are partitioned into a buckets called "directories". - // - // Each "directory" contains nodes with exactly the same "quality" (meaning - // the conversion rate between one corrency and the next). - // - // The "directories" are ordered in "increasing" "quality" value, which - // means that the first "directory" has the "best" (i.e. numerically least) - // "quality". - // https://ripple.com/wiki/Ledger_Format#Prioritizing_a_continuous_key_space - - NodeDirectory directory; - - STAmount saOfrRate; // For correct ratio. - - // PaymentNode - bool bEntryAdvance; // Need to advance entry. - unsigned int uEntry; - uint256 offerIndex_; - SLE::pointer sleOffer; - AccountID offerOwnerAccount_; - - // Do we need to refresh saOfferFunds, saTakerPays, & saTakerGets? - bool bFundsDirty; - STAmount saOfferFunds; - STAmount saTakerPays; - STAmount saTakerGets; - - /** Clear input and output amounts. */ - void clear() - { - saRevRedeem.clear (); - saRevIssue.clear (); - saRevDeliver.clear (); - saFwdDeliver.clear (); - } -}; - -} // path -} // ripple - -#endif diff --git a/src/ripple/app/paths/PathState.cpp b/src/ripple/app/paths/PathState.cpp deleted file mode 100644 index 2202b7285e..0000000000 --- a/src/ripple/app/paths/PathState.cpp +++ /dev/null @@ -1,861 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include -#include -#include -#include -#include - -namespace ripple { - -// OPTIMIZE: When calculating path increment, note if increment consumes all -// liquidity. No need to revisit path in the future if all liquidity is used. -// - -class RippleCalc; // for logging - -void PathState::clear() -{ - saInPass = saInReq.zeroed(); - saOutPass = saOutReq.zeroed(); - unfundedOffers_.clear (); - umReverse.clear (); - - for (auto& node: nodes_) - node.clear(); -} - -void PathState::reset(STAmount const& in, STAmount const& out) -{ - clear(); - - // Update to current amount processed. - saInAct = in; - saOutAct = out; - - if (inReq() > beast::zero && inAct() >= inReq()) - { - JLOG (j_.warn()) - << "rippleCalc: DONE:" - << " inAct()=" << inAct() - << " inReq()=" << inReq(); - } - - assert (inReq() < beast::zero || inAct() < inReq()); - // Error if done. - - if (outAct() >= outReq()) - { - JLOG (j_.warn()) - << "rippleCalc: ALREADY DONE:" - << " saOutAct=" << outAct() - << " saOutReq=" << outReq(); - } - - assert(outAct() < outReq()); - assert (nodes().size () >= 2); -} - -// Return true, iff lhs has less priority than rhs. -bool PathState::lessPriority (PathState const& lhs, PathState const& rhs) -{ - // First rank is quality. - if (lhs.uQuality != rhs.uQuality) - return lhs.uQuality > rhs.uQuality; // Bigger is worse. - - // Second rank is best quantity. - if (lhs.saOutPass != rhs.saOutPass) - return lhs.saOutPass < rhs.saOutPass; // Smaller is worse. - - // Third rank is path index. - return lhs.mIndex > rhs.mIndex; // Bigger is worse. -} - -// Make sure last path node delivers to account_: currency from.issue_.account. -// -// If the unadded next node as specified by arguments would not work as is, then -// add the necessary nodes so it would work. -// PRECONDITION: the PathState must be non-empty. -// -// Rules: -// - Currencies must be converted via an offer. -// - A node names its output. - -// - A ripple nodes output issuer must be the node's account or the next node's -// account. -// - Offers can only go directly to another offer if the currency and issuer are -// an exact match. -// - Real issuers must be specified for non-XRP. -TER PathState::pushImpliedNodes ( - AccountID const& account, // --> Delivering to this account. - Currency const& currency, // --> Delivering this currency. - AccountID const& issuer) // --> Delivering this issuer. -{ - TER resultCode = tesSUCCESS; - - JLOG (j_.trace()) << "pushImpliedNodes>" << - " " << account << - " " << currency << - " " << issuer; - - if (nodes_.back ().issue_.currency != currency) - { - // Currency is different, need to convert via an offer from an order - // book. xrpAccount() does double duty as signaling "this is an order - // book". - - // Corresponds to "Implies an offer directory" in the diagram, currently - // at http://goo.gl/Uj3HAB. - - auto type = isXRP(currency) ? STPathElement::typeCurrency - : STPathElement::typeCurrency | STPathElement::typeIssuer; - - // The offer's output is what is now wanted. - // xrpAccount() is a placeholder for offers. - resultCode = pushNode (type, xrpAccount(), currency, issuer); - } - - - // For ripple, non-XRP, ensure the issuer is on at least one side of the - // transaction. - if (resultCode == tesSUCCESS - && !isXRP(currency) - && nodes_.back ().account_ != issuer - // Previous is not issuing own IOUs. - && account != issuer) - // Current is not receiving own IOUs. - { - // Need to ripple through issuer's account. - // Case "Implies an another node: (pushImpliedNodes)" in the document. - // Intermediate account is the needed issuer. - resultCode = pushNode ( - STPathElement::typeAll, issuer, currency, issuer); - } - - JLOG (j_.trace()) - << "pushImpliedNodes< : " << transToken (resultCode); - - return resultCode; -} - -// Append a node, then create and insert before it any implied nodes. Order -// book nodes may go back to back. -// -// For each non-matching pair of IssuedCurrency, there's an order book. -// -// <-- resultCode: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH, -// terNO_LINE, tecPATH_DRY -TER PathState::pushNode ( - const int iType, - AccountID const& account, // If not specified, means an order book. - Currency const& currency, // If not specified, default to previous. - AccountID const& issuer) // If not specified, default to previous. -{ - path::Node node; - const bool pathIsEmpty = nodes_.empty (); - - // TODO(tom): if pathIsEmpty, we probably don't need to do ANYTHING below. - // Indeed, we might just not even call pushNode in the first place! - - auto const& backNodeOuter = pathIsEmpty ? path::Node () : nodes_.back (); - - // true, iff node is a ripple account. false, iff node is an offer node. - const bool hasAccount = (iType & STPathElement::typeAccount); - - // Is currency specified for the output of the current node? - const bool hasCurrency = (iType & STPathElement::typeCurrency); - - // Issuer is specified for the output of the current node. - const bool hasIssuer = (iType & STPathElement::typeIssuer); - - TER resultCode = tesSUCCESS; - - JLOG (j_.trace()) - << "pushNode> " << iType << ": " - << (hasAccount ? to_string(account) : std::string("-")) << " " - << (hasCurrency ? to_string(currency) : std::string("-")) << "/" - << (hasIssuer ? to_string(issuer) : std::string("-")) << "/"; - - node.uFlags = iType; - node.issue_.currency = hasCurrency ? - currency : backNodeOuter.issue_.currency; - - // TODO(tom): we can probably just return immediately whenever we hit an - // error in these next pages. - - if (iType & ~STPathElement::typeAll) - { - // Of course, this could never happen. - JLOG (j_.debug()) << "pushNode: bad bits."; - resultCode = temBAD_PATH; - } - else if (hasIssuer && isXRP (node.issue_)) - { - JLOG (j_.debug()) << "pushNode: issuer specified for XRP."; - - resultCode = temBAD_PATH; - } - else if (hasIssuer && !issuer) - { - JLOG (j_.debug()) << "pushNode: specified bad issuer."; - - resultCode = temBAD_PATH; - } - else if (!hasAccount && !hasCurrency && !hasIssuer) - { - // You can't default everything to the previous node as you would make - // no progress. - JLOG (j_.debug()) - << "pushNode: offer must specify at least currency or issuer."; - resultCode = temBAD_PATH; - } - else if (hasAccount) - { - // Account link - node.account_ = account; - node.issue_.account = hasIssuer ? issuer : - (isXRP (node.issue_) ? xrpAccount() : account); - // Zero value - for accounts. - node.saRevRedeem = STAmount ({node.issue_.currency, account}); - node.saRevIssue = node.saRevRedeem; - - // For order books only - zero currency with the issuer ID. - node.saRevDeliver = STAmount (node.issue_); - node.saFwdDeliver = node.saRevDeliver; - - if (pathIsEmpty) - { - // The first node is always correct as is. - } - else if (!account) - { - JLOG (j_.debug()) - << "pushNode: specified bad account."; - resultCode = temBAD_PATH; - } - else - { - // Add required intermediate nodes to deliver to current account. - JLOG (j_.trace()) - << "pushNode: imply for account."; - - resultCode = pushImpliedNodes ( - node.account_, - node.issue_.currency, - isXRP(node.issue_.currency) ? xrpAccount() : account); - - // Note: backNodeOuter may no longer be the immediately previous node. - } - - if (resultCode == tesSUCCESS && !nodes_.empty ()) - { - auto const& backNode = nodes_.back (); - if (backNode.isAccount()) - { - auto sleRippleState = view().peek( - keylet::line(backNode.account_, node.account_, backNode.issue_.currency)); - - // A "RippleState" means a balance betweeen two accounts for a - // specific currency. - if (!sleRippleState) - { - JLOG (j_.trace()) - << "pushNode: No credit line between " - << backNode.account_ << " and " << node.account_ - << " for " << node.issue_.currency << "." ; - - JLOG (j_.trace()) << getJson (); - - resultCode = terNO_LINE; - } - else - { - JLOG (j_.trace()) - << "pushNode: Credit line found between " - << backNode.account_ << " and " << node.account_ - << " for " << node.issue_.currency << "." ; - - auto sleBck = view().peek ( - keylet::account(backNode.account_)); - // Is the source account the highest numbered account ID? - bool bHigh = backNode.account_ > node.account_; - - if (!sleBck) - { - JLOG (j_.warn()) - << "pushNode: delay: can't receive IOUs from " - << "non-existent issuer: " << backNode.account_; - - resultCode = terNO_ACCOUNT; - } - else if ((sleBck->getFieldU32 (sfFlags) & lsfRequireAuth) && - !(sleRippleState->getFieldU32 (sfFlags) & - (bHigh ? lsfHighAuth : lsfLowAuth)) && - sleRippleState->getFieldAmount(sfBalance) == beast::zero) - { - JLOG (j_.warn()) - << "pushNode: delay: can't receive IOUs from " - << "issuer without auth."; - - resultCode = terNO_AUTH; - } - - if (resultCode == tesSUCCESS) - { - STAmount saOwed = creditBalance (view(), - node.account_, backNode.account_, - node.issue_.currency); - STAmount saLimit; - - if (saOwed <= beast::zero) - { - saLimit = creditLimit (view(), - node.account_, - backNode.account_, - node.issue_.currency); - if (-saOwed >= saLimit) - { - JLOG (j_.debug()) << - "pushNode: dry:" << - " saOwed=" << saOwed << - " saLimit=" << saLimit; - - resultCode = tecPATH_DRY; - } - } - } - } - } - } - - if (resultCode == tesSUCCESS) - nodes_.push_back (node); - } - else - { - // Offer link. - // - // Offers bridge a change in currency and issuer, or just a change in - // issuer. - if (hasIssuer) - node.issue_.account = issuer; - else if (isXRP (node.issue_.currency)) - node.issue_.account = xrpAccount(); - else if (isXRP (backNodeOuter.issue_.account)) - node.issue_.account = backNodeOuter.account_; - else - node.issue_.account = backNodeOuter.issue_.account; - - node.saRevDeliver = STAmount (node.issue_); - node.saFwdDeliver = node.saRevDeliver; - - if (!isConsistent (node.issue_)) - { - JLOG (j_.debug()) - << "pushNode: currency is inconsistent with issuer."; - - resultCode = temBAD_PATH; - } - else if (backNodeOuter.issue_ == node.issue_) - { - JLOG (j_.debug()) << - "pushNode: bad path: offer to same currency and issuer"; - resultCode = temBAD_PATH; - } - else { - JLOG (j_.trace()) << "pushNode: imply for offer."; - - // Insert intermediary issuer account if needed. - resultCode = pushImpliedNodes ( - xrpAccount(), // Rippling, but offers don't have an account. - backNodeOuter.issue_.currency, - backNodeOuter.issue_.account); - } - - if (resultCode == tesSUCCESS) - nodes_.push_back (node); - } - - JLOG (j_.trace()) << "pushNode< : " << transToken (resultCode); - return resultCode; -} - -// Set this object to be an expanded path from spSourcePath - take the implied -// nodes and makes them explicit. It also sanitizes the path. -// -// There are only two types of nodes: account nodes and order books nodes. -// -// You can infer some nodes automatically. If you're paying me bitstamp USD, -// then there must be an intermediate bitstamp node. -// -// If you have accounts A and B, and they're delivery currency issued by C, then -// there must be a node with account C in the middle. -// -// If you're paying USD and getting bitcoins, there has to be an order book in -// between. -// -// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, terNO_ACCOUNT, terNO_AUTH, -// or temBAD_PATH_LOOP -TER PathState::expandPath ( - STPath const& spSourcePath, - AccountID const& uReceiverID, - AccountID const& uSenderID) -{ - uQuality = 1; // Mark path as active. - - Currency const& uMaxCurrencyID = saInReq.getCurrency (); - AccountID const& uMaxIssuerID = saInReq.getIssuer (); - - Currency const& currencyOutID = saOutReq.getCurrency (); - AccountID const& issuerOutID = saOutReq.getIssuer (); - AccountID const& uSenderIssuerID - = isXRP(uMaxCurrencyID) ? xrpAccount() : uSenderID; - // Sender is always issuer for non-XRP. - - JLOG (j_.trace()) - << "expandPath> " << spSourcePath.getJson (JsonOptions::none); - - terStatus = tesSUCCESS; - - // XRP with issuer is malformed. - if ((isXRP (uMaxCurrencyID) && !isXRP (uMaxIssuerID)) - || (isXRP (currencyOutID) && !isXRP (issuerOutID))) - { - JLOG (j_.debug()) - << "expandPath> issuer with XRP"; - terStatus = temBAD_PATH; - } - - // Push sending node. - // For non-XRP, issuer is always sending account. - // - Trying to expand, not-compact. - // - Every issuer will be traversed through. - if (terStatus == tesSUCCESS) - { - terStatus = pushNode ( - !isXRP(uMaxCurrencyID) - ? STPathElement::typeAccount | STPathElement::typeCurrency | - STPathElement::typeIssuer - : STPathElement::typeAccount | STPathElement::typeCurrency, - uSenderID, - uMaxCurrencyID, // Max specifies the currency. - uSenderIssuerID); - } - - JLOG (j_.debug()) - << "expandPath: pushed:" - << " account=" << uSenderID - << " currency=" << uMaxCurrencyID - << " issuer=" << uSenderIssuerID; - - // Issuer was not same as sender. - if (tesSUCCESS == terStatus && uMaxIssuerID != uSenderIssuerID) - { - // May have an implied account node. - // - If it was XRP, then issuers would have matched. - - // Figure out next node properties for implied node. - const auto uNxtCurrencyID = spSourcePath.size () - ? Currency(spSourcePath.front ().getCurrency ()) - // Use next node. - : currencyOutID; - // Use send. - - // TODO(tom): complexify this next logic further in case someone - // understands it. - const auto nextAccountID = spSourcePath.size () - ? AccountID(spSourcePath. front ().getAccountID ()) - : !isXRP(currencyOutID) - ? (issuerOutID == uReceiverID) - ? AccountID(uReceiverID) - : AccountID(issuerOutID) // Use implied node. - : xrpAccount(); - - JLOG (j_.debug()) - << "expandPath: implied check:" - << " uMaxIssuerID=" << uMaxIssuerID - << " uSenderIssuerID=" << uSenderIssuerID - << " uNxtCurrencyID=" << uNxtCurrencyID - << " nextAccountID=" << nextAccountID; - - // Can't just use push implied, because it can't compensate for next - // account. - if (!uNxtCurrencyID - // Next is XRP, offer next. Must go through issuer. - || uMaxCurrencyID != uNxtCurrencyID - // Next is different currency, offer next... - || uMaxIssuerID != nextAccountID) - // Next is not implied issuer - { - JLOG (j_.debug()) - << "expandPath: sender implied:" - << " account=" << uMaxIssuerID - << " currency=" << uMaxCurrencyID - << " issuer=" << uMaxIssuerID; - - // Add account implied by SendMax. - terStatus = pushNode ( - !isXRP(uMaxCurrencyID) - ? STPathElement::typeAccount | STPathElement::typeCurrency | - STPathElement::typeIssuer - : STPathElement::typeAccount | STPathElement::typeCurrency, - uMaxIssuerID, - uMaxCurrencyID, - uMaxIssuerID); - } - } - - for (auto & speElement: spSourcePath) - { - if (terStatus == tesSUCCESS) - { - JLOG (j_.trace()) << "expandPath: element in path"; - terStatus = pushNode ( - speElement.getNodeType (), speElement.getAccountID (), - speElement.getCurrency (), speElement.getIssuerID ()); - } - } - - if (terStatus == tesSUCCESS - && !isXRP(currencyOutID) // Next is not XRP - && issuerOutID != uReceiverID) // Out issuer is not receiver - { - assert (!nodes_.empty ()); - - auto const& backNode = nodes_.back (); - - if (backNode.issue_.currency != currencyOutID // Previous will be offer - || backNode.account_ != issuerOutID) // Need implied issuer - { - // Add implied account. - JLOG (j_.debug()) - << "expandPath: receiver implied:" - << " account=" << issuerOutID - << " currency=" << currencyOutID - << " issuer=" << issuerOutID; - - terStatus = pushNode ( - !isXRP(currencyOutID) - ? STPathElement::typeAccount | STPathElement::typeCurrency | - STPathElement::typeIssuer - : STPathElement::typeAccount | STPathElement::typeCurrency, - issuerOutID, - currencyOutID, - issuerOutID); - } - } - - if (terStatus == tesSUCCESS) - { - // Create receiver node. - // Last node is always an account. - - terStatus = pushNode ( - !isXRP(currencyOutID) - ? STPathElement::typeAccount | STPathElement::typeCurrency | - STPathElement::typeIssuer - : STPathElement::typeAccount | STPathElement::typeCurrency, - uReceiverID, // Receive to output - currencyOutID, // Desired currency - uReceiverID); - } - - if (terStatus == tesSUCCESS) - { - // Look for first mention of source in nodes and detect loops. - // Note: The output is not allowed to be a source. - unsigned int index = 0; - for (auto& node: nodes_) - { - AccountIssue accountIssue (node.account_, node.issue_); - if (!umForward.insert ({accountIssue, index++}).second) - { - // Failed to insert. Have a loop. - JLOG (j_.debug()) << - "expandPath: loop detected: " << getJson (); - - terStatus = temBAD_PATH_LOOP; - break; - } - } - } - - JLOG (j_.trace()) - << "expandPath:" - << " in=" << uMaxCurrencyID - << "/" << uMaxIssuerID - << " out=" << currencyOutID - << "/" << issuerOutID - << ": " << getJson (); - return terStatus; -} - - -/** Check if an expanded path violates freeze rules */ -void PathState::checkFreeze() -{ - assert (nodes_.size() >= 2); - - // A path with no intermediaries -- pure issue/redeem - // cannot be frozen. - if (nodes_.size() == 2) - return; - - SLE::pointer sle; - - for (std::size_t i = 0; i < (nodes_.size() - 1); ++i) - { - // Check each order book for a global freeze - if (nodes_[i].uFlags & STPathElement::typeIssuer) - { - sle = view().peek (keylet::account(nodes_[i].issue_.account)); - - if (sle && sle->isFlag (lsfGlobalFreeze)) - { - terStatus = terNO_LINE; - return; - } - } - - // Check each account change to make sure funds can leave - if (nodes_[i].uFlags & STPathElement::typeAccount) - { - Currency const& currencyID = nodes_[i].issue_.currency; - AccountID const& inAccount = nodes_[i].account_; - AccountID const& outAccount = nodes_[i+1].account_; - - if (inAccount != outAccount) - { - sle = view().peek (keylet::account(outAccount)); - - if (sle && sle->isFlag (lsfGlobalFreeze)) - { - terStatus = terNO_LINE; - return; - } - - sle = view().peek (keylet::line(inAccount, - outAccount, currencyID)); - - if (sle && sle->isFlag ( - (outAccount > inAccount) ? lsfHighFreeze : lsfLowFreeze)) - { - terStatus = terNO_LINE; - return; - } - } - } - } -} - -/** Check if a sequence of three accounts violates the no ripple constrains - [first] -> [second] -> [third] - Disallowed if 'second' set no ripple on [first]->[second] and - [second]->[third] -*/ -TER PathState::checkNoRipple ( - AccountID const& firstAccount, - AccountID const& secondAccount, - // This is the account whose constraints we are checking - AccountID const& thirdAccount, - Currency const& currency) -{ - // fetch the ripple lines into and out of this node - SLE::pointer sleIn = view().peek ( - keylet::line(firstAccount, secondAccount, currency)); - SLE::pointer sleOut = view().peek ( - keylet::line(secondAccount, thirdAccount, currency)); - - if (!sleIn || !sleOut) - { - terStatus = terNO_LINE; - } - else if ( - sleIn->getFieldU32 (sfFlags) & - ((secondAccount > firstAccount) ? lsfHighNoRipple : lsfLowNoRipple) && - sleOut->getFieldU32 (sfFlags) & - ((secondAccount > thirdAccount) ? lsfHighNoRipple : lsfLowNoRipple)) - { - JLOG (j_.info()) - << "Path violates noRipple constraint between " - << firstAccount << ", " - << secondAccount << " and " - << thirdAccount; - - terStatus = terNO_RIPPLE; - } - return terStatus; -} - -// Check a fully-expanded path to make sure it doesn't violate no-Ripple -// settings. -TER PathState::checkNoRipple ( - AccountID const& uDstAccountID, - AccountID const& uSrcAccountID) -{ - // There must be at least one node for there to be two consecutive ripple - // lines. - if (nodes_.size() == 0) - return terStatus; - - if (nodes_.size() == 1) - { - // There's just one link in the path - // We only need to check source-node-dest - if (nodes_[0].isAccount() && - (nodes_[0].account_ != uSrcAccountID) && - (nodes_[0].account_ != uDstAccountID)) - { - if (saInReq.getCurrency() != saOutReq.getCurrency()) - { - terStatus = terNO_LINE; - } - else - { - terStatus = checkNoRipple ( - uSrcAccountID, nodes_[0].account_, uDstAccountID, - nodes_[0].issue_.currency); - } - } - return terStatus; - } - - // Check source <-> first <-> second - if (nodes_[0].isAccount() && - nodes_[1].isAccount() && - (nodes_[0].account_ != uSrcAccountID)) - { - if ((nodes_[0].issue_.currency != nodes_[1].issue_.currency)) - { - terStatus = terNO_LINE; - return terStatus; - } - else - { - terStatus = checkNoRipple ( - uSrcAccountID, nodes_[0].account_, nodes_[1].account_, - nodes_[0].issue_.currency); - if (terStatus != tesSUCCESS) - return terStatus; - } - } - - // Check second_from_last <-> last <-> destination - size_t s = nodes_.size() - 2; - if (nodes_[s].isAccount() && - nodes_[s + 1].isAccount() && - (uDstAccountID != nodes_[s+1].account_)) - { - if ((nodes_[s].issue_.currency != nodes_[s+1].issue_.currency)) - { - terStatus = terNO_LINE; - return terStatus; - } - else - { - terStatus = checkNoRipple ( - nodes_[s].account_, nodes_[s+1].account_, - uDstAccountID, nodes_[s].issue_.currency); - if (tesSUCCESS != terStatus) - return terStatus; - } - } - - // Loop through all nodes that have a prior node and successor nodes - // These are the nodes whose no ripple constraints could be violated - for (int i = 1; i < nodes_.size() - 1; ++i) - { - if (nodes_[i - 1].isAccount() && - nodes_[i].isAccount() && - nodes_[i + 1].isAccount()) - { // Two consecutive account-to-account links - - auto const& currencyID = nodes_[i].issue_.currency; - if ((nodes_[i-1].issue_.currency != currencyID) || - (nodes_[i+1].issue_.currency != currencyID)) - { - terStatus = temBAD_PATH; - return terStatus; - } - terStatus = checkNoRipple ( - nodes_[i-1].account_, nodes_[i].account_, nodes_[i+1].account_, - currencyID); - if (terStatus != tesSUCCESS) - return terStatus; - } - - if (!nodes_[i - 1].isAccount() && - nodes_[i].isAccount() && - nodes_[i + 1].isAccount() && - nodes_[i -1].issue_.account != nodes_[i].account_) - { // offer -> account -> account - auto const& currencyID = nodes_[i].issue_.currency; - terStatus = checkNoRipple ( - nodes_[i-1].issue_.account, nodes_[i].account_, nodes_[i+1].account_, - currencyID); - if (terStatus != tesSUCCESS) - return terStatus; - } - } - - return tesSUCCESS; -} - -// This is for debugging not end users. Output names can be changed without -// warning. -Json::Value PathState::getJson () const -{ - Json::Value jvPathState (Json::objectValue); - Json::Value jvNodes (Json::arrayValue); - - for (auto const &pnNode: nodes_) - jvNodes.append (pnNode.getJson ()); - - jvPathState[jss::status] = terStatus; - jvPathState[jss::index] = mIndex; - jvPathState[jss::nodes] = jvNodes; - - if (saInReq) - jvPathState["in_req"] = saInReq.getJson (JsonOptions::none); - - if (saInAct) - jvPathState["in_act"] = saInAct.getJson (JsonOptions::none); - - if (saInPass) - jvPathState["in_pass"] = saInPass.getJson (JsonOptions::none); - - if (saOutReq) - jvPathState["out_req"] = saOutReq.getJson (JsonOptions::none); - - if (saOutAct) - jvPathState["out_act"] = saOutAct.getJson (JsonOptions::none); - - if (saOutPass) - jvPathState["out_pass"] = saOutPass.getJson (JsonOptions::none); - - if (uQuality) - jvPathState["uQuality"] = boost::lexical_cast(uQuality); - - return jvPathState; -} - -} // ripple diff --git a/src/ripple/app/paths/PathState.h b/src/ripple/app/paths/PathState.h deleted file mode 100644 index 83927a3b1a..0000000000 --- a/src/ripple/app/paths/PathState.h +++ /dev/null @@ -1,177 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_APP_PATHS_PATHSTATE_H_INCLUDED -#define RIPPLE_APP_PATHS_PATHSTATE_H_INCLUDED - -#include -#include -#include -#include - -namespace ripple { - -// Holds a single path state under incremental application. -class PathState : public CountedObject -{ - public: - using OfferIndexList = std::vector; - using Ptr = std::shared_ptr; - using List = std::vector; - - PathState (PaymentSandbox const& parent, - STAmount const& saSend, - STAmount const& saSendMax, - beast::Journal j) - : mIndex (0) - , uQuality (0) - , saInReq (saSendMax) - , saOutReq (saSend) - , j_ (j) - { - view_.emplace(&parent); - } - - void reset(STAmount const& in, STAmount const& out); - - TER expandPath ( - STPath const& spSourcePath, - AccountID const& uReceiverID, - AccountID const& uSenderID - ); - - path::Node::List& nodes() { return nodes_; } - - STAmount const& inPass() const { return saInPass; } - STAmount const& outPass() const { return saOutPass; } - STAmount const& outReq() const { return saOutReq; } - - STAmount const& inAct() const { return saInAct; } - STAmount const& outAct() const { return saOutAct; } - STAmount const& inReq() const { return saInReq; } - - void setInPass(STAmount const& sa) - { - saInPass = sa; - } - - void setOutPass(STAmount const& sa) - { - saOutPass = sa; - } - - AccountIssueToNodeIndex const& forward() { return umForward; } - AccountIssueToNodeIndex const& reverse() { return umReverse; } - - void insertReverse (AccountIssue const& ai, path::NodeIndex i) - { - umReverse.insert({ai, i}); - } - - static char const* getCountedObjectName () { return "PathState"; } - OfferIndexList& unfundedOffers() { return unfundedOffers_; } - - void setStatus(TER status) { terStatus = status; } - TER status() const { return terStatus; } - - std::uint64_t quality() const { return uQuality; } - void setQuality (std::uint64_t q) { uQuality = q; } - - void setIndex (int i) { mIndex = i; } - int index() const { return mIndex; } - - TER checkNoRipple (AccountID const& destinationAccountID, - AccountID const& sourceAccountID); - void checkFreeze (); - - static bool lessPriority (PathState const& lhs, PathState const& rhs); - - PaymentSandbox& - view() - { - return *view_; - } - - void resetView (PaymentSandbox const& view) - { - view_.emplace(&view); - } - - bool isDry() const - { - return !(saInPass && saOutPass); - } - -private: - TER checkNoRipple ( - AccountID const&, AccountID const&, AccountID const&, Currency const&); - - /** Clear path structures, and clear each node. */ - void clear(); - - TER pushNode ( - int const iType, - AccountID const& account, - Currency const& currency, - AccountID const& issuer); - - TER pushImpliedNodes ( - AccountID const& account, - Currency const& currency, - AccountID const& issuer); - - Json::Value getJson () const; - -private: - boost::optional view_; - - int mIndex; // Index/rank amoung siblings. - std::uint64_t uQuality; // 0 = no quality/liquity left. - - STAmount const& saInReq; // --> Max amount to spend by sender. - STAmount saInAct; // --> Amount spent by sender so far. - STAmount saInPass; // <-- Amount spent by sender. - - STAmount const& saOutReq; // --> Amount to send. - STAmount saOutAct; // --> Amount actually sent so far. - STAmount saOutPass; // <-- Amount actually sent. - - TER terStatus; - - path::Node::List nodes_; - - // When processing, don't want to complicate directory walking with - // deletion. Offers that became unfunded or were completely consumed go - // here and are deleted at the end. - OfferIndexList unfundedOffers_; - - // First time scanning foward, as part of path construction, a funding - // source was mentioned for accounts. Source may only be used there. - AccountIssueToNodeIndex umForward; - - // First time working in reverse a funding source was used. - // Source may only be used there if not mentioned by an account. - AccountIssueToNodeIndex umReverse; - - beast::Journal const j_; -}; - -} // ripple - -#endif diff --git a/src/ripple/app/paths/RippleCalc.cpp b/src/ripple/app/paths/RippleCalc.cpp index 63bf8a67cf..6fc644badd 100644 --- a/src/ripple/app/paths/RippleCalc.cpp +++ b/src/ripple/app/paths/RippleCalc.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -29,19 +28,8 @@ namespace ripple { namespace path { -static -TER -deleteOffers (ApplyView& view, - boost::container::flat_set const& offers, beast::Journal j) -{ - for (auto& e: offers) - if (TER r = offerDelete (view, - view.peek(keylet::offer(e)), j)) - return r; - return tesSUCCESS; -} - -RippleCalc::Output RippleCalc::rippleCalculate ( +RippleCalc::Output +RippleCalc::rippleCalculate( PaymentSandbox& view, // Compute paths using this ledger entry set. Up to caller to actually @@ -51,7 +39,7 @@ RippleCalc::Output RippleCalc::rippleCalculate ( // XRP: xrpAccount() // non-XRP: uSrcAccountID (for any issuer) or another account with // trust node. - STAmount const& saMaxAmountReq, // --> -1 = no limit. + STAmount const& saMaxAmountReq, // --> -1 = no limit. // Issuer: // XRP: xrpAccount() @@ -68,521 +56,84 @@ RippleCalc::Output RippleCalc::rippleCalculate ( Logs& l, Input const* const pInputs) { - // call flow v1 and v2 so results may be compared - bool const compareFlowV1V2 = - view.rules ().enabled (featureCompareFlowV1V2); + Output flowOut; + PaymentSandbox flowSB(&view); + auto j = l.journal("Flow"); - bool const useFlowV1Output = - !view.rules().enabled(featureFlow); - bool const callFlowV1 = useFlowV1Output || compareFlowV1V2; - bool const callFlowV2 = !useFlowV1Output || compareFlowV1V2; - - Output flowV1Out; - PaymentSandbox flowV1SB (&view); - - auto const inNative = saMaxAmountReq.native(); - auto const outNative = saDstAmountReq.native(); - detail::FlowDebugInfo flowV1FlowDebugInfo (inNative, outNative); - if (callFlowV1) + if (!view.rules().enabled(featureFlow)) { - auto const timeIt = flowV1FlowDebugInfo.timeBlock ("main"); - RippleCalc rc ( - flowV1SB, - saMaxAmountReq, - saDstAmountReq, - uDstAccountID, - uSrcAccountID, - spsPaths, - l); - if (pInputs != nullptr) - { - rc.inputFlags = *pInputs; - } - - auto result = rc.rippleCalculate (compareFlowV1V2 ? &flowV1FlowDebugInfo : nullptr); - flowV1Out.setResult (result); - flowV1Out.actualAmountIn = rc.actualAmountIn_; - flowV1Out.actualAmountOut = rc.actualAmountOut_; - if (result != tesSUCCESS && !rc.permanentlyUnfundedOffers_.empty ()) - flowV1Out.removableOffers = std::move (rc.permanentlyUnfundedOffers_); + // The new payment engine was enabled several years ago. New transaction + // should never use the old rules. Assume this is a replay + j.fatal() + << "Old payment rules are required for this transaction. Assuming " + "this is a replay and running with the new rules."; } - Output flowV2Out; - PaymentSandbox flowV2SB (&view); - detail::FlowDebugInfo flowV2FlowDebugInfo (inNative, outNative); - auto j = l.journal ("Flow"); - if (callFlowV2) { - bool defaultPaths = true; - bool partialPayment = false; - boost::optional limitQuality; - boost::optional sendMax; + bool const defaultPaths = + !pInputs ? true : pInputs->defaultPathsAllowed; - if (pInputs) - { - defaultPaths = pInputs->defaultPathsAllowed; - partialPayment = pInputs->partialPaymentAllowed; - if (pInputs->limitQuality && saMaxAmountReq > beast::zero) - limitQuality.emplace ( - Amounts (saMaxAmountReq, saDstAmountReq)); - } + bool const partialPayment = + !pInputs ? false : pInputs->partialPaymentAllowed; - if (saMaxAmountReq >= beast::zero || - saMaxAmountReq.getCurrency () != saDstAmountReq.getCurrency () || - saMaxAmountReq.getIssuer () != uSrcAccountID) - { - sendMax.emplace (saMaxAmountReq); - } + auto const limitQuality = [&]() -> boost::optional { + if (pInputs && pInputs->limitQuality && + saMaxAmountReq > beast::zero) + return Quality{Amounts(saMaxAmountReq, saDstAmountReq)}; + return boost::none; + }(); + + auto const sendMax = [&]() -> boost::optional { + if (saMaxAmountReq >= beast::zero || + saMaxAmountReq.getCurrency() != saDstAmountReq.getCurrency() || + saMaxAmountReq.getIssuer() != uSrcAccountID) + { + return saMaxAmountReq; + } + return boost::none; + }(); + + bool const ownerPaysTransferFee = + view.rules().enabled(featureOwnerPaysFee); try { - bool const ownerPaysTransferFee = - view.rules ().enabled (featureOwnerPaysFee); - auto const timeIt = flowV2FlowDebugInfo.timeBlock ("main"); - flowV2Out = flow (flowV2SB, saDstAmountReq, uSrcAccountID, - uDstAccountID, spsPaths, defaultPaths, partialPayment, - ownerPaysTransferFee, /* offerCrossing */ false, limitQuality, sendMax, j, - compareFlowV1V2 ? &flowV2FlowDebugInfo : nullptr); + flowOut = flow( + flowSB, + saDstAmountReq, + uSrcAccountID, + uDstAccountID, + spsPaths, + defaultPaths, + partialPayment, + ownerPaysTransferFee, + /* offerCrossing */ false, + limitQuality, + sendMax, + j, + nullptr); } catch (std::exception& e) { - JLOG (j.error()) << "Exception from flow: " << e.what (); - if (!useFlowV1Output) - { - // return a tec so the tx is stored - path::RippleCalc::Output exceptResult; - exceptResult.setResult(tecINTERNAL); - return exceptResult; - } + JLOG(j.error()) << "Exception from flow: " << e.what(); + + // return a tec so the tx is stored + path::RippleCalc::Output exceptResult; + exceptResult.setResult(tecINTERNAL); + return exceptResult; } } - if (j.debug()) - { - using BalanceDiffs = detail::BalanceDiffs; - auto logResult = [&](std::string const& algoName, - Output const& result, - detail::FlowDebugInfo const& flowDebugInfo, - boost::optional const& balanceDiffs, - bool outputPassInfo, - bool outputBalanceDiffs) { - j.debug () << "RippleCalc Result> " << - " actualIn: " << result.actualAmountIn << - ", actualOut: " << result.actualAmountOut << - ", result: " << result.result () << - ", dstAmtReq: " << saDstAmountReq << - ", sendMax: " << saMaxAmountReq << - (compareFlowV1V2 ? ", " + flowDebugInfo.to_string (outputPassInfo): "") << - (outputBalanceDiffs && balanceDiffs - ? ", " + detail::balanceDiffsToString(balanceDiffs) : "") << - ", algo: " << algoName; - }; - bool outputPassInfo = false; - bool outputBalanceDiffs = false; - boost::optional bdV1, bdV2; - if (compareFlowV1V2) - { - auto const v1r = flowV1Out.result (); - auto const v2r = flowV2Out.result (); - if (v1r != v2r || - (((v1r == tesSUCCESS) || (v1r == tecPATH_PARTIAL)) && - ((flowV1Out.actualAmountIn != - flowV2Out.actualAmountIn) || - (flowV1Out.actualAmountOut != - flowV2Out.actualAmountOut)))) - { - outputPassInfo = true; - } - bdV1 = detail::balanceDiffs (flowV1SB, view); - bdV2 = detail::balanceDiffs (flowV2SB, view); - outputBalanceDiffs = bdV1 != bdV2; - } + j.debug() << "RippleCalc Result> " + << " actualIn: " << flowOut.actualAmountIn + << ", actualOut: " << flowOut.actualAmountOut + << ", result: " << flowOut.result() + << ", dstAmtReq: " << saDstAmountReq + << ", sendMax: " << saMaxAmountReq; - if (callFlowV1) - { - logResult ("V1", flowV1Out, flowV1FlowDebugInfo, bdV1, - outputPassInfo, outputBalanceDiffs); - } - if (callFlowV2) - { - logResult ("V2", flowV2Out, flowV2FlowDebugInfo, bdV2, - outputPassInfo, outputBalanceDiffs); - } - } - - JLOG (j.trace()) << "Using old flow: " << useFlowV1Output; - - if (!useFlowV1Output) - { - flowV2SB.apply (view); - return flowV2Out; - } - flowV1SB.apply (view); - return flowV1Out; + flowSB.apply(view); + return flowOut; } -bool RippleCalc::addPathState(STPath const& path, TER& resultCode) -{ - auto pathState = std::make_shared ( - view, saDstAmountReq_, saMaxAmountReq_, j_); - - if (!pathState) - { - resultCode = temUNKNOWN; - return false; - } - - pathState->expandPath ( - path, - uDstAccountID_, - uSrcAccountID_); - - if (pathState->status() == tesSUCCESS) - pathState->checkNoRipple (uDstAccountID_, uSrcAccountID_); - - if (pathState->status() == tesSUCCESS) - pathState->checkFreeze (); - - pathState->setIndex (pathStateList_.size ()); - - JLOG (j_.debug()) - << "rippleCalc: Build direct:" - << " status: " << transToken (pathState->status()); - - // Return if malformed. - if (isTemMalformed (pathState->status())) - { - resultCode = pathState->status(); - return false; - } - - if (pathState->status () == tesSUCCESS) - { - resultCode = pathState->status(); - pathStateList_.push_back (pathState); - } - else if (pathState->status () != terNO_LINE) - { - resultCode = pathState->status(); - } - - return true; -} - -// OPTIMIZE: When calculating path increment, note if increment consumes all -// liquidity. No need to revisit path in the future if all liquidity is used. - -// <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed. -TER RippleCalc::rippleCalculate (detail::FlowDebugInfo* flowDebugInfo) -{ - 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. - boost::container::flat_set unfundedOffersFromBestPaths; - - int iPass = 0; - - while (resultCode == temUNCERTAIN) - { - int iBest = -1; - int iDry = 0; - - // True, if ever computed multi-quality. - bool multiQuality = false; - - if (flowDebugInfo) flowDebugInfo->newLiquidityPass(); - // Find the best path. - for (auto pathState : pathStateList_) - { - if (pathState->quality()) - // Only do active paths. - { - // If computing the only non-dry path, and not limiting quality, - // compute multi-quality. - multiQuality = !inputFlags.limitQuality && - ((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=" << amountFromQuality (pathState->quality()); - - if (flowDebugInfo) - flowDebugInfo->pushLiquiditySrc ( - toEitherAmount (pathState->inPass ()), - toEitherAmount (pathState->outPass ())); - - if (!pathState->quality()) - { - // Path was dry. - ++iDry; - } - else if (pathState->outPass() == beast::zero) - { - // Path is not dry, but moved no funds - // This should never happen. Consider the path dry - - JLOG (j_.warn()) - << "rippleCalc: Non-dry path moves no funds"; - - assert (false); - - pathState->setQuality (0); - ++iDry; - } - else - { - if (!pathState->inPass() || !pathState->outPass()) - { - JLOG (j_.debug()) - << "rippleCalc: better:" - << " uQuality=" - << amountFromQuality (pathState->quality()) - << " inPass()=" << pathState->inPass() - << " saOutPass=" << pathState->outPass(); - } - - assert (pathState->inPass() && pathState->outPass()); - - JLOG (j_.debug()) - << "Old flow iter (iter, in, out): " - << iPass << " " - << pathState->inPass() << " " - << pathState->outPass(); - - if ((!inputFlags.limitQuality || - pathState->quality() <= uQualityLimit) - // Quality is not limited or increment has allowed - // quality. - && (iBest < 0 - // Best is not yet set. - || PathState::lessPriority ( - *pathStateList_[iBest], *pathState))) - // Current is better than set. - { - JLOG (j_.debug()) - << "rippleCalc: better:" - << " mIndex=" << pathState->index() - << " uQuality=" << pathState->quality() - << " rate=" - << amountFromQuality (pathState->quality()) - << " inPass()=" << pathState->inPass() - << " saOutPass=" << pathState->outPass(); - - iBest = pathState->index (); - } - } - } - } - - ++iPass; - - if (auto stream = j_.debug()) - { - stream - << "rippleCalc: Summary:" - << " Pass: " << iPass - << " Dry: " << iDry - << " Paths: " << pathStateList_.size (); - for (auto pathState: pathStateList_) - { - stream - << "rippleCalc: " - << "Summary: " << pathState->index() - << " rate: " - << amountFromQuality (pathState->quality()) - << " quality:" << pathState->quality() - << " best: " << (iBest == pathState->index ()); - } - } - - if (iBest >= 0) - { - // Apply best path. - auto pathState = pathStateList_[iBest]; - - if (flowDebugInfo) - flowDebugInfo->pushPass (toEitherAmount (pathState->inPass ()), - toEitherAmount (pathState->outPass ()), - pathStateList_.size () - iDry); - - JLOG (j_.debug ()) - << "rippleCalc: best:" - << " uQuality=" << amountFromQuality (pathState->quality ()) - << " inPass()=" << pathState->inPass () - << " saOutPass=" << pathState->outPass () << " iBest=" << iBest; - - // Record best pass' offers that became unfunded for deletion on - // success. - - unfundedOffersFromBestPaths.insert ( - pathState->unfundedOffers().begin (), - pathState->unfundedOffers().end ()); - - // Apply best pass' view - pathState->view().apply(view); - - actualAmountIn_ += pathState->inPass(); - actualAmountOut_ += pathState->outPass(); - - JLOG (j_.trace()) - << "rippleCalc: best:" - << " uQuality=" - << amountFromQuality (pathState->quality()) - << " inPass()=" << pathState->inPass() - << " saOutPass=" << pathState->outPass() - << " actualIn=" << actualAmountIn_ - << " actualOut=" << actualAmountOut_ - << " iBest=" << iBest; - - if (multiQuality) - { - ++iDry; - pathState->setQuality(0); - } - - if (actualAmountOut_ == saDstAmountReq_) - { - // Done. Delivered requested amount. - - resultCode = tesSUCCESS; - } - else if (actualAmountOut_ > saDstAmountReq_) - { - JLOG (j_.fatal()) - << "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; -} - -} // path -} // ripple +} // namespace path +} // namespace ripple diff --git a/src/ripple/app/paths/RippleCalc.h b/src/ripple/app/paths/RippleCalc.h index 111ad68288..fd9ff59811 100644 --- a/src/ripple/app/paths/RippleCalc.h +++ b/src/ripple/app/paths/RippleCalc.h @@ -20,9 +20,8 @@ #ifndef RIPPLE_APP_PATHS_RIPPLECALC_H_INCLUDED #define RIPPLE_APP_PATHS_RIPPLECALC_H_INCLUDED -#include -#include #include +#include #include #include @@ -69,23 +68,24 @@ public: // could have been removed but were not because the payment fails. It is // useful for offer crossing, which does remove the offers. boost::container::flat_set removableOffers; + private: TER calculationResult_ = temUNKNOWN; public: - TER result () const + TER + result() const { return calculationResult_; } - void setResult (TER const value) + void + setResult(TER const value) { calculationResult_ = value; } - }; - static - Output + static Output rippleCalculate( PaymentSandbox& view, @@ -96,7 +96,7 @@ public: // XRP: xrpAccount() // non-XRP: uSrcAccountID (for any issuer) or another account with // trust node. - STAmount const& saMaxAmountReq, // --> -1 = no limit. + STAmount const& saMaxAmountReq, // --> -1 = no limit. // Issuer: // XRP: xrpAccount() @@ -121,63 +121,9 @@ public: // // Offers that were found unfunded. boost::container::flat_set permanentlyUnfundedOffers_; - - // First time working in reverse a funding source was mentioned. Source may - // only be used there. - - // Map of currency, issuer to node index. - AccountIssueToNodeIndex mumSource_; - beast::Journal const j_; - Logs& logs_; - -private: - RippleCalc ( - PaymentSandbox& view_, - STAmount const& saMaxAmountReq, // --> -1 = no limit. - STAmount const& saDstAmountReq, - - AccountID const& uDstAccountID, - AccountID const& uSrcAccountID, - STPathSet const& spsPaths, - Logs& l) - : view (view_), - j_ (l.journal ("RippleCalc")), - logs_ (l), - saDstAmountReq_(saDstAmountReq), - saMaxAmountReq_(saMaxAmountReq), - uDstAccountID_(uDstAccountID), - uSrcAccountID_(uSrcAccountID), - spsPaths_(spsPaths) - { - } - - /** Compute liquidity through these path sets. */ - TER rippleCalculate (detail::FlowDebugInfo* flowDebugInfo=nullptr); - - /** Add a single PathState. Returns true on success.*/ - bool addPathState(STPath const&, TER&); - - STAmount const& saDstAmountReq_; - STAmount const& saMaxAmountReq_; - AccountID const& uDstAccountID_; - AccountID const& uSrcAccountID_; - STPathSet const& spsPaths_; - - // The computed input amount. - STAmount actualAmountIn_; - - // The computed output amount. - STAmount actualAmountOut_; - - // Expanded path with all the actual nodes in it. - // A path starts with the source account, ends with the destination account - // and goes through other acounts or order books. - PathState::List pathStateList_; - - Input inputFlags; }; -} // path -} // ripple +} // namespace path +} // namespace ripple #endif diff --git a/src/ripple/app/paths/cursor/AdvanceNode.cpp b/src/ripple/app/paths/cursor/AdvanceNode.cpp deleted file mode 100644 index b1ed6b238c..0000000000 --- a/src/ripple/app/paths/cursor/AdvanceNode.cpp +++ /dev/null @@ -1,399 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { -namespace path { - -TER PathCursor::advanceNode (STAmount const& amount, bool reverse, bool callerHasLiquidity) const -{ - bool const multi = - (multiQuality_ || (!callerHasLiquidity && amount == beast::zero)); - - // If the multiQuality_ is unchanged, use the PathCursor we're using now. - if (multi == multiQuality_) - return advanceNode (reverse); - - // Otherwise, use a new PathCursor with the new multiQuality_. - PathCursor withMultiQuality {rippleCalc_, pathState_, multi, j_, nodeIndex_}; - return withMultiQuality.advanceNode (reverse); -} - -// OPTIMIZE: When calculating path increment, note if increment consumes all -// liquidity. No need to revisit path in the future if all liquidity is used. -// -TER PathCursor::advanceNode (bool const bReverse) const -{ - TER resultCode = tesSUCCESS; - - // Taker is the active party against an offer in the ledger - the entity - // that is taking advantage of an offer in the order book. - JLOG (j_.trace()) - << "advanceNode: TakerPays:" - << node().saTakerPays << " TakerGets:" << node().saTakerGets; - - int loopCount = 0; - auto viewJ = rippleCalc_.logs_.journal ("View"); - - do - { - // VFALCO NOTE Why not use a for() loop? - // VFALCO TODO The limit on loop iterations puts an - // upper limit on the number of different quality - // levels (ratio of pay:get) that will be considered for one path. - // Changing this value has repercusssions on validation and consensus. - // - if (++loopCount > NODE_ADVANCE_MAX_LOOPS) - { - JLOG (j_.warn()) << "Loop count exceeded"; - return tefEXCEPTION; - } - - bool bDirectDirDirty = node().directory.initialize ( - { previousNode().issue_, node().issue_}, - view()); - - if (auto advance = node().directory.advance (view())) - { - bDirectDirDirty = true; - if (advance == NodeDirectory::NEW_QUALITY) - { - // We didn't run off the end of this order book and found - // another quality directory. - JLOG (j_.trace()) - << "advanceNode: Quality advance: node.directory.current=" - << node().directory.current; - } - else if (bReverse) - { - JLOG (j_.trace()) - << "advanceNode: No more offers."; - - node().offerIndex_ = beast::zero; - break; - } - else - { - // No more offers. Should be done rather than fall off end of - // book. - JLOG (j_.warn()) - << "advanceNode: Unreachable: " - << "Fell off end of order book."; - // FIXME: why? - return telFAILED_PROCESSING; - } - } - - if (bDirectDirDirty) - { - // Our quality changed since last iteration. - // Use the rate from the directory. - node().saOfrRate = amountFromQuality ( - getQuality (node().directory.current)); - // For correct ratio - node().uEntry = 0; - node().bEntryAdvance = true; - - JLOG (j_.trace()) - << "advanceNode: directory dirty: node.saOfrRate=" - << node().saOfrRate; - } - - if (!node().bEntryAdvance) - { - if (node().bFundsDirty) - { - // We were called again probably merely to update structure - // variables. - node().saTakerPays - = node().sleOffer->getFieldAmount (sfTakerPays); - node().saTakerGets - = node().sleOffer->getFieldAmount (sfTakerGets); - - // Funds left. - node().saOfferFunds = accountFunds(view(), - node().offerOwnerAccount_, - node().saTakerGets, - fhZERO_IF_FROZEN, viewJ); - node().bFundsDirty = false; - - JLOG (j_.trace()) - << "advanceNode: funds dirty: node().saOfrRate=" - << node().saOfrRate; - } - else - { - JLOG (j_.trace()) << "advanceNode: as is"; - } - } - else if (!dirNext (view(), - node().directory.current, - node().directory.ledgerEntry, - node().uEntry, - node().offerIndex_, viewJ)) - // This is the only place that offerIndex_ changes. - { - // Failed to find an entry in directory. - // Do another cur directory iff multiQuality_ - if (multiQuality_) - { - // We are allowed to process multiple qualities if this is the - // only path. - JLOG (j_.trace()) - << "advanceNode: next quality"; - node().directory.advanceNeeded = true; // Process next quality. - } - else if (!bReverse) - { - // We didn't run dry going backwards - why are we running dry - // going forwards - this should be impossible! - // TODO(tom): these warnings occur in production! They - // shouldn't. - JLOG (j_.warn()) - << "advanceNode: unreachable: ran out of offers"; - return telFAILED_PROCESSING; - } - else - { - // Ran off end of offers. - node().bEntryAdvance = false; // Done. - node().offerIndex_ = beast::zero; // Report no more entries. - } - } - else - { - // Got a new offer. - node().sleOffer = view().peek (keylet::offer(node().offerIndex_)); - - if (!node().sleOffer) - { - // Corrupt directory that points to an entry that doesn't exist. - // This has happened in production. - JLOG (j_.warn()) << - "Missing offer in directory"; - node().bEntryAdvance = true; - } - else - { - node().offerOwnerAccount_ - = node().sleOffer->getAccountID (sfAccount); - node().saTakerPays - = node().sleOffer->getFieldAmount (sfTakerPays); - node().saTakerGets - = node().sleOffer->getFieldAmount (sfTakerGets); - - AccountIssue const accountIssue ( - node().offerOwnerAccount_, node().issue_); - - JLOG (j_.trace()) - << "advanceNode: offerOwnerAccount_=" - << to_string (node().offerOwnerAccount_) - << " node.saTakerPays=" << node().saTakerPays - << " node.saTakerGets=" << node().saTakerGets - << " node.offerIndex_=" << node().offerIndex_; - - if (node().sleOffer->isFieldPresent (sfExpiration) && - (node().sleOffer->getFieldU32 (sfExpiration) <= - view().parentCloseTime().time_since_epoch().count())) - { - // Offer is expired. - JLOG (j_.trace()) - << "advanceNode: expired offer"; - rippleCalc_.permanentlyUnfundedOffers_.insert( - node().offerIndex_); - continue; - } - - if (node().saTakerPays <= beast::zero || node().saTakerGets <= beast::zero) - { - // Offer has bad amounts. Offers should never have a bad - // amounts. - auto const index = node().offerIndex_; - if (bReverse) - { - // Past internal error, offer had bad amounts. - // This has occurred in production. - JLOG (j_.warn()) - << "advanceNode: PAST INTERNAL ERROR" - << " REVERSE: OFFER NON-POSITIVE:" - << " node.saTakerPays=" << node().saTakerPays - << " node.saTakerGets=" << node().saTakerGets; - - // Mark offer for always deletion. - rippleCalc_.permanentlyUnfundedOffers_.insert ( - node().offerIndex_); - } - else if (rippleCalc_.permanentlyUnfundedOffers_.find (index) - != rippleCalc_.permanentlyUnfundedOffers_.end ()) - { - // Past internal error, offer was found failed to place - // this in permanentlyUnfundedOffers_. - // Just skip it. It will be deleted. - JLOG (j_.debug()) - << "advanceNode: PAST INTERNAL ERROR " - << " FORWARD CONFIRM: OFFER NON-POSITIVE:" - << " node.saTakerPays=" << node().saTakerPays - << " node.saTakerGets=" << node().saTakerGets; - - } - else - { - // Reverse should have previously put bad offer in list. - // An internal error previously left a bad offer. - JLOG (j_.warn()) - << "advanceNode: INTERNAL ERROR" - - <<" FORWARD NEWLY FOUND: OFFER NON-POSITIVE:" - << " node.saTakerPays=" << node().saTakerPays - << " node.saTakerGets=" << node().saTakerGets; - - // Don't process at all, things are in an unexpected - // state for this transactions. - resultCode = tefEXCEPTION; - } - - continue; - } - - // Allowed to access source from this node? - // - // XXX This can get called multiple times for same source in a - // row, caching result would be nice. - // - // XXX Going forward could we fund something with a worse - // quality which was previously skipped? Might need to check - // quality. - auto itForward = pathState_.forward().find (accountIssue); - const bool bFoundForward = - itForward != pathState_.forward().end (); - - // Only allow a source to be used once, in the first node - // encountered from initial path scan. This prevents - // conflicting uses of the same balance when going reverse vs - // forward. - if (bFoundForward && - itForward->second != nodeIndex_ && - node().offerOwnerAccount_ != node().issue_.account) - { - // Temporarily unfunded. Another node uses this source, - // ignore in this offer. - JLOG (j_.trace()) - << "advanceNode: temporarily unfunded offer" - << " (forward)"; - continue; - } - - // This is overly strict. For contributions to past. We should - // only count source if actually used. - auto itReverse = pathState_.reverse().find (accountIssue); - bool bFoundReverse = itReverse != pathState_.reverse().end (); - - // For this quality increment, only allow a source to be used - // from a single node, in the first node encountered from - // applying offers in reverse. - if (bFoundReverse && - itReverse->second != nodeIndex_ && - node().offerOwnerAccount_ != node().issue_.account) - { - // Temporarily unfunded. Another node uses this source, - // ignore in this offer. - JLOG (j_.trace()) - << "advanceNode: temporarily unfunded offer" - <<" (reverse)"; - continue; - } - - // Determine if used in past. - // We only need to know if it might need to be marked unfunded. - auto itPast = rippleCalc_.mumSource_.find (accountIssue); - bool bFoundPast = (itPast != rippleCalc_.mumSource_.end ()); - - // Only the current node is allowed to use the source. - - node().saOfferFunds = accountFunds(view(), - node().offerOwnerAccount_, - node().saTakerGets, - fhZERO_IF_FROZEN, viewJ); - // Funds held. - - if (node().saOfferFunds <= beast::zero) - { - // Offer is unfunded. - JLOG (j_.trace()) - << "advanceNode: unfunded offer"; - - if (bReverse && !bFoundReverse && !bFoundPast) - { - // Never mentioned before, clearly just: found unfunded. - // That is, even if this offer fails due to fill or kill - // still do deletions. - // Mark offer for always deletion. - rippleCalc_.permanentlyUnfundedOffers_.insert (node().offerIndex_); - } - else - { - // Moving forward, don't need to insert again - // Or, already found it. - } - - // YYY Could verify offer is correct place for unfundeds. - continue; - } - - if (bReverse // Need to remember reverse mention. - && !bFoundPast // Not mentioned in previous passes. - && !bFoundReverse) // New to pass. - { - // Consider source mentioned by current path state. - JLOG (j_.trace()) - << "advanceNode: remember=" - << node().offerOwnerAccount_ - << "/" - << node().issue_; - - pathState_.insertReverse (accountIssue, nodeIndex_); - } - - node().bFundsDirty = false; - node().bEntryAdvance = false; - } - } - } - while (resultCode == tesSUCCESS && - (node().bEntryAdvance || node().directory.advanceNeeded)); - - if (resultCode == tesSUCCESS) - { - JLOG (j_.trace()) - << "advanceNode: node.offerIndex_=" << node().offerIndex_; - } - else - { - JLOG (j_.debug()) - << "advanceNode: resultCode=" << transToken (resultCode); - } - - return resultCode; -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/DeliverNodeForward.cpp b/src/ripple/app/paths/cursor/DeliverNodeForward.cpp deleted file mode 100644 index bafff6b545..0000000000 --- a/src/ripple/app/paths/cursor/DeliverNodeForward.cpp +++ /dev/null @@ -1,370 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { -namespace path { - -// For current offer, get input from deliver/limbo and output to next account or -// deliver for next offers. -// -// <-- node.saFwdDeliver: For forwardLiquidityForAccount to know -// how much went through -// --> node.saRevDeliver: Do not exceed. - -TER PathCursor::deliverNodeForward ( - AccountID const& uInAccountID, // --> Input owner's account. - STAmount const& saInReq, // --> Amount to deliver. - STAmount& saInAct, // <-- Amount delivered, this invocation. - STAmount& saInFees, // <-- Fees charged, this invocation. - bool callerHasLiquidity) const -{ - TER resultCode = tesSUCCESS; - - // Don't deliver more than wanted. - // Zeroed in reverse pass. - node().directory.restart(multiQuality_); - - saInAct.clear (saInReq); - saInFees.clear (saInReq); - - int loopCount = 0; - auto viewJ = rippleCalc_.logs_.journal ("View"); - - // XXX Perhaps make sure do not exceed node().saRevDeliver as another way to - // stop? - while (resultCode == tesSUCCESS && saInAct + saInFees < saInReq) - { - // Did not spend all inbound deliver funds. - if (++loopCount > - (multiQuality_ ? - CALC_NODE_DELIVER_MAX_LOOPS_MQ : - CALC_NODE_DELIVER_MAX_LOOPS)) - { - JLOG (j_.warn()) - << "deliverNodeForward: max loops cndf"; - return telFAILED_PROCESSING; - } - - // Determine values for pass to adjust saInAct, saInFees, and - // node().saFwdDeliver. - advanceNode (saInAct, false, callerHasLiquidity); - - // If needed, advance to next funded offer. - - if (resultCode != tesSUCCESS) - { - } - else if (!node().offerIndex_) - { - JLOG (j_.warn()) - << "deliverNodeForward: INTERNAL ERROR: Ran out of offers."; - return telFAILED_PROCESSING; - } - else if (resultCode == tesSUCCESS) - { - auto const xferRate = effectiveRate ( - previousNode().issue_, - uInAccountID, - node().offerOwnerAccount_, - previousNode().transferRate_); - - // First calculate assuming no output fees: saInPassAct, - // saInPassFees, saOutPassAct. - - // Offer maximum out - limited by funds with out fees. - auto saOutFunded = std::min ( - node().saOfferFunds, node().saTakerGets); - - // Offer maximum out - limit by most to deliver. - auto saOutPassFunded = std::min ( - saOutFunded, - node().saRevDeliver - node().saFwdDeliver); - - // Offer maximum in - Limited by by payout. - auto saInFunded = mulRound ( - saOutPassFunded, - node().saOfrRate, - node().saTakerPays.issue (), - true); - - // Offer maximum in with fees. - auto saInTotal = multiplyRound ( - saInFunded, xferRate, true); - auto saInRemaining = saInReq - saInAct - saInFees; - - if (saInRemaining < beast::zero) - saInRemaining.clear(); - - // In limited by remaining. - auto saInSum = std::min (saInTotal, saInRemaining); - - // In without fees. - auto saInPassAct = std::min ( - node().saTakerPays, - divideRound (saInSum, xferRate, true)); - - // Out limited by in remaining. - auto outPass = divRound ( - saInPassAct, node().saOfrRate, node().saTakerGets.issue (), true); - STAmount saOutPassMax = std::min (saOutPassFunded, outPass); - - STAmount saInPassFeesMax = saInSum - saInPassAct; - - // Will be determined by next node(). - STAmount saOutPassAct; - - // Will be determined by adjusted saInPassAct. - STAmount saInPassFees; - - JLOG (j_.trace()) - << "deliverNodeForward:" - << " nodeIndex_=" << nodeIndex_ - << " saOutFunded=" << saOutFunded - << " saOutPassFunded=" << saOutPassFunded - << " node().saOfferFunds=" << node().saOfferFunds - << " node().saTakerGets=" << node().saTakerGets - << " saInReq=" << saInReq - << " saInAct=" << saInAct - << " saInFees=" << saInFees - << " saInFunded=" << saInFunded - << " saInTotal=" << saInTotal - << " saInSum=" << saInSum - << " saInPassAct=" << saInPassAct - << " saOutPassMax=" << saOutPassMax; - - // FIXME: We remove an offer if WE didn't want anything out of it? - if (!node().saTakerPays || saInSum <= beast::zero) - { - JLOG (j_.debug()) - << "deliverNodeForward: Microscopic offer unfunded."; - - // After math offer is effectively unfunded. - pathState_.unfundedOffers().push_back (node().offerIndex_); - node().bEntryAdvance = true; - continue; - } - - if (!saInFunded) - { - // Previous check should catch this. - JLOG (j_.warn()) - << "deliverNodeForward: UNREACHABLE REACHED"; - - // After math offer is effectively unfunded. - pathState_.unfundedOffers().push_back (node().offerIndex_); - node().bEntryAdvance = true; - continue; - } - - if (!isXRP(nextNode().account_)) - { - // ? --> OFFER --> account - // Input fees: vary based upon the consumed offer's owner. - // Output fees: none as XRP or the destination account is the - // issuer. - - saOutPassAct = saOutPassMax; - saInPassFees = saInPassFeesMax; - - JLOG (j_.trace()) - << "deliverNodeForward: ? --> OFFER --> account:" - << " offerOwnerAccount_=" - << node().offerOwnerAccount_ - << " nextNode().account_=" - << nextNode().account_ - << " saOutPassAct=" << saOutPassAct - << " saOutFunded=" << saOutFunded; - - // Output: Debit offer owner, send XRP or non-XPR to next - // account. - resultCode = accountSend(view(), - node().offerOwnerAccount_, - nextNode().account_, - saOutPassAct, viewJ); - - if (resultCode != tesSUCCESS) - break; - } - else - { - // ? --> OFFER --> offer - // - // Offer to offer means current order book's output currency and - // issuer match next order book's input current and issuer. - // - // Output fees: possible if issuer has fees and is not on either - // side. - STAmount saOutPassFees; - - // Output fees vary as the next nodes offer owners may vary. - // Therefore, immediately push through output for current offer. - resultCode = increment().deliverNodeForward ( - node().offerOwnerAccount_, // --> Current holder. - saOutPassMax, // --> Amount available. - saOutPassAct, // <-- Amount delivered. - saOutPassFees, // <-- Fees charged. - saInAct > beast::zero); - - if (resultCode != tesSUCCESS) - break; - - if (saOutPassAct == saOutPassMax) - { - // No fees and entire output amount. - - saInPassFees = saInPassFeesMax; - } - else - { - // Fraction of output amount. - // Output fees are paid by offer owner and not passed to - // previous. - - assert (saOutPassAct < saOutPassMax); - auto inPassAct = mulRound ( - saOutPassAct, node().saOfrRate, saInReq.issue (), true); - saInPassAct = std::min (node().saTakerPays, inPassAct); - auto inPassFees = multiplyRound ( - saInPassAct, xferRate, true); - saInPassFees = std::min (saInPassFeesMax, inPassFees); - } - - // Do outbound debiting. - // Send to issuer/limbo total amount including fees (issuer gets - // fees). - auto const& id = isXRP(node().issue_) ? - xrpAccount() : node().issue_.account; - auto outPassTotal = saOutPassAct + saOutPassFees; - accountSend(view(), - node().offerOwnerAccount_, - id, - outPassTotal, - viewJ); - - JLOG (j_.trace()) - << "deliverNodeForward: ? --> OFFER --> offer:" - << " saOutPassAct=" << saOutPassAct - << " saOutPassFees=" << saOutPassFees; - } - - JLOG (j_.trace()) - << "deliverNodeForward: " - << " nodeIndex_=" << nodeIndex_ - << " node().saTakerGets=" << node().saTakerGets - << " node().saTakerPays=" << node().saTakerPays - << " saInPassAct=" << saInPassAct - << " saInPassFees=" << saInPassFees - << " saOutPassAct=" << saOutPassAct - << " saOutFunded=" << saOutFunded; - - // Funds were spent. - node().bFundsDirty = true; - - // Do inbound crediting. - // - // Credit offer owner from in issuer/limbo (input transfer fees left - // with owner). Don't attempt to have someone credit themselves, it - // is redundant. - if (isXRP (previousNode().issue_.currency) - || uInAccountID != node().offerOwnerAccount_) - { - auto id = !isXRP(previousNode().issue_.currency) ? - uInAccountID : xrpAccount(); - resultCode = accountSend(view(), - id, - node().offerOwnerAccount_, - saInPassAct, - viewJ); - - if (resultCode != tesSUCCESS) - break; - } - - // Adjust offer. - // - // Fees are considered paid from a seperate budget and are not named - // in the offer. - STAmount saTakerGetsNew = node().saTakerGets - saOutPassAct; - STAmount saTakerPaysNew = node().saTakerPays - saInPassAct; - - if (saTakerPaysNew < beast::zero || saTakerGetsNew < beast::zero) - { - JLOG (j_.warn()) - << "deliverNodeForward: NEGATIVE:" - << " saTakerPaysNew=" << saTakerPaysNew - << " saTakerGetsNew=" << saTakerGetsNew; - - resultCode = telFAILED_PROCESSING; - break; - } - - node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); - node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); - - view().update (node().sleOffer); - - if (saOutPassAct == saOutFunded || saTakerGetsNew == beast::zero) - { - // Offer became unfunded. - - JLOG (j_.debug()) - << "deliverNodeForward: unfunded:" - << " saOutPassAct=" << saOutPassAct - << " saOutFunded=" << saOutFunded; - - pathState_.unfundedOffers().push_back (node().offerIndex_); - node().bEntryAdvance = true; - } - else - { - if (saOutPassAct >= saOutFunded) - { - JLOG (j_.warn()) - << "deliverNodeForward: TOO MUCH:" - << " saOutPassAct=" << saOutPassAct - << " saOutFunded=" << saOutFunded; - } - - assert (saOutPassAct < saOutFunded); - } - - saInAct += saInPassAct; - saInFees += saInPassFees; - - // Adjust amount available to next node(). - node().saFwdDeliver = std::min (node().saRevDeliver, - node().saFwdDeliver + saOutPassAct); - } - } - - JLOG (j_.trace()) - << "deliverNodeForward<" - << " nodeIndex_=" << nodeIndex_ - << " saInAct=" << saInAct - << " saInFees=" << saInFees; - - return resultCode; -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp b/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp deleted file mode 100644 index d959eba6d7..0000000000 --- a/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp +++ /dev/null @@ -1,371 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { -namespace path { - -// At the right most node of a list of consecutive offer nodes, given the amount -// requested to be delivered, push towards the left nodes the amount requested -// for the right nodes so we can compute how much to deliver from the source. -// -// Between offer nodes, the fee charged may vary. Therefore, process one -// inbound offer at a time. Propagate the inbound offer's requirements to the -// previous node. The previous node adjusts the amount output and the amount -// spent on fees. Continue processing until the request is satisified as long -// as the rate does not increase past the initial rate. - -// To deliver from an order book, when computing -TER PathCursor::deliverNodeReverseImpl ( - AccountID const& uOutAccountID, // --> Output owner's account. - STAmount const& saOutReq, // --> Funds requested to be - // delivered for an increment. - STAmount& saOutAct, // <-- Funds actually delivered for an - // increment - bool callerHasLiquidity - ) const -{ - TER resultCode = tesSUCCESS; - - // Accumulation of what the previous node must deliver. - // Possible optimization: Note this gets zeroed on each increment, ideally - // only on first increment, then it could be a limit on the forward pass. - saOutAct.clear (saOutReq); - - JLOG (j_.trace()) - << "deliverNodeReverse>" - << " saOutAct=" << saOutAct - << " saOutReq=" << saOutReq - << " saPrvDlvReq=" << previousNode().saRevDeliver; - - assert (saOutReq != beast::zero); - - int loopCount = 0; - auto viewJ = rippleCalc_.logs_.journal ("View"); - - // While we did not deliver as much as requested: - while (saOutAct < saOutReq) - { - if (++loopCount > - (multiQuality_ ? - CALC_NODE_DELIVER_MAX_LOOPS_MQ : - CALC_NODE_DELIVER_MAX_LOOPS)) - { - JLOG (j_.warn()) << "loop count exceeded"; - return telFAILED_PROCESSING; - } - - resultCode = advanceNode (saOutAct, true, callerHasLiquidity); - // If needed, advance to next funded offer. - - if (resultCode != tesSUCCESS || !node().offerIndex_) - // Error or out of offers. - break; - - auto const xferRate = effectiveRate ( - node().issue_, - uOutAccountID, - node().offerOwnerAccount_, - node().transferRate_); - - JLOG (j_.trace()) - << "deliverNodeReverse:" - << " offerOwnerAccount_=" << node().offerOwnerAccount_ - << " uOutAccountID=" << uOutAccountID - << " node().issue_.account=" << node().issue_.account - << " xferRate=" << xferRate; - - // Only use rate when not in multi-quality mode - if (!multiQuality_) - { - if (!node().rateMax) - { - // Set initial rate. - JLOG (j_.trace()) - << "Set initial rate"; - - node().rateMax = xferRate; - } - else if (xferRate > node().rateMax) - { - // Offer exceeds initial rate. - JLOG (j_.trace()) - << "Offer exceeds initial rate: " << *node().rateMax; - - break; // Done. Don't bother looking for smaller transferRates. - } - else if (xferRate < node().rateMax) - { - // Reducing rate. Additional offers will only - // be considered for this increment if they - // are at least this good. - // - // At this point, the overall rate is reducing, - // while the overall rate is not xferRate, it - // would be wrong to add anything with a rate - // above xferRate. - // - // The rate would be reduced if the current - // offer was from the issuer and the previous - // offer wasn't. - - JLOG (j_.trace()) - << "Reducing rate: " << *node().rateMax; - - node().rateMax = xferRate; - } - } - - // Amount that goes to the taker. - STAmount saOutPassReq = std::min ( - std::min (node().saOfferFunds, node().saTakerGets), - saOutReq - saOutAct); - - // Maximum out - assuming no out fees. - STAmount saOutPassAct = saOutPassReq; - - // Amount charged to the offer owner. - // - // The fee goes to issuer. The fee is paid by offer owner and not passed - // as a cost to taker. - // - // Round down: prefer liquidity rather than microscopic fees. - STAmount saOutPlusFees = multiplyRound ( - saOutPassAct, xferRate, false); - - - // Offer out with fees. - - JLOG (j_.trace()) - << "deliverNodeReverse:" - << " saOutReq=" << saOutReq - << " saOutAct=" << saOutAct - << " node().saTakerGets=" << node().saTakerGets - << " saOutPassAct=" << saOutPassAct - << " saOutPlusFees=" << saOutPlusFees - << " node().saOfferFunds=" << node().saOfferFunds; - - if (saOutPlusFees > node().saOfferFunds) - { - // Offer owner can not cover all fees, compute saOutPassAct based on - // node().saOfferFunds. - saOutPlusFees = node().saOfferFunds; - - // Round up: prefer liquidity rather than microscopic fees. But, - // limit by requested. - auto fee = divideRound (saOutPlusFees, xferRate, true); - saOutPassAct = std::min (saOutPassReq, fee); - - JLOG (j_.trace()) - << "deliverNodeReverse: Total exceeds fees:" - << " saOutPassAct=" << saOutPassAct - << " saOutPlusFees=" << saOutPlusFees - << " node().saOfferFunds=" << node().saOfferFunds; - } - - // Compute portion of input needed to cover actual output. - auto outputFee = mulRound ( - saOutPassAct, node().saOfrRate, node().saTakerPays.issue (), true); - STAmount saInPassReq = std::min (node().saTakerPays, outputFee); - STAmount saInPassAct; - - JLOG (j_.trace()) - << "deliverNodeReverse:" - << " outputFee=" << outputFee - << " saInPassReq=" << saInPassReq - << " node().saOfrRate=" << node().saOfrRate - << " saOutPassAct=" << saOutPassAct - << " saOutPlusFees=" << saOutPlusFees; - - if (!saInPassReq) // FIXME: This is bogus - { - // After rounding did not want anything. - JLOG (j_.debug()) - << "deliverNodeReverse: micro offer is unfunded."; - - node().bEntryAdvance = true; - continue; - } - // Find out input amount actually available at current rate. - else if (!isXRP(previousNode().account_)) - { - // account --> OFFER --> ? - // Due to node expansion, previous is guaranteed to be the issuer. - // - // Previous is the issuer and receiver is an offer, so no fee or - // quality. - // - // Previous is the issuer and has unlimited funds. - // - // Offer owner is obtaining IOUs via an offer, so credit line limits - // are ignored. As limits are ignored, don't need to adjust - // previous account's balance. - - saInPassAct = saInPassReq; - - JLOG (j_.trace()) - << "deliverNodeReverse: account --> OFFER --> ? :" - << " saInPassAct=" << saInPassAct; - } - else - { - // offer --> OFFER --> ? - // Compute in previous offer node how much could come in. - - // TODO(tom): Fix nasty recursion here! - resultCode = increment(-1).deliverNodeReverseImpl( - node().offerOwnerAccount_, - saInPassReq, - saInPassAct, - saOutAct > beast::zero); - - // The recursive call is dry this time, but we have liquidity - // from previous calls - if (resultCode == tecPATH_DRY && saOutAct > beast::zero) - { - resultCode = tesSUCCESS; - break; - } - - JLOG (j_.trace()) - << "deliverNodeReverse: offer --> OFFER --> ? :" - << " saInPassAct=" << saInPassAct; - } - - if (resultCode != tesSUCCESS) - break; - - if (saInPassAct < saInPassReq) - { - // Adjust output to conform to limited input. - auto outputRequirements = divRound (saInPassAct, node ().saOfrRate, - node ().saTakerGets.issue (), true); - saOutPassAct = std::min (saOutPassReq, outputRequirements); - auto outputFees = multiplyRound (saOutPassAct, xferRate, true); - saOutPlusFees = std::min (node().saOfferFunds, outputFees); - - JLOG (j_.trace()) - << "deliverNodeReverse: adjusted:" - << " saOutPassAct=" << saOutPassAct - << " saOutPlusFees=" << saOutPlusFees; - } - else - { - // TODO(tom): more logging here. - assert (saInPassAct == saInPassReq); - } - - // Funds were spent. - node().bFundsDirty = true; - - // Want to deduct output to limit calculations while computing reverse. - // Don't actually need to send. - // - // Sending could be complicated: could fund a previous offer not yet - // visited. However, these deductions and adjustments are tenative. - // - // Must reset balances when going forward to perform actual transfers. - resultCode = accountSend(view(), - node().offerOwnerAccount_, node().issue_.account, saOutPassAct, viewJ); - - if (resultCode != tesSUCCESS) - break; - - // Adjust offer - STAmount saTakerGetsNew = node().saTakerGets - saOutPassAct; - STAmount saTakerPaysNew = node().saTakerPays - saInPassAct; - - if (saTakerPaysNew < beast::zero || saTakerGetsNew < beast::zero) - { - JLOG (j_.warn()) - << "deliverNodeReverse: NEGATIVE:" - << " node().saTakerPaysNew=" << saTakerPaysNew - << " node().saTakerGetsNew=" << saTakerGetsNew; - - resultCode = telFAILED_PROCESSING; - break; - } - - node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); - node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); - - view().update (node().sleOffer); - - if (saOutPassAct == node().saTakerGets) - { - // Offer became unfunded. - JLOG (j_.debug()) - << "deliverNodeReverse: offer became unfunded."; - - node().bEntryAdvance = true; - // XXX When don't we want to set advance? - } - else - { - assert (saOutPassAct < node().saTakerGets); - } - - saOutAct += saOutPassAct; - // Accumulate what is to be delivered from previous node. - previousNode().saRevDeliver += saInPassAct; - } - - if (saOutAct > saOutReq) - { - JLOG (j_.warn()) - << "deliverNodeReverse: TOO MUCH:" - << " saOutAct=" << saOutAct - << " saOutReq=" << saOutReq; - } - - assert(saOutAct <= saOutReq); - - if (resultCode == tesSUCCESS && !saOutAct) - resultCode = tecPATH_DRY; - // Unable to meet request, consider path dry. - // Design invariant: if nothing was actually delivered, return tecPATH_DRY. - - JLOG (j_.trace()) - << "deliverNodeReverse<" - << " saOutAct=" << saOutAct - << " saOutReq=" << saOutReq - << " saPrvDlvReq=" << previousNode().saRevDeliver; - - return resultCode; -} - -TER PathCursor::deliverNodeReverse ( - AccountID const& uOutAccountID, // --> Output owner's account. - STAmount const& saOutReq, // --> Funds requested to be - // delivered for an increment. - STAmount& saOutAct // <-- Funds actually delivered for an - // increment - ) const -{ - for (int i = nodeIndex_; i >= 0 && !node (i).isAccount(); --i) - node (i).directory.restart (multiQuality_); - - return deliverNodeReverseImpl(uOutAccountID, saOutReq, saOutAct, /*callerHasLiquidity*/false); -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/EffectiveRate.cpp b/src/ripple/app/paths/cursor/EffectiveRate.cpp deleted file mode 100644 index c7b068b165..0000000000 --- a/src/ripple/app/paths/cursor/EffectiveRate.cpp +++ /dev/null @@ -1,48 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include - -namespace ripple { -namespace path { - -Rate -effectiveRate( - Issue const& issue, - AccountID const& account1, - AccountID const& account2, - boost::optional const& rate) -{ - // 1:1 transfer rate for XRP - if (isXRP (issue)) - return parityRate; - - if (!rate) - LogicError ("No transfer rate set for node."); - - // 1:1 transfer rate if either of the accounts is the issuer - if (issue.account == account1 || issue.account == account2) - return parityRate; - - return rate.get(); -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/EffectiveRate.h b/src/ripple/app/paths/cursor/EffectiveRate.h deleted file mode 100644 index 21f4ef2fba..0000000000 --- a/src/ripple/app/paths/cursor/EffectiveRate.h +++ /dev/null @@ -1,41 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_APP_PATHS_CURSOR_EFFECTIVERATE_H_INCLUDED -#define RIPPLE_APP_PATHS_CURSOR_EFFECTIVERATE_H_INCLUDED - -#include -#include -#include -#include - -namespace ripple { -namespace path { - -Rate -effectiveRate( - Issue const& issue, - AccountID const& account1, - AccountID const& account2, - boost::optional const& rate); - -} // path -} // ripple - -#endif diff --git a/src/ripple/app/paths/cursor/ForwardLiquidity.cpp b/src/ripple/app/paths/cursor/ForwardLiquidity.cpp deleted file mode 100644 index 46450a18d0..0000000000 --- a/src/ripple/app/paths/cursor/ForwardLiquidity.cpp +++ /dev/null @@ -1,64 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { -namespace path { - -TER PathCursor::forwardLiquidity () const -{ - if (node().isAccount()) - return forwardLiquidityForAccount (); - - // Otherwise, node is an offer. - if (previousNode().account_ == beast::zero) - return tesSUCCESS; - - // Previous is an account node, resolve its deliver. - STAmount saInAct; - STAmount saInFees; - - auto resultCode = deliverNodeForward ( - previousNode().account_, - previousNode().saFwdDeliver, // Previous is sending this much. - saInAct, - saInFees, - false); - - assert (resultCode != tesSUCCESS || - previousNode().saFwdDeliver == saInAct + saInFees); - return resultCode; -} - -} // path -} // ripple - -// Original comments: - -// Called to drive the from the first offer node in a chain. -// -// - Offer input is in issuer/limbo. -// - Current offers consumed. -// - Current offer owners debited. -// - Transfer fees credited to issuer. -// - Payout to issuer or limbo. -// - Deliver is set without transfer fees. diff --git a/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp b/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp deleted file mode 100644 index 4e35187025..0000000000 --- a/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp +++ /dev/null @@ -1,501 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { -namespace path { - -// The reverse pass has been narrowing by credit available and inflating by fees -// as it worked backwards. Now, for the current account node, take the actual -// amount from previous and adjust forward balances. -// -// Perform balance adjustments between previous and current node. -// - The previous node: specifies what to push through to current. -// - All of previous output is consumed. -// -// Then, compute current node's output for next node. -// - Current node: specify what to push through to next. -// - Output to next node is computed as input minus quality or transfer fee. -// - If next node is an offer and output is non-XRP then we are the issuer and -// do not need to push funds. -// - If next node is an offer and output is XRP then we need to deliver funds to -// limbo. -TER PathCursor::forwardLiquidityForAccount () const -{ - TER resultCode = tesSUCCESS; - auto const lastNodeIndex = pathState_.nodes().size () - 1; - auto viewJ = rippleCalc_.logs_.journal ("View"); - - std::uint64_t uRateMax = 0; - - AccountID const& previousAccountID = - previousNode().isAccount() ? previousNode().account_ : - node().account_; - // Offers are always issue. - AccountID const& nextAccountID = - nextNode().isAccount() ? nextNode().account_ : node().account_; - - auto const qualityIn = nodeIndex_ - ? quality_in (view(), - node().account_, - previousAccountID, - node().issue_.currency) - : parityRate; - - auto const qualityOut = (nodeIndex_ == lastNodeIndex) - ? quality_out (view(), - node().account_, - nextAccountID, - node().issue_.currency) - : parityRate; - - // When looking backward (prv) for req we care about what we just - // calculated: use fwd. - // When looking forward (cur) for req we care about what was desired: use - // rev. - - // For nextNode().isAccount() - auto saPrvRedeemAct = previousNode().saFwdRedeem.zeroed(); - auto saPrvIssueAct = previousNode().saFwdIssue.zeroed(); - - // For !previousNode().isAccount() - auto saPrvDeliverAct = previousNode().saFwdDeliver.zeroed (); - - JLOG (j_.trace()) - << "forwardLiquidityForAccount> " - << "nodeIndex_=" << nodeIndex_ << "/" << lastNodeIndex - << " previousNode.saFwdRedeem:" << previousNode().saFwdRedeem - << " saPrvIssueReq:" << previousNode().saFwdIssue - << " previousNode.saFwdDeliver:" << previousNode().saFwdDeliver - << " node.saRevRedeem:" << node().saRevRedeem - << " node.saRevIssue:" << node().saRevIssue - << " node.saRevDeliver:" << node().saRevDeliver; - - // Ripple through account. - - if (previousNode().isAccount() && nextNode().isAccount()) - { - // Next is an account, must be rippling. - - if (!nodeIndex_) - { - // ^ --> ACCOUNT --> account - - // For the first node, calculate amount to ripple based on what is - // available. - node().saFwdRedeem = node().saRevRedeem; - - if (pathState_.inReq() >= beast::zero) - { - // Limit by send max. - node().saFwdRedeem = std::min ( - node().saFwdRedeem, pathState_.inReq() - pathState_.inAct()); - } - - pathState_.setInPass (node().saFwdRedeem); - - node().saFwdIssue = node().saFwdRedeem == node().saRevRedeem - // Fully redeemed. - ? node().saRevIssue : STAmount (node().saRevIssue); - - if (node().saFwdIssue && pathState_.inReq() >= beast::zero) - { - // Limit by send max. - node().saFwdIssue = std::min ( - node().saFwdIssue, - pathState_.inReq() - pathState_.inAct() - node().saFwdRedeem); - } - - pathState_.setInPass (pathState_.inPass() + node().saFwdIssue); - - JLOG (j_.trace()) - << "forwardLiquidityForAccount: ^ --> " - << "ACCOUNT --> account :" - << " saInReq=" << pathState_.inReq() - << " saInAct=" << pathState_.inAct() - << " node.saFwdRedeem:" << node().saFwdRedeem - << " node.saRevIssue:" << node().saRevIssue - << " node.saFwdIssue:" << node().saFwdIssue - << " pathState_.saInPass:" << pathState_.inPass(); - } - else if (nodeIndex_ == lastNodeIndex) - { - // account --> ACCOUNT --> $ - JLOG (j_.trace()) - << "forwardLiquidityForAccount: account --> " - << "ACCOUNT --> $ :" - << " previousAccountID=" - << to_string (previousAccountID) - << " node.account_=" - << to_string (node().account_) - << " previousNode.saFwdRedeem:" << previousNode().saFwdRedeem - << " previousNode.saFwdIssue:" << previousNode().saFwdIssue; - - // Last node. Accept all funds. Calculate amount actually to credit. - - auto& saCurReceive = pathState_.outPass(); - STAmount saIssueCrd = qualityIn >= parityRate - ? previousNode().saFwdIssue // No fee. - : multiplyRound ( - previousNode().saFwdIssue, - qualityIn, - true); // Amount to credit. - - // Amount to credit. Credit for less than received as a surcharge. - pathState_.setOutPass (previousNode().saFwdRedeem + saIssueCrd); - - if (saCurReceive) - { - // Actually receive. - resultCode = rippleCredit(view(), - previousAccountID, - node().account_, - previousNode().saFwdRedeem + previousNode().saFwdIssue, - false, viewJ); - } - else - { - // After applying quality, total payment was microscopic. - resultCode = tecPATH_DRY; - } - } - else - { - // account --> ACCOUNT --> account - JLOG (j_.trace()) - << "forwardLiquidityForAccount: account --> " - << "ACCOUNT --> account"; - - node().saFwdRedeem.clear (node().saRevRedeem); - node().saFwdIssue.clear (node().saRevIssue); - - // Previous redeem part 1: redeem -> redeem - if (previousNode().saFwdRedeem && node().saRevRedeem) - // Previous wants to redeem. - { - // Rate : 1.0 : quality out - rippleLiquidity ( - rippleCalc_, - parityRate, - qualityOut, - previousNode().saFwdRedeem, - node().saRevRedeem, - saPrvRedeemAct, - node().saFwdRedeem, - uRateMax); - } - - // Previous issue part 1: issue -> redeem - if (previousNode().saFwdIssue != saPrvIssueAct - // Previous wants to issue. - && node().saRevRedeem != node().saFwdRedeem) - // Current has more to redeem to next. - { - // Rate: quality in : quality out - rippleLiquidity ( - rippleCalc_, - qualityIn, - qualityOut, - previousNode().saFwdIssue, - node().saRevRedeem, - saPrvIssueAct, - node().saFwdRedeem, - uRateMax); - } - - // Previous redeem part 2: redeem -> issue. - if (previousNode().saFwdRedeem != saPrvRedeemAct - // Previous still wants to redeem. - && node().saRevRedeem == node().saFwdRedeem - // Current redeeming is done can issue. - && node().saRevIssue) - // Current wants to issue. - { - // Rate : 1.0 : transfer_rate - rippleLiquidity ( - rippleCalc_, - parityRate, - transferRate (view(), node().account_), - previousNode().saFwdRedeem, - node().saRevIssue, - saPrvRedeemAct, - node().saFwdIssue, - uRateMax); - } - - // Previous issue part 2 : issue -> issue - if (previousNode().saFwdIssue != saPrvIssueAct - // Previous wants to issue. - && node().saRevRedeem == node().saFwdRedeem - // Current redeeming is done can issue. - && node().saRevIssue) - // Current wants to issue. - { - // Rate: quality in : 1.0 - rippleLiquidity ( - rippleCalc_, - qualityIn, - parityRate, - previousNode().saFwdIssue, - node().saRevIssue, - saPrvIssueAct, - node().saFwdIssue, - uRateMax); - } - - STAmount saProvide = node().saFwdRedeem + node().saFwdIssue; - - // Adjust prv --> cur balance : take all inbound - resultCode = saProvide - ? rippleCredit(view(), - previousAccountID, - node().account_, - previousNode().saFwdRedeem + previousNode().saFwdIssue, - false, viewJ) - : tecPATH_DRY; - } - } - else if (previousNode().isAccount() && !nextNode().isAccount()) - { - // Current account is issuer to next offer. - // Determine deliver to offer amount. - // Don't adjust outbound balances- keep funds with issuer as limbo. - // If issuer hold's an offer owners inbound IOUs, there is no fee and - // redeem/issue will transparently happen. - - if (nodeIndex_) - { - // Non-XRP, current node is the issuer. - JLOG (j_.trace()) - << "forwardLiquidityForAccount: account --> " - << "ACCOUNT --> offer"; - - node().saFwdDeliver.clear (node().saRevDeliver); - - // redeem -> issue/deliver. - // Previous wants to redeem. - // Current is issuing to an offer so leave funds in account as - // "limbo". - if (previousNode().saFwdRedeem) - // Previous wants to redeem. - { - // Rate : 1.0 : transfer_rate - // XXX Is having the transfer rate here correct? - rippleLiquidity ( - rippleCalc_, - parityRate, - transferRate (view(), node().account_), - previousNode().saFwdRedeem, - node().saRevDeliver, - saPrvRedeemAct, - node().saFwdDeliver, - uRateMax); - } - - // issue -> issue/deliver - if (previousNode().saFwdRedeem == saPrvRedeemAct - // Previous done redeeming: Previous has no IOUs. - && previousNode().saFwdIssue) - // Previous wants to issue. To next must be ok. - { - // Rate: quality in : 1.0 - rippleLiquidity ( - rippleCalc_, - qualityIn, - parityRate, - previousNode().saFwdIssue, - node().saRevDeliver, - saPrvIssueAct, - node().saFwdDeliver, - uRateMax); - } - - // Adjust prv --> cur balance : take all inbound - resultCode = node().saFwdDeliver - ? rippleCredit(view(), - previousAccountID, node().account_, - previousNode().saFwdRedeem + previousNode().saFwdIssue, - false, viewJ) - : tecPATH_DRY; // Didn't actually deliver anything. - } - else - { - // Delivering amount requested from downstream. - node().saFwdDeliver = node().saRevDeliver; - - // If limited, then limit by send max and available. - if (pathState_.inReq() >= beast::zero) - { - // Limit by send max. - node().saFwdDeliver = std::min ( - node().saFwdDeliver, pathState_.inReq() - pathState_.inAct()); - - // Limit XRP by available. No limit for non-XRP as issuer. - if (isXRP (node().issue_)) - node().saFwdDeliver = std::min ( - node().saFwdDeliver, - accountHolds(view(), - node().account_, - xrpCurrency(), - xrpAccount(), - fhIGNORE_FREEZE, viewJ)); // XRP can't be frozen - - } - - // Record amount sent for pass. - pathState_.setInPass (node().saFwdDeliver); - - if (!node().saFwdDeliver) - { - resultCode = tecPATH_DRY; - } - else if (!isXRP (node().issue_)) - { - // Non-XRP, current node is the issuer. - // We could be delivering to multiple accounts, so we don't know - // which ripple balance will be adjusted. Assume just issuing. - - JLOG (j_.trace()) - << "forwardLiquidityForAccount: ^ --> " - << "ACCOUNT -- !XRP --> offer"; - - // As the issuer, would only issue. - // Don't need to actually deliver. As from delivering leave in - // the issuer as limbo. - } - else - { - JLOG (j_.trace()) - << "forwardLiquidityForAccount: ^ --> " - << "ACCOUNT -- XRP --> offer"; - - // Deliver XRP to limbo. - resultCode = accountSend(view(), - node().account_, xrpAccount(), node().saFwdDeliver, viewJ); - } - } - } - else if (!previousNode().isAccount() && nextNode().isAccount()) - { - if (nodeIndex_ == lastNodeIndex) - { - // offer --> ACCOUNT --> $ - JLOG (j_.trace()) - << "forwardLiquidityForAccount: offer --> " - << "ACCOUNT --> $ : " - << previousNode().saFwdDeliver; - - // Amount to credit. - pathState_.setOutPass (previousNode().saFwdDeliver); - - // No income balance adjustments necessary. The paying side inside - // the offer paid to this account. - } - else - { - // offer --> ACCOUNT --> account - JLOG (j_.trace()) - << "forwardLiquidityForAccount: offer --> " - << "ACCOUNT --> account"; - - node().saFwdRedeem.clear (node().saRevRedeem); - node().saFwdIssue.clear (node().saRevIssue); - - // deliver -> redeem - if (previousNode().saFwdDeliver && node().saRevRedeem) - // Previous wants to deliver and can current redeem. - { - // Rate : 1.0 : quality out - rippleLiquidity ( - rippleCalc_, - parityRate, - qualityOut, - previousNode().saFwdDeliver, - node().saRevRedeem, - saPrvDeliverAct, - node().saFwdRedeem, - uRateMax); - } - - // deliver -> issue - // Wants to redeem and current would and can issue. - if (previousNode().saFwdDeliver != saPrvDeliverAct - // Previous still wants to deliver. - && node().saRevRedeem == node().saFwdRedeem - // Current has more to redeem to next. - && node().saRevIssue) - // Current wants issue. - { - // Rate : 1.0 : transfer_rate - rippleLiquidity ( - rippleCalc_, - parityRate, - transferRate (view(), node().account_), - previousNode().saFwdDeliver, - node().saRevIssue, - saPrvDeliverAct, - node().saFwdIssue, - uRateMax); - } - - // No income balance adjustments necessary. The paying side inside - // the offer paid and the next link will receive. - STAmount saProvide = node().saFwdRedeem + node().saFwdIssue; - - if (!saProvide) - resultCode = tecPATH_DRY; - } - } - else - { - // offer --> ACCOUNT --> offer - // deliver/redeem -> deliver/issue. - JLOG (j_.trace()) - << "forwardLiquidityForAccount: offer --> ACCOUNT --> offer"; - - node().saFwdDeliver.clear (node().saRevDeliver); - - if (previousNode().saFwdDeliver && node().saRevDeliver) - { - // Rate : 1.0 : transfer_rate - rippleLiquidity ( - rippleCalc_, - parityRate, - transferRate (view(), node().account_), - previousNode().saFwdDeliver, - node().saRevDeliver, - saPrvDeliverAct, - node().saFwdDeliver, - uRateMax); - } - - // No income balance adjustments necessary. The paying side inside the - // offer paid and the next link will receive. - if (!node().saFwdDeliver) - resultCode = tecPATH_DRY; - } - - return resultCode; -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/Liquidity.cpp b/src/ripple/app/paths/cursor/Liquidity.cpp deleted file mode 100644 index 3625cb0add..0000000000 --- a/src/ripple/app/paths/cursor/Liquidity.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { -namespace path { - -TER PathCursor::liquidity () const -{ - TER resultCode = tecPATH_DRY; - PathCursor pc = *this; - - pathState_.resetView (rippleCalc_.view); - - for (pc.nodeIndex_ = pc.nodeSize(); pc.nodeIndex_--; ) - { - JLOG (j_.trace()) - << "reverseLiquidity>" - << " nodeIndex=" << pc.nodeIndex_ - << ".issue_.account=" << to_string (pc.node().issue_.account); - - resultCode = pc.reverseLiquidity(); - - if (!pc.node().transferRate_) - return tefINTERNAL; - - JLOG (j_.trace()) - << "reverseLiquidity< " - << "nodeIndex=" << pc.nodeIndex_ - << " resultCode=" << transToken (resultCode) - << " transferRate_=" << *pc.node().transferRate_ - << ": " << resultCode; - - if (resultCode != tesSUCCESS) - break; - } - - // VFALCO-FIXME this generates errors - // JLOG (j_.trace()) - // << "nextIncrement: Path after reverse: " << pathState_.getJson (); - - if (resultCode != tesSUCCESS) - return resultCode; - - pathState_.resetView (rippleCalc_.view); - - for (pc.nodeIndex_ = 0; pc.nodeIndex_ < pc.nodeSize(); ++pc.nodeIndex_) - { - JLOG (j_.trace()) - << "forwardLiquidity> nodeIndex=" << nodeIndex_; - - resultCode = pc.forwardLiquidity(); - if (resultCode != tesSUCCESS) - return resultCode; - - JLOG (j_.trace()) - << "forwardLiquidity<" - << " nodeIndex:" << pc.nodeIndex_ - << " resultCode:" << resultCode; - - if (pathState_.isDry()) - resultCode = tecPATH_DRY; - } - return resultCode; -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/NextIncrement.cpp b/src/ripple/app/paths/cursor/NextIncrement.cpp deleted file mode 100644 index bd3676c935..0000000000 --- a/src/ripple/app/paths/cursor/NextIncrement.cpp +++ /dev/null @@ -1,67 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { -namespace path { - -// Calculate the next increment of a path. -// -// The increment is what can satisfy a portion or all of the requested output at -// the best quality. -// -// <-- pathState.uQuality -// -// This is the wrapper that restores a checkpointed version of the ledger so we -// can write all over it without consequence. - -void PathCursor::nextIncrement () const -{ - // The next state is what is available in preference order. - // This is calculated when referenced accounts changed. - - auto status = liquidity(); - - if (status == tesSUCCESS) - { - if (pathState_.isDry()) - { - JLOG (j_.debug()) - << "nextIncrement: success on dry path:" - << " outPass=" << pathState_.outPass() - << " inPass=" << pathState_.inPass(); - Throw ("Made no progress."); - } - - // Calculate relative quality. - pathState_.setQuality(getRate ( - pathState_.outPass(), pathState_.inPass())); - } - else - { - pathState_.setQuality(0); - } - pathState_.setStatus (status); -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/PathCursor.h b/src/ripple/app/paths/cursor/PathCursor.h deleted file mode 100644 index cf8243671f..0000000000 --- a/src/ripple/app/paths/cursor/PathCursor.h +++ /dev/null @@ -1,151 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_APP_PATHS_CURSOR_PATHCURSOR_H_INCLUDED -#define RIPPLE_APP_PATHS_CURSOR_PATHCURSOR_H_INCLUDED - -#include - -namespace ripple { -namespace path { - -// The next section contains methods that compute the liquidity along a path, -// either backward or forward. -// -// We need to do these computations twice - once backward to figure out the -// maximum possible liqiudity along a path, and then forward to compute the -// actual liquidity of the paths we actually chose. -// -// Some of these routines use recursion to loop over all nodes in a path. -// TODO(tom): replace this recursion with a loop. - -class PathCursor -{ -public: - PathCursor( - RippleCalc& rippleCalc, - PathState& pathState, - bool multiQuality, - beast::Journal j, - NodeIndex nodeIndex = 0) - : rippleCalc_(rippleCalc), - pathState_(pathState), - multiQuality_(multiQuality), - nodeIndex_(restrict(nodeIndex)), - j_ (j) - { - } - - void nextIncrement() const; - -private: - PathCursor(PathCursor const&) = default; - - PathCursor increment(int delta = 1) const - { - return {rippleCalc_, pathState_, multiQuality_, j_, nodeIndex_ + delta}; - } - - TER liquidity() const; - TER reverseLiquidity () const; - TER forwardLiquidity () const; - - TER forwardLiquidityForAccount () const; - TER reverseLiquidityForOffer () const; - TER forwardLiquidityForOffer () const; - TER reverseLiquidityForAccount () const; - - // To send money out of an account. - /** advanceNode advances through offers in an order book. - If needed, advance to next funded offer. - - Automatically advances to first offer. - --> bEntryAdvance: true, to advance to next entry. false, recalculate. - <-- uOfferIndex : 0=end of list. - */ - TER advanceNode (bool reverse) const; - TER advanceNode (STAmount const& amount, bool reverse, bool callerHasLiquidity) const; - - // To deliver from an order book, when computing - TER deliverNodeReverse ( - AccountID const& uOutAccountID, - STAmount const& saOutReq, - STAmount& saOutAct) const; - - // To deliver from an order book, when computing - TER deliverNodeReverseImpl ( - AccountID const& uOutAccountID, - STAmount const& saOutReq, - STAmount& saOutAct, - bool callerHasLiquidity) const; - - TER deliverNodeForward ( - AccountID const& uInAccountID, - STAmount const& saInReq, - STAmount& saInAct, - STAmount& saInFees, - bool callerHasLiquidity) const; - - // VFALCO TODO Rename this to view() - PaymentSandbox& - view() const - { - return pathState_.view(); - } - - NodeIndex nodeSize() const - { - return pathState_.nodes().size(); - } - - NodeIndex restrict(NodeIndex i) const - { - return std::min (i, nodeSize() - 1); - } - - Node& node(NodeIndex i) const - { - return pathState_.nodes()[i]; - } - - Node& node() const - { - return node (nodeIndex_); - } - - Node& previousNode() const - { - return node (restrict (nodeIndex_ - 1)); - } - - Node& nextNode() const - { - return node (restrict (nodeIndex_ + 1)); - } - - RippleCalc& rippleCalc_; - PathState& pathState_; - bool multiQuality_; - NodeIndex nodeIndex_; - beast::Journal const j_; -}; - -} // path -} // ripple - -#endif diff --git a/src/ripple/app/paths/cursor/ReverseLiquidity.cpp b/src/ripple/app/paths/cursor/ReverseLiquidity.cpp deleted file mode 100644 index 3c912fc9e0..0000000000 --- a/src/ripple/app/paths/cursor/ReverseLiquidity.cpp +++ /dev/null @@ -1,92 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include -#include - -namespace ripple { -namespace path { - -// Calculate a node and its previous nodes. The eventual goal is to determine1 -// how much input currency we need in the forward direction to satisfy the -// output. -// -// From the destination work in reverse towards the source calculating how much -// must be asked for. As we move backwards, individual nodes may further limit -// the amount of liquidity available. -// -// This is just a controlling loop that sets things up and then hands the work -// off to either reverseLiquidityForAccount or -// reverseLiquidityForOffer. -// -// Later on the result of this will be used to work forward, figuring out how -// much can actually be delivered. -// -// <-- resultCode: tesSUCCESS or tecPATH_DRY -// <-> pnNodes: -// --> [end]saWanted.mAmount -// --> [all]saWanted.mCurrency -// --> [all]saAccount -// <-> [0]saWanted.mAmount : --> limit, <-- actual - -TER PathCursor::reverseLiquidity () const -{ - // Every account has a transfer rate for its issuances. - - // TOMOVE: The account charges - // a fee when third parties transfer that account's own issuances. - - // Cache the output transfer rate for this node. - node().transferRate_ = transferRate (view(), node().issue_.account); - - if (node().isAccount ()) - return reverseLiquidityForAccount (); - - // Otherwise the node is an Offer. - if (isXRP (nextNode().account_)) - { - JLOG (j_.trace()) - << "reverseLiquidityForOffer: " - << "OFFER --> offer: nodeIndex_=" << nodeIndex_; - return tesSUCCESS; - - // This control structure ensures deliverNodeReverse is only called for the - // rightmost offer in a chain of offers - which means that - // deliverNodeReverse has to take all of those offers into consideration. - } - - // Next is an account node, resolve current offer node's deliver. - STAmount saDeliverAct; - - JLOG (j_.trace()) - << "reverseLiquidityForOffer: OFFER --> account:" - << " nodeIndex_=" << nodeIndex_ - << " saRevDeliver=" << node().saRevDeliver; - - // The next node wants the current node to deliver this much: - return deliverNodeReverse ( - nextNode().account_, - node().saRevDeliver, - saDeliverAct); -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp b/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp deleted file mode 100644 index e58a41e63d..0000000000 --- a/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp +++ /dev/null @@ -1,599 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include -#include - -namespace ripple { -namespace path { - -// Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur, based on -// required deliverable, propagate redeem, issue (for accounts) and deliver -// requests (for order books) to the previous node. -// -// Inflate amount requested by required fees. -// Reedems are limited based on IOUs previous has on hand. -// Issues are limited based on credit limits and amount owed. -// -// Currency cannot be XRP because we are rippling. -// -// No permanent account balance adjustments as we don't know how much is going -// to actually be pushed through yet - changes are only in the scratch pad -// ledger. -// -// <-- tesSUCCESS or tecPATH_DRY - -TER PathCursor::reverseLiquidityForAccount () const -{ - TER terResult = tesSUCCESS; - auto const lastNodeIndex = nodeSize () - 1; - auto const isFinalNode = (nodeIndex_ == lastNodeIndex); - - // 0 quality means none has yet been determined. - std::uint64_t uRateMax = 0; - - // Current is allowed to redeem to next. - const bool previousNodeIsAccount = !nodeIndex_ || - previousNode().isAccount(); - - const bool nextNodeIsAccount = isFinalNode || nextNode().isAccount(); - - AccountID const& previousAccountID = previousNodeIsAccount - ? previousNode().account_ : node().account_; - AccountID const& nextAccountID = nextNodeIsAccount ? nextNode().account_ - : node().account_; // Offers are always issue. - - // This is the quality from from the previous node to this one. - auto const qualityIn - = (nodeIndex_ != 0) - ? quality_in (view(), - node().account_, - previousAccountID, - node().issue_.currency) - : parityRate; - - // And this is the quality from the next one to this one. - auto const qualityOut - = (nodeIndex_ != lastNodeIndex) - ? quality_out (view(), - node().account_, - nextAccountID, - node().issue_.currency) - : parityRate; - - // For previousNodeIsAccount: - // Previous account is already owed. - const STAmount saPrvOwed = (previousNodeIsAccount && nodeIndex_ != 0) - ? creditBalance (view(), - node().account_, - previousAccountID, - node().issue_.currency) - : STAmount (node().issue_); - - // The limit amount that the previous account may owe. - const STAmount saPrvLimit = (previousNodeIsAccount && nodeIndex_ != 0) - ? creditLimit (view(), - node().account_, - previousAccountID, - node().issue_.currency) - : STAmount (node().issue_); - - // Next account is owed. - const STAmount saNxtOwed = (nextNodeIsAccount && nodeIndex_ != lastNodeIndex) - ? creditBalance (view(), - node().account_, - nextAccountID, - node().issue_.currency) - : STAmount (node().issue_); - - JLOG (j_.trace()) - << "reverseLiquidityForAccount>" - << " nodeIndex_=" << nodeIndex_ << "/" << lastNodeIndex - << " previousAccountID=" << previousAccountID - << " node.account_=" << node().account_ - << " nextAccountID=" << nextAccountID - << " currency=" << node().issue_.currency - << " qualityIn=" << qualityIn - << " qualityOut=" << qualityOut - << " saPrvOwed=" << saPrvOwed - << " saPrvLimit=" << saPrvLimit; - - // Requests are computed to be the maximum flow possible. - // Previous can redeem the owed IOUs it holds. - const STAmount saPrvRedeemReq = (saPrvOwed > beast::zero) - ? saPrvOwed - : STAmount (saPrvOwed.issue ()); - - // Previous can issue up to limit minus whatever portion of limit already - // used (not including redeemable amount) - another "maximum flow". - const STAmount saPrvIssueReq = (saPrvOwed < beast::zero) - ? saPrvLimit + saPrvOwed : saPrvLimit; - - // Precompute these values in case we have an order book. - auto deliverCurrency = previousNode().saRevDeliver.getCurrency (); - const STAmount saPrvDeliverReq ( - {deliverCurrency, previousNode().saRevDeliver.getIssuer ()}, -1); - // -1 means unlimited delivery. - - // Set to zero, because we're trying to hit the previous node. - auto saCurRedeemAct = node().saRevRedeem.zeroed(); - - // Track the amount we actually redeem. - auto saCurIssueAct = node().saRevIssue.zeroed(); - - // For !nextNodeIsAccount - auto saCurDeliverAct = node().saRevDeliver.zeroed(); - - JLOG (j_.trace()) - << "reverseLiquidityForAccount:" - << " saPrvRedeemReq:" << saPrvRedeemReq - << " saPrvIssueReq:" << saPrvIssueReq - << " previousNode.saRevDeliver:" << previousNode().saRevDeliver - << " saPrvDeliverReq:" << saPrvDeliverReq - << " node.saRevRedeem:" << node().saRevRedeem - << " node.saRevIssue:" << node().saRevIssue - << " saNxtOwed:" << saNxtOwed; - - // VFALCO-FIXME this generates errors - //JLOG (j_.trace()) << pathState_.getJson (); - - // Current redeem req can't be more than IOUs on hand. - assert (!node().saRevRedeem || -saNxtOwed >= node().saRevRedeem); - assert (!node().saRevIssue // If not issuing, fine. - || saNxtOwed >= beast::zero - // saNxtOwed >= 0: Sender not holding next IOUs, saNxtOwed < 0: - // Sender holding next IOUs. - || -saNxtOwed == node().saRevRedeem); - // If issue req, then redeem req must consume all owed. - - if (nodeIndex_ == 0) - { - // ^ --> ACCOUNT --> account|offer - // Nothing to do, there is no previous to adjust. - // - // TODO(tom): we could have skipped all that setup and just left - // or even just never call this whole routine on nodeIndex_ = 0! - } - - // The next four cases correspond to the table at the bottom of this Wiki - // page section: https://ripple.com/wiki/Transit_Fees#Implementation - else if (previousNodeIsAccount && nextNodeIsAccount) - { - if (isFinalNode) - { - // account --> ACCOUNT --> $ - // Overall deliverable. - const STAmount saCurWantedReq = std::min ( - pathState_.outReq() - pathState_.outAct(), - saPrvLimit + saPrvOwed); - auto saCurWantedAct = saCurWantedReq.zeroed (); - - JLOG (j_.trace()) - << "reverseLiquidityForAccount: account --> " - << "ACCOUNT --> $ :" - << " saCurWantedReq=" << saCurWantedReq; - - // Calculate redeem - if (saPrvRedeemReq) // Previous has IOUs to redeem. - { - // Redeem your own IOUs at 1:1 - - saCurWantedAct = std::min (saPrvRedeemReq, saCurWantedReq); - previousNode().saRevRedeem = saCurWantedAct; - - uRateMax = STAmount::uRateOne; - - JLOG (j_.trace()) - << "reverseLiquidityForAccount: Redeem at 1:1" - << " saPrvRedeemReq=" << saPrvRedeemReq - << " (available) previousNode.saRevRedeem=" - << previousNode().saRevRedeem - << " uRateMax=" - << amountFromQuality (uRateMax).getText (); - } - else - { - previousNode().saRevRedeem.clear (saPrvRedeemReq); - } - - // Calculate issuing. - previousNode().saRevIssue.clear (saPrvIssueReq); - - if (saCurWantedReq != saCurWantedAct // Need more. - && saPrvIssueReq) // Will accept IOUs from previous. - { - // Rate: quality in : 1.0 - - // If we previously redeemed and this has a poorer rate, this - // won't be included the current increment. - rippleLiquidity ( - rippleCalc_, - qualityIn, - parityRate, - saPrvIssueReq, - saCurWantedReq, - previousNode().saRevIssue, - saCurWantedAct, - uRateMax); - - JLOG (j_.trace()) - << "reverseLiquidityForAccount: Issuing: Rate: " - << "quality in : 1.0" - << " previousNode.saRevIssue:" << previousNode().saRevIssue - << " saCurWantedAct:" << saCurWantedAct; - } - - if (!saCurWantedAct) - { - // Must have processed something. - terResult = tecPATH_DRY; - } - } - else - { - // Not final node. - // account --> ACCOUNT --> account - previousNode().saRevRedeem.clear (saPrvRedeemReq); - previousNode().saRevIssue.clear (saPrvIssueReq); - - // redeem (part 1) -> redeem - if (node().saRevRedeem - // Next wants IOUs redeemed from current account. - && saPrvRedeemReq) - // Previous has IOUs to redeem to the current account. - { - // TODO(tom): add English. - // Rate : 1.0 : quality out - we must accept our own IOUs - // as 1:1. - rippleLiquidity ( - rippleCalc_, - parityRate, - qualityOut, - saPrvRedeemReq, - node().saRevRedeem, - previousNode().saRevRedeem, - saCurRedeemAct, - uRateMax); - - JLOG (j_.trace()) - << "reverseLiquidityForAccount: " - << "Rate : 1.0 : quality out" - << " previousNode.saRevRedeem:" << previousNode().saRevRedeem - << " saCurRedeemAct:" << saCurRedeemAct; - } - - // issue (part 1) -> redeem - if (node().saRevRedeem != saCurRedeemAct - // The current node has more IOUs to redeem. - && previousNode().saRevRedeem == saPrvRedeemReq) - // The previous node has no IOUs to redeem remaining, so issues. - { - // Rate: quality in : quality out - rippleLiquidity ( - rippleCalc_, - qualityIn, - qualityOut, - saPrvIssueReq, - node().saRevRedeem, - previousNode().saRevIssue, - saCurRedeemAct, - uRateMax); - - JLOG (j_.trace()) - << "reverseLiquidityForAccount: " - << "Rate: quality in : quality out:" - << " previousNode.saRevIssue:" << previousNode().saRevIssue - << " saCurRedeemAct:" << saCurRedeemAct; - } - - // redeem (part 2) -> issue. - if (node().saRevIssue // Next wants IOUs issued. - // TODO(tom): this condition seems redundant. - && saCurRedeemAct == node().saRevRedeem - // Can only issue if completed redeeming. - && previousNode().saRevRedeem != saPrvRedeemReq) - // Did not complete redeeming previous IOUs. - { - // Rate : 1.0 : transfer_rate - rippleLiquidity ( - rippleCalc_, - parityRate, - transferRate (view(), node().account_), - saPrvRedeemReq, - node().saRevIssue, - previousNode().saRevRedeem, - saCurIssueAct, - uRateMax); - - JLOG (j_.debug()) - << "reverseLiquidityForAccount: " - << "Rate : 1.0 : transfer_rate:" - << " previousNode.saRevRedeem:" << previousNode().saRevRedeem - << " saCurIssueAct:" << saCurIssueAct; - } - - // issue (part 2) -> issue - if (node().saRevIssue != saCurIssueAct - // Need wants more IOUs issued. - && saCurRedeemAct == node().saRevRedeem - // Can only issue if completed redeeming. - && saPrvRedeemReq == previousNode().saRevRedeem - // Previously redeemed all owed IOUs. - && saPrvIssueReq) - // Previous can issue. - { - // Rate: quality in : 1.0 - rippleLiquidity ( - rippleCalc_, - qualityIn, - parityRate, - saPrvIssueReq, - node().saRevIssue, - previousNode().saRevIssue, - saCurIssueAct, - uRateMax); - - JLOG (j_.trace()) - << "reverseLiquidityForAccount: " - << "Rate: quality in : 1.0:" - << " previousNode.saRevIssue:" << previousNode().saRevIssue - << " saCurIssueAct:" << saCurIssueAct; - } - - if (!saCurRedeemAct && !saCurIssueAct) - { - // Did not make progress. - terResult = tecPATH_DRY; - } - - JLOG (j_.trace()) - << "reverseLiquidityForAccount: " - << "^|account --> ACCOUNT --> account :" - << " node.saRevRedeem:" << node().saRevRedeem - << " node.saRevIssue:" << node().saRevIssue - << " saPrvOwed:" << saPrvOwed - << " saCurRedeemAct:" << saCurRedeemAct - << " saCurIssueAct:" << saCurIssueAct; - } - } - else if (previousNodeIsAccount && !nextNodeIsAccount) - { - // account --> ACCOUNT --> offer - // Note: deliver is always issue as ACCOUNT is the issuer for the offer - // input. - JLOG (j_.trace()) - << "reverseLiquidityForAccount: " - << "account --> ACCOUNT --> offer"; - - previousNode().saRevRedeem.clear (saPrvRedeemReq); - previousNode().saRevIssue.clear (saPrvIssueReq); - - // We have three cases: the nxt offer can be owned by current account, - // previous account or some third party account. - // - // Also, the current account may or may not have a redeemable balance - // with the account for the next offer, so we don't yet know if we're - // redeeming or issuing. - // - // TODO(tom): Make sure deliver was cleared, or check actual is zero. - // redeem -> deliver/issue. - if (saPrvOwed > beast::zero // Previous has IOUs to redeem. - && node().saRevDeliver) // Need some issued. - { - // Rate : 1.0 : transfer_rate - rippleLiquidity ( - rippleCalc_, - parityRate, - transferRate (view(), node().account_), - saPrvRedeemReq, - node().saRevDeliver, - previousNode().saRevRedeem, - saCurDeliverAct, - uRateMax); - } - - // issue -> deliver/issue - if (saPrvRedeemReq == previousNode().saRevRedeem - // Previously redeemed all owed. - && node().saRevDeliver != saCurDeliverAct) // Still need some issued. - { - // Rate: quality in : 1.0 - rippleLiquidity ( - rippleCalc_, - qualityIn, - parityRate, - saPrvIssueReq, - node().saRevDeliver, - previousNode().saRevIssue, - saCurDeliverAct, - uRateMax); - } - - if (!saCurDeliverAct) - { - // Must want something. - terResult = tecPATH_DRY; - } - - JLOG (j_.trace()) - << "reverseLiquidityForAccount: " - << " node.saRevDeliver:" << node().saRevDeliver - << " saCurDeliverAct:" << saCurDeliverAct - << " saPrvOwed:" << saPrvOwed; - } - else if (!previousNodeIsAccount && nextNodeIsAccount) - { - if (isFinalNode) - { - // offer --> ACCOUNT --> $ - // Previous is an offer, no limit: redeem own IOUs. - // - // This is the final node; we can't look to the right to get values; - // we have to go up to get the out value for the entire path state. - STAmount const& saCurWantedReq = - pathState_.outReq() - pathState_.outAct(); - STAmount saCurWantedAct = saCurWantedReq.zeroed(); - - JLOG (j_.trace()) - << "reverseLiquidityForAccount: " - << "offer --> ACCOUNT --> $ :" - << " saCurWantedReq:" << saCurWantedReq - << " saOutAct:" << pathState_.outAct() - << " saOutReq:" << pathState_.outReq(); - - if (saCurWantedReq <= beast::zero) - { - assert(false); - JLOG (j_.fatal()) << "CurWantReq was not positive"; - return tefEXCEPTION; - } - - // The previous node is an offer; we are receiving our own currency. - - // The previous order book's entries might hold our issuances; might - // not hold our issuances; might be our own offer. - // - // Assume the worst case, the case which costs the most to go - // through, which is that it is not our own offer or our own - // issuances. Later on the forward pass we may be able to do - // better. - // - // TODO: this comment applies generally to this section - move it up - // to a document. - - // Rate: quality in : 1.0 - rippleLiquidity ( - rippleCalc_, - qualityIn, - parityRate, - saPrvDeliverReq, - saCurWantedReq, - previousNode().saRevDeliver, - saCurWantedAct, - uRateMax); - - if (!saCurWantedAct) - { - // Must have processed something. - terResult = tecPATH_DRY; - } - - JLOG (j_.trace()) - << "reverseLiquidityForAccount:" - << " previousNode().saRevDeliver:" << previousNode().saRevDeliver - << " saPrvDeliverReq:" << saPrvDeliverReq - << " saCurWantedAct:" << saCurWantedAct - << " saCurWantedReq:" << saCurWantedReq; - } - else - { - // offer --> ACCOUNT --> account - // Note: offer is always delivering(redeeming) as account is issuer. - JLOG (j_.trace()) - << "reverseLiquidityForAccount: " - << "offer --> ACCOUNT --> account :" - << " node.saRevRedeem:" << node().saRevRedeem - << " node.saRevIssue:" << node().saRevIssue; - - // deliver -> redeem - // TODO(tom): now we have more checking in nodeRipple, these checks - // might be redundant. - if (node().saRevRedeem) // Next wants us to redeem. - { - // cur holds IOUs from the account to the right, the nxt - // account. If someone is making the current account get rid of - // the nxt account's IOUs, then charge the input for quality - // out. - // - // Rate : 1.0 : quality out - rippleLiquidity ( - rippleCalc_, - parityRate, - qualityOut, - saPrvDeliverReq, - node().saRevRedeem, - previousNode().saRevDeliver, - saCurRedeemAct, - uRateMax); - } - - // deliver -> issue. - if (node().saRevRedeem == saCurRedeemAct - // Can only issue if previously redeemed all. - && node().saRevIssue) - // Need some issued. - { - // Rate : 1.0 : transfer_rate - rippleLiquidity ( - rippleCalc_, - parityRate, - transferRate (view(), node().account_), - saPrvDeliverReq, - node().saRevIssue, - previousNode().saRevDeliver, - saCurIssueAct, - uRateMax); - } - - JLOG (j_.trace()) - << "reverseLiquidityForAccount:" - << " saCurRedeemAct:" << saCurRedeemAct - << " node.saRevRedeem:" << node().saRevRedeem - << " previousNode.saRevDeliver:" << previousNode().saRevDeliver - << " node.saRevIssue:" << node().saRevIssue; - - if (!previousNode().saRevDeliver) - { - // Must want something. - terResult = tecPATH_DRY; - } - } - } - else - { - // offer --> ACCOUNT --> offer - // deliver/redeem -> deliver/issue. - JLOG (j_.trace()) - << "reverseLiquidityForAccount: offer --> ACCOUNT --> offer"; - - // Rate : 1.0 : transfer_rate - rippleLiquidity ( - rippleCalc_, - parityRate, - transferRate (view(), node().account_), - saPrvDeliverReq, - node().saRevDeliver, - previousNode().saRevDeliver, - saCurDeliverAct, - uRateMax); - - if (!saCurDeliverAct) - { - // Must want something. - terResult = tecPATH_DRY; - } - } - - return terResult; -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/RippleLiquidity.cpp b/src/ripple/app/paths/cursor/RippleLiquidity.cpp deleted file mode 100644 index 9fd9b4e5e1..0000000000 --- a/src/ripple/app/paths/cursor/RippleLiquidity.cpp +++ /dev/null @@ -1,250 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { -namespace path { - -// Compute how much might flow for the node for the pass. Does not actually -// adjust balances. -// -// uQualityIn -> uQualityOut -// saPrvReq -> saCurReq -// sqPrvAct -> saCurAct -// -// This is a minimizing routine: moving in reverse it propagates the send limit -// to the sender, moving forward it propagates the actual send toward the -// receiver. -// -// When this routine works backwards, saCurReq is the driving variable: it -// calculates previous wants based on previous credit limits and current wants. -// -// When this routine works forwards, saPrvReq is the driving variable: it -// calculates current deliver based on previous delivery limits and current -// wants. -// -// This routine is called one or two times for a node in a pass. If called once, -// it will work and set a rate. If called again, the new work must not worsen -// the previous rate. - -void rippleLiquidity ( - RippleCalc& rippleCalc, - Rate const& qualityIn, - Rate const& qualityOut, - STAmount const& saPrvReq, // --> in limit including fees, <0 = unlimited - STAmount const& saCurReq, // --> out limit - STAmount& saPrvAct, // <-> in limit including achieved so far: <-- <= --> - STAmount& saCurAct, // <-> out limit including achieved so far: <-- <= --> - std::uint64_t& uRateMax) -{ - JLOG (rippleCalc.j_.trace()) - << "rippleLiquidity>" - << " qualityIn=" << qualityIn - << " qualityOut=" << qualityOut - << " saPrvReq=" << saPrvReq - << " saCurReq=" << saCurReq - << " saPrvAct=" << saPrvAct - << " saCurAct=" << saCurAct; - - // saCurAct was once beast::zero in a production server. - assert (saCurReq != beast::zero); - assert (saCurReq > beast::zero); - - assert (saPrvReq.getCurrency () == saCurReq.getCurrency ()); - assert (saPrvReq.getCurrency () == saPrvAct.getCurrency ()); - assert (saPrvReq.getIssuer () == saPrvAct.getIssuer ()); - - const bool bPrvUnlimited = (saPrvReq < beast::zero); // -1 means unlimited. - - // Unlimited stays unlimited - don't do calculations. - - // How much could possibly flow through the previous node? - const STAmount saPrv = bPrvUnlimited ? saPrvReq : saPrvReq - saPrvAct; - - // How much could possibly flow through the current node? - const STAmount saCur = saCurReq - saCurAct; - - JLOG (rippleCalc.j_.trace()) - << "rippleLiquidity: " - << " bPrvUnlimited=" << bPrvUnlimited - << " saPrv=" << saPrv - << " saCur=" << saCur; - - // If nothing can flow, we might as well not do any work. - if (saPrv == beast::zero || saCur == beast::zero) - return; - - if (qualityIn >= qualityOut) - { - // You're getting better quality than you asked for, so no fee. - JLOG (rippleCalc.j_.trace()) << "rippleLiquidity: No fees"; - - // Only process if the current rate, 1:1, is not worse than the previous - // rate, uRateMax - otherwise there is no flow. - if (!uRateMax || STAmount::uRateOne <= uRateMax) - { - // Limit amount to transfer if need - the minimum of amount being - // paid and the amount that's wanted. - STAmount saTransfer = bPrvUnlimited ? saCur - : std::min (saPrv, saCur); - - // In reverse, we want to propagate the limited cur to prv and set - // actual cur. - // - // In forward, we want to propagate the limited prv to cur and set - // actual prv. - // - // This is the actual flow. - saPrvAct += saTransfer; - saCurAct += saTransfer; - - // If no rate limit, set rate limit to avoid combining with - // something with a worse rate. - if (uRateMax == 0) - uRateMax = STAmount::uRateOne; - } - } - else - { - // If the quality is worse than the previous - JLOG (rippleCalc.j_.trace()) << "rippleLiquidity: Fee"; - - std::uint64_t const uRate = getRate ( - STAmount (qualityOut.value), - STAmount (qualityIn.value)); - - // If the next rate is at least as good as the current rate, process. - if (!uRateMax || uRate <= uRateMax) - { - // current actual = current request * (quality out / quality in). - auto n = multiplyRound (saCur, qualityOut, true); - // True means "round up" to get best flow. - - STAmount saCurIn = divideRound (n, qualityIn, true); - - JLOG (rippleCalc.j_.trace()) - << "rippleLiquidity:" - << " bPrvUnlimited=" << bPrvUnlimited - << " saPrv=" << saPrv - << " saCurIn=" << saCurIn; - - if (bPrvUnlimited || saCurIn <= saPrv) - { - // All of current. Some amount of previous. - saCurAct += saCur; - saPrvAct += saCurIn; - JLOG (rippleCalc.j_.trace()) - << "rippleLiquidity:3c:" - << " saCurReq=" << saCurReq - << " saPrvAct=" << saPrvAct; - } - else - { - // There wasn't enough money to start with - so given the - // limited input, how much could we deliver? - // current actual = previous request - // * (quality in / quality out). - // This is inverted compared to the code above because we're - // going the other way - auto numerator = multiplyRound (saPrv, - qualityIn, saCur.issue(), true); - // A part of current. All of previous. (Cur is the driver - // variable.) - STAmount saCurOut = divideRound (numerator, - qualityOut, saCur.issue(), true); - - JLOG (rippleCalc.j_.trace()) - << "rippleLiquidity:4: saCurReq=" << saCurReq; - - saCurAct += saCurOut; - saPrvAct = saPrvReq; - } - if (!uRateMax) - uRateMax = uRate; - } - } - - JLOG (rippleCalc.j_.trace()) - << "rippleLiquidity<" - << " qualityIn=" << qualityIn - << " qualityOut=" << qualityOut - << " saPrvReq=" << saPrvReq - << " saCurReq=" << saCurReq - << " saPrvAct=" << saPrvAct - << " saCurAct=" << saCurAct; -} - -static -Rate -rippleQuality ( - ReadView const& view, - AccountID const& destination, - AccountID const& source, - Currency const& currency, - SField const& sfLow, - SField const& sfHigh) -{ - if (destination == source) - return parityRate; - - auto const& sfField = destination < source ? sfLow : sfHigh; - - auto const sle = view.read( - keylet::line(destination, source, currency)); - - if (!sle || !sle->isFieldPresent (sfField)) - return parityRate; - - auto quality = sle->getFieldU32 (sfField); - - // Avoid divide by zero. NIKB CHECKME: if we - // allow zero qualities now, then we shouldn't. - if (quality == 0) - quality = 1; - - return Rate{ quality }; -} - -Rate -quality_in ( - ReadView const& view, - AccountID const& uToAccountID, - AccountID const& uFromAccountID, - Currency const& currency) -{ - return rippleQuality (view, uToAccountID, uFromAccountID, currency, - sfLowQualityIn, sfHighQualityIn); -} - -Rate -quality_out ( - ReadView const& view, - AccountID const& uToAccountID, - AccountID const& uFromAccountID, - Currency const& currency) -{ - return rippleQuality (view, uToAccountID, uFromAccountID, currency, - sfLowQualityOut, sfHighQualityOut); -} - -} // path -} // ripple diff --git a/src/ripple/app/paths/cursor/RippleLiquidity.h b/src/ripple/app/paths/cursor/RippleLiquidity.h deleted file mode 100644 index 767c248deb..0000000000 --- a/src/ripple/app/paths/cursor/RippleLiquidity.h +++ /dev/null @@ -1,59 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_APP_PATHS_CURSOR_RIPPLELIQUIDITY_H_INCLUDED -#define RIPPLE_APP_PATHS_CURSOR_RIPPLELIQUIDITY_H_INCLUDED - -#include -#include -#include -#include -#include - -namespace ripple { -namespace path { - -void rippleLiquidity ( - RippleCalc&, - Rate const& qualityIn, - Rate const& qualityOut, - STAmount const& saPrvReq, - STAmount const& saCurReq, - STAmount& saPrvAct, - STAmount& saCurAct, - std::uint64_t& uRateMax); - -Rate -quality_in ( - ReadView const& view, - AccountID const& uToAccountID, - AccountID const& uFromAccountID, - Currency const& currency); - -Rate -quality_out ( - ReadView const& view, - AccountID const& uToAccountID, - AccountID const& uFromAccountID, - Currency const& currency); - -} // path -} // ripple - -#endif diff --git a/src/ripple/ledger/View.h b/src/ripple/ledger/View.h index 46eb3be593..638d4991e7 100644 --- a/src/ripple/ledger/View.h +++ b/src/ripple/ledger/View.h @@ -299,7 +299,7 @@ rippleCredit (ApplyView& view, const STAmount & saAmount, bool bCheckIssuer, beast::Journal j); -// [[nodiscard]] // nodiscard commented out so DeliverNodeForward.cpp compiles. +[[nodiscard]] TER accountSend (ApplyView& view, AccountID const& from, diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index ab850f7220..5f00cebf1e 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -78,7 +78,7 @@ class FeatureCollections "OwnerPaysFee", "CompareFlowV1V2", "PayChan", - "Flow", + "Flow", // Unconditionally supported. "CompareTakerFlowCross", "FlowCross", "CryptoConditions", diff --git a/src/ripple/protocol/jss.h b/src/ripple/protocol/jss.h index de235198e1..4af0aebb5a 100644 --- a/src/ripple/protocol/jss.h +++ b/src/ripple/protocol/jss.h @@ -177,7 +177,7 @@ JSS ( converge_time_s ); // out: NetworkOPs JSS ( count ); // in: AccountTx*, ValidatorList JSS ( counters ); // in/out: retrieve counters JSS ( currency ); // in: paths/PathRequest, STAmount - // out: paths/Node, STPathSet, STAmount, + // out: STPathSet, STAmount, // AccountLines JSS ( current ); // out: OwnerInfo JSS ( current_activities ); @@ -236,7 +236,7 @@ JSS ( fetch_pack ); // out: NetworkOPs JSS ( first ); // out: rpc/Version JSS ( finished ); JSS ( fix_txns ); // in: LedgerCleaner -JSS ( flags ); // out: paths/Node, AccountOffers, +JSS ( flags ); // out: AccountOffers, // NetworkOPs JSS ( forward ); // in: AccountTx JSS ( freeze ); // out: AccountLines @@ -262,7 +262,7 @@ JSS ( ident ); // in: AccountCurrencies, AccountInfo, JSS ( inLedger ); // out: tx/Transaction JSS ( inbound ); // out: PeerImp JSS ( index ); // in: LedgerEntry, DownloadShard - // out: PathState, STLedgerEntry, + // out: STLedgerEntry, // LedgerEntry, TxHistory, LedgerData // field JSS ( info ); // out: ServerInfo, ConsensusInfo, FetchInfo @@ -273,7 +273,7 @@ JSS ( io_latency_ms ); // out: NetworkOPs JSS ( ip ); // in: Connect, out: OverlayImpl JSS ( issuer ); // in: RipplePathFind, Subscribe, // Unsubscribe, BookOffers - // out: paths/Node, STPathSet, STAmount + // out: STPathSet, STAmount JSS ( job ); JSS ( job_queue ); JSS ( jobs ); @@ -373,7 +373,6 @@ JSS ( node_reads_hit ); // out: GetCounts JSS ( node_reads_total ); // out: GetCounts JSS ( node_writes ); // out: GetCounts JSS ( node_written_bytes ); // out: GetCounts -JSS ( nodes ); // out: PathState JSS ( obligations ); // out: GatewayBalances JSS ( offer ); // in: LedgerEntry JSS ( offers ); // out: NetworkOPs, AccountOffers, Subscribe @@ -532,7 +531,7 @@ JSS ( txn_count ); // out: NetworkOPs JSS ( txs ); // out: TxHistory JSS ( type ); // in: AccountObjects // out: NetworkOPs - // paths/Node.cpp, OverlayImpl, Logic + // OverlayImpl, Logic JSS ( type_hex ); // out: STPathSet JSS ( unl ); // out: UnlList JSS ( unlimited); // out: Connection.h diff --git a/src/ripple/unity/app_paths.cpp b/src/ripple/unity/app_paths.cpp index 031229ac1d..f67b39da37 100644 --- a/src/ripple/unity/app_paths.cpp +++ b/src/ripple/unity/app_paths.cpp @@ -22,10 +22,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -33,15 +31,3 @@ #include #include #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/src/test/app/CrossingLimits_test.cpp b/src/test/app/CrossingLimits_test.cpp index 914a1b2f33..6f8a304fd6 100644 --- a/src/test/app/CrossingLimits_test.cpp +++ b/src/test/app/CrossingLimits_test.cpp @@ -528,10 +528,9 @@ public: }; using namespace jtx; auto const sa = supported_amendments(); - testAll(sa - featureFlow - fix1373 - featureFlowCross); - testAll(sa - fix1373 - featureFlowCross); - testAll(sa - featureFlowCross); - testAll(sa ); + testAll(sa - fix1373 - featureFlowCross); + testAll(sa - featureFlowCross); + testAll(sa ); } }; diff --git a/src/test/app/DeliverMin_test.cpp b/src/test/app/DeliverMin_test.cpp index 8a23f0a514..af05d7f79a 100644 --- a/src/test/app/DeliverMin_test.cpp +++ b/src/test/app/DeliverMin_test.cpp @@ -112,9 +112,8 @@ public: { using namespace jtx; auto const sa = supported_amendments(); - test_convert_all_of_an_asset(sa - featureFlow - fix1373 - featureFlowCross); test_convert_all_of_an_asset(sa - fix1373 - featureFlowCross); - test_convert_all_of_an_asset(sa - featureFlowCross); + test_convert_all_of_an_asset(sa - featureFlowCross); test_convert_all_of_an_asset(sa); } }; diff --git a/src/test/app/Discrepancy_test.cpp b/src/test/app/Discrepancy_test.cpp index 859e3f55a0..999b00e43d 100644 --- a/src/test/app/Discrepancy_test.cpp +++ b/src/test/app/Discrepancy_test.cpp @@ -146,9 +146,8 @@ public: { using namespace test::jtx; auto const sa = supported_amendments(); - testXRPDiscrepancy (sa - featureFlow - fix1373 - featureFlowCross); - testXRPDiscrepancy (sa - fix1373 - featureFlowCross); - testXRPDiscrepancy (sa - featureFlowCross); + testXRPDiscrepancy (sa - fix1373 - featureFlowCross); + testXRPDiscrepancy (sa - featureFlowCross); testXRPDiscrepancy (sa); } }; diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index fd34f15e72..9a7637fa47 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -212,9 +212,6 @@ struct Flow_test : public beast::unit_test::suite for (auto bobDanQIn : {80, 100, 120}) for (auto bobAliceQOut : {80, 100, 120}) { - if (!features[featureFlow] && bobDanQIn < 100 && - bobAliceQOut < 100) - continue; // Bug in flow v1 Env env(*this, features); env.fund(XRP(10000), alice, bob, carol, dan); env(trust(bob, USDD(100)), qualityInPercent(bobDanQIn)); @@ -728,20 +725,21 @@ struct Flow_test : public beast::unit_test::suite Account const bob ("bob"); Account const carol ("carol"); - Env env (*this, FeatureBitset{}); + { + Env env (*this, supported_amendments()); - env.fund (XRP(10000), alice, bob, carol, gw); + env.fund (XRP(10000), alice, bob, carol, gw); - env.trust (USD(100), alice, bob, carol); - env (pay (gw, bob, USD (100))); - env (offer (bob, XRP (50), USD (50))); - env (offer (bob, XRP (100), USD (50))); + env.trust (USD(100), alice, bob, carol); + env (pay (gw, bob, USD (100))); + env (offer (bob, XRP (50), USD (50))); + env (offer (bob, XRP (100), USD (50))); - env (pay (alice, carol, USD (100)), path (~USD), sendmax (XRP (100)), - txflags (tfNoRippleDirect | tfPartialPayment | tfLimitQuality), - ter (tesSUCCESS)); + env (pay (alice, carol, USD (100)), path (~USD), sendmax (XRP (100)), + txflags (tfNoRippleDirect | tfPartialPayment | tfLimitQuality)); - env.require (balance (carol, USD (50))); + env.require (balance (carol, USD (50))); + } } // Helper function that returns the reserve on an account based on @@ -1189,9 +1187,6 @@ struct Flow_test : public beast::unit_test::suite testLineQuality(features); testFalseDry(features); - // Only do the rest of the tests if featureFlow is enabled. - if (!features[featureFlow]) - return; testDirectStep(features); testBookStep(features); testDirectStep(features | ownerPaysFee); @@ -1214,9 +1209,8 @@ struct Flow_test : public beast::unit_test::suite using namespace jtx; auto const sa = supported_amendments(); - testWithFeats(sa - featureFlow - fix1373 - featureFlowCross); - testWithFeats(sa - fix1373 - featureFlowCross); - testWithFeats(sa - featureFlowCross); + testWithFeats(sa - fix1373 - featureFlowCross); + testWithFeats(sa - featureFlowCross); testWithFeats(sa); testEmptyStrand(sa); } @@ -1228,22 +1222,19 @@ struct Flow_manual_test : public Flow_test { using namespace jtx; auto const all = supported_amendments(); - FeatureBitset const flow{featureFlow}; FeatureBitset const f1373{fix1373}; FeatureBitset const flowCross{featureFlowCross}; FeatureBitset const f1513{fix1513}; - testWithFeats(all - flow - f1373 - flowCross - f1513); - testWithFeats(all - flow - f1373 - flowCross ); - testWithFeats(all - f1373 - flowCross - f1513); - testWithFeats(all - f1373 - flowCross ); - testWithFeats(all - flowCross - f1513); - testWithFeats(all - flowCross ); - testWithFeats(all - f1513); - testWithFeats(all ); + testWithFeats(all - f1373 - flowCross - f1513); + testWithFeats(all - f1373 - flowCross ); + testWithFeats(all - flowCross - f1513); + testWithFeats(all - flowCross ); + testWithFeats(all - f1513); + testWithFeats(all ); - testEmptyStrand(all - f1513); - testEmptyStrand(all ); + testEmptyStrand(all - f1513); + testEmptyStrand(all ); } }; diff --git a/src/test/app/Freeze_test.cpp b/src/test/app/Freeze_test.cpp index 637a4c67b0..9b6cf5f82c 100644 --- a/src/test/app/Freeze_test.cpp +++ b/src/test/app/Freeze_test.cpp @@ -545,9 +545,8 @@ public: }; using namespace test::jtx; auto const sa = supported_amendments(); - testAll(sa - featureFlow - fix1373 - featureFlowCross); - testAll(sa - fix1373 - featureFlowCross); - testAll(sa - featureFlowCross); + testAll(sa - fix1373 - featureFlowCross); + testAll(sa - featureFlowCross); testAll(sa); } }; diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index bbbc686145..0fa937c5a1 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -263,7 +263,10 @@ public: env (pay (gw, carol, EUR (100))); // Create more offers than the loop max count in DeliverNodeReverse - for (int i=0;i<101;++i) + // Note: the DeliverNodeReverse code has been removed; however since + // this is a regression test the original test is being left as-is for + // now. + for (int i = 0; i < 101; ++i) env (offer (carol, USD (1), EUR (2))); env (pay (alice, bob, EUR (epsilon)), path (~EUR), sendmax (USD (100))); @@ -3351,13 +3354,9 @@ public: env (trust (ann, D_BUX(100))); env.close(); - // Determine which TEC code we expect. - TER const tecExpect = - features[featureFlow] ? TER {temBAD_PATH} : TER {tecPATH_DRY}; - // This payment caused the assert. env (pay (ann, ann, D_BUX(30)), - path (A_BUX, D_BUX), sendmax (A_BUX(30)), ter (tecExpect)); + path (A_BUX, D_BUX), sendmax (A_BUX(30)), ter (temBAD_PATH)); env.close(); env.require (balance (ann, A_BUX(none))); @@ -4617,28 +4616,21 @@ class Offer_manual_test : public Offer_test { using namespace jtx; FeatureBitset const all{supported_amendments()}; - FeatureBitset const flow{featureFlow}; FeatureBitset const f1373{fix1373}; FeatureBitset const flowCross{featureFlowCross}; FeatureBitset const f1513{fix1513}; FeatureBitset const takerDryOffer{fixTakerDryOfferRemoval}; - testAll(all - flow - f1373 - flowCross - f1513); - testAll(all - flow - f1373 - flowCross ); - testAll(all - flow - f1373 - f1513); - testAll(all - flow - f1373 ); - testAll(all - f1373 - flowCross - f1513); - testAll(all - f1373 - flowCross ); - testAll(all - f1373 - f1513); - testAll(all - f1373 ); - testAll(all - flowCross - f1513); - testAll(all - flowCross ); - testAll(all - f1513); - testAll(all ); + testAll(all - f1373 - flowCross - f1513); + testAll(all - f1373 - flowCross ); + testAll(all - f1373 - f1513); + testAll(all - f1373 ); + testAll(all - flowCross - f1513); + testAll(all - flowCross ); + testAll(all - f1513); + testAll(all ); - testAll(all - flow - f1373 - flowCross - takerDryOffer); - testAll(all - flow - f1373 - takerDryOffer); - testAll(all - f1373 - flowCross - takerDryOffer); + testAll(all - f1373 - flowCross - takerDryOffer); } }; diff --git a/src/test/app/Path_test.cpp b/src/test/app/Path_test.cpp index a801cc4eef..18e82f7084 100644 --- a/src/test/app/Path_test.cpp +++ b/src/test/app/Path_test.cpp @@ -1031,84 +1031,6 @@ public: BEAST_EXPECT(same(st, stpath(G3, IPE(xrpIssue())))); } - void path_find_03() - { - testcase("Path Find: CNY"); - using namespace jtx; - Env env{*this, - supported_amendments().reset(featureFlow)}; - - Account A1 {"A1"}; - Account A2 {"A2"}; - Account A3 {"A3"}; - Account SRC {"SRC"}; - Account GATEWAY_DST {"GATEWAY_DST"}; - Account MONEY_MAKER_1 {"MONEY_MAKER_1"}; - Account MONEY_MAKER_2 {"MONEY_MAKER_2"}; - - env.fund(XRP(4999.999898), SRC); - env.fund(XRP(10846.168060), GATEWAY_DST); - env.fund(XRP(4291.430036), MONEY_MAKER_1); - env.fund(XRP(106839.375770), MONEY_MAKER_2); - env.fund(XRP(1240.997150), A1); - env.fund(XRP(14115.046893), A2); - env.fund(XRP(512087.883181), A3); - env.close(); - - env.trust(MONEY_MAKER_1["CNY"](1001), MONEY_MAKER_2); - env.trust(GATEWAY_DST["CNY"](1001), MONEY_MAKER_2); - env.trust(MONEY_MAKER_1["CNY"](1000000), A1); - env.trust(MONEY_MAKER_1["BTC"](10000), A1); - env.trust(GATEWAY_DST["USD"](1000), A1); - env.trust(GATEWAY_DST["CNY"](1000), A1); - env.trust(MONEY_MAKER_1["CNY"](3000), A2); - env.trust(GATEWAY_DST["CNY"](3000), A2); - env.trust(MONEY_MAKER_1["CNY"](10000), A3); - env.trust(GATEWAY_DST["CNY"](10000), A3); - env.close(); - - env(pay(MONEY_MAKER_1, MONEY_MAKER_2, - STAmount{ MONEY_MAKER_1["CNY"].issue(), UINT64_C(3599), -13})); - env(pay(GATEWAY_DST, MONEY_MAKER_2, - GATEWAY_DST["CNY"](137.6852546843001))); - env(pay(MONEY_MAKER_1, A1, - STAmount{ MONEY_MAKER_1["CNY"].issue(), UINT64_C(119761), -13})); - env(pay(GATEWAY_DST, A1, GATEWAY_DST["CNY"](33.047994))); - env(pay(MONEY_MAKER_1, A2, MONEY_MAKER_1["CNY"](209.3081873019994))); - env(pay(GATEWAY_DST, A2, GATEWAY_DST["CNY"](694.6251706504019))); - env(pay(MONEY_MAKER_1, A3, MONEY_MAKER_1["CNY"](23.617050013581))); - env(pay(GATEWAY_DST, A3, GATEWAY_DST["CNY"](70.999614649799))); - env.close(); - - env(offer(MONEY_MAKER_2, XRP(1), GATEWAY_DST["CNY"](1))); - env(offer(MONEY_MAKER_2, GATEWAY_DST["CNY"](1), XRP(1))); - env(offer(MONEY_MAKER_2, GATEWAY_DST["CNY"](318000), XRP(53000))); - env(offer(MONEY_MAKER_2, XRP(209), MONEY_MAKER_2["CNY"](4.18))); - env(offer(MONEY_MAKER_2, MONEY_MAKER_1["CNY"](990000), XRP(10000))); - env(offer(MONEY_MAKER_2, MONEY_MAKER_1["CNY"](9990000), XRP(10000))); - env(offer(MONEY_MAKER_2, GATEWAY_DST["CNY"](8870000), XRP(10000))); - env(offer(MONEY_MAKER_2, XRP(232), MONEY_MAKER_2["CNY"](5.568))); - env(offer(A2, XRP(2000), MONEY_MAKER_1["CNY"](66.8))); - env(offer(A2, XRP(1200), GATEWAY_DST["CNY"](42))); - env(offer(A2, MONEY_MAKER_1["CNY"](43.2), XRP(900))); - env(offer(A3, MONEY_MAKER_1["CNY"](2240), XRP(50000))); - - STPathSet st; - STAmount sa, da; - - auto const& send_amt = GATEWAY_DST["CNY"](10.1); - std::tie(st, sa, da) = find_paths(env, SRC, GATEWAY_DST, send_amt, - boost::none, xrpCurrency()); - BEAST_EXPECT(equal(da, send_amt)); - BEAST_EXPECT(equal(sa, XRP(288.571429))); - BEAST_EXPECT(same(st, - stpath(IPE(MONEY_MAKER_1["CNY"]), MONEY_MAKER_1, A3), - stpath(IPE(MONEY_MAKER_1["CNY"]), MONEY_MAKER_1, MONEY_MAKER_2), - stpath(IPE(MONEY_MAKER_1["CNY"]), MONEY_MAKER_1, A2), - stpath(IPE(MONEY_MAKER_1["CNY"]), MONEY_MAKER_1, A1) - )); - } - void path_find_04() { testcase("Path Find: Bitstamp and SnapSwap, liquidity with no offers"); @@ -1393,7 +1315,6 @@ public: path_find_01(); path_find_02(); - path_find_03(); path_find_04(); path_find_05(); path_find_06(); diff --git a/src/test/app/PayStrand_test.cpp b/src/test/app/PayStrand_test.cpp index 7f0835d35e..3b7c4c6f9a 100644 --- a/src/test/app/PayStrand_test.cpp +++ b/src/test/app/PayStrand_test.cpp @@ -622,237 +622,6 @@ struct ExistingElementPool } }; -struct PayStrandAllPairs_test : public beast::unit_test::suite -{ - // Test every combination of element type pairs on a path - void - testAllPairs(FeatureBitset features) - { - testcase("All pairs"); - using namespace jtx; - using RippleCalc = ::ripple::path::RippleCalc; - - ExistingElementPool eep; - Env env(*this, features); - - eep.setupEnv(env, /*numAcc*/ 9, /*numCur*/ 6, boost::none); - env.close(); - - auto const src = eep.getAvailAccount(); - auto const dst = eep.getAvailAccount(); - - RippleCalc::Input inputs; - inputs.defaultPathsAllowed = false; - - auto callback = [&]( - STAmount const& sendMax, - STAmount const& deliver, - std::vector const& p) { - std::array sbs{ - {PaymentSandbox{env.current().get(), tapNONE}, - PaymentSandbox{env.current().get(), tapNONE}}}; - std::array rcOutputs; - // pay with both env1 and env2 - // check all result and account balances match - // save results so can see if run out of funds or somesuch - STPathSet paths; - paths.emplace_back(p); - for (auto i = 0; i < 2; ++i) - { - if (i == 0) - env.app().config().features.insert(featureFlow); - else - env.app().config().features.erase(featureFlow); - - try - { - rcOutputs[i] = RippleCalc::rippleCalculate( - sbs[i], - sendMax, - deliver, - dst, - src, - paths, - env.app().logs(), - &inputs); - } - catch (...) - { - this->fail(); - } - } - - // check combinations of src and dst currencies (inc xrp) - // Check the results - auto const terMatch = [&] { - if (rcOutputs[0].result() == rcOutputs[1].result()) - return true; - - // handle some know error code mismatches - if (p.empty() || - !(rcOutputs[0].result() == temBAD_PATH || - rcOutputs[0].result() == temBAD_PATH_LOOP)) - return false; - - if (rcOutputs[1].result() == temBAD_PATH) - return true; - - if (rcOutputs[1].result() == terNO_LINE) - return true; - - for (auto const& pe : p) - { - auto const t = pe.getNodeType(); - if ((t & STPathElement::typeAccount) && - t != STPathElement::typeAccount) - { - return true; - } - } - - // xrp followed by offer that doesn't specify both currency and - // issuer (and currency is not xrp, if specifyed) - if (isXRP(sendMax) && - !(p[0].hasCurrency() && isXRP(p[0].getCurrency())) && - !(p[0].hasCurrency() && p[0].hasIssuer())) - { - return true; - } - - for (size_t i = 0; i < p.size() - 1; ++i) - { - auto const tCur = p[i].getNodeType(); - auto const tNext = p[i + 1].getNodeType(); - if ((tCur & STPathElement::typeCurrency) && - isXRP(p[i].getCurrency()) && - (tNext & STPathElement::typeAccount) && - !isXRP(p[i + 1].getAccountID())) - { - return true; - } - } - return false; - }(); - - this->BEAST_EXPECT( - terMatch && (rcOutputs[0].result() == tesSUCCESS || - rcOutputs[0].result() == temBAD_PATH || - rcOutputs[0].result() == temBAD_PATH_LOOP)); - if (terMatch && rcOutputs[0].result() == tesSUCCESS) - this->BEAST_EXPECT(eep.checkBalances(sbs[0], sbs[1])); - }; - - std::vector prefix; - std::vector suffix; - - for (auto const srcAmtIsXRP : {false, true}) - { - for (auto const dstAmtIsXRP : {false, true}) - { - for (auto const hasPrefix : {false, true}) - { - ExistingElementPool::StateGuard esg{eep}; - prefix.clear(); - suffix.clear(); - - STAmount const sendMax{ - srcAmtIsXRP ? xrpIssue() : Issue{eep.getAvailCurrency(), - eep.getAvailAccount()}, - -1, // (-1 == no limit) - 0}; - - STAmount const deliver{ - dstAmtIsXRP ? xrpIssue() : Issue{eep.getAvailCurrency(), - eep.getAvailAccount()}, - 1, - 0}; - - if (hasPrefix) - { - for(auto const e0IsAccount : {false, true}) - { - for (auto const e1IsAccount : {false, true}) - { - ExistingElementPool::StateGuard presg{eep}; - prefix.clear(); - auto pushElement = - [&prefix, &eep](bool isAccount) mutable { - if (isAccount) - prefix.emplace_back( - eep.getAvailAccount().id(), - boost::none, - boost::none); - else - prefix.emplace_back( - boost::none, - eep.getAvailCurrency(), - eep.getAvailAccount().id()); - }; - pushElement(e0IsAccount); - pushElement(e1IsAccount); - boost::optional existingAcc; - boost::optional existingCur; - boost::optional existingIss; - if (e0IsAccount) - { - existingAcc = prefix[0].getAccountID(); - } - else - { - existingIss = prefix[0].getIssuerID(); - existingCur = prefix[0].getCurrency(); - } - if (e1IsAccount) - { - if (!existingAcc) - existingAcc = prefix[1].getAccountID(); - } - else - { - if (!existingIss) - existingIss = prefix[1].getIssuerID(); - if (!existingCur) - existingCur = prefix[1].getCurrency(); - } - eep.for_each_element_pair( - sendMax, - deliver, - prefix, - suffix, - existingAcc, - existingCur, - existingIss, - callback); - } - } - } - else - { - eep.for_each_element_pair( - sendMax, - deliver, - prefix, - suffix, - /*existingAcc*/ boost::none, - /*existingCur*/ boost::none, - /*existingIss*/ boost::none, - callback); - } - } - } - } - } - void - run() override - { - auto const sa = jtx::supported_amendments(); - testAllPairs(sa - featureFlowCross); - testAllPairs(sa); - } -}; - -BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(PayStrandAllPairs, app, ripple, 12); - struct PayStrand_test : public beast::unit_test::suite { void @@ -1379,7 +1148,7 @@ struct PayStrand_test : public beast::unit_test::suite env(offer(bob, USD(100), XRP(100)), txflags(tfPassive)); auto const expectedResult = [&] () -> TER { - if (features[featureFlow] && !features[fix1373]) + if (!features[fix1373]) return tesSUCCESS; return temBAD_PATH_LOOP; }(); @@ -1480,13 +1249,12 @@ struct PayStrand_test : public beast::unit_test::suite testToStrand(sa - featureFlowCross); testToStrand(sa); - testRIPD1373(sa - featureFlow - fix1373 - featureFlowCross); - testRIPD1373(sa - featureFlowCross); + testRIPD1373(sa - fix1373 - featureFlowCross); + testRIPD1373(sa - featureFlowCross); testRIPD1373(sa); - testLoop(sa - featureFlow - fix1373 - featureFlowCross); - testLoop(sa - fix1373 - featureFlowCross); - testLoop(sa - featureFlowCross); + testLoop(sa - fix1373 - featureFlowCross); + testLoop(sa - featureFlowCross); testLoop(sa); testNoAccount(sa); diff --git a/src/test/app/SetAuth_test.cpp b/src/test/app/SetAuth_test.cpp index 4eb1b0d266..f9a2134862 100644 --- a/src/test/app/SetAuth_test.cpp +++ b/src/test/app/SetAuth_test.cpp @@ -70,7 +70,6 @@ struct SetAuth_test : public beast::unit_test::suite { using namespace jtx; auto const sa = supported_amendments(); - testAuth(sa - featureFlow - fix1373 - featureFlowCross); testAuth(sa - fix1373 - featureFlowCross); testAuth(sa - featureFlowCross); testAuth(sa); diff --git a/src/test/app/TrustAndBalance_test.cpp b/src/test/app/TrustAndBalance_test.cpp index ab43bd1e8c..bc65dec2aa 100644 --- a/src/test/app/TrustAndBalance_test.cpp +++ b/src/test/app/TrustAndBalance_test.cpp @@ -510,9 +510,8 @@ public: using namespace test::jtx; auto const sa = supported_amendments(); - testWithFeatures(sa - featureFlow - fix1373 - featureFlowCross); - testWithFeatures(sa - fix1373 - featureFlowCross); - testWithFeatures(sa -featureFlowCross); + testWithFeatures(sa - fix1373 - featureFlowCross); + testWithFeatures(sa -featureFlowCross); testWithFeatures(sa); } }; diff --git a/src/test/ledger/BookDirs_test.cpp b/src/test/ledger/BookDirs_test.cpp index f9781f29fd..2df04bf975 100644 --- a/src/test/ledger/BookDirs_test.cpp +++ b/src/test/ledger/BookDirs_test.cpp @@ -95,8 +95,8 @@ struct BookDirs_test : public beast::unit_test::suite { using namespace jtx; auto const sa = supported_amendments(); - test_bookdir(sa - featureFlow - fix1373 - featureFlowCross); - test_bookdir(sa - featureFlowCross); + test_bookdir(sa - fix1373 - featureFlowCross); + test_bookdir(sa - featureFlowCross); test_bookdir(sa); } }; diff --git a/src/test/ledger/PaymentSandbox_test.cpp b/src/test/ledger/PaymentSandbox_test.cpp index 48a8449978..2f0ed7ce43 100644 --- a/src/test/ledger/PaymentSandbox_test.cpp +++ b/src/test/ledger/PaymentSandbox_test.cpp @@ -127,13 +127,17 @@ class PaymentSandbox_test : public beast::unit_test::suite auto const iss = USD_gw1.issue (); auto const startingAmount = accountHolds ( av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); - - accountSend (av, gw1, alice, toCredit, j); + { + auto r = accountSend (av, gw1, alice, toCredit, j); + BEAST_EXPECT(r == tesSUCCESS); + } BEAST_EXPECT(accountHolds (av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount + toCredit); - - accountSend (av, alice, gw1, toDebit, j); + { + auto r = accountSend(av, alice, gw1, toDebit, j); + BEAST_EXPECT(r == tesSUCCESS); + } BEAST_EXPECT(accountHolds (av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount + toCredit - toDebit); @@ -167,12 +171,18 @@ class PaymentSandbox_test : public beast::unit_test::suite auto const startingAmount = accountHolds ( pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); - accountSend (pv, gw1, alice, toCredit, j); + { + auto r = accountSend (pv, gw1, alice, toCredit, j); + BEAST_EXPECT(r == tesSUCCESS); + } BEAST_EXPECT(accountHolds (pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); - accountSend (pv, alice, gw1, toDebit, j); + { + auto r = accountSend (pv, alice, gw1, toDebit, j); + BEAST_EXPECT(r == tesSUCCESS); + } BEAST_EXPECT(accountHolds (pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount - toDebit); @@ -232,7 +242,10 @@ class PaymentSandbox_test : public beast::unit_test::suite auto const startingAmount = accountHolds ( pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j); - accountSend (pv, gw1, alice, toCredit, j); + { + auto r = accountSend (pv, gw1, alice, toCredit, j); + BEAST_EXPECT(r == tesSUCCESS); + } BEAST_EXPECT(accountHolds (pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); @@ -242,13 +255,19 @@ class PaymentSandbox_test : public beast::unit_test::suite BEAST_EXPECT(accountHolds (pv2, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); - accountSend (pv2, gw1, alice, toCredit, j); + { + auto r = accountSend (pv2, gw1, alice, toCredit, j); + BEAST_EXPECT(r == tesSUCCESS); + } BEAST_EXPECT(accountHolds (pv2, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount); } - accountSend (pv, alice, gw1, toDebit, j); + { + auto r = accountSend (pv, alice, gw1, toDebit, j); + BEAST_EXPECT(r == tesSUCCESS); + } BEAST_EXPECT(accountHolds (pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) == startingAmount - toDebit); @@ -313,8 +332,14 @@ class PaymentSandbox_test : public beast::unit_test::suite // to drop below the reserve. Make sure her funds are zero (there was a bug that // caused her funds to become negative). - accountSend (sb, xrpAccount (), alice, XRP(100), env.journal); - accountSend (sb, alice, xrpAccount (), XRP(100), env.journal); + { + auto r = accountSend (sb, xrpAccount (), alice, XRP(100), env.journal); + BEAST_EXPECT(r == tesSUCCESS); + } + { + auto r = accountSend (sb, alice, xrpAccount (), XRP(100), env.journal); + BEAST_EXPECT(r == tesSUCCESS); + } BEAST_EXPECT( accountFundsXRP (sb, alice, env.journal) == beast::zero); } @@ -362,8 +387,8 @@ public: }; using namespace jtx; auto const sa = supported_amendments(); - testAll(sa - featureFlow - fix1373 - featureFlowCross); - testAll(sa - featureFlowCross); + testAll(sa - fix1373 - featureFlowCross); + testAll(sa - featureFlowCross); testAll(sa); } }; diff --git a/src/test/rpc/GatewayBalances_test.cpp b/src/test/rpc/GatewayBalances_test.cpp index 7715ec9742..0f09021bf0 100644 --- a/src/test/rpc/GatewayBalances_test.cpp +++ b/src/test/rpc/GatewayBalances_test.cpp @@ -154,8 +154,8 @@ public: { using namespace jtx; auto const sa = supported_amendments(); - testGWB(sa - featureFlow - fix1373 - featureFlowCross); - testGWB(sa - featureFlowCross); + testGWB(sa - fix1373 - featureFlowCross); + testGWB(sa - featureFlowCross); testGWB(sa); } }; diff --git a/src/test/rpc/NoRipple_test.cpp b/src/test/rpc/NoRipple_test.cpp index c8491561b5..71c62b6877 100644 --- a/src/test/rpc/NoRipple_test.cpp +++ b/src/test/rpc/NoRipple_test.cpp @@ -269,9 +269,8 @@ public: }; using namespace jtx; auto const sa = supported_amendments(); - withFeatsTests(sa - featureFlow - fix1373 - featureFlowCross); - withFeatsTests(sa - fix1373 - featureFlowCross); - withFeatsTests(sa - featureFlowCross); + withFeatsTests(sa - fix1373 - featureFlowCross); + withFeatsTests(sa - featureFlowCross); withFeatsTests(sa); } };