diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 70695407b8..c0c32519c5 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -836,7 +836,6 @@ SLE::pointer TransactionEngine::entryCreate(LedgerEntryType letType, const uint2 return sleNew; } - void TransactionEngine::entryDelete(SLE::pointer sleEntry, bool unfunded) { mNodes.entryDelete(sleEntry, unfunded); @@ -3046,16 +3045,86 @@ bool PathState::lessPriority(const PathState::pointer& lhs, const PathState::poi return lhs->mIndex > rhs->mIndex; // Bigger is worse. } -// Add a node and insert any implied nodes. +// Make sure the path delivers to uAccountID: uCurrencyID from uIssuerID. +bool PathState::pushImply(uint160 uAccountID, uint160 uCurrencyID, uint160 uIssuerID) +{ + const paymentNode& pnPrv = vpnNodes.back(); + bool bValid = true; + + Log(lsINFO) << "pushImply> " + << NewcoinAddress::createHumanAccountID(uAccountID) + << " " << STAmount::createHumanCurrency(uCurrencyID) + << " " << NewcoinAddress::createHumanAccountID(uIssuerID); + + if (pnPrv.uCurrencyID != uCurrencyID) + { + // Need to convert via an offer. +#if 0 +// XXX Don't know if need this. + bool bPrvAccount = !!pnPrv.uAccountID; + + if (!bPrvAccount) // Previous is an offer. + { + // Direct offer --> offer is not allowed for non-stamps. + // Need to ripple through uIssuerID's account. + + bValid = pushNode( + STPathElement::typeAccount + | STPathElement::typeIssue, + pnPrv.uIssuerID, // Intermediate account is the needed issuer. + CURRENCY_ONE, // Inherit from previous. + ACCOUNT_ONE); // Default same as account. + } +#endif + + bValid = pushNode( + 0, // Offer + ACCOUNT_ONE, // Placeholder for offers. + uCurrencyID, // The offer's output is what is now wanted. + uIssuerID); + + } + + if (bValid + && !!uCurrencyID // Not stamps. + && (pnPrv.uAccountID != uIssuerID // Previous is not issuing own IOUs. + && uAccountID != uIssuerID)) // Current is not receiving own IOUs. + { + // Need to ripple through uIssuerID's account. + + bValid = pushNode( + STPathElement::typeAccount + | STPathElement::typeRedeem + | STPathElement::typeIssue, + uIssuerID, // Intermediate account is the needed issuer. + uCurrencyID, + uIssuerID); + } + + Log(lsINFO) << "pushImply< " << bValid; + + return bValid; +} + +// Append a node and insert before it any implied nodes. // <-- bValid: true, if node is valid. false, if node is malformed. bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint160 uIssuerID) { + Log(lsINFO) << "pushNode> " + << NewcoinAddress::createHumanAccountID(uAccountID) + << " " << STAmount::createHumanCurrency(uCurrencyID) + << " " << NewcoinAddress::createHumanAccountID(uIssuerID); paymentNode pnCur; bool bFirst = vpnNodes.empty(); const paymentNode& pnPrv = bFirst ? paymentNode() : vpnNodes.back(); + // true, iff node is a ripple account. false, iff node is an offer node. bool bAccount = !!(iType & STPathElement::typeAccount); + // true, iff currency supplied. + // Currency is specified for the output of the current node. bool bCurrency = !!(iType & STPathElement::typeCurrency); + // Issuer is specified for the output of the current node. bool bIssuer = !!(iType & STPathElement::typeIssuer); + // true, iff account is allowed to redeem it's IOUs to next node. bool bRedeem = !!(iType & STPathElement::typeRedeem); bool bIssue = !!(iType & STPathElement::typeIssue); bool bValid = true; @@ -3064,6 +3133,8 @@ bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uin if (iType & ~STPathElement::typeValidBits) { + Log(lsINFO) << "pushNode: bad bits."; + bValid = false; } else if (bAccount) @@ -3076,30 +3147,13 @@ bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uin pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID; pnCur.uIssuerID = bIssuer ? uIssuerID : uAccountID; - // An intermediate node may be implied. - - if (uCurrencyID != pnPrv.uCurrencyID) + if (!bFirst) { - // Implied preceeding offer. - - bValid = pushNode( - 0, - ACCOUNT_ONE, - CURRENCY_ONE, // Inherit from previous - ACCOUNT_ONE); // Inherit from previous - } - - if (bValid && uIssuerID != pnPrv.uIssuerID) - { - // Implied preceeding account. - - bValid = pushNode( - STPathElement::typeAccount - | STPathElement::typeRedeem - | STPathElement::typeIssue, - uIssuerID, - CURRENCY_ONE, // Inherit from previous - ACCOUNT_ONE); // Default same as account. + // Add required intermediate nodes to deliver to current account. + bValid = pushImply( + pnCur.uAccountID, // Current account. + pnCur.uCurrencyID, // Wanted currency. + !!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as issuer. } if (bValid) @@ -3107,6 +3161,8 @@ bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uin } else { + Log(lsINFO) << "pushNode: Account must redeem and/or issue."; + bValid = false; } } @@ -3119,23 +3175,28 @@ bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uin } else { - pnCur.uAccountID = uAccountID; + // Offers bridge a change in currency & issuer or just a change in issuer. pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID; pnCur.uIssuerID = bIssuer ? uIssuerID : pnCur.uAccountID; - if (!!pnPrv.uAccountID // Previous is an account. - && pnPrv.uAccountID != pnCur.uIssuerID) // Account is not issuer. + if (!!pnPrv.uAccountID) { - // Implied preceeding account. + // Previous is an account. - bValid = pushNode( - STPathElement::typeAccount - | STPathElement::typeIssue, - uIssuerID, - CURRENCY_ONE, // Inherit from previous - ACCOUNT_ONE); // Default same as account. + // Insert intermediary account if needed. + bValid = pushImply( + !!pnPrv.uCurrencyID ? ACCOUNT_ONE : ACCOUNT_XNS, + pnPrv.uCurrencyID, + pnPrv.uIssuerID); } - else if (bValid) + else + { + // Previous is an offer. + // XXX Need code if we don't do offer to offer. + nothing(); + } + + if (bValid) { // Verify that previous account is allowed to issue. const paymentNode& pnLst = vpnNodes.back(); @@ -3143,13 +3204,18 @@ bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uin bool bLstIssue = !!(pnLst.uFlags & STPathElement::typeIssue); if (bLstAccount && !bLstIssue) + { + Log(lsINFO) << "pushNode: previous account must be allowed to issue."; + bValid = false; // Malformed path. + } } if (bValid) vpnNodes.push_back(pnCur); } } + Log(lsINFO) << "pushNode< " << bValid; return bValid; } @@ -3167,24 +3233,55 @@ PathState::PathState( ) : mIndex(iIndex), uQuality(0) { + uint160 uInCurrencyID = saSendMax.getCurrency(); + uint160 uOutCurrencyID = saSend.getCurrency(); + uint160 uInIssuerID = !!uInCurrencyID ? uSenderID : ACCOUNT_XNS; + uint160 uOutIssuerID = !!uOutCurrencyID ? uReceiverID : ACCOUNT_XNS; + lesEntries = lesSource.duplicate(); - saOutReq = saSend; saInReq = saSendMax; + saOutReq = saSend; - pushNode(STPathElement::typeAccount, uSenderID, saSendMax.getCurrency(), saSendMax.getIssuer()); + bValid = pushNode( + STPathElement::typeAccount + | STPathElement::typeRedeem + | STPathElement::typeIssue + | STPathElement::typeCurrency + | STPathElement::typeIssuer, + uSenderID, + uInCurrencyID, + uInIssuerID); BOOST_FOREACH(const STPathElement& speElement, spSourcePath) { - pushNode(speElement.getNodeType(), speElement.getAccountID(), speElement.getCurrency(), speElement.getIssuerID()); + if (bValid) + bValid = pushNode(speElement.getNodeType(), speElement.getAccountID(), speElement.getCurrency(), speElement.getIssuerID()); } - pushNode(STPathElement::typeAccount, uReceiverID, saOutReq.getCurrency(), saOutReq.getIssuer()); + if (bValid) + { + // Create receiver node. + + bValid = pushImply(uReceiverID, uOutCurrencyID, uOutIssuerID); + if (bValid) + { + bValid = pushNode( + STPathElement::typeAccount // Last node is always an account. + | STPathElement::typeRedeem // Does not matter just pass error check. + | STPathElement::typeIssue, // Does not matter just pass error check. + uReceiverID, // Receive to output + uOutCurrencyID, // Desired currency + uOutIssuerID); + } + } + + Log(lsINFO) << "PathState: " << getJson(); } Json::Value PathState::getJson() const { - Json::Value jvPathState(Json::arrayValue); + Json::Value jvPathState(Json::objectValue); Json::Value jvNodes(Json::arrayValue); BOOST_FOREACH(const paymentNode& pnNode, vpnNodes) @@ -3213,14 +3310,14 @@ Json::Value PathState::getJson() const jvNodes.append(jvNode); } - jvPathState["nodes"] = jvNodes; - + jvPathState["valid"] = bValid; jvPathState["index"] = mIndex; + jvPathState["nodes"] = jvNodes; if (!!saInReq) jvPathState["in_req"] = saInReq.getJson(0); - if (!!saOutReq) + if (!!saInAct) jvPathState["in_act"] = saInAct.getJson(0); if (!!saOutReq) @@ -3232,7 +3329,7 @@ Json::Value PathState::getJson() const if (uQuality) jvPathState["uQuality"] = Json::Value::UInt(uQuality); - return jvNodes; + return jvPathState; } // Calculate a node and its previous nodes. @@ -3285,6 +3382,8 @@ void TransactionEngine::pathNext(PathState::pointer pspCur, int iPaths) Log(lsINFO) << "Path In: " << pspCur->getJson(); + assert(pspCur->vpnNodes.size() >= 2); + bool bZero = !calcNode(uLast, pspCur, iPaths == 1); pspCur->uQuality = bZero @@ -3502,6 +3601,7 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction if (!bNoRippleDirect) { // Direct path. + Log(lsINFO) << "doPayment: Build direct:"; vpsPaths.push_back(PathState::createPathState( vpsPaths.size(), mNodes, @@ -3516,6 +3616,7 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction BOOST_FOREACH(const STPath& spPath, spsPaths) { + Log(lsINFO) << "doPayment: Build path:"; vpsPaths.push_back(PathState::createPathState( vpsPaths.size(), mNodes, @@ -3536,14 +3637,13 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction while (tenUNKNOWN == terResult) { PathState::pointer pspBest; - LedgerEntrySet lesCheckpoint; - - mNodes.swapWith(lesCheckpoint); // Checkpoint ledger prior to path application. + LedgerEntrySet lesCheckpoint = mNodes; // Find the best path. BOOST_FOREACH(PathState::pointer pspCur, vpsPaths) { - mNodes.setTo(lesCheckpoint.duplicate()); // Vary ledger from checkpoint. + mNodes = lesCheckpoint; // Restore from checkpoint. + mNodes.bumpSeq(); // Begin ledger varance. pathNext(pspCur, vpsPaths.size()); // Compute increment diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 7bb68509e0..f6e054e4cd 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -126,10 +126,12 @@ class PathState { protected: bool pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint160 uIssuerID); + bool pushImply(uint160 uAccountID, uint160 uCurrencyID, uint160 uIssuerID); public: typedef boost::shared_ptr pointer; + bool bValid; std::vector vpnNodes; LedgerEntrySet lesEntries;