diff --git a/src/LedgerEntrySet.cpp b/src/LedgerEntrySet.cpp index e325b47dee..ad65ddd9f4 100644 --- a/src/LedgerEntrySet.cpp +++ b/src/LedgerEntrySet.cpp @@ -15,7 +15,7 @@ void LedgerEntrySet::clear() mSet.clear(); } -LedgerEntrySet LedgerEntrySet::duplicate() +LedgerEntrySet LedgerEntrySet::duplicate() const { return LedgerEntrySet(mEntries, mSet, mSeq + 1); } @@ -180,4 +180,9 @@ void LedgerEntrySet::entryDelete(SLE::pointer& sle) } } +bool LedgerEntrySet::intersect(const LedgerEntrySet& lesLeft, const LedgerEntrySet& lesRight) +{ + return true; // XXX Needs implementation +} + // vim:ts=4 diff --git a/src/LedgerEntrySet.h b/src/LedgerEntrySet.h index 01312e0b52..885281a01f 100644 --- a/src/LedgerEntrySet.h +++ b/src/LedgerEntrySet.h @@ -34,14 +34,14 @@ protected: TransactionMetaSet mSet; int mSeq; - LedgerEntrySet(const boost::unordered_map &e, TransactionMetaSet& s, int m) : + LedgerEntrySet(const boost::unordered_map &e, const TransactionMetaSet& s, int m) : mEntries(e), mSet(s), mSeq(m) { ; } public: LedgerEntrySet() : mSeq(0) { ; } // set functions - LedgerEntrySet duplicate(); // Make a duplicate of this set + LedgerEntrySet duplicate() const; // Make a duplicate of this set void setTo(LedgerEntrySet&); // Set this set to have the same contents as another void swapWith(LedgerEntrySet&); // Swap the contents of two sets @@ -64,6 +64,8 @@ public: boost::unordered_map::const_iterator end() const { return mEntries.end(); } boost::unordered_map::iterator begin() { return mEntries.begin(); } boost::unordered_map::iterator end() { return mEntries.end(); } + + static bool intersect(const LedgerEntrySet& lesLeft, const LedgerEntrySet& lesRight); }; #endif diff --git a/src/SerializedTypes.cpp b/src/SerializedTypes.cpp index fbd861c440..a341225d19 100644 --- a/src/SerializedTypes.cpp +++ b/src/SerializedTypes.cpp @@ -464,13 +464,13 @@ void STPathSet::add(Serializer& s) const s.add8(iType); - if (iType && STPathElement::typeAccount) + if (iType & STPathElement::typeAccount) s.add160(speElement.getAccountID()); - if (iType && STPathElement::typeCurrency) + if (iType & STPathElement::typeCurrency) s.add160(speElement.getCurrency()); - if (iType && STPathElement::typeIssuer) + if (iType & STPathElement::typeIssuer) s.add160(speElement.getIssuerID()); } } diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 69dde7cb91..5fb1fc94d4 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -2161,51 +2161,67 @@ bool calcPaymentForward(std::vector& pnNodes) } #endif -// Ensure sort order is complelely deterministic. -class PathStateCompare +bool PathState::less(const PathState::pointer& lhs, const PathState::pointer& rhs) { -public: - bool operator()(const PathState::pointer& lhs, const PathState::pointer& rhs) - { - // Return true, iff lhs has less priority than rhs. + // Return true, iff lhs has less priority than rhs. - if (lhs->uQuality != rhs->uQuality) - return lhs->uQuality > rhs->uQuality; // Bigger is worse. + if (lhs->uQuality != rhs->uQuality) + return lhs->uQuality > rhs->uQuality; // Bigger is worse. - // Best quanity is second rank. - if (lhs->saOut != rhs->saOut) - return lhs->saOut < rhs->saOut; // Smaller is worse. + // Best quanity is second rank. + if (lhs->saOut != rhs->saOut) + return lhs->saOut < rhs->saOut; // Smaller is worse. - // Path index is third rank. - return lhs->mIndex > rhs->mIndex; // Bigger is worse. - } -}; - -PathState::pointer TransactionEngine::pathCreate(const STPath& spPath) -{ - return PathState::pointer(); + // Path index is third rank. + return lhs->mIndex > rhs->mIndex; // Bigger is worse. } -// Calcuate the next increment of a path. +PathState::PathState( + int iIndex, + const LedgerEntrySet& lesSource, + const STPath& spSourcePath, + uint160 uReceiverID, + uint160 uSenderID, + STAmount saSend, + STAmount saSendMax, + bool bPartialPayment + ) + : mIndex(iIndex), uQuality(0), bDirty(true) +{ + lesEntries = lesSource.duplicate(); + + paymentNode pnFirst; + paymentNode pnLast; + + pnLast.bPartialPayment = bPartialPayment; + pnLast.uAccount = uReceiverID; + pnLast.saWanted = saSend; + + pnFirst.uAccount = uSenderID; + pnFirst.saWanted = saSendMax; + + vpnNodes.push_back(pnFirst); + vpnNodes.push_back(pnLast); +} + +// Calculate the next increment of a path. void TransactionEngine::pathNext(PathState::pointer pspCur) { - } // Apply an increment of the path, then calculate the next increment. void TransactionEngine::pathApply(PathState::pointer pspCur) { - - pathNext(pspCur); } // XXX Need to audit for things like setting accountID not having memory. TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction& txn) { // Ripple if source or destination is non-native or if there are paths. - uint32 txFlags = txn.getFlags(); - bool bCreate = !!(txFlags & tfCreateAccount); - bool bNoRippleDirect = !!(txFlags & tfNoRippleDirect); + uint32 uTxFlags = txn.getFlags(); + bool bCreate = !!(uTxFlags & tfCreateAccount); + bool bNoRippleDirect = !!(uTxFlags & tfNoRippleDirect); + bool bPartialPayment = !!(uTxFlags & tfPartialPayment); bool bPaths = txn.getITFieldPresent(sfPaths); bool bMax = txn.getITFieldPresent(sfSendMax); uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); @@ -2385,7 +2401,6 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction STPathSet spsPaths = txn.getITFieldPathSet(sfPaths); - // XXX If we are parsing for determing forwarding check maximum path count. if (!spsPaths.isEmpty()) { Log(lsINFO) << "doPayment: Invalid transaction: No paths."; @@ -2398,45 +2413,80 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction } // Incrementally search paths. - std::priority_queue, PathStateCompare> pqPaths; -#if 0 - BOOST_FOREACH(std::vector::const_iterator::value_type spPath, spsPaths) - { - PathState::pointer pspCur = pathCreate(spPath); + std::vector vpsPaths; - pqPaths.push(pspCur); + BOOST_FOREACH(const STPath& spPath, spsPaths) + { + vpsPaths.push_back(PathState::createPathState( + vpsPaths.size(), + mNodes, + spPath, + uDstAccountID, + mTxnAccountID, + saDstAmount, + saMaxAmount, + bPartialPayment + )); } -#endif + TransactionEngineResult terResult; STAmount saPaid; STAmount saWanted; - uint32 uFlags = txn.getFlags(); // XXX Redundant. terResult = tenUNKNOWN; while (tenUNKNOWN == terResult) { - if (!pqPaths.empty()) + PathState::pointer pspBest; + + // Find the best path. + BOOST_FOREACH(PathState::pointer pspCur, vpsPaths) { - // Have a path to contribute. - PathState::pointer pspCur = pqPaths.top(); + if (pspCur->bDirty) + { + pspCur->bDirty = false; + pspCur->lesEntries = mNodes.duplicate(); - pqPaths.pop(); + // XXX Compute increment + pathNext(pspCur); + } - pathApply(pspCur); + if (!pspBest || (pspCur->uQuality && PathState::less(pspBest, pspCur))) + pspBest = pspCur; + } + if (!pspBest) + { + // + // Apply path. + // + + // Install changes for path. + mNodes.swapWith(pspBest->lesEntries); + + // Mark that path as dirty. + pspBest->bDirty = true; + + // Mark as dirty any other path that intersected. + BOOST_FOREACH(PathState::pointer& pspOther, vpsPaths) + { + // Look for intersection of best and the others. + // - Will forget the intersection applied. + // - Anything left will not interfere with it. + // - Will remember the non-intersection non-applied for future consideration. + if (!pspOther->bDirty + && pspOther->uQuality + && LedgerEntrySet::intersect(pspBest->lesEntries, pspOther->lesEntries)) + pspOther->bDirty = true; + } + + // Figure out if done. if (tenUNKNOWN == terResult && saPaid == saWanted) { terResult = terSUCCESS; } - - if (tenUNKNOWN == terResult && pspCur->uQuality) - { - // Current path still has something to contribute. - pqPaths.push(pspCur); - } } // Ran out of paths. - else if ((!uFlags & tfPartialPayment)) + else if (!bPartialPayment) { // Partial payment not allowed. terResult = terPATH_PARTIAL; // XXX No effect, except unfunded and charge fee. diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 82a651181b..f470e98ab4 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -105,14 +105,60 @@ class PathState public: typedef boost::shared_ptr pointer; - int mIndex; - uint64 uQuality; // 0 = none. - STAmount saIn; - STAmount saOut; + typedef struct { + bool bPartialPayment; // --> + uint16 uFlags; // --> from path - PathState(int iIndex) : mIndex(iIndex) { ; }; + uint160 uAccount; // --> recieving/sending account - static PathState::pointer createPathState(int iIndex) { return boost::make_shared(iIndex); }; + STAmount saWanted; // --> What this node wants from upstream. + + // Maybe this should just be a bool: + // STAmount saIOURedeemMax; // --> Max amount of IOUs to redeem downstream. + // Maybe this should just be a bool: + // STAmount saIOUIssueMax; // --> Max Amount of IOUs to issue downstream. + + STAmount saIOURedeem; // <-- What this node will redeem downstream. + STAmount saIOUIssue; // <-- What this node will issue downstream. + STAmount saSend; // <-- Stamps this node will send downstream. + + STAmount saRecieve; // Amount stamps to receive. + + } paymentNode; + + std::vector vpnNodes; + LedgerEntrySet lesEntries; + + int mIndex; + uint64 uQuality; // 0 = none. + STAmount saIn; + STAmount saOut; + bool bDirty; // Path not computed. + + PathState( + int iIndex, + const LedgerEntrySet& lesSource, + const STPath& spSourcePath, + uint160 uReceiverID, + uint160 uSenderID, + STAmount saSend, + STAmount saSendMax, + bool bPartialPayment + ); + + static PathState::pointer createPathState( + int iIndex, + const LedgerEntrySet& lesSource, + const STPath& spSourcePath, + uint160 uReceiverID, + uint160 uSenderID, + STAmount saSend, + STAmount saSendMax, + bool bPartialPayment + ) + { return boost::make_shared(iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax, bPartialPayment); }; + + static bool less(const PathState::pointer& lhs, const PathState::pointer& rhs); }; // One instance per ledger. @@ -137,25 +183,6 @@ private: bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex); #ifdef WORK_IN_PROGRESS - typedef struct { - uint16 uFlags; // --> from path - - STAccount saAccount; // --> recieving/sending account - - STAmount saWanted; // --> What this node wants from upstream. - - // Maybe this should just be a bool: - STAmount saIOURedeemMax; // --> Max amount of IOUs to redeem downstream. - // Maybe this should just be a bool: - STAmount saIOUIssueMax; // --> Max Amount of IOUs to issue downstream. - - STAmount saIOURedeem; // <-- What this node will redeem downstream. - STAmount saIOUIssue; // <-- What this node will issue downstream. - STAmount saSend; // <-- Stamps this node will send downstream. - - STAmount saRecieve; // Amount stamps to receive. - - } paymentNode; typedef struct { std::vector vpnNodes;