bool RippleCalc::addPathState(STPath const& path, TER& resultCode) { auto pathState = std::make_shared<PathState> ( saDstAmountReq_, saMaxAmountReq_); if (!pathState) { resultCode = temUNKNOWN; return false; } pathState->expandPath ( mActiveLedger, path, uDstAccountID_, uSrcAccountID_); if (pathState->status() == tesSUCCESS) pathState->checkNoRipple (uDstAccountID_, uSrcAccountID_); if (pathState->status() == tesSUCCESS) pathState->checkFreeze (); pathState->setIndex (pathStateList_.size ()); WriteLog (lsDEBUG, RippleCalc) << "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; }
// Check a fully-expanded path to make sure it doesn't violate no-Ripple settings void PathState::checkNoRipple (uint160 const& uDstAccountID, uint160 const& uSrcAccountID) { // There must be at least one node for there to be two consecutive ripple lines if (vpnNodes.size() == 0) return; if (vpnNodes.size() == 1) { // There's just one link in the path // We only need to check source-node-dest if (is_bit_set (vpnNodes[0].uFlags, STPathElement::typeAccount) && (vpnNodes[0].uAccountID != uSrcAccountID) && (vpnNodes[0].uAccountID != uDstAccountID)) { if (saInReq.getCurrency() != saOutReq.getCurrency()) terStatus = terNO_LINE; else checkNoRipple (uSrcAccountID, vpnNodes[0].uAccountID, uDstAccountID, vpnNodes[0].uCurrencyID); } return; } // Check source <-> first <-> second if (is_bit_set (vpnNodes[0].uFlags, STPathElement::typeAccount) && is_bit_set (vpnNodes[1].uFlags, STPathElement::typeAccount) && (vpnNodes[0].uAccountID != uSrcAccountID)) { if ((vpnNodes[0].uCurrencyID != vpnNodes[1].uCurrencyID)) { terStatus = terNO_LINE; return; } else { checkNoRipple (uSrcAccountID, vpnNodes[0].uAccountID, vpnNodes[1].uAccountID, vpnNodes[0].uCurrencyID); if (tesSUCCESS != terStatus) return; } } // Check second_from_last <-> last <-> destination size_t s = vpnNodes.size() - 2; if (is_bit_set (vpnNodes[s].uFlags, STPathElement::typeAccount) && is_bit_set (vpnNodes[s+1].uFlags, STPathElement::typeAccount) && (uDstAccountID != vpnNodes[s+1].uAccountID)) { if ((vpnNodes[s].uCurrencyID != vpnNodes[s+1].uCurrencyID)) { terStatus = terNO_LINE; return; } else { checkNoRipple (vpnNodes[s].uAccountID, vpnNodes[s+1].uAccountID, uDstAccountID, vpnNodes[s].uCurrencyID); if (tesSUCCESS != terStatus) return; } } // 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 < (vpnNodes.size() - 1); ++i) { if (is_bit_set (vpnNodes[i-1].uFlags, STPathElement::typeAccount) && is_bit_set (vpnNodes[i].uFlags, STPathElement::typeAccount) && is_bit_set (vpnNodes[i+1].uFlags, STPathElement::typeAccount)) { // two consecutive account-to-account links uint160 const& currencyID = vpnNodes[i].uCurrencyID; if ((vpnNodes[i-1].uCurrencyID != currencyID) || (vpnNodes[i+1].uCurrencyID != currencyID)) { terStatus = temBAD_PATH; return; } checkNoRipple ( vpnNodes[i-1].uAccountID, vpnNodes[i].uAccountID, vpnNodes[i+1].uAccountID, currencyID); if (terStatus != tesSUCCESS) return; } } }
// 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; } } return tesSUCCESS; }