diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index ef2ce81650d..57070d1745d 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -7,17 +7,17 @@ true - - debug - x64 - release x64 + + debug + x64 + - + MultiByte Application v120 @@ -25,10 +25,10 @@ False False false - ..\..\build\msvc.debug\src\ - ..\..\build\msvc.debug\ + ..\..\build\msvc.release\src\ + ..\..\build\msvc.release\ - + MultiByte Application v120 @@ -36,40 +36,38 @@ False False false - ..\..\build\msvc.release\src\ - ..\..\build\msvc.release\ + ..\..\build\msvc.debug\src\ + ..\..\build\msvc.debug\ - + - + - + - _WIN32_WINNT=0x6000;DEBUG;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;_DEBUG;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _WIN32_WINNT=0x6000;NDEBUG;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) ..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;%(AdditionalIncludeDirectories) 4800;4244;4267;4018 Async - MultiThreadedDebug Precise False - EnableFastChecks None - True True True + MultiThreaded False True - Disabled False False ProgramDatabase Cdecl True + Full Level3 /bigobj /FS %(AdditionalOptions) @@ -85,26 +83,28 @@ /MANIFEST /TLBID:1 %(AdditionalOptions) - + - _WIN32_WINNT=0x6000;NDEBUG;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _WIN32_WINNT=0x6000;DEBUG;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;_DEBUG;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) ..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;%(AdditionalIncludeDirectories) 4800;4244;4267;4018 Async + MultiThreadedDebug Precise False + EnableFastChecks None + True True True - MultiThreaded False True + Disabled False False ProgramDatabase Cdecl True - Full Level3 /bigobj /FS %(AdditionalOptions) @@ -2250,42 +2250,13 @@ - - True - - - True - - - True - - - - - - - True - - - True - - - True - - - True - - - True - True - - True - + + True @@ -2306,8 +2277,6 @@ - - True @@ -2327,6 +2296,40 @@ + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + + + True + + + True + + + True + + + @@ -3210,16 +3213,16 @@ - Document - protoc --cpp_out=..\..\build\proto --proto_path=%(RelativeDir) %(Identity) - ..\..\build\proto\ripple.pb.h;..\..\build\proto\ripple.pb.cc - protoc --cpp_out=..\..\build\proto --proto_path=%(RelativeDir) %(Identity) - false Document protoc --cpp_out=..\..\build\proto --proto_path=%(RelativeDir) %(Identity) ..\..\build\proto\ripple.pb.h;..\..\build\proto\ripple.pb.cc protoc --cpp_out=..\..\build\proto --proto_path=%(RelativeDir) %(Identity) false + Document + protoc --cpp_out=..\..\build\proto --proto_path=%(RelativeDir) %(Identity) + ..\..\build\proto\ripple.pb.h;..\..\build\proto\ripple.pb.cc + protoc --cpp_out=..\..\build\proto --proto_path=%(RelativeDir) %(Identity) + false @@ -3452,8 +3455,8 @@ - ..\..\src\hyperleveldb;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) ..\..\src\hyperleveldb;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) + ..\..\src\hyperleveldb;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) @@ -3462,8 +3465,8 @@ - ..\..\src\leveldb;..\..\src\leveldb\include;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) ..\..\src\leveldb;..\..\src\leveldb\include;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) + ..\..\src\leveldb;..\..\src\leveldb\include;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) @@ -3472,8 +3475,8 @@ - ..\..\src\leveldb\include;..\..\src\rocksdb\include;%(AdditionalIncludeDirectories) ..\..\src\leveldb\include;..\..\src\rocksdb\include;%(AdditionalIncludeDirectories) + ..\..\src\leveldb\include;..\..\src\rocksdb\include;%(AdditionalIncludeDirectories) @@ -3494,8 +3497,8 @@ - ..\..\src\rocksdb;..\..\src\rocksdb\include;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) ..\..\src\rocksdb;..\..\src\rocksdb\include;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) + ..\..\src\rocksdb;..\..\src\rocksdb\include;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) @@ -3508,8 +3511,8 @@ - ..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) ..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) + ..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index ee3d75c7489..9fb59986996 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -382,6 +382,9 @@ {4A33F050-B4FA-BECE-FA9B-963BE4AAF1A0} + + {1ADC0318-326E-565A-CF79-DF72B8D889DB} + {08C8AFC4-AE5D-8717-F687-1E7D1900FCC2} @@ -3282,45 +3285,15 @@ ripple\module\app\node - - ripple\module\app\paths - - - ripple\module\app\paths - - - ripple\module\app\paths - - - ripple\module\app\paths - - - ripple\module\app\paths - - - ripple\module\app\paths - - - ripple\module\app\paths - - - ripple\module\app\paths - - - ripple\module\app\paths - - - ripple\module\app\paths - ripple\module\app\paths ripple\module\app\paths - + ripple\module\app\paths - + ripple\module\app\paths @@ -3345,9 +3318,6 @@ ripple\module\app\paths - - ripple\module\app\paths - ripple\module\app\paths @@ -3372,6 +3342,42 @@ ripple\module\app\paths + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + + + ripple\module\app\paths\cursor + ripple\module\app\peers diff --git a/src/ripple/module/app/consensus/LedgerConsensus.cpp b/src/ripple/module/app/consensus/LedgerConsensus.cpp index 46c0a234ac1..f6a8df6f116 100644 --- a/src/ripple/module/app/consensus/LedgerConsensus.cpp +++ b/src/ripple/module/app/consensus/LedgerConsensus.cpp @@ -24,13 +24,13 @@ namespace ripple { /** Provides the implementation for LedgerConsensus. - + Achieves consensus on the next ledger. This object is created when the consensus process starts, and is destroyed when the process is complete. - + Nearly everything herein is invoked with the master lock. - + Two things need consensus: 1. The set of transactions. 2. The close time for the ledger. @@ -53,7 +53,7 @@ class LedgerConsensusImp /** The result of applying a transaction to a ledger. - + @param clock The clock which will be used to measure time. @param localtx A set of local transactions to apply. @param prevLCLHash The hash of the Last Closed Ledger (LCL). @@ -139,7 +139,7 @@ class LedgerConsensusImp /** This function is called, but its return value is always ignored. - + @return 1. */ int startup () @@ -150,7 +150,7 @@ class LedgerConsensusImp /** Get the Json state of the consensus process. Called by the consensus_info RPC. - + @param full True if verbose response desired. @return The Json state. */ @@ -290,7 +290,7 @@ class LedgerConsensusImp Get a transaction tree, fetching it from the network if required and requested. When the transaction acquire engine successfully acquires a transaction set, it will call back. - + @param hash hash of the requested transaction tree. @param doAcquire true if we should get this from the network if we don't already have it. @@ -347,7 +347,7 @@ class LedgerConsensusImp /** We have a complete transaction set, typically acquired from the network - + @param hash hash of the transaction set. @param map the transaction set. @param acquired true if we have acquired the transaction set. @@ -438,7 +438,7 @@ class LedgerConsensusImp If a transaction set is popular, we probably have it. If it's unpopular, we probably don't need it (and the peer that initially made us retrieve it has probably already changed its position). - + @param hash hash of the transaction set. @return true if we need to acquire it, else false. */ @@ -546,7 +546,7 @@ class LedgerConsensusImp /** Change our view of the last closed ledger - + @param lclHash Hash of the last closed ledger. */ void handleLCL (uint256 const& lclHash) @@ -707,7 +707,7 @@ class LedgerConsensusImp /** We are establishing a consensus Update our position only on the timer, and in this state. - If we have consensus, move to the finish state + If we have consensus, move to the finish state */ void stateEstablish () { @@ -800,7 +800,7 @@ class LedgerConsensusImp /** A server has taken a new position, adjust our tracking Called when a peer takes a new postion. - + @param newPosition the new position @return true if we should do delayed relay of this position. */ @@ -873,7 +873,7 @@ class LedgerConsensusImp /** A peer has informed us that it can give us a transaction set - + @param peer The peer we can get it from. @param hashSet The transaction set we can get. @param status Says whether or not the peer has the transaction set @@ -903,7 +903,7 @@ class LedgerConsensusImp /** A peer has sent us some nodes from a transaction set - + @param peer The peer which has sent the nodes @param setHash The transaction set @param nodeIDs The nodes in the transaction set @@ -945,7 +945,7 @@ class LedgerConsensusImp } private: /** We have a new last closed ledger, process it. Final accept logic - + @param set Our consensus set */ void accept (SHAMap::pointer set) @@ -1189,7 +1189,7 @@ class LedgerConsensusImp /** Begin acquiring a transaction set - + @param acquire The transaction set to acquire. */ void startAcquiring (TransactionAcquire::pointer acquire) @@ -1244,7 +1244,7 @@ class LedgerConsensusImp /** Compare two proposed transaction sets and create disputed transctions structures for any mismatches - + @param m1 One transaction set @param m2 The other transaction set */ @@ -1287,7 +1287,7 @@ class LedgerConsensusImp /** Add a disputed transaction (one that at least one node wants in the consensus set and at least one node does not) to our tracking - + @param txID The ID of the disputed transaction @param tx The data of the disputed transaction */ @@ -1343,7 +1343,7 @@ class LedgerConsensusImp /** Adjust the votes on all disputed transactions based on the set of peers taking this position - + @param map A disputed position @param peers peers which are taking the position map */ @@ -1385,7 +1385,7 @@ class LedgerConsensusImp /** Let peers know that we a particular transactions set so they can fetch it from us. - + @param hash The ID of the transaction. @param direct true if we have this transaction set locally, else a directly connected peer has it. @@ -1401,7 +1401,7 @@ class LedgerConsensusImp } /** Apply a set of transactions to a ledger - + @param set The set of transactions to apply @param applyLedger The ledger to which the transactions should be applied. @@ -1510,7 +1510,7 @@ class LedgerConsensusImp } /** Apply a transaction to a ledger - + @param engine The transaction engine containing the ledger. @param txn The transaction to be applied to ledger. @param openLedger true if ledger is open @@ -1584,9 +1584,9 @@ class LedgerConsensusImp /** Round the close time to the close time resolution. - + @param closeTime The time to be rouned. - @return The rounded close time. + @return The rounded close time. */ std::uint32_t roundCloseTime (std::uint32_t closeTime) { @@ -1594,7 +1594,7 @@ class LedgerConsensusImp } /** Send a node status change message to our directly connected peers - + @param event The event which caused the status change. This is typically neACCEPTED_LEDGER or neCLOSING_LEDGER. @param ledger The ledger associated with the event. @@ -1638,7 +1638,7 @@ class LedgerConsensusImp /** Take an initial position on what we think the consensus should be based on the transactions that made it into our open ledger - + @param initialLedger The ledger that contains our initial position. */ void takeInitialPosition (Ledger& initialLedger) @@ -1710,7 +1710,7 @@ class LedgerConsensusImp /** For a given number of participants and required percent for consensus, how many participants must agree? - + @param size number of validators @param percent desired percent for consensus @return number of participates which must agree diff --git a/src/ripple/module/app/ledger/AcceptedLedger.cpp b/src/ripple/module/app/ledger/AcceptedLedger.cpp index 201d96d7eef..3778d94fb19 100644 --- a/src/ripple/module/app/ledger/AcceptedLedger.cpp +++ b/src/ripple/module/app/ledger/AcceptedLedger.cpp @@ -30,10 +30,12 @@ AcceptedLedger::AcceptedLedger (Ledger::ref ledger) : mLedger (ledger) { SHAMap& txSet = *ledger->peekTransactionMap (); - for (SHAMapItem::pointer item = txSet.peekFirstItem (); !!item; item = txSet.peekNextItem (item->getTag ())) + for (SHAMapItem::pointer item = txSet.peekFirstItem (); item; + item = txSet.peekNextItem (item->getTag ())) { SerializerIterator sit (item->peekSerializer ()); - insert (std::make_shared (ledger->getLedgerSeq (), std::ref (sit))); + insert (std::make_shared ( + ledger->getLedgerSeq (), sit)); } } diff --git a/src/ripple/module/app/ledger/AcceptedLedgerTx.h b/src/ripple/module/app/ledger/AcceptedLedgerTx.h index 87f8036a23c..cdc504cf7d2 100644 --- a/src/ripple/module/app/ledger/AcceptedLedgerTx.h +++ b/src/ripple/module/app/ledger/AcceptedLedgerTx.h @@ -84,7 +84,7 @@ class AcceptedLedgerTx bool isApplied () const { - return !!mMeta; + return bool(mMeta); } int getIndex () const { diff --git a/src/ripple/module/app/ledger/Ledger.cpp b/src/ripple/module/app/ledger/Ledger.cpp index ee0f205119f..e8d63374f36 100644 --- a/src/ripple/module/app/ledger/Ledger.cpp +++ b/src/ripple/module/app/ledger/Ledger.cpp @@ -765,7 +765,7 @@ bool Ledger::saveValidatedLedger (bool current) { auto sl (getApp().getLedgerDB ()->lock ()); - // TODO(tom): ARG!! + // TODO(tom): ARG! getApp().getLedgerDB ()->getDB ()->executeSQL (boost::str (addLedger % to_string (getHash ()) % mLedgerSeq % to_string (mParentHash) % beast::lexicalCastThrow (mTotCoins) % mCloseTime % @@ -1187,7 +1187,7 @@ Json::Value Ledger::getJson (int options) Json::Value& txns = (ledger[jss::transactions] = Json::arrayValue); SHAMapTreeNode::TNType type; - for (auto item = mTransactionMap->peekFirstItem (type); !!item; + for (auto item = mTransactionMap->peekFirstItem (type); item; item = mTransactionMap->peekNextItem (item->getTag (), type)) { if (bFull || bExpand) diff --git a/src/ripple/module/app/ledger/LedgerEntrySet.cpp b/src/ripple/module/app/ledger/LedgerEntrySet.cpp index 51bb6972253..db8a6d47b9c 100644 --- a/src/ripple/module/app/ledger/LedgerEntrySet.cpp +++ b/src/ripple/module/app/ledger/LedgerEntrySet.cpp @@ -1157,7 +1157,7 @@ std::uint32_t LedgerEntrySet::rippleTransferRate (Account const& issuer) WriteLog (lsTRACE, LedgerEntrySet) << "rippleTransferRate:" << " issuer=" << to_string (issuer) << - " account_exists=" << std::boolalpha << !!sleAccount << + " account_exists=" << std::boolalpha << bool(sleAccount) << " transfer_rate=" << (uQuality / 1000000000.0); return uQuality; @@ -1211,7 +1211,7 @@ LedgerEntrySet::rippleQualityIn ( " uToAccountID=" << to_string (uToAccountID) << " uFromAccountID=" << to_string (uFromAccountID) << " uCurrencyID=" << to_string (uCurrencyID) << - " bLine=" << std::boolalpha << !!sleRippleState << + " bLine=" << std::boolalpha << bool(sleRippleState) << " uQuality=" << (uQuality / 1000000000.0); return uQuality; @@ -1518,8 +1518,8 @@ TER LedgerEntrySet::rippleCredit ( TER terResult; - assert (!!uSenderID && uSenderID != noAccount()); - assert (!!uReceiverID && uReceiverID != noAccount()); + assert (!isXRP (uSenderID) && uSenderID != noAccount()); + assert (!isXRP (uReceiverID) && uReceiverID != noAccount()); if (!sleRippleState) { @@ -1636,17 +1636,14 @@ TER LedgerEntrySet::rippleSend ( auto const issuer = saAmount.getIssuer (); TER terResult; - assert (!!uSenderID && !!uReceiverID); + assert (!isXRP (uSenderID) && !isXRP (uReceiverID)); assert (uSenderID != uReceiverID); - if (uSenderID == issuer || uReceiverID == issuer || - issuer == noAccount()) + if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) { // Direct send: redeeming IOUs and/or sending own IOUs. terResult = rippleCredit (uSenderID, uReceiverID, saAmount, false); - saActual = saAmount; - terResult = tesSUCCESS; } else diff --git a/src/ripple/module/app/ledger/OrderBookIterator.cpp b/src/ripple/module/app/ledger/OrderBookIterator.cpp index 70c4d66648f..efcd5a20a1c 100644 --- a/src/ripple/module/app/ledger/OrderBookIterator.cpp +++ b/src/ripple/module/app/ledger/OrderBookIterator.cpp @@ -57,7 +57,7 @@ bool BookDirIterator::nextDirectory (LedgerEntrySet& les) mOfferDir = les.entryCache (ltDIR_NODE, mIndex); assert (mOfferDir); - return !!mOfferDir; + return bool(mOfferDir); } bool BookDirIterator::firstDirectory (LedgerEntrySet& les) diff --git a/src/ripple/module/app/main/Application.cpp b/src/ripple/module/app/main/Application.cpp index 30f1096caf8..9f2d64f10db 100644 --- a/src/ripple/module/app/main/Application.cpp +++ b/src/ripple/module/app/main/Application.cpp @@ -1111,7 +1111,7 @@ void ApplicationImp::startNewLedger () { Ledger::pointer firstLedger = std::make_shared (rootAddress, SYSTEM_CURRENCY_START); - assert (!!firstLedger->getAccountState (rootAddress)); + assert (firstLedger->getAccountState (rootAddress)); // TODO(david): Add any default amendments // TODO(david): Set default fee/reserve firstLedger->updateHash (); @@ -1123,7 +1123,7 @@ void ApplicationImp::startNewLedger () secondLedger->setClosed (); secondLedger->setAccepted (); m_ledgerMaster->pushLedger (secondLedger, std::make_shared (true, std::ref (*secondLedger))); - assert (!!secondLedger->getAccountState (rootAddress)); + assert (secondLedger->getAccountState (rootAddress)); m_networkOPs->setLastCloseTime (secondLedger->getCloseTimeNC ()); } } diff --git a/src/ripple/module/app/main/Main.cpp b/src/ripple/module/app/main/Main.cpp index 621d69ae4b2..d2372c2d48d 100644 --- a/src/ripple/module/app/main/Main.cpp +++ b/src/ripple/module/app/main/Main.cpp @@ -282,9 +282,11 @@ int run (int argc, char** argv) if (!iResult) { - getConfig ().setup ( - vm.count ("conf") ? vm["conf"].as () : "", // Config file. - !!vm.count ("quiet")); // Quiet flag. + auto configFile = vm.count ("conf") ? + vm["conf"].as () : std::string(); + + // config file, quiet flag. + getConfig ().setup (configFile, bool (vm.count ("quiet"))); if (vm.count ("standalone")) { diff --git a/src/ripple/module/app/misc/NetworkOPs.cpp b/src/ripple/module/app/misc/NetworkOPs.cpp index 36ea8878675..40da4252865 100644 --- a/src/ripple/module/app/misc/NetworkOPs.cpp +++ b/src/ripple/module/app/misc/NetworkOPs.cpp @@ -1049,7 +1049,7 @@ Transaction::pointer NetworkOPsImp::processTransactionCb ( m_journal.info << "Transaction is now included in open ledger"; trans->setStatus (INCLUDED); - // VFALCO NOTE The value of trans can be changed here!! + // VFALCO NOTE The value of trans can be changed here! getApp().getMasterTransaction ().canonicalize (&trans); } else if (r == tefPAST_SEQ) @@ -2942,7 +2942,7 @@ bool NetworkOPsImp::subLedger (InfoSub::ref isrListener, Json::Value& jvResult) bool NetworkOPsImp::unsubLedger (std::uint64_t uSeq) { ScopedLockType sl (mLock); - return !!mSubLedger.erase (uSeq); + return mSubLedger.erase (uSeq); } // <-- bool: true=added, false=already there @@ -2968,7 +2968,7 @@ bool NetworkOPsImp::subServer (InfoSub::ref isrListener, Json::Value& jvResult) bool NetworkOPsImp::unsubServer (std::uint64_t uSeq) { ScopedLockType sl (mLock); - return !!mSubServer.erase (uSeq); + return mSubServer.erase (uSeq); } // <-- bool: true=added, false=already there @@ -2983,7 +2983,7 @@ bool NetworkOPsImp::subTransactions (InfoSub::ref isrListener) bool NetworkOPsImp::unsubTransactions (std::uint64_t uSeq) { ScopedLockType sl (mLock); - return !!mSubTransactions.erase (uSeq); + return mSubTransactions.erase (uSeq); } // <-- bool: true=added, false=already there @@ -2998,7 +2998,7 @@ bool NetworkOPsImp::subRTTransactions (InfoSub::ref isrListener) bool NetworkOPsImp::unsubRTTransactions (std::uint64_t uSeq) { ScopedLockType sl (mLock); - return !!mSubRTTransactions.erase (uSeq); + return mSubRTTransactions.erase (uSeq); } InfoSub::pointer NetworkOPsImp::findRpcSub (const std::string& strUrl) diff --git a/src/ripple/module/app/misc/OrderBook.h b/src/ripple/module/app/misc/OrderBook.h index 9663999f7db..08be8e74ad0 100644 --- a/src/ripple/module/app/misc/OrderBook.h +++ b/src/ripple/module/app/misc/OrderBook.h @@ -30,7 +30,6 @@ class OrderBook : beast::LeakChecked typedef std::shared_ptr const& ref; typedef std::vector List; -public: /** Construct from a currency specification. @param index ??? diff --git a/src/ripple/module/app/paths/Calculators.h b/src/ripple/module/app/paths/Calculators.h deleted file mode 100644 index db784394a72..00000000000 --- a/src/ripple/module/app/paths/Calculators.h +++ /dev/null @@ -1,139 +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_PATHS_CALCULATORS_H -#define RIPPLE_PATHS_CALCULATORS_H - -#include - -#include -#include -#include -#include - -namespace ripple { - -/** RippleCalc calculates the quality of a payment path. - - Quality is the amount of input required to produce a given output along a - specified path - another name for this is exchange rate. -*/ -class RippleCalc -{ -public: - LedgerEntrySet& mActiveLedger; - bool mOpenLedger; - - // First time working in reverse a funding source was mentioned. Source may - // only be used there. - - // Map of currency, issuer to node index. - AccountCurrencyIssuerToNodeIndex mumSource; - - // If the transaction fails to meet some constraint, still need to delete - // unfunded offers. - // - // Offers that were found unfunded. - hash_set mUnfundedOffers; - - RippleCalc (LedgerEntrySet& activeLedger, const bool bOpenLedger) - : mActiveLedger (activeLedger), mOpenLedger (bOpenLedger) - { - } -}; - -namespace path { - -void pathNext ( - RippleCalc&, - PathState& pathState, const bool bMultiQuality, - const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent); - -// The next section contains functions that compute the liqudity 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. -// -// These functions were originally methods on RippleCalc, but will end up as -// methods on another class which keeps the state that's currently passed around -// as many parameters. For the moment, they're free functions that take a -// RippleCalc (the path calculation "God object"). -// -// Many of these routines use recursion to loop over all nodes in a path. -// TODO(tom): replace this recursion with a loop. - -TER computeReverseLiquidity ( - RippleCalc&, PathState& pathState, bool bMultiQuality); -TER computeForwardLiquidity ( - RippleCalc&, PathState& pathState, bool bMultiQuality); -TER computeReverseLiquidityForOffer ( - RippleCalc&, - unsigned int nodeIndex, PathState& pathState, bool bMultiQuality); -TER computeForwardLiquidityForOffer ( - RippleCalc&, - unsigned int nodeIndex, PathState& pathState, bool bMultiQuality); -TER computeReverseLiquidityForAccount ( - RippleCalc&, - unsigned int nodeIndex, PathState& pathState, bool bMultiQuality); -TER computeForwardLiquidityForAccount ( - RippleCalc&, - unsigned int nodeIndex, PathState& pathState, bool bMultiQuality); - -void computeRippleLiquidity ( - RippleCalc&, - const std::uint32_t uQualityIn, - const std::uint32_t uQualityOut, - const STAmount& saPrvReq, - const STAmount& saCurReq, - STAmount& saPrvAct, - STAmount& saCurAct, - std::uint64_t& uRateMax); - -// To send money out of an account. -TER nodeAdvance ( - RippleCalc&, - unsigned int nodeIndex, PathState& pathState, bool bMultiQuality, - bool bReverse); - -// To deliver from an order book, when computing -TER nodeDeliverRev ( - RippleCalc&, - const unsigned int nodeIndex, - PathState& pathState, - const bool bMultiQuality, - Account const& uOutAccountID, - const STAmount& saOutReq, - STAmount& saOutAct); - -TER nodeDeliverFwd ( - RippleCalc&, - const unsigned int nodeIndex, - PathState& pathState, - const bool bMultiQuality, - Account const& uInAccountID, - const STAmount& saInReq, - STAmount& saInAct, - STAmount& saInFees); - -} // path -} // ripple - -#endif diff --git a/src/ripple/module/app/paths/ComputeAccountLiquidityForward.cpp b/src/ripple/module/app/paths/ComputeAccountLiquidityForward.cpp deleted file mode 100644 index 449ec0a4c64..00000000000 --- a/src/ripple/module/app/paths/ComputeAccountLiquidityForward.cpp +++ /dev/null @@ -1,454 +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 computeForwardLiquidityForAccount ( - RippleCalc& rippleCalc, - const unsigned int nodeIndex, // 0 <= nodeIndex <= lastNodeIndex - PathState& pathState, - const bool bMultiQuality) -{ - TER resultCode = tesSUCCESS; - const unsigned int lastNodeIndex = pathState.nodes().size () - 1; - - std::uint64_t uRateMax = 0; - - auto& previousNode = pathState.nodes()[nodeIndex ? nodeIndex - 1 : 0]; - auto& node = pathState.nodes()[nodeIndex]; - auto& nextNode = pathState.nodes()[nodeIndex == lastNodeIndex ? lastNodeIndex : nodeIndex + 1]; - - const bool previousNodeIsAccount = previousNode.isAccount(); - const bool nextNodeIsAccount = nextNode.isAccount(); - - Account const& previousAccountID - = previousNodeIsAccount ? previousNode.account_ : node.account_; - // Offers are always issue. - Account const& nextAccountID - = nextNodeIsAccount ? nextNode.account_ : node.account_; - - std::uint32_t uQualityIn = nodeIndex - ? rippleCalc.mActiveLedger.rippleQualityIn ( - node.account_, previousAccountID, node.currency_) - : QUALITY_ONE; - std::uint32_t uQualityOut = (nodeIndex == lastNodeIndex) - ? rippleCalc.mActiveLedger.rippleQualityOut ( - node.account_, nextAccountID, node.currency_) - : QUALITY_ONE; - - // 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 nextNodeIsAccount - auto saPrvRedeemAct = previousNode.saFwdRedeem.zeroed(); - auto saPrvIssueAct = previousNode.saFwdIssue.zeroed(); - - // For !previousNodeIsAccount - auto saPrvDeliverAct = previousNode.saFwdDeliver.zeroed (); - - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount> " - << "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 (previousNodeIsAccount && nextNodeIsAccount) - { - // 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() >= zero) - { - // Limit by send max. - node.saFwdRedeem = std::min ( - node.saFwdRedeem, pathState.inReq() - pathState.inAct()); - } - - pathState.inPass() = node.saFwdRedeem; - - node.saFwdIssue = node.saFwdRedeem == node.saRevRedeem - // Fully redeemed. - ? node.saRevIssue : STAmount (node.saRevIssue); - - if (!!node.saFwdIssue && pathState.inReq() >= zero) - { - // Limit by send max. - node.saFwdIssue = std::min ( - node.saFwdIssue, - pathState.inReq() - pathState.inAct() - node.saFwdRedeem); - } - - pathState.inPass() += node.saFwdIssue; - - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount: ^ --> 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 --> $ - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount: 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. - - STAmount& saCurReceive = pathState.outPass(); - - STAmount saIssueCrd = uQualityIn >= QUALITY_ONE - ? previousNode.saFwdIssue // No fee. - : STAmount::mulRound ( - previousNode.saFwdIssue, - STAmount (noIssue(), uQualityIn, -9), - true); // Amount to credit. - - // Amount to credit. Credit for less than received as a surcharge. - saCurReceive = previousNode.saFwdRedeem + saIssueCrd; - - if (saCurReceive) - { - // Actually receive. - resultCode = rippleCalc.mActiveLedger.rippleCredit ( - previousAccountID, node.account_, - previousNode.saFwdRedeem + previousNode.saFwdIssue, false); - } - else - { - // After applying quality, total payment was microscopic. - resultCode = tecPATH_DRY; - } - } - else - { - // account --> ACCOUNT --> account - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount: 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 - computeRippleLiquidity ( - rippleCalc, - QUALITY_ONE, uQualityOut, 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 - computeRippleLiquidity ( - rippleCalc, - uQualityIn, uQualityOut, 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 - computeRippleLiquidity ( - rippleCalc, QUALITY_ONE, - rippleCalc.mActiveLedger.rippleTransferRate (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 - computeRippleLiquidity ( - rippleCalc, - uQualityIn, QUALITY_ONE, previousNode.saFwdIssue, node.saRevIssue, - saPrvIssueAct, node.saFwdIssue, uRateMax); - } - - STAmount saProvide = node.saFwdRedeem + node.saFwdIssue; - - // Adjust prv --> cur balance : take all inbound - resultCode = saProvide - ? rippleCalc.mActiveLedger.rippleCredit ( - previousAccountID, node.account_, - previousNode.saFwdRedeem + previousNode.saFwdIssue, false) - : tecPATH_DRY; - } - } - else if (previousNodeIsAccount && !nextNodeIsAccount) - { - // 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. - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount: 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? - computeRippleLiquidity ( - rippleCalc, QUALITY_ONE, - rippleCalc.mActiveLedger.rippleTransferRate (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 - computeRippleLiquidity ( - rippleCalc, - uQualityIn, QUALITY_ONE, previousNode.saFwdIssue, node.saRevDeliver, - saPrvIssueAct, node.saFwdDeliver, uRateMax); - } - - // Adjust prv --> cur balance : take all inbound - resultCode = node.saFwdDeliver - ? rippleCalc.mActiveLedger.rippleCredit ( - previousAccountID, node.account_, - previousNode.saFwdRedeem + previousNode.saFwdIssue, false) - : 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() >= 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 (node.currency_.isZero ()) - node.saFwdDeliver = std::min ( - node.saFwdDeliver, - rippleCalc.mActiveLedger.accountHolds ( - node.account_, xrpCurrency(), xrpAccount())); - - } - - // Record amount sent for pass. - pathState.inPass() = node.saFwdDeliver; - - if (!node.saFwdDeliver) - { - resultCode = tecPATH_DRY; - } - else if (!!node.currency_) - { - // 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. - - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount: ^ --> 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 - { - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount: ^ --> ACCOUNT -- XRP --> offer"; - - // Deliver XRP to limbo. - resultCode = rippleCalc.mActiveLedger.accountSend ( - node.account_, xrpAccount(), node.saFwdDeliver); - } - } - } - else if (!previousNodeIsAccount && nextNodeIsAccount) - { - if (nodeIndex == lastNodeIndex) - { - // offer --> ACCOUNT --> $ - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount: offer --> ACCOUNT --> $ : " - << previousNode.saFwdDeliver; - - STAmount& saCurReceive = pathState.outPass(); - - // Amount to credit. - saCurReceive = previousNode.saFwdDeliver; - - // No income balance adjustments necessary. The paying side inside - // the offer paid to this account. - } - else - { - // offer --> ACCOUNT --> account - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount: 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 - computeRippleLiquidity ( - rippleCalc, - QUALITY_ONE, uQualityOut, 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 - computeRippleLiquidity ( - rippleCalc, QUALITY_ONE, - rippleCalc.mActiveLedger.rippleTransferRate (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. - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidityForAccount: offer --> ACCOUNT --> offer"; - - node.saFwdDeliver.clear (node.saRevDeliver); - - if (previousNode.saFwdDeliver - // Previous wants to deliver - && node.saRevIssue) - // Current wants issue. - { - // Rate : 1.0 : transfer_rate - computeRippleLiquidity ( - rippleCalc, QUALITY_ONE, - rippleCalc.mActiveLedger.rippleTransferRate (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/module/app/paths/ComputeLiquidity.cpp b/src/ripple/module/app/paths/ComputeLiquidity.cpp deleted file mode 100644 index 7e5aba36404..00000000000 --- a/src/ripple/module/app/paths/ComputeLiquidity.cpp +++ /dev/null @@ -1,128 +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 - -namespace ripple { -namespace path { - -TER computeForwardLiquidity ( - RippleCalc& rippleCalc, PathState& pathState, const bool bMultiQuality) -{ - TER resultCode; - for (auto nodeIndex = 0; nodeIndex < pathState.nodes().size(); ++nodeIndex) - { - auto const& node = pathState.nodes()[nodeIndex]; - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidity> nodeIndex=" << nodeIndex; - - resultCode = node.isAccount() - ? computeForwardLiquidityForAccount ( - rippleCalc, nodeIndex, pathState, bMultiQuality) - : computeForwardLiquidityForOffer ( - rippleCalc, nodeIndex, pathState, bMultiQuality); - - bool success = (resultCode == tesSUCCESS); - - if (success && !(pathState.inPass() && pathState.outPass())) - resultCode = tecPATH_DRY; - - WriteLog (lsTRACE, RippleCalc) - << "computeForwardLiquidity<" - << " nodeIndex:" << nodeIndex - << " resultCode:" << resultCode; - - if (!success) - return resultCode; - } - return resultCode; -} - -// Calculate a node and its previous nodes. The eventual goal is to determine -// 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 computeReverseLiquidityForAccount or -// computeReverseLiquidityForOffer. -// -// 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 computeReverseLiquidity ( - RippleCalc& rippleCalc, - PathState& pathState, - const bool bMultiQuality) -{ - for (int nodeIndex = pathState.nodes().size(); nodeIndex--; ) - { - auto& node = pathState.nodes()[nodeIndex]; - - // Every account has a transfer rate for its issuances. - - // TOMOVE: The account charges - // a fee when third parties transfer that account's own issuances. - - // node.transferRate_ caches the output transfer rate for this node. - node.transferRate_ = STAmount::saFromRate ( - rippleCalc.mActiveLedger.rippleTransferRate (node.issuer_)); - - WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidity>" - << " nodeIndex=" << nodeIndex - << " issuer_=" << to_string (node.issuer_) - << " transferRate_=" << node.transferRate_; - - auto resultCode = node.isAccount() - ? computeReverseLiquidityForAccount ( - rippleCalc, nodeIndex, pathState, bMultiQuality) - : computeReverseLiquidityForOffer ( - rippleCalc, nodeIndex, pathState, bMultiQuality); - - WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidity< " - << "nodeIndex=" << nodeIndex - << " resultCode=%s" << transToken (resultCode) - << "/" << resultCode; - - if (resultCode != tesSUCCESS) - return resultCode; - - } - return tesSUCCESS; -} - -} // path -} // ripple diff --git a/src/ripple/module/app/paths/ComputeOfferLiquidity.cpp b/src/ripple/module/app/paths/ComputeOfferLiquidity.cpp deleted file mode 100644 index 0eb82971211..00000000000 --- a/src/ripple/module/app/paths/ComputeOfferLiquidity.cpp +++ /dev/null @@ -1,111 +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 { - -// 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. -TER computeForwardLiquidityForOffer ( - RippleCalc& rippleCalc, - const unsigned int nodeIndex, - PathState& pathState, - const bool bMultiQuality) -{ - auto& previousNode = pathState.nodes() [nodeIndex - 1]; - - if (previousNode.account_ == zero) - return tesSUCCESS; - - // Previous is an account node, resolve its deliver. - STAmount saInAct; - STAmount saInFees; - - auto resultCode = nodeDeliverFwd ( - rippleCalc, - nodeIndex, - pathState, - bMultiQuality, - previousNode.account_, - previousNode.saFwdDeliver, // Previous is sending this much. - saInAct, - saInFees); - - assert (resultCode != tesSUCCESS || - previousNode.saFwdDeliver == saInAct + saInFees); - return resultCode; - -} - -// Called to drive from the last offer node in a chain. -// nodeIndex doesn't refer to the node at either end because both ends must be -// an account. -TER computeReverseLiquidityForOffer ( - RippleCalc& rippleCalc, - const unsigned int nodeIndex, - PathState& pathState, - const bool bMultiQuality) -{ - auto& nextNode = pathState.nodes() [nodeIndex + 1]; - if (nextNode.account_ == zero) - { - WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForOffer: " - << "OFFER --> offer: nodeIndex=" << nodeIndex; - return tesSUCCESS; - - // This control structure ensures nodeDeliverRev is only called for the - // rightmost offer in a chain of offers - which means that - // nodeDeliverRev has to take all of those offers into consideration. - } - - auto& node = pathState.nodes() [nodeIndex]; - - // Next is an account node, resolve current offer node's deliver. - STAmount saDeliverAct; - - WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForOffer: OFFER --> account:" - << " nodeIndex=" << nodeIndex - << " saRevDeliver=" << node.saRevDeliver; - - return nodeDeliverRev ( - rippleCalc, - nodeIndex, - pathState, - bMultiQuality, - nextNode.account_, - - // The next node wants the current node to deliver this much: - node.saRevDeliver, - saDeliverAct); -} - -} // path -} // ripple diff --git a/src/ripple/module/app/paths/Node.cpp b/src/ripple/module/app/paths/Node.cpp index 78a20bd68af..833a08fa162 100644 --- a/src/ripple/module/app/paths/Node.cpp +++ b/src/ripple/module/app/paths/Node.cpp @@ -24,9 +24,8 @@ namespace path { bool Node::operator== (const Node& other) const { return other.uFlags == uFlags - && other.account_ == account_ - && other.currency_ == currency_ - && other.issuer_ == issuer_; + && other.account_ == account_ + && other.issue_ == issue_; } // This is for debugging not end users. Output names can be changed without @@ -38,14 +37,12 @@ Json::Value Node::getJson () const jvNode["type"] = uFlags; - bool const hasCurrency = (currency_ != zero); - bool const hasAccount = (account_ != zero); - bool const hasIssuer = (issuer_ != zero); + 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) { @@ -63,14 +60,14 @@ Json::Value Node::getJson () const jvNode["flags"] = jvFlags; - if (!!account_) + if (!isXRP (account_)) jvNode["account"] = to_string (account_); - if (!!currency_) - jvNode["currency"] = to_string (currency_); + if (!isXRP (issue_.currency)) + jvNode["currency"] = to_string (issue_.currency); - if (!!issuer_) - jvNode["issuer"] = to_string (issuer_); + if (!isXRP (issue_.account)) + jvNode["issuer"] = to_string (issue_.account); if (saRevRedeem) jvNode["rev_redeem"] = saRevRedeem.getFullText (); diff --git a/src/ripple/module/app/paths/Node.h b/src/ripple/module/app/paths/Node.h index ca6e21d16de..883be1069ef 100644 --- a/src/ripple/module/app/paths/Node.h +++ b/src/ripple/module/app/paths/Node.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_APP_PATH_NODE_H #define RIPPLE_APP_PATH_NODE_H +#include #include #include @@ -42,9 +43,9 @@ struct Node std::uint16_t uFlags; // --> From path. Account account_; // --> Accounts: Receiving/sending account. - Currency currency_; // --> Accounts: Receive and send, Offers: send. + + Issue issue_; // --> Accounts: Receive and send, Offers: send. // --- For offer's next has currency out. - Account issuer_; // --> Currency's issuer STAmount transferRate_; // Transfer rate for issuer. @@ -77,19 +78,8 @@ struct Node // "quality". // https://ripple.com/wiki/Ledger_Format#Prioritizing_a_continuous_key_space - // Current directory - the last 64 bits of this are the quality. - uint256 currentDirectory_; - - // Start of the next order book - one past the worst quality possible for - // the current order book. - uint256 nextDirectory_; - - // TODO(tom): currentDirectory_ and nextDirectory_ should be of type - // Directory. + NodeDirectory directory; - bool bDirectAdvance; // Need to advance directory. - bool bDirectRestart; // Need to restart directory. - SLE::pointer sleDirectDir; STAmount saOfrRate; // For correct ratio. // PaymentNode @@ -104,6 +94,15 @@ struct Node STAmount saOfferFunds; STAmount saTakerPays; STAmount saTakerGets; + + /** Clear input and output amounts. */ + void clear() + { + saRevRedeem.clear (); + saRevIssue.clear (); + saRevDeliver.clear (); + saFwdDeliver.clear (); + } }; } // path diff --git a/src/ripple/module/app/paths/NodeDirectory.h b/src/ripple/module/app/paths/NodeDirectory.h new file mode 100644 index 00000000000..7b449d17331 --- /dev/null +++ b/src/ripple/module/app/paths/NodeDirectory.h @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +/* + 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 RIPPLED_RIPPLE_MODULE_APP_PATHS_NODEDIRECTORY_H +#define RIPPLED_RIPPLE_MODULE_APP_PATHS_NODEDIRECTORY_H + +namespace ripple { + +class NodeDirectory { + public: + // Current directory - the last 64 bits of this are the quality. + uint256 current; + + // Start of the next order book - one past the worst quality possible + // for the current order book. + uint256 next; + + // TODO(tom): directory.current and directory.next should be of type + // Directory. + + bool advanceNeeded; // Need to advance directory. + bool restartNeeded; // Need to restart directory. + + SLE::pointer ledgerEntry; + + void restart (bool multiQuality) + { + if (multiQuality) + current = 0; // Restart book searching. + else + restartNeeded = true; // Restart at same quality. + } + + bool initialize (Book const& book, LedgerEntrySet& les) + { + if (current != zero) + return false; + + current.copyFrom (Ledger::getBookBase (book)); + next.copyFrom (Ledger::getQualityNext (current)); + + // TODO(tom): it seems impossible that any actual offers with + // quality == 0 could occur - we should disallow them, and clear + // directory.ledgerEntry without the database call in the next line. + ledgerEntry = les.entryCache (ltDIR_NODE, current); + + // Advance, if didn't find it. Normal not to be unable to lookup + // firstdirectory. Maybe even skip this lookup. + advanceNeeded = !ledgerEntry; + restartNeeded = false; + + // Associated vars are dirty, if found it. + return bool(ledgerEntry); + } + + enum Advance {NO_ADVANCE, NEW_QUALITY, END_ADVANCE}; + Advance advance(LedgerEntrySet& les) + { + if (!(advanceNeeded || restartNeeded)) + return NO_ADVANCE; + + // Get next quality. + // The Merkel radix tree is ordered by key so we can go to the next + // quality in O(1). + if (advanceNeeded) + current = les.getNextLedgerIndex (current, next); + + advanceNeeded = false; + restartNeeded = false; + + if (current == zero) + return END_ADVANCE; + + ledgerEntry = les.entryCache (ltDIR_NODE, current); + return NEW_QUALITY; + } +}; + +} // ripple + +#endif diff --git a/src/ripple/module/app/paths/PathNext.cpp b/src/ripple/module/app/paths/PathNext.cpp deleted file mode 100644 index 7fcb112ef02..00000000000 --- a/src/ripple/module/app/paths/PathNext.cpp +++ /dev/null @@ -1,103 +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 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 pathNext ( - RippleCalc& rippleCalc, - PathState& pathState, const bool bMultiQuality, - const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent) -{ - // The next state is what is available in preference order. - // This is calculated when referenced accounts changed. - pathState.clear(); - - WriteLog (lsTRACE, RippleCalc) - << "pathNext: Path In: " << pathState.getJson (); - - assert (pathState.nodes().size () >= 2); - - lesCurrent = lesCheckpoint.duplicate (); // Restore from checkpoint. - - for (unsigned int uIndex = pathState.nodes().size (); uIndex--;) - { - auto& node = pathState.nodes()[uIndex]; - - node.saRevRedeem.clear (); - node.saRevIssue.clear (); - node.saRevDeliver.clear (); - node.saFwdDeliver.clear (); - } - - pathState.setStatus(computeReverseLiquidity ( - rippleCalc, pathState, bMultiQuality)); - - WriteLog (lsTRACE, RippleCalc) - << "pathNext: Path after reverse: " << pathState.getJson (); - - if (tesSUCCESS == pathState.status()) - { - // Do forward. - lesCurrent = lesCheckpoint.duplicate (); // Restore from checkpoint. - - pathState.setStatus(computeForwardLiquidity ( - rippleCalc, pathState, bMultiQuality)); - } - - if (tesSUCCESS == pathState.status()) - { - CondLog (!pathState.inPass() || !pathState.outPass(), lsDEBUG, RippleCalc) - << "pathNext: Error computeForwardLiquidity reported success for nothing:" - << " saOutPass=" << pathState.outPass() - << " inPass()=" << pathState.inPass(); - - if (!pathState.outPass() || !pathState.inPass()) - throw std::runtime_error ("Made no progress."); - - // Calculate relative quality. - pathState.setQuality(STAmount::getRate ( - pathState.outPass(), pathState.inPass())); - - WriteLog (lsTRACE, RippleCalc) - << "pathNext: Path after forward: " << pathState.getJson (); - } - else - { - pathState.setQuality(0); - } -} - -} // path -} // ripple diff --git a/src/ripple/module/app/paths/PathRequest.cpp b/src/ripple/module/app/paths/PathRequest.cpp index 763a2df5527..4f176418369 100644 --- a/src/ripple/module/app/paths/PathRequest.cpp +++ b/src/ripple/module/app/paths/PathRequest.cpp @@ -21,7 +21,6 @@ #include #include -#include namespace ripple { @@ -128,12 +127,14 @@ void PathRequest::updateComplete () bool PathRequest::isValid (RippleLineCache::ref crCache) { ScopedLockType sl (mLock); - bValid = raSrcAccount.isSet () && raDstAccount.isSet () && saDstAmount > zero; + bValid = raSrcAccount.isSet () && raDstAccount.isSet () && + saDstAmount > zero; Ledger::pointer lrLedger = crCache->getLedger (); if (bValid) { - AccountState::pointer asSrc = getApp().getOPs ().getAccountState (crCache->getLedger(), raSrcAccount); + auto asSrc = getApp().getOPs ().getAccountState ( + crCache->getLedger(), raSrcAccount); if (!asSrc) { @@ -143,9 +144,11 @@ bool PathRequest::isValid (RippleLineCache::ref crCache) } else { - AccountState::pointer asDst = getApp().getOPs ().getAccountState (lrLedger, raDstAccount); + auto asDst = getApp().getOPs ().getAccountState ( + lrLedger, raDstAccount); - Json::Value& jvDestCur = (jvStatus["destination_currencies"] = Json::arrayValue); + Json::Value& jvDestCur = + (jvStatus["destination_currencies"] = Json::arrayValue); if (!asDst) { @@ -170,13 +173,15 @@ bool PathRequest::isValid (RippleLineCache::ref crCache) bool const disallowXRP ( asDst->peekSLE ().getFlags() & lsfDisallowXRP); - CurrencySet usDestCurrID = - usAccountDestCurrencies (raDstAccount, crCache, !disallowXRP); + auto usDestCurrID = usAccountDestCurrencies ( + raDstAccount, crCache, !disallowXRP); for (auto const& currency : usDestCurrID) jvDestCur.append (to_string (currency)); - jvStatus["destination_tag"] = (asDst->peekSLE ().getFlags () & lsfRequireDestTag) != 0; + jvStatus["destination_tag"] = + (asDst->peekSLE ().getFlags () & lsfRequireDestTag) + != 0; } } } @@ -189,8 +194,11 @@ bool PathRequest::isValid (RippleLineCache::ref crCache) return bValid; } -Json::Value PathRequest::doCreate (Ledger::ref lrLedger, RippleLineCache::ref& cache, - const Json::Value& value, bool& valid) +Json::Value PathRequest::doCreate ( + Ledger::ref lrLedger, + RippleLineCache::ref& cache, + Json::Value const& value, + bool& valid) { Json::Value status; @@ -433,9 +441,6 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) if (valid && pf.findPaths (iLevel, 4, spsPaths, extraPath)) { LedgerEntrySet lesSandbox (cache->getLedger (), tapNONE); - PathState::List pathStateList; - STAmount saMaxAmountAct; - STAmount saDstAmountAct; auto& account = currIssuer.second.isNonZero () ? Account(currIssuer.second) : isXRP (currIssuer.first) @@ -445,23 +450,23 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) saMaxAmount.negate (); m_journal.debug << iIdentifier << " Paths found, calling rippleCalc"; - TER resultCode = path::rippleCalculate ( - lesSandbox, saMaxAmountAct, saDstAmountAct, - pathStateList, saMaxAmount, saDstAmount, - raDstAccount.getAccountID (), raSrcAccount.getAccountID (), - spsPaths, false, false, false, true); - - if ((extraPath.size() > 0) && ((resultCode == terNO_LINE) || (resultCode == tecPATH_PARTIAL))) + path::RippleCalc rc ( + lesSandbox, + saMaxAmount, + saDstAmount, + raDstAccount.getAccountID (), + raSrcAccount.getAccountID (), + spsPaths); + TER resultCode = rc.rippleCalculate (); + + if (!extraPath.empty() && + (resultCode == terNO_LINE || resultCode == tecPATH_PARTIAL)) { m_journal.debug << iIdentifier << " Trying with an extra path element"; spsPaths.addPath(extraPath); - pathStateList.clear (); - resultCode = path::rippleCalculate ( - lesSandbox, saMaxAmountAct, saDstAmountAct, - pathStateList, saMaxAmount, saDstAmount, - raDstAccount.getAccountID (), raSrcAccount.getAccountID (), - spsPaths, false, false, false, true); + rc.pathStateList_.clear (); + resultCode = rc.rippleCalculate (); m_journal.debug << iIdentifier << " Extra path element gives " << transHuman (resultCode); @@ -470,7 +475,7 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) if (resultCode == tesSUCCESS) { Json::Value jvEntry (Json::objectValue); - jvEntry["source_amount"] = saMaxAmountAct.getJson (0); + jvEntry["source_amount"] = rc.actualAmountIn_.getJson (0); jvEntry["paths_computed"] = spsPaths.getJson (0); found = true; jvArray.append (jvEntry); diff --git a/src/ripple/module/app/paths/PathRequest.h b/src/ripple/module/app/paths/PathRequest.h index c68158e5cf0..286594ce94b 100644 --- a/src/ripple/module/app/paths/PathRequest.h +++ b/src/ripple/module/app/paths/PathRequest.h @@ -61,11 +61,16 @@ class PathRequest : void updateComplete (); Json::Value getStatus (); - Json::Value doCreate (const std::shared_ptr&, const RippleLineCache::pointer&, - const Json::Value&, bool&); + Json::Value doCreate ( + const std::shared_ptr&, + const RippleLineCache::pointer&, + const Json::Value&, + bool&); Json::Value doClose (const Json::Value&); Json::Value doStatus (const Json::Value&); - Json::Value doUpdate (const std::shared_ptr&, bool fast); // update jvStatus + + // update jvStatus + Json::Value doUpdate (const std::shared_ptr&, bool fast); InfoSub::pointer getSubscriber (); private: @@ -80,35 +85,35 @@ class PathRequest : typedef std::lock_guard ScopedLockType; LockType mLock; - PathRequests& mOwner; + PathRequests& mOwner; + + std::weak_ptr wpSubscriber; // Who this request came from - std::weak_ptr wpSubscriber; // Who this request came from - Json::Value jvId; - Json::Value jvStatus; // Last result + Json::Value jvId; + Json::Value jvStatus; // Last result // Client request parameters - RippleAddress raSrcAccount; - RippleAddress raDstAccount; - STAmount saDstAmount; - std::set sciSourceCurrencies; - // std::vector vjvBridges; - std::map mContext; + RippleAddress raSrcAccount; + RippleAddress raDstAccount; + STAmount saDstAmount; - bool bValid; + std::set sciSourceCurrencies; + std::map mContext; - LockType mIndexLock; - LedgerIndex mLastIndex; - bool mInProgress; + bool bValid; - int iLastLevel; - bool bLastSuccess; + LockType mIndexLock; + LedgerIndex mLastIndex; + bool mInProgress; - int iIdentifier; + int iLastLevel; + bool bLastSuccess; - boost::posix_time::ptime ptCreated; - boost::posix_time::ptime ptQuickReply; - boost::posix_time::ptime ptFullReply; + int iIdentifier; + boost::posix_time::ptime ptCreated; + boost::posix_time::ptime ptQuickReply; + boost::posix_time::ptime ptFullReply; }; } // ripple diff --git a/src/ripple/module/app/paths/PathState.cpp b/src/ripple/module/app/paths/PathState.cpp index a1945312b80..e7aa723f7ac 100644 --- a/src/ripple/module/app/paths/PathState.cpp +++ b/src/ripple/module/app/paths/PathState.cpp @@ -25,12 +25,42 @@ namespace ripple { class RippleCalc; // for logging -void PathState::clear() { +void PathState::clear() +{ allLiquidityConsumed_ = false; saInPass = saInReq.zeroed(); saOutPass = saOutReq.zeroed(); - vUnfundedBecame.clear (); + 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; + + CondLog (inReq() > zero && inAct() >= inReq(), + lsWARNING, RippleCalc) + << "rippleCalc: DONE:" + << " inAct()=" << inAct() + << " inReq()=" << inReq(); + + assert (inReq() < zero || inAct() < inReq()); + // Error if done. + + CondLog (outAct() >= outReq(), lsWARNING, RippleCalc) + << "rippleCalc: ALREADY DONE:" + << " saOutAct=" << outAct() + << " saOutReq=%s" << outReq(); + + assert (outAct() < outReq()); + assert (nodes().size () >= 2); } // Return true, iff lhs has less priority than rhs. @@ -48,7 +78,7 @@ bool PathState::lessPriority (PathState& lhs, PathState& rhs) return lhs.mIndex > rhs.mIndex; // Bigger is worse. } -// Make sure last path node delivers to account_: currency_ from issuer_. +// 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. @@ -75,7 +105,7 @@ TER PathState::pushImpliedNodes ( " " << currency << " " << issuer; - if (nodes_.back ().currency_ != currency) + 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 @@ -134,7 +164,7 @@ TER PathState::pushNode ( // 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& previousNode = pathIsEmpty ? path::Node () : nodes_.back (); + auto const& backNode = 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); @@ -154,7 +184,8 @@ TER PathState::pushNode ( << (hasIssuer ? to_string(issuer) : std::string("-")) << "/"; node.uFlags = iType; - node.currency_ = hasCurrency ? currency : Currency(previousNode.currency_); + node.issue_.currency = hasCurrency ? + currency : backNode.issue_.currency; // TODO(tom): we can probably just return immediately whenever we hit an // error in these next pages. @@ -163,19 +194,19 @@ TER PathState::pushNode ( { // Of course, this could never happen. WriteLog (lsDEBUG, RippleCalc) << "pushNode: bad bits."; - resultCode = temBAD_PATH; + resultCode = temBAD_PATH; } - else if (hasIssuer && !node.currency_) + else if (hasIssuer && isXRP (node.issue_)) { WriteLog (lsDEBUG, RippleCalc) << "pushNode: issuer specified for XRP."; - resultCode = temBAD_PATH; + resultCode = temBAD_PATH; } else if (hasIssuer && !issuer) { WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad issuer."; - resultCode = temBAD_PATH; + resultCode = temBAD_PATH; } else if (!hasAccount && !hasCurrency && !hasIssuer) { @@ -183,24 +214,21 @@ TER PathState::pushNode ( // no progress. WriteLog (lsDEBUG, RippleCalc) << "pushNode: offer must specify at least currency or issuer."; - resultCode = temBAD_PATH; + resultCode = temBAD_PATH; } else if (hasAccount) { // Account link - node.account_ = account; - node.issuer_ = hasIssuer - ? issuer - : !!node.currency_ // Not XRP. - ? account - : xrpAccount(); + node.account_ = account; + node.issue_.account = hasIssuer ? issuer : + (isXRP (node.issue_) ? xrpAccount() : account); // Zero value - for accounts. - node.saRevRedeem = STAmount ({node.currency_, account}); - node.saRevIssue = node.saRevRedeem; + 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.currency_, node.issuer_}); - node.saFwdDeliver = node.saRevDeliver; + node.saRevDeliver = STAmount (node.issue_); + node.saFwdDeliver = node.saRevDeliver; if (pathIsEmpty) { @@ -210,7 +238,7 @@ TER PathState::pushNode ( { WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad account."; - resultCode = temBAD_PATH; + resultCode = temBAD_PATH; } else { @@ -219,10 +247,11 @@ TER PathState::pushNode ( << "pushNode: imply for account."; resultCode = pushImpliedNodes ( - node.account_, node.currency_, - isXRP(node.currency_) ? xrpAccount() : account); + node.account_, + node.issue_.currency, + isXRP(node.issue_.currency) ? xrpAccount() : account); - // Note: previousNode may no longer be the immediately previous node. + // Note: backNode may no longer be the immediately previous node. } if (resultCode == tesSUCCESS && !nodes_.empty ()) @@ -233,7 +262,9 @@ TER PathState::pushNode ( auto sleRippleState = lesEntries.entryCache ( ltRIPPLE_STATE, Ledger::getRippleStateIndex ( - backNode.account_, node.account_, backNode.currency_)); + backNode.account_, + node.account_, + backNode.issue_.currency)); // A "RippleState" means a balance betweeen two accounts for a // specific currency. @@ -242,7 +273,7 @@ TER PathState::pushNode ( WriteLog (lsTRACE, RippleCalc) << "pushNode: No credit line between " << backNode.account_ << " and " << node.account_ - << " for " << node.currency_ << "." ; + << " for " << node.issue_.currency << "." ; WriteLog (lsTRACE, RippleCalc) << getJson (); @@ -253,7 +284,7 @@ TER PathState::pushNode ( WriteLog (lsTRACE, RippleCalc) << "pushNode: Credit line found between " << backNode.account_ << " and " << node.account_ - << " for " << node.currency_ << "." ; + << " for " << node.issue_.currency << "." ; auto sleBck = lesEntries.entryCache ( ltACCOUNT_ROOT, @@ -284,13 +315,15 @@ TER PathState::pushNode ( if (resultCode == tesSUCCESS) { STAmount saOwed = lesEntries.rippleOwed ( - node.account_, backNode.account_, node.currency_); + node.account_, backNode.account_, + node.issue_.currency); STAmount saLimit; if (saOwed <= zero) { saLimit = lesEntries.rippleLimit ( - node.account_, backNode.account_, - node.currency_); + node.account_, + backNode.account_, + node.issue_.currency); if (-saOwed >= saLimit) { WriteLog (lsWARNING, RippleCalc) << @@ -315,45 +348,44 @@ TER PathState::pushNode ( // // Offers bridge a change in currency and issuer, or just a change in // issuer. - node.issuer_ = hasIssuer - ? issuer - : !!node.currency_ - ? !!previousNode.issuer_ - ? Account(previousNode.issuer_) // Default to previous issuer - : Account(previousNode.account_) - // Or previous account if no previous issuer. - : xrpAccount(); + if (hasIssuer) + node.issue_.account = issuer; + else if (isXRP (node.issue_.currency)) + node.issue_.account = xrpAccount(); + else if (isXRP (backNode.issue_.account)) + node.issue_.account = backNode.account_; + else + node.issue_.account = backNode.issue_.account; + node.saRateMax = saZero; - node.saRevDeliver = STAmount({node.currency_, node.issuer_}); + node.saRevDeliver = STAmount (node.issue_); node.saFwdDeliver = node.saRevDeliver; - if (node.currency_.isZero() != node.issuer_.isZero()) + if (!isConsistent (node.issue_)) { WriteLog (lsDEBUG, RippleCalc) << "pushNode: currency is inconsistent with issuer."; resultCode = temBAD_PATH; } - else if (previousNode.currency_ == node.currency_ && - previousNode.issuer_ == node.issuer_) + else if (backNode.issue_ == node.issue_) { WriteLog (lsDEBUG, RippleCalc) << "pushNode: bad path: offer to same currency and issuer"; resultCode = temBAD_PATH; - } else { + } + else { WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for offer."; // Insert intermediary issuer account if needed. resultCode = pushImpliedNodes ( xrpAccount(), // Rippling, but offers don't have an account. - previousNode.currency_, - previousNode.issuer_); + backNode.issue_.currency, + backNode.issue_.account); } if (resultCode == tesSUCCESS) - { nodes_.push_back (node); - } } WriteLog (lsTRACE, RippleCalc) << "pushNode< : " << transToken (resultCode); @@ -373,22 +405,23 @@ TER PathState::pushNode ( // // 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 -void PathState::expandPath ( +TER PathState::expandPath ( const LedgerEntrySet& lesSource, - const STPath& spSourcePath, + STPath const& spSourcePath, Account const& uReceiverID, Account const& uSenderID) { uQuality = 1; // Mark path as active. - const Currency uMaxCurrencyID = saInReq.getCurrency (); - const Account uMaxIssuerID = saInReq.getIssuer (); + Currency const& uMaxCurrencyID = saInReq.getCurrency (); + Account const& uMaxIssuerID = saInReq.getIssuer (); - const Currency currencyOutID = saOutReq.getCurrency (); - const Account issuerOutID = saOutReq.getIssuer (); - const Account uSenderIssuerID + Currency const& currencyOutID = saOutReq.getCurrency (); + Account const& issuerOutID = saOutReq.getIssuer (); + Account const& uSenderIssuerID = isXRP(uMaxCurrencyID) ? xrpAccount() : uSenderID; // Sender is always issuer for non-XRP. @@ -400,8 +433,8 @@ void PathState::expandPath ( terStatus = tesSUCCESS; // XRP with issuer is malformed. - if ((!uMaxCurrencyID && !!uMaxIssuerID) - || (!currencyOutID && !!issuerOutID)) + if ((isXRP (uMaxCurrencyID) && !isXRP (uMaxIssuerID)) + || (isXRP (currencyOutID) && !isXRP (issuerOutID))) { terStatus = temBAD_PATH; } @@ -497,14 +530,14 @@ void PathState::expandPath ( } } - auto const& previousNode = nodes_.back (); + auto const& backNode = nodes_.back (); if (terStatus == tesSUCCESS && !isXRP(currencyOutID) // Next is not XRP && issuerOutID != uReceiverID // Out issuer is not receiver - && (previousNode.currency_ != currencyOutID + && (backNode.issue_.currency != currencyOutID // Previous will be an offer. - || previousNode.account_ != issuerOutID)) + || backNode.account_ != issuerOutID)) // Need the implied issuer. { // Add implied account. @@ -543,23 +576,18 @@ void PathState::expandPath ( { // Look for first mention of source in nodes and detect loops. // Note: The output is not allowed to be a source. - - const unsigned int uNodes = nodes_.size (); - - for (unsigned int nodeIndex = 0; - tesSUCCESS == terStatus && nodeIndex != uNodes; ++nodeIndex) + unsigned int index = 0; + for (auto& node: nodes_) { - const auto& node = nodes_[nodeIndex]; - - AccountCurrencyIssuer aci( - node.account_, node.currency_, node.issuer_); - if (!umForward.insert (std::make_pair (aci, nodeIndex)).second) + AccountIssue accountIssue (node.account_, node.issue_); + if (!umForward.insert ({accountIssue, index++}).second) { // Failed to insert. Have a loop. WriteLog (lsDEBUG, RippleCalc) << "expandPath: loop detected: " << getJson (); - terStatus = temBAD_PATH_LOOP; + terStatus = temBAD_PATH_LOOP; + break; } } } @@ -571,6 +599,7 @@ void PathState::expandPath ( << " out=" << currencyOutID << "/" << issuerOutID << ": " << getJson (); + return terStatus; } /** Check if a sequence of three accounts violates the no ripple constrains @@ -578,7 +607,7 @@ void PathState::expandPath ( Disallowed if 'second' set no ripple on [first]->[second] and [second]->[third] */ -void PathState::checkNoRipple ( +TER PathState::checkNoRipple ( Account const& firstAccount, Account const& secondAccount, // This is the account whose constraints we are checking @@ -609,17 +638,19 @@ void PathState::checkNoRipple ( terStatus = terNO_RIPPLE; } + return terStatus; } // Check a fully-expanded path to make sure it doesn't violate no-Ripple // settings. -void PathState::checkNoRipple ( - Account const& uDstAccountID, Account const& uSrcAccountID) +TER PathState::checkNoRipple ( + Account const& uDstAccountID, + Account const& uSrcAccountID) { // There must be at least one node for there to be two consecutive ripple // lines. if (nodes_.size() == 0) - return; + return terStatus; if (nodes_.size() == 1) { @@ -630,12 +661,17 @@ void PathState::checkNoRipple ( (nodes_[0].account_ != uDstAccountID)) { if (saInReq.getCurrency() != saOutReq.getCurrency()) + { terStatus = terNO_LINE; + } else - checkNoRipple (uSrcAccountID, nodes_[0].account_, uDstAccountID, - nodes_[0].currency_); + { + terStatus = checkNoRipple ( + uSrcAccountID, nodes_[0].account_, uDstAccountID, + nodes_[0].issue_.currency); + } } - return; + return terStatus; } // Check source <-> first <-> second @@ -643,17 +679,18 @@ void PathState::checkNoRipple ( nodes_[1].isAccount() && (nodes_[0].account_ != uSrcAccountID)) { - if ((nodes_[0].currency_ != nodes_[1].currency_)) + if ((nodes_[0].issue_.currency != nodes_[1].issue_.currency)) { terStatus = terNO_LINE; - return; + return terStatus; } else { - checkNoRipple (uSrcAccountID, nodes_[0].account_, nodes_[1].account_, - nodes_[0].currency_); - if (tesSUCCESS != terStatus) - return; + terStatus = checkNoRipple ( + uSrcAccountID, nodes_[0].account_, nodes_[1].account_, + nodes_[0].issue_.currency); + if (terStatus != tesSUCCESS) + return terStatus; } } @@ -663,17 +700,18 @@ void PathState::checkNoRipple ( nodes_[s + 1].isAccount() && (uDstAccountID != nodes_[s+1].account_)) { - if ((nodes_[s].currency_ != nodes_[s+1].currency_)) + if ((nodes_[s].issue_.currency != nodes_[s+1].issue_.currency)) { terStatus = terNO_LINE; - return; + return terStatus; } else { - checkNoRipple (nodes_[s].account_, nodes_[s+1].account_, - uDstAccountID, nodes_[s].currency_); + terStatus = checkNoRipple ( + nodes_[s].account_, nodes_[s+1].account_, + uDstAccountID, nodes_[s].issue_.currency); if (tesSUCCESS != terStatus) - return; + return terStatus; } } @@ -686,21 +724,22 @@ void PathState::checkNoRipple ( nodes_[i + 1].isAccount()) { // Two consecutive account-to-account links - auto const& currencyID = nodes_[i].currency_; - if ((nodes_[i-1].currency_ != currencyID) || - (nodes_[i+1].currency_ != currencyID)) + auto const& currencyID = nodes_[i].issue_.currency; + if ((nodes_[i-1].issue_.currency != currencyID) || + (nodes_[i+1].issue_.currency != currencyID)) { terStatus = temBAD_PATH; - return; + return terStatus; } - checkNoRipple ( + terStatus = checkNoRipple ( nodes_[i-1].account_, nodes_[i].account_, nodes_[i+1].account_, currencyID); if (terStatus != tesSUCCESS) - return; + return terStatus; } - } + + return tesSUCCESS; } // This is for debugging not end users. Output names can be changed without diff --git a/src/ripple/module/app/paths/PathState.h b/src/ripple/module/app/paths/PathState.h index 829c2539df8..6807d0bb8f5 100644 --- a/src/ripple/module/app/paths/PathState.h +++ b/src/ripple/module/app/paths/PathState.h @@ -25,30 +25,25 @@ namespace ripple { -// Holds a path state under incremental application. +// Holds a single path state under incremental application. class PathState : public CountedObject { public: typedef std::vector OfferIndexList; - typedef std::vector> List; + typedef std::shared_ptr Ptr; + typedef std::vector List; PathState (const STAmount& saSend, const STAmount& saSendMax) : saInReq (saSendMax) , saOutReq (saSend) - , allLiquidityConsumed_ (false) { } - PathState (const PathState& psSrc, bool bUnused) - : saInReq (psSrc.saInReq) - , saOutReq (psSrc.saOutReq) - , allLiquidityConsumed_ (false) - { - } + explicit PathState (const PathState& psSrc) = default; - void clear(); + void reset(STAmount const& in, STAmount const& out); - void expandPath ( + TER expandPath ( LedgerEntrySet const& lesSource, STPath const& spSourcePath, Account const& uReceiverID, @@ -57,20 +52,36 @@ class PathState : public CountedObject path::Node::List& nodes() { return nodes_; } - STAmount& inPass() { return saInPass; } - STAmount& outPass() { return saOutPass; } - const STAmount& outReq() const { return saOutReq; } + 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; } - STAmount& inAct() { return saInAct; } - STAmount& outAct() { return saOutAct; } - const STAmount& inReq() const { return saInReq; } + void insertReverse (AccountIssue const& ai, path::NodeIndex i) + { + umReverse.insert({ai, i}); + } - AccountCurrencyIssuerToNodeIndex& forward() { return umForward; } - AccountCurrencyIssuerToNodeIndex& reverse() { return umReverse; } Json::Value getJson () const; static char const* getCountedObjectName () { return "PathState"; } - OfferIndexList& becameUnfunded() { return vUnfundedBecame; } + OfferIndexList& unfundedOffers() { return unfundedOffers_; } void setStatus(TER status) { terStatus = status; } TER status() const { return terStatus; } @@ -84,44 +95,55 @@ class PathState : public CountedObject void setIndex (int i) { mIndex = i; } int index() const { return mIndex; } - void checkNoRipple (Account const& destinationAccountID, - Account const& sourceAccountID); + TER checkNoRipple (Account const& destinationAccountID, + Account const& sourceAccountID); static bool lessPriority (PathState& lhs, PathState& rhs); LedgerEntrySet& ledgerEntries() { return lesEntries; } + bool isDry() const + { + return !(saInPass && saOutPass); + } + private: - void checkNoRipple ( + TER checkNoRipple ( Account const&, Account const&, Account const&, Currency const&); + /** Clear path structures, and clear each node. */ + void clear(); + 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. - OfferIndexList vUnfundedBecame; + // 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. - AccountCurrencyIssuerToNodeIndex umForward; + AccountIssueToNodeIndex umForward; // First time working in reverse a funding source was used. // Source may only be used there if not mentioned by an account. - AccountCurrencyIssuerToNodeIndex umReverse; + AccountIssueToNodeIndex umReverse; - LedgerEntrySet lesEntries; + LedgerEntrySet lesEntries; int mIndex; // Index/rank amoung siblings. std::uint64_t uQuality; // 0 = no quality/liquity left. + const STAmount& saInReq; // --> Max amount to spend by sender. STAmount saInAct; // --> Amount spent by sender so far. STAmount saInPass; // <-- Amount spent by sender. + const STAmount& saOutReq; // --> Amount to send. STAmount saOutAct; // --> Amount actually sent so far. STAmount saOutPass; // <-- Amount actually sent. // If true, all liquidity on this path has been consumed. - bool allLiquidityConsumed_; + bool allLiquidityConsumed_ = false; TER pushNode ( int const iType, diff --git a/src/ripple/module/app/paths/Pathfinder.cpp b/src/ripple/module/app/paths/Pathfinder.cpp index 14182f4fc3c..f01f25846dd 100644 --- a/src/ripple/module/app/paths/Pathfinder.cpp +++ b/src/ripple/module/app/paths/Pathfinder.cpp @@ -17,14 +17,15 @@ */ //============================================================================== -#include +#include -#include +#include namespace ripple { /* -we just need to find a succession of the highest quality paths there until we find enough width +we just need to find a succession of the highest quality paths there until we +find enough width Don't do branching within each path @@ -64,7 +65,7 @@ Test USD to EUR typedef std::tuple path_LQ_t; // Lower numbers have better quality. Sort higher quality first. -static bool bQualityCmp (const path_LQ_t& a, const path_LQ_t& b) +static bool bQualityCmp (path_LQ_t const& a, path_LQ_t const& b) { // 1) Higher quality (lower cost) is better if (std::get<0> (a) != std::get<0> (b)) @@ -87,7 +88,7 @@ typedef std::vector AccountCandidates; static bool candCmp ( std::uint32_t seq, - const AccountCandidate& first, const AccountCandidate& second) + AccountCandidate const& first, AccountCandidate const& second) { if (first.first < second.first) return false; @@ -118,7 +119,8 @@ Pathfinder::Pathfinder ( if ((mSrcAccountID == mDstAccountID && mSrcCurrencyID == mDstAmount.getCurrency ()) || mDstAmount == zero) { - // no need to send to same account with same currency, must send non-zero + // No need to send to same account with same currency, must send + // non-zero. bValid = false; mLedger.reset (); return; @@ -244,7 +246,8 @@ bool Pathfinder::findPaths ( } } - WriteLog (lsDEBUG, Pathfinder) << mCompletePaths.size() << " paths to filter"; + WriteLog (lsDEBUG, Pathfinder) + << mCompletePaths.size() << " paths to filter"; if (mCompletePaths.size() > iMaxPaths) pathsOut = filterPaths(iMaxPaths, extraPath); @@ -263,33 +266,26 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath) STAmount remaining = mDstAmount; - // must subtract liquidity in default path from remaining amount + // Must subtract liquidity in default path from remaining amount try { - STAmount saMaxAmountAct, saDstAmountAct; - PathState::List pathStateList; LedgerEntrySet lesSandbox (mLedger, tapNONE); + path::RippleCalc rc( + lesSandbox, + mSrcAmount, + mDstAmount, + mDstAccountID, + mSrcAccountID, + STPathSet ()); + rc.partialPaymentAllowed_ = true; - TER result = path::rippleCalculate ( - lesSandbox, - saMaxAmountAct, - saDstAmountAct, - pathStateList, - mSrcAmount, - mDstAmount, - mDstAccountID, - mSrcAccountID, - STPathSet (), - true, // allow partial payment - false, - false, // don't suppress default paths, that's the point - true); + TER result = rc.rippleCalculate (); if (tesSUCCESS == result) { WriteLog (lsDEBUG, Pathfinder) - << "Default path contributes: " << saDstAmountAct; - remaining -= saDstAmountAct; + << "Default path contributes: " << rc.actualAmountIn_; + remaining -= rc.actualAmountOut_; } else { @@ -311,34 +307,28 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath) // Build map of quality to entry. for (int i = mCompletePaths.size (); i--;) { - STAmount saMaxAmountAct; - STAmount saDstAmountAct; - PathState::List pathStateList; STPathSet spsPaths; STPath& spCurrent = mCompletePaths[i]; - spsPaths.addPath (spCurrent); // Just checking the current path. + spsPaths.addPath (spCurrent); // Just checking the current path. + + TER resultCode; + + LedgerEntrySet lesSandbox (mLedger, tapNONE); + path::RippleCalc rc( + lesSandbox, + mSrcAmount, // --> amount to send max. + mDstAmount, // --> amount to deliver. + mDstAccountID, + mSrcAccountID, + spsPaths); - TER resultCode; + rc.partialPaymentAllowed_ = true; + rc.defaultPathsAllowed_ = false; try { - LedgerEntrySet lesSandbox (mLedger, tapNONE); - - resultCode = path::rippleCalculate ( - lesSandbox, - saMaxAmountAct, // --> computed input - saDstAmountAct, // --> computed output - pathStateList, - mSrcAmount, // --> amount to send max. - mDstAmount, // --> amount to deliver. - mDstAccountID, - mSrcAccountID, - spsPaths, - true, // --> bPartialPayment: Allow, it might contribute. - false, // --> bLimitQuality: Assume normal transaction. - true, // --> bNoRippleDirect: Providing the only path. - true); // --> bStandAlone: Don't need to delete unfundeds. + resultCode = rc.rippleCalculate (); } catch (const std::exception& e) { @@ -353,23 +343,23 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath) "findPaths: dropping: " << transToken (resultCode) << ": " << spCurrent.getJson (0); } - else if (saDstAmountAct < saMinDstAmount) + else if (rc.actualAmountOut_ < saMinDstAmount) { WriteLog (lsDEBUG, Pathfinder) << - "findPaths: dropping: outputs " << saDstAmountAct << - ": %s" << spCurrent.getJson (0); + "findPaths: dropping: outputs " << rc.actualAmountOut_ << + ": " << spCurrent.getJson (0); } else { std::uint64_t uQuality ( - STAmount::getRate (saDstAmountAct, saMaxAmountAct)); + STAmount::getRate (rc.actualAmountOut_, rc.actualAmountIn_)); WriteLog (lsDEBUG, Pathfinder) << "findPaths: quality: " << uQuality << ": " << spCurrent.getJson (0); vMap.push_back (path_LQ_t ( - uQuality, spCurrent.mPath.size (), saDstAmountAct, i)); + uQuality, spCurrent.mPath.size (), rc.actualAmountOut_, i)); } } @@ -429,8 +419,8 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath) } CurrencySet usAccountSourceCurrencies ( - const RippleAddress& raAccountID, RippleLineCache::ref lrCache, - bool includeXRP) + const RippleAddress& raAccountID, RippleLineCache::ref lrCache, + bool includeXRP) { CurrencySet usCurrencies; @@ -441,10 +431,10 @@ CurrencySet usAccountSourceCurrencies ( // List of ripple lines. auto& rippleLines (lrCache->getRippleLines (raAccountID.getAccountID ())); - for (auto item: rippleLines.getItems ()) + for (auto const& item: rippleLines.getItems ()) { - RippleState* rspEntry = (RippleState*) item.get (); - const STAmount& saBalance = rspEntry->getBalance (); + auto rspEntry = (RippleState*) item.get (); + auto& saBalance = rspEntry->getBalance (); // Filter out non if (saBalance > zero @@ -462,9 +452,9 @@ CurrencySet usAccountSourceCurrencies ( } CurrencySet usAccountDestCurrencies ( - const RippleAddress& raAccountID, - RippleLineCache::ref lrCache, - bool includeXRP) + const RippleAddress& raAccountID, + RippleLineCache::ref lrCache, + bool includeXRP) { CurrencySet usCurrencies; @@ -488,15 +478,12 @@ CurrencySet usAccountDestCurrencies ( return usCurrencies; } -bool Pathfinder::matchesOrigin (Currency const& currency, Account const& issuer) +bool Pathfinder::matchesOrigin (Issue const& issue) { - if (currency != mSrcCurrencyID) - return false; - - if (currency.isZero()) - return true; - - return (issuer == mSrcIssuerID) || (issuer == mSrcAccountID); + return issue.currency == mSrcCurrencyID && + (isXRP (issue) || + issue.account == mSrcIssuerID || + issue.account == mSrcAccountID); } // VFALCO TODO Use Currency, RippleAccount, et. al. in argument list here @@ -551,15 +538,15 @@ int Pathfinder::getPathsOut ( } void Pathfinder::addLink( - const STPathSet& currentPaths, // The paths to build from + STPathSet const& currentPaths, // The paths to build from STPathSet& incompletePaths, // The set of partial paths we add to int addFlags) { - WriteLog (lsDEBUG, Pathfinder) << "addLink< on " << currentPaths.size() << " source(s), flags=" << addFlags; - for (auto const& path : currentPaths) - { + WriteLog (lsDEBUG, Pathfinder) + << "addLink< on " << currentPaths.size() + << " source(s), flags=" << addFlags; + for (auto const& path: currentPaths) addLink(path, incompletePaths, addFlags); - } } STPathSet& Pathfinder::getPaths(PathType_t const& type, bool addComplete) @@ -616,7 +603,8 @@ STPathSet& Pathfinder::getPaths(PathType_t const& type, bool addComplete) break; case nt_DESTINATION: - // FIXME: What if a different issuer was specified on the destination amount + // FIXME: What if a different issuer was specified on the + // destination amount? addLink(pathsIn, pathsOut, afADD_ACCOUNTS | afAC_LAST); break; @@ -625,7 +613,8 @@ STPathSet& Pathfinder::getPaths(PathType_t const& type, bool addComplete) CondLog (mCompletePaths.size() != cp, lsDEBUG, Pathfinder) << (mCompletePaths.size() - cp) << " complete paths added"; - WriteLog (lsDEBUG, Pathfinder) << "getPaths> " << pathsOut.size() << " partial paths found"; + WriteLog (lsDEBUG, Pathfinder) + << "getPaths> " << pathsOut.size() << " partial paths found"; return pathsOut; } @@ -642,7 +631,7 @@ bool Pathfinder::isNoRipple ( // Does this path end on an account-to-account link whose last account // has set no ripple on the link? -bool Pathfinder::isNoRippleOut (const STPath& currentPath) +bool Pathfinder::isNoRippleOut (STPath const& currentPath) { // Must have at least one link if (currentPath.size() == 0) @@ -655,9 +644,11 @@ bool Pathfinder::isNoRippleOut (const STPath& currentPath) // What account are we leaving? auto const& fromAccount = - (currentPath.size() == 1) ? mSrcAccountID : (currentPath.end() - 2)->mAccountID; + (currentPath.size() == 1) ? mSrcAccountID : (currentPath.end() - 2)-> + mAccountID; - return isNoRipple (endElement.mAccountID, fromAccount, endElement.mCurrencyID); + return isNoRipple ( + endElement.mAccountID, fromAccount, endElement.mCurrencyID); } void Pathfinder::addLink( @@ -665,13 +656,15 @@ void Pathfinder::addLink( STPathSet& incompletePaths, // The set of partial paths we add to int addFlags) { - STPathElement const& pathEnd = currentPath.isEmpty() ? mSource : currentPath.mPath.back (); + auto const& pathEnd = currentPath.isEmpty() ? + mSource : currentPath.mPath.back (); auto const& uEndCurrency = pathEnd.mCurrencyID; auto const& uEndIssuer = pathEnd.mIssuerID; auto const& uEndAccount = pathEnd.mAccountID; - bool const bOnXRP = uEndCurrency.isZero(); + bool const bOnXRP = uEndCurrency.isZero(); - WriteLog (lsTRACE, Pathfinder) << "addLink< flags=" << addFlags << " onXRP=" << bOnXRP; + WriteLog (lsTRACE, Pathfinder) << "addLink< flags=" + << addFlags << " onXRP=" << bOnXRP; WriteLog (lsTRACE, Pathfinder) << currentPath.getJson(0); if (addFlags & afADD_ACCOUNTS) @@ -680,7 +673,8 @@ void Pathfinder::addLink( { if (mDstAmount.isNative() && !currentPath.isEmpty()) { // non-default path to XRP destination - WriteLog (lsTRACE, Pathfinder) << "complete path found ax: " << currentPath.getJson(0); + WriteLog (lsTRACE, Pathfinder) + << "complete path found ax: " << currentPath.getJson(0); mCompletePaths.addUniquePath(currentPath); } } @@ -778,15 +772,21 @@ void Pathfinder::addLink( std::placeholders::_2)); int count = candidates.size(); - if ((count > 10) && (uEndAccount != mSrcAccountID)) // allow more paths from source + // allow more paths from source + if ((count > 10) && (uEndAccount != mSrcAccountID)) count = 10; else if (count > 50) count = 50; auto it = candidates.begin(); while (count-- != 0) - { // Add accounts to incompletePaths - incompletePaths.assembleAdd(currentPath, STPathElement(STPathElement::typeAccount, it->second, uEndCurrency, it->second)); + { + // Add accounts to incompletePaths + incompletePaths.assembleAdd( + currentPath, + STPathElement(STPathElement::typeAccount, + it->second, uEndCurrency, + it->second)); ++it; } } @@ -794,7 +794,8 @@ void Pathfinder::addLink( } else { - WriteLog(lsWARNING, Pathfinder) << "Path ends on non-existent issuer"; + WriteLog(lsWARNING, Pathfinder) + << "Path ends on non-existent issuer"; } } } @@ -802,7 +803,8 @@ void Pathfinder::addLink( { // add order books if (addFlags & afOB_XRP) { // to XRP only - if (!bOnXRP && getApp().getOrderBookDB().isBookToXRP({uEndCurrency, uEndIssuer})) + if (!bOnXRP && getApp().getOrderBookDB().isBookToXRP ( + {uEndCurrency, uEndIssuer})) { STPathElement pathElement( STPathElement::typeCurrency, @@ -814,17 +816,17 @@ void Pathfinder::addLink( { bool bDestOnly = (addFlags & afOB_LAST) != 0; std::vector books; - getApp().getOrderBookDB().getBooksByTakerPays({uEndCurrency, uEndIssuer}, books); - WriteLog (lsTRACE, Pathfinder) << books.size() << " books found from this currency/issuer"; + getApp().getOrderBookDB().getBooksByTakerPays( + {uEndCurrency, uEndIssuer}, books); + WriteLog (lsTRACE, Pathfinder) + << books.size() << " books found from this currency/issuer"; for (auto const& book : books) { if (!currentPath.hasSeen ( xrpAccount(), book->getCurrencyOut(), book->getIssuerOut()) && - !matchesOrigin( - book->getCurrencyOut(), - book->getIssuerOut()) && + !matchesOrigin (book->book().out) && (!bDestOnly || (book->getCurrencyOut() == mDstAmount.getCurrency()))) { @@ -836,32 +838,50 @@ void Pathfinder::addLink( // add the order book itself newPath.addElement(STPathElement( STPathElement::typeCurrency, - xrpAccount(), xrpCurrency(), xrpAccount())); + xrpAccount(), + xrpCurrency(), + xrpAccount())); if (mDstAmount.getCurrency().isZero()) - { // destination is XRP, add account and path is complete - WriteLog (lsTRACE, Pathfinder) << "complete path found bx: " << currentPath.getJson(0); + { + // destination is XRP, add account and path is + // complete + WriteLog (lsTRACE, Pathfinder) + << "complete path found bx: " + << currentPath.getJson(0); mCompletePaths.addUniquePath(newPath); } else incompletePaths.addPath(newPath); } - else if (!currentPath.hasSeen(book->getIssuerOut(), book->getCurrencyOut(), book->getIssuerOut())) + else if (!currentPath.hasSeen( + book->getIssuerOut(), + book->getCurrencyOut(), + book->getIssuerOut())) { // Don't want the book if we've already seen the issuer // add the order book itself - newPath.addElement(STPathElement(STPathElement::typeCurrency | STPathElement::typeIssuer, - xrpAccount(), book->getCurrencyOut(), book->getIssuerOut())); - - if ((book->getIssuerOut() == mDstAccountID) && book->getCurrencyOut() == mDstAmount.getCurrency()) + newPath.addElement( + STPathElement(STPathElement::typeCurrency | + STPathElement::typeIssuer, + xrpAccount(), + book->getCurrencyOut(), book-> + getIssuerOut())); + + if (book->getIssuerOut() == mDstAccountID && + book->getCurrencyOut() == mDstAmount.getCurrency()) { // with the destination account, this path is complete - WriteLog (lsTRACE, Pathfinder) << "complete path found ba: " << currentPath.getJson(0); + WriteLog (lsTRACE, Pathfinder) + << "complete path found ba: " + << currentPath.getJson(0); mCompletePaths.addUniquePath(newPath); } else { // add issuer's account, path still incomplete incompletePaths.assembleAdd(newPath, STPathElement(STPathElement::typeAccount, - book->getIssuerOut(), book->getCurrencyOut(), book->getIssuerOut())); + book->getIssuerOut(), + book->getCurrencyOut(), + book->getIssuerOut())); } } @@ -871,7 +891,7 @@ void Pathfinder::addLink( } } -std::map Pathfinder::mPathTable; +Pathfinder::PathTable Pathfinder::mPathTable; Pathfinder::PathType_t Pathfinder::makePath(char const *string) { @@ -901,7 +921,9 @@ Pathfinder::PathType_t Pathfinder::makePath(char const *string) ret.push_back(nt_DEST_BOOK); break; - case 'd': // destination (with account, if required and not already present) + case 'd': + // Destination (with account, if required and not already + // present). ret.push_back(nt_DESTINATION); break; @@ -965,23 +987,23 @@ void Pathfinder::initPathTable() fillPaths( pt_XRP_to_nonXRP, { - {1, "sfd"}, // source -> book -> gateway - {3, "sfad"}, // source -> book -> account -> destination - {5, "sfaad"}, // source -> book -> account -> account -> destination - {6, "sbfd"}, // source -> book -> book -> destination - {8, "sbafd"}, // source -> book -> account -> book -> destination - {9, "sbfad"}, // source -> book -> book -> account -> destination + {1, "sfd"}, // source -> book -> gateway + {3, "sfad"}, // source -> book -> account -> destination + {5, "sfaad"}, // source -> book -> account -> account -> destination + {6, "sbfd"}, // source -> book -> book -> destination + {8, "sbafd"}, // source -> book -> account -> book -> destination + {9, "sbfad"}, // source -> book -> book -> account -> destination {10, "sbafad"} }); fillPaths( pt_XRP_to_nonXRP, { {1, "sfd"}, - {3, "sfad"}, // source -> book -> account -> destination - {5, "sfaad"}, // source -> book -> account -> account -> destination - {6, "sbfd"}, // source -> book -> book -> destination - {8, "sbafd"}, // source -> book -> account -> book -> destination - {9, "sbfad"}, // source -> book -> book -> account -> destination + {3, "sfad"}, // source -> book -> account -> destination + {5, "sfaad"}, // source -> book -> account -> account -> destination + {6, "sbfd"}, // source -> book -> book -> destination + {8, "sbafd"}, // source -> book -> account -> book -> destination + {9, "sbfad"}, // source -> book -> book -> account -> destination {10, "sbafad"} }); diff --git a/src/ripple/module/app/paths/Pathfinder.h b/src/ripple/module/app/paths/Pathfinder.h index fce606e55c2..35b82958606 100644 --- a/src/ripple/module/app/paths/Pathfinder.h +++ b/src/ripple/module/app/paths/Pathfinder.h @@ -22,34 +22,6 @@ namespace ripple { -// VFALCO TODO Remove this unused stuff? -#if 0 -// -// This is a very simple implementation. This can be made way better. -// We are simply flooding from the start. And doing an exhaustive search of all paths under maxSearchSteps. An easy improvement would -be to flood from both directions. -// - -class PathOption -{ -public: - typedef std::shared_ptr pointer; - typedef const std::shared_ptr& ref; - - STPath mPath; - bool mCorrectCurrency; // for the sorting - Currency mCurrencyID; // what currency we currently have at the end of the path - Account mCurrentAccount; // what account is at the end of the path - int mTotalCost; // in send currency - STAmount mMinWidth; // in dest currency - float mQuality; - - PathOption (Account& srcAccount, Currency& srcCurrencyID, - Currency const& dstCurrencyID); - PathOption (PathOption::pointer other); -}; -#endif - /** Calculates payment paths. The @ref RippleCalc determines the quality of the found paths. @@ -61,19 +33,22 @@ class Pathfinder public: Pathfinder ( RippleLineCache::ref cache, - const RippleAddress& srcAccountID, - const RippleAddress& dstAccountID, + RippleAddress const& srcAccountID, + RippleAddress const& dstAccountID, Currency const& srcCurrencyID, Account const& srcIssuerID, - const STAmount& dstAmount, bool& bValid); + STAmount const& dstAmount, + bool& bValid); static void initPathTable(); + bool findPaths ( - int iLevel, const unsigned int iMaxPaths, - STPathSet& spsDst, STPath& spExtraPath); + int iLevel, + unsigned int const iMaxPaths, + STPathSet& spsDst, + STPath& spExtraPath); private: - enum PaymentType { pt_XRP_to_XRP, @@ -85,50 +60,66 @@ class Pathfinder enum NodeType { - nt_SOURCE, // The source account with an issuer account, if required - nt_ACCOUNTS, // Accounts that connect from this source/currency - nt_BOOKS, // Order books that connect to this currency - nt_XRP_BOOK, // The order book from this currency to XRP - nt_DEST_BOOK, // The order book to the destination currency/issuer - nt_DESTINATION // The destination account only + nt_SOURCE, // The source account with an issuer account, if required + nt_ACCOUNTS, // Accounts that connect from this source/currency + nt_BOOKS, // Order books that connect to this currency + nt_XRP_BOOK, // The order book from this currency to XRP + nt_DEST_BOOK, // The order book to the destination currency/issuer + nt_DESTINATION // The destination account only }; typedef std::vector PathType_t; typedef std::pair CostedPath_t; typedef std::vector CostedPathList_t; - typedef std::pair PathCost; - typedef std::vector PathCostList; + typedef std::pair PathCost; + typedef std::vector PathCostList; + + typedef std::map PathTable; + /** Fill a CostedPathList_t from its description. */ - static void fillPaths(PaymentType type, PathCostList const& costs); + static void fillPaths(PaymentType type, + PathCostList const& costs); /** @return true if any building paths are now complete. */ bool checkComplete (STPathSet& retPathSet); static std::string pathTypeToString(PathType_t const&); - bool matchesOrigin (Currency const& currency, Account const& issuer); + bool matchesOrigin (Issue const&); - int getPathsOut (Currency const& currency, Account const& accountID, - bool isDestCurrency, Account const& dest); + int getPathsOut ( + Currency const& currency, + Account const& accountID, + bool isDestCurrency, + Account const& dest); void addLink( - const STPath& currentPath, STPathSet& incompletePaths, int addFlags); + STPath const& currentPath, + STPathSet& incompletePaths, + int addFlags); + void addLink( - const STPathSet& currentPaths, STPathSet& incompletePaths, int addFlags); + STPathSet const& currentPaths, + STPathSet& incompletePaths, + int addFlags); + + STPathSet& getPaths(PathType_t const& type, + bool addComplete = true); - STPathSet& getPaths(const PathType_t& type, bool addComplete = true); - STPathSet filterPaths(int iMaxPaths, STPath& extraPath); + STPathSet filterPaths(int iMaxPaths, + STPath& extraPath); - bool isNoRippleOut (const STPath& currentPath); + bool isNoRippleOut (STPath const& currentPath); bool isNoRipple ( - Account const& setByID, Account const& setOnID, + Account const& setByID, + Account const& setOnID, Currency const& currencyID); // Our main table of paths - static std::map mPathTable; + static PathTable mPathTable; static PathType_t makePath(char const*); Account mSrcAccountID; @@ -144,35 +135,35 @@ class Pathfinder STPathElement mSource; STPathSet mCompletePaths; - std::map< PathType_t, STPathSet > mPaths; + std::map mPaths; hash_map, int> mPOMap; // Add ripple paths - static const std::uint32_t afADD_ACCOUNTS = 0x001; + static std::uint32_t const afADD_ACCOUNTS = 0x001; // Add order books - static const std::uint32_t afADD_BOOKS = 0x002; + static std::uint32_t const afADD_BOOKS = 0x002; // Add order book to XRP only - static const std::uint32_t afOB_XRP = 0x010; + static std::uint32_t const afOB_XRP = 0x010; // Must link to destination currency - static const std::uint32_t afOB_LAST = 0x040; + static std::uint32_t const afOB_LAST = 0x040; // Destination account only - static const std::uint32_t afAC_LAST = 0x080; + static std::uint32_t const afAC_LAST = 0x080; }; CurrencySet usAccountDestCurrencies - (const RippleAddress& raAccountID, - RippleLineCache::ref cache, - bool includeXRP); + (RippleAddress const& raAccountID, + RippleLineCache::ref cache, + bool includeXRP); CurrencySet usAccountSourceCurrencies - (const RippleAddress& raAccountID, - RippleLineCache::ref lrLedger, - bool includeXRP); + (RippleAddress const& raAccountID, + RippleLineCache::ref lrLedger, + bool includeXRP); } // ripple diff --git a/src/ripple/module/app/paths/RippleCalc.cpp b/src/ripple/module/app/paths/RippleCalc.cpp index 15911a7321c..200167c3a7f 100644 --- a/src/ripple/module/app/paths/RippleCalc.cpp +++ b/src/ripple/module/app/paths/RippleCalc.cpp @@ -17,83 +17,77 @@ */ //============================================================================== -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include namespace ripple { - namespace path { +bool RippleCalc::addPathState(STPath const& path, TER& resultCode) +{ + auto pathState = std::make_shared ( + saDstAmountReq_, saMaxAmountReq_); + + if (!pathState) + return false; + + pathState->expandPath ( + mActiveLedger, + path, + uDstAccountID_, + uSrcAccountID_); + + if (tesSUCCESS == pathState->status()) + pathState->checkNoRipple (uDstAccountID_, uSrcAccountID_); + + 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 (tesSUCCESS == pathState->status()) + { + resultCode = tesSUCCESS; + pathStateList_.push_back (pathState); + } + else if (terNO_LINE != pathState->status()) + { + 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 !bPartialPayment. -TER rippleCalculate ( - // Compute paths vs this ledger entry set. Up to caller to actually apply - // to ledger. - LedgerEntrySet& activeLedger, - // <-> --> = Fee already applied to src balance. - - STAmount& saMaxAmountAct, // <-- The computed input amount. - STAmount& saDstAmountAct, // <-- The computed output amount. - - // Expanded path with all the actual nodes in it. - // TODO(tom): does it put in default paths? - - // A path starts with the source account, ends with the destination account - // and goes through other acounts or order books. - PathState::List& pathStateList, - - // Issuer: - // XRP: xrpAccount() - // non-XRP: uSrcAccountID (for any issuer) or another account with trust - // node. - STAmount const& saMaxAmountReq, // --> -1 = no limit. - - // Issuer: - // XRP: xrpAccount() - // non-XRP: uDstAccountID (for any issuer) or another account with trust - // node. - STAmount const& saDstAmountReq, - - Account const& uDstAccountID, - Account const& uSrcAccountID, - - // A set of paths that are included in the transaction that we'll explore - // for liquidity. - STPathSet const& spsPaths, - - bool const bPartialPayment, - bool const bLimitQuality, - bool const bNoRippleDirect, - bool const bStandAlone, - - // True, not to delete unfundeds. - bool const bOpenLedger) +// <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed. +TER RippleCalc::rippleCalculate () { - assert (activeLedger.isValid ()); - RippleCalc rc (activeLedger, bOpenLedger); - + assert (mActiveLedger.isValid ()); WriteLog (lsTRACE, RippleCalc) << "rippleCalc>" - << " saMaxAmountReq:" << saMaxAmountReq - << " saDstAmountReq:" << saDstAmountReq; + << " saMaxAmountReq_:" << saMaxAmountReq_ + << " saDstAmountReq_:" << saDstAmountReq_; - TER resultCode = temUNCERTAIN; + TER resultCode = temUNCERTAIN; // YYY Might do basic checks on src and dst validity as per doPayment. - if (bNoRippleDirect && spsPaths.isEmpty ()) + // Incrementally search paths. + if (defaultPathsAllowed_) + { + if (!addPathState (STPath(), resultCode)) + return resultCode; + } + else if (spsPaths_.isEmpty ()) { WriteLog (lsDEBUG, RippleCalc) << "rippleCalc: Invalid transaction:" @@ -102,173 +96,78 @@ TER rippleCalculate ( return temRIPPLE_EMPTY; } - // Incrementally search paths. - - // bNoRippleDirect is a slight misnomer, it really means make no ripple - // default path. - if (!bNoRippleDirect) - { - // Build a default path. Use saDstAmountReq and saMaxAmountReq to imply - // nodes. - // XXX Might also make a XRP bridge by default. - - auto pspDirect = std::make_shared ( - saDstAmountReq, saMaxAmountReq); - - if (!pspDirect) - return temUNKNOWN; - - pspDirect->expandPath ( - activeLedger, STPath (), uDstAccountID, uSrcAccountID); - - if (tesSUCCESS == pspDirect->status()) - pspDirect->checkNoRipple (uDstAccountID, uSrcAccountID); + // Build a default path. Use saDstAmountReq_ and saMaxAmountReq_ to imply + // nodes. + // XXX Might also make a XRP bridge by default. - pspDirect->setIndex (pathStateList.size ()); - - WriteLog (lsDEBUG, RippleCalc) - << "rippleCalc: Build direct:" - << " status: " << transToken (pspDirect->status()); - - // Return if malformed. - if (isTemMalformed (pspDirect->status())) - return pspDirect->status(); - - if (tesSUCCESS == pspDirect->status()) - { - resultCode = tesSUCCESS; - pathStateList.push_back (pspDirect); - } - else if (terNO_LINE != pspDirect->status()) - { - resultCode = pspDirect->status(); - } - } WriteLog (lsTRACE, RippleCalc) - << "rippleCalc: Paths in set: " << spsPaths.size (); + << "rippleCalc: Paths in set: " << spsPaths_.size (); - int iIndex = 0; - for (auto const& spPath: spsPaths) + // Now expand the path state. + for (auto const& spPath: spsPaths_) { - auto pspExpanded = std::make_shared ( - saDstAmountReq, saMaxAmountReq); - - if (!pspExpanded) - return temUNKNOWN; - - WriteLog (lsTRACE, RippleCalc) - << "rippleCalc: EXPAND: " - << " saDstAmountReq:" << saDstAmountReq - << " saMaxAmountReq:" << saMaxAmountReq - << " uDstAccountID:" - << to_string (uDstAccountID) - << " uSrcAccountID:" - << to_string (uSrcAccountID); - - pspExpanded->expandPath ( - activeLedger, spPath, uDstAccountID, uSrcAccountID); - - if (tesSUCCESS == pspExpanded->status()) - pspExpanded->checkNoRipple (uDstAccountID, uSrcAccountID); - - WriteLog (lsDEBUG, RippleCalc) - << "rippleCalc:" - << " Build path:" << ++iIndex - << " status: " << transToken (pspExpanded->status()); - - // Return, if the path specification was malformed. - if (isTemMalformed (pspExpanded->status())) - return pspExpanded->status(); - - if (tesSUCCESS == pspExpanded->status()) - { - resultCode = tesSUCCESS; // Had a success. - - pspExpanded->setIndex (pathStateList.size ()); - pathStateList.push_back (pspExpanded); - } - else if (terNO_LINE != pspExpanded->status()) - { - resultCode = pspExpanded->status(); - } + if (!addPathState (spPath, resultCode)) + return resultCode; } if (resultCode != tesSUCCESS) - return resultCode == temUNCERTAIN ? terNO_LINE : resultCode; - else - resultCode = temUNCERTAIN; + return (resultCode == temUNCERTAIN) ? terNO_LINE : resultCode; - saMaxAmountAct = saMaxAmountReq.zeroed(); - saDstAmountAct = saDstAmountReq.zeroed(); + 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 = bLimitQuality - ? STAmount::getRate (saDstAmountReq, saMaxAmountReq) : 0; + const std::uint64_t uQualityLimit = limitQuality_ ? + STAmount::getRate (saDstAmountReq_, saMaxAmountReq_) : 0; // Offers that became unfunded. - std::vector vuUnfundedBecame; + std::vector unfundedOffers; - int iPass = 0; + int iPass = 0; while (resultCode == temUNCERTAIN) { int iBest = -1; - LedgerEntrySet lesCheckpoint = activeLedger; + LedgerEntrySet lesCheckpoint = mActiveLedger; int iDry = 0; // True, if ever computed multi-quality. - bool bMultiQuality = false; + bool multiQuality = false; // Find the best path. - for (auto pspCur: pathStateList) + for (auto pathState: pathStateList_) { - if (pspCur->quality()) + if (pathState->quality()) // Only do active paths. { - bMultiQuality = 1 == pathStateList.size () - iDry; - // Computing the only non-dry path, compute multi-quality. + // If computing the only non-dry path, compute multi-quality. + multiQuality = ((pathStateList_.size () - iDry) == 1); - pspCur->inAct() = saMaxAmountAct; // Update to current amount processed. + pathState->reset (actualAmountIn_, actualAmountOut_); - pspCur->outAct() = saDstAmountAct; - - CondLog (pspCur->inReq() > zero - && pspCur->inAct() >= pspCur->inReq(), - lsWARNING, RippleCalc) - << "rippleCalc: DONE:" - << " inAct()=" << pspCur->inAct() - << " inReq()=" << pspCur->inReq(); - - assert (pspCur->inReq() < zero || - pspCur->inAct() < pspCur->inReq()); // Error if done. - - CondLog (pspCur->outAct() >= pspCur->outReq(), - lsWARNING, RippleCalc) - << "rippleCalc: ALREADY DONE:" - << " saOutAct=" << pspCur->outAct() - << " saOutReq=%s" << pspCur->outReq(); - - assert (pspCur->outAct() < pspCur->outReq()); // Error if done, output met. + PathCursor pc(*this, *pathState, multiQuality); + pc.nextIncrement (lesCheckpoint); - pathNext (rc, *pspCur, bMultiQuality, lesCheckpoint, rc.mActiveLedger); // Compute increment. WriteLog (lsDEBUG, RippleCalc) << "rippleCalc: AFTER:" - << " mIndex=" << pspCur->index() - << " uQuality=" << pspCur->quality() - << " rate=%s" << STAmount::saFromRate (pspCur->quality()); + << " mIndex=" << pathState->index() + << " uQuality=" << pathState->quality() + << " rate=" << STAmount::saFromRate (pathState->quality()); - if (!pspCur->quality()) + if (!pathState->quality()) { // Path was dry. ++iDry; } - else if (pspCur->outPass() == zero) + else if (pathState->outPass() == zero) { // Path is not dry, but moved no funds // This should never happen. Consider the path dry @@ -278,45 +177,46 @@ TER rippleCalculate ( assert (false); - pspCur->setQuality (0); + pathState->setQuality (0); ++iDry; } else { - CondLog (!pspCur->inPass() || !pspCur->outPass(), + CondLog (!pathState->inPass() || !pathState->outPass(), lsDEBUG, RippleCalc) << "rippleCalc: better:" << " uQuality=" - << STAmount::saFromRate (pspCur->quality()) - << " inPass()=" << pspCur->inPass() - << " saOutPass=" << pspCur->outPass(); + << STAmount::saFromRate (pathState->quality()) + << " inPass()=" << pathState->inPass() + << " saOutPass=" << pathState->outPass(); - assert (!!pspCur->inPass() && !!pspCur->outPass()); + assert (pathState->inPass() && pathState->outPass()); - if ((!bLimitQuality || pspCur->quality() <= uQualityLimit) + if ((!limitQuality_ || + pathState->quality() <= uQualityLimit) // Quality is not limited or increment has allowed // quality. && (iBest < 0 // Best is not yet set. - || PathState::lessPriority (*pathStateList[iBest], - *pspCur))) + || PathState::lessPriority ( + *pathStateList_[iBest], *pathState))) // Current is better than set. { WriteLog (lsDEBUG, RippleCalc) << "rippleCalc: better:" - << " mIndex=" << pspCur->index() - << " uQuality=" << pspCur->quality() + << " mIndex=" << pathState->index() + << " uQuality=" << pathState->quality() << " rate=" - << STAmount::saFromRate (pspCur->quality()) - << " inPass()=" << pspCur->inPass() - << " saOutPass=" << pspCur->outPass(); + << STAmount::saFromRate (pathState->quality()) + << " inPass()=" << pathState->inPass() + << " saOutPass=" << pathState->outPass(); - assert (activeLedger.isValid ()); - activeLedger.swapWith (pspCur->ledgerEntries()); + assert (mActiveLedger.isValid ()); + mActiveLedger.swapWith (pathState->ledgerEntries()); // For the path, save ledger state. - activeLedger.invalidate (); + mActiveLedger.invalidate (); - iBest = pspCur->index (); + iBest = pathState->index (); } } } @@ -328,81 +228,81 @@ TER rippleCalculate ( << "rippleCalc: Summary:" << " Pass: " << ++iPass << " Dry: " << iDry - << " Paths: " << pathStateList.size (); - for (auto pspCur: pathStateList) + << " Paths: " << pathStateList_.size (); + for (auto pathState: pathStateList_) { WriteLog (lsDEBUG, RippleCalc) << "rippleCalc: " - << "Summary: " << pspCur->index() + << "Summary: " << pathState->index() << " rate: " - << STAmount::saFromRate (pspCur->quality()) - << " quality:" << pspCur->quality() - << " best: " << (iBest == pspCur->index ()); + << STAmount::saFromRate (pathState->quality()) + << " quality:" << pathState->quality() + << " best: " << (iBest == pathState->index ()); } } if (iBest >= 0) { // Apply best path. - auto pspBest = pathStateList[iBest]; + auto pathState = pathStateList_[iBest]; WriteLog (lsDEBUG, RippleCalc) << "rippleCalc: best:" << " uQuality=" - << STAmount::saFromRate (pspBest->quality()) - << " inPass()=" << pspBest->inPass() - << " saOutPass=" << pspBest->outPass(); + << STAmount::saFromRate (pathState->quality()) + << " inPass()=" << pathState->inPass() + << " saOutPass=" << pathState->outPass(); // Record best pass' offers that became unfunded for deletion on // success. - vuUnfundedBecame.insert ( - vuUnfundedBecame.end (), - pspBest->becameUnfunded().begin (), - pspBest->becameUnfunded().end ()); + unfundedOffers.insert ( + unfundedOffers.end (), + pathState->unfundedOffers().begin (), + pathState->unfundedOffers().end ()); // Record best pass' LedgerEntrySet to build off of and potentially // return. - assert (pspBest->ledgerEntries().isValid ()); - activeLedger.swapWith (pspBest->ledgerEntries()); - pspBest->ledgerEntries().invalidate (); + assert (pathState->ledgerEntries().isValid ()); + mActiveLedger.swapWith (pathState->ledgerEntries()); + pathState->ledgerEntries().invalidate (); - saMaxAmountAct += pspBest->inPass(); - saDstAmountAct += pspBest->outPass(); + actualAmountIn_ += pathState->inPass(); + actualAmountOut_ += pathState->outPass(); - if (pspBest->allLiquidityConsumed() || bMultiQuality) + if (pathState->allLiquidityConsumed() || multiQuality) { ++iDry; - pspBest->setQuality(0); + pathState->setQuality(0); } - if (saDstAmountAct == saDstAmountReq) + if (actualAmountOut_ == saDstAmountReq_) { // Done. Delivered requested amount. resultCode = tesSUCCESS; } - else if (saDstAmountAct > saDstAmountReq) + else if (actualAmountOut_ > saDstAmountReq_) { WriteLog (lsFATAL, RippleCalc) << "rippleCalc: TOO MUCH:" - << " saDstAmountAct:" << saDstAmountAct - << " saDstAmountReq:" << saDstAmountReq; + << " actualAmountOut_:" << actualAmountOut_ + << " saDstAmountReq_:" << saDstAmountReq_; return tefEXCEPTION; // TEMPORARY assert (false); } - else if (saMaxAmountAct != saMaxAmountReq && - iDry != pathStateList.size ()) + 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. - rc.mumSource.insert ( - pspBest->reverse().begin (), pspBest->reverse().end ()); + mumSource.insert ( + pathState->reverse().begin (), pathState->reverse().end ()); } - else if (!bPartialPayment) + else if (!partialPaymentAllowed_) { // Have sent maximum allowed. Partial payment not allowed. @@ -416,55 +316,58 @@ TER rippleCalculate ( } } // Not done and ran out of paths. - else if (!bPartialPayment) + else if (!partialPaymentAllowed_) { // Partial payment not allowed. - resultCode = tecPATH_PARTIAL; + resultCode = tecPATH_PARTIAL; } // Partial payment ok. - else if (!saDstAmountAct) + else if (!actualAmountOut_) { // No payment at all. - resultCode = tecPATH_DRY; + resultCode = tecPATH_DRY; } else { // We must restore the activeLedger from lesCheckpoint in the case // when iBest is -1 and just before the result is set to tesSUCCESS. - activeLedger.swapWith (lesCheckpoint); + mActiveLedger.swapWith (lesCheckpoint); resultCode = tesSUCCESS; } } - if (!bStandAlone) + if (deleteUnfundedOffers_) { if (resultCode == tesSUCCESS) { - // Delete became unfunded offers. - for (auto const& offerIndex: vuUnfundedBecame) + // Delete offers that became unfunded. + for (auto const& offerIndex: unfundedOffers) { if (resultCode == tesSUCCESS) { WriteLog (lsDEBUG, RippleCalc) << "Became unfunded " << to_string (offerIndex); - resultCode = activeLedger.offerDelete (offerIndex); + resultCode = mActiveLedger.offerDelete (offerIndex); } } } // Delete found unfunded offers. - for (auto const& offerIndex: rc.mUnfundedOffers) + for (auto const& offerIndex: unfundedOffers) { if (resultCode == tesSUCCESS) { WriteLog (lsDEBUG, RippleCalc) << "Delete unfunded " << to_string (offerIndex); - resultCode = activeLedger.offerDelete (offerIndex); + resultCode = mActiveLedger.offerDelete (offerIndex); } } } + // If isOpenLedger, then ledger is not final, can vote no. + if (resultCode == telFAILED_PROCESSING && !isLedgerOpen_) + return tecFAILED_PROCESSING; return resultCode; } diff --git a/src/ripple/module/app/paths/RippleCalc.h b/src/ripple/module/app/paths/RippleCalc.h index 085e0d181ea..1fe225f2b74 100644 --- a/src/ripple/module/app/paths/RippleCalc.h +++ b/src/ripple/module/app/paths/RippleCalc.h @@ -23,30 +23,87 @@ namespace ripple { namespace path { -/** Calculate the quality of a payment path. +/** RippleCalc calculates the quality of a payment path. - The quality is a synonym for price. Specifically, the amount of - input required to produce a given output along a specified path. + Quality is the amount of input required to produce a given output along a + specified path - another name for this is exchange rate. */ +struct RippleCalc +{ + RippleCalc ( + LedgerEntrySet& activeLedger, -TER rippleCalculate ( - LedgerEntrySet& lesActive, - STAmount& saMaxAmountAct, - STAmount& saDstAmountAct, - PathState::List& pathStateList, - const STAmount& saDstAmountReq, - const STAmount& saMaxAmountReq, - Account const& uDstAccountID, - Account const& uSrcAccountID, - const STPathSet& spsPaths, - const bool bPartialPayment, - const bool bLimitQuality, - const bool bNoRippleDirect, - // --> True, not to affect accounts. - const bool bStandAlone, - // --> What kind of errors to return. - const bool bOpenLedger = true -); + // Compute paths vs this ledger entry set. Up to caller to actually + // apply to ledger. + + // Issuer: + // XRP: xrpAccount() + // non-XRP: uSrcAccountID (for any issuer) or another account with + // trust node. + STAmount const& saMaxAmountReq, // --> -1 = no limit. + + // Issuer: + // XRP: xrpAccount() + // non-XRP: uDstAccountID (for any issuer) or another account with + // trust node. + STAmount const& saDstAmountReq, + + Account const& uDstAccountID, + Account const& uSrcAccountID, + + // A set of paths that are included in the transaction that we'll + // explore for liquidity. + STPathSet const& spsPaths) + : mActiveLedger (activeLedger), + saDstAmountReq_(saDstAmountReq), + saMaxAmountReq_(saMaxAmountReq), + uDstAccountID_(uDstAccountID), + uSrcAccountID_(uSrcAccountID), + spsPaths_(spsPaths) + { + } + + LedgerEntrySet& mActiveLedger; + TER rippleCalculate (); + + /** Add a single PathState. Returns true on success.*/ + bool addPathState(STPath const&, TER&); + + STAmount const& saDstAmountReq_; + STAmount const& saMaxAmountReq_; + Account const& uDstAccountID_; + Account const& uSrcAccountID_; + STPathSet const& spsPaths_; + + // 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; + + // If the transaction fails to meet some constraint, still need to delete + // unfunded offers. + // + // Offers that were found unfunded. + hash_set unfundedOffers; + + // 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_; + + bool partialPaymentAllowed_ = false; + bool limitQuality_ = false; + bool defaultPathsAllowed_ = true; + bool deleteUnfundedOffers_ = false; + bool isLedgerOpen_ = true; +}; } // path } // ripple diff --git a/src/ripple/module/app/paths/RippleLineCache.cpp b/src/ripple/module/app/paths/RippleLineCache.cpp index 526c92ddde1..056ab61a833 100644 --- a/src/ripple/module/app/paths/RippleLineCache.cpp +++ b/src/ripple/module/app/paths/RippleLineCache.cpp @@ -32,9 +32,9 @@ AccountItems& RippleLineCache::getRippleLines (Account const& accountID) if (it == mRLMap.end ()) { - it = mRLMap.insert (std::make_pair (accountID, std::make_shared - (accountID, mLedger, - AccountItem::pointer (new RippleState ())))).first; + auto accountItems = std::make_shared ( + accountID, mLedger, AccountItem::pointer (new RippleState ())); + it = mRLMap.insert ({accountID, accountItems}).first; } return *it->second; } diff --git a/src/ripple/module/app/paths/Tuning.h b/src/ripple/module/app/paths/Tuning.h index cdf4e3e3eaa..d786502df54 100644 --- a/src/ripple/module/app/paths/Tuning.h +++ b/src/ripple/module/app/paths/Tuning.h @@ -22,10 +22,13 @@ namespace ripple { -// VFALCO TODO Why 40? is the number 40 part of protocol? -const int CALC_NODE_DELIVER_MAX_LOOPS = 40; -const int NODE_ADVANCE_MAX_LOOPS = 100; -const int PATHFINDER_MAX_COMPLETE_PATHS = 1000; +int const CALC_NODE_DELIVER_MAX_LOOPS = 40; +int const NODE_ADVANCE_MAX_LOOPS = 100; +int const PATHFINDER_HIGH_PRIORITY = 100000; +int const PATHFINDER_MAX_PATHS = 50; +int const PATHFINDER_MAX_COMPLETE_PATHS = 1000; +int const PATHFINDER_MAX_PATHS_FROM_SOURCE = 10; + } // ripple #endif diff --git a/src/ripple/module/app/paths/Types.h b/src/ripple/module/app/paths/Types.h index cec206b07fb..e37ce13aa4e 100644 --- a/src/ripple/module/app/paths/Types.h +++ b/src/ripple/module/app/paths/Types.h @@ -22,12 +22,17 @@ namespace ripple { -// account id, currency id, issuer id. -typedef std::tuple AccountCurrencyIssuer; +// account id, issue. +typedef std::pair AccountIssue; -// Map of account, currency, issuer to node index. -typedef hash_map -AccountCurrencyIssuerToNodeIndex; +// Map of account, issue to node index. +namespace path { + +typedef unsigned int NodeIndex; + +} + +typedef hash_map AccountIssueToNodeIndex; } // ripple diff --git a/src/ripple/module/app/paths/CalcNodeAdvance.cpp b/src/ripple/module/app/paths/cursor/AdvanceNode.cpp similarity index 50% rename from src/ripple/module/app/paths/CalcNodeAdvance.cpp rename to src/ripple/module/app/paths/cursor/AdvanceNode.cpp index 2bc35b393f7..e7a7b24abf1 100644 --- a/src/ripple/module/app/paths/CalcNodeAdvance.cpp +++ b/src/ripple/module/app/paths/cursor/AdvanceNode.cpp @@ -17,37 +17,32 @@ */ //============================================================================== -#include -#include -#include +#include namespace ripple { namespace path { +TER PathCursor::advanceNode (STAmount const& amount, bool reverse) const +{ + if (!multiQuality_ || amount != zero) + return advanceNode (reverse); + + PathCursor withMultiQuality{rippleCalc_, pathState_, true, 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. // -// nodeAdvance 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 nodeAdvance ( - RippleCalc& rippleCalc, - const unsigned int nodeIndex, - PathState& pathState, - const bool bMultiQuality, - const bool bReverse) +TER PathCursor::advanceNode (bool const bReverse) const { - auto& previousNode = pathState.nodes()[nodeIndex - 1]; - auto& node = pathState.nodes()[nodeIndex]; - TER resultCode = tesSUCCESS; + 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. WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: TakerPays:" - << node.saTakerPays << " TakerGets:" << node.saTakerGets; + << "advanceNode: TakerPays:" + << node().saTakerPays << " TakerGets:" << node().saTakerGets; int loopCount = 0; @@ -65,70 +60,27 @@ TER nodeAdvance ( return tefEXCEPTION; } - bool bDirectDirDirty = false; + bool bDirectDirDirty = node().directory.initialize ( + {previousNode().issue_, node().issue_}, + ledger()); - if (!node.currentDirectory_) + if (auto advance = node().directory.advance (ledger())) { - // Need to initialize current node. - - node.currentDirectory_.copyFrom(Ledger::getBookBase ({ - {previousNode.currency_, previousNode.issuer_}, - {node.currency_, node.issuer_}})); - node.nextDirectory_.copyFrom( - Ledger::getQualityNext (node.currentDirectory_)); - - // TODO(tom): it seems impossible that any actual offers with - // quality == 0 could occur - we should disallow them, and clear - // sleDirectDir without the database call in the next line. - node.sleDirectDir = rippleCalc.mActiveLedger.entryCache ( - ltDIR_NODE, node.currentDirectory_); - - // Associated vars are dirty, if found it. - bDirectDirDirty = !!node.sleDirectDir; - - // Advance, if didn't find it. Normal not to be unable to lookup - // firstdirectory. Maybe even skip this lookup. - node.bDirectAdvance = !node.sleDirectDir; - node.bDirectRestart = false; - - WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: Initialize node:" - << " node.currentDirectory_=" << node.currentDirectory_ - <<" node.nextDirectory_=" << node.nextDirectory_ - << " node.bDirectAdvance=" << node.bDirectAdvance; - } - - if (node.bDirectAdvance || node.bDirectRestart) - { - // Get next quality. - if (node.bDirectAdvance) - { - // This works because the Merkel radix tree is ordered by key so - // we can go to the next one in O(1). - node.currentDirectory_ = rippleCalc.mActiveLedger.getNextLedgerIndex ( - node.currentDirectory_, node.nextDirectory_); - } - bDirectDirDirty = true; - node.bDirectAdvance = false; - node.bDirectRestart = false; - - if (node.currentDirectory_ != zero) + if (advance == NodeDirectory::NEW_QUALITY) { // We didn't run off the end of this order book and found // another quality directory. WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: Quality advance: node.currentDirectory_=" - << node.currentDirectory_; - - node.sleDirectDir = rippleCalc.mActiveLedger.entryCache (ltDIR_NODE, node.currentDirectory_); + << "advanceNode: Quality advance: node.directory.current=" + << node().directory.current; } else if (bReverse) { WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: No more offers."; + << "advanceNode: No more offers."; - node.offerIndex_ = 0; + node().offerIndex_ = 0; break; } else @@ -136,11 +88,10 @@ TER nodeAdvance ( // No more offers. Should be done rather than fall off end of // book. WriteLog (lsWARNING, RippleCalc) - << "nodeAdvance: Unreachable: " + << "advanceNode: Unreachable: " << "Fell off end of order book."; // FIXME: why? - return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : - tecFAILED_PROCESSING; + return telFAILED_PROCESSING; } } @@ -148,52 +99,59 @@ TER nodeAdvance ( { // Our quality changed since last iteration. // Use the rate from the directory. - node.saOfrRate = STAmount::setRate (Ledger::getQuality (node.currentDirectory_)); + node().saOfrRate = STAmount::setRate ( + Ledger::getQuality (node().directory.current)); // For correct ratio - node.uEntry = 0; - node.bEntryAdvance = true; + node().uEntry = 0; + node().bEntryAdvance = true; WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: directory dirty: node.saOfrRate=" - << node.saOfrRate; + << "advanceNode: directory dirty: node.saOfrRate=" + << node().saOfrRate; } - if (!node.bEntryAdvance) + if (!node().bEntryAdvance) { - if (node.bFundsDirty) + 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); + node().saTakerPays + = node().sleOffer->getFieldAmount (sfTakerPays); + node().saTakerGets + = node().sleOffer->getFieldAmount (sfTakerGets); // Funds left. - node.saOfferFunds = rippleCalc.mActiveLedger.accountFunds ( - node.offerOwnerAccount_, node.saTakerGets); - node.bFundsDirty = false; + node().saOfferFunds = ledger().accountFunds ( + node().offerOwnerAccount_, + node().saTakerGets); + node().bFundsDirty = false; WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: funds dirty: node.saOfrRate=" - << node.saOfrRate; + << "advanceNode: funds dirty: node().saOfrRate=" + << node().saOfrRate; } else { - WriteLog (lsTRACE, RippleCalc) << "nodeAdvance: as is"; + WriteLog (lsTRACE, RippleCalc) << "advanceNode: as is"; } } - else if (!rippleCalc.mActiveLedger.dirNext ( - node.currentDirectory_, node.sleDirectDir, node.uEntry, node.offerIndex_)) + else if (!ledger().dirNext ( + node().directory.current, + node().directory.ledgerEntry, + node().uEntry, + node().offerIndex_)) // This is the only place that offerIndex_ changes. { // Failed to find an entry in directory. - // Do another cur directory iff bMultiQuality - if (bMultiQuality) + // Do another cur directory iff multiQuality_ + if (multiQuality_) { // We are allowed to process multiple qualities if this is the // only path. WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: next quality"; - node.bDirectAdvance = true; // Process next quality. + << "advanceNode: next quality"; + node().directory.advanceNeeded = true; // Process next quality. } else if (!bReverse) { @@ -202,60 +160,62 @@ TER nodeAdvance ( // TODO(tom): these warnings occur in production! They // shouldn't. WriteLog (lsWARNING, RippleCalc) - << "nodeAdvance: unreachable: ran out of offers"; - return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : - tecFAILED_PROCESSING; + << "advanceNode: unreachable: ran out of offers"; + return telFAILED_PROCESSING; } else { // Ran off end of offers. - node.bEntryAdvance = false; // Done. - node.offerIndex_ = 0; // Report no more entries. + node().bEntryAdvance = false; // Done. + node().offerIndex_ = 0; // Report no more entries. } } else { // Got a new offer. - node.sleOffer = rippleCalc.mActiveLedger.entryCache ( - ltOFFER, node.offerIndex_); + node().sleOffer = ledger().entryCache ( + ltOFFER, node().offerIndex_); - if (!node.sleOffer) + if (!node().sleOffer) { // Corrupt directory that points to an entry that doesn't exist. // This has happened in production. WriteLog (lsWARNING, RippleCalc) << "Missing offer in directory"; - node.bEntryAdvance = true; + node().bEntryAdvance = true; } else { - node.offerOwnerAccount_ - = node.sleOffer->getFieldAccount160 (sfAccount); - node.saTakerPays = node.sleOffer->getFieldAmount (sfTakerPays); - node.saTakerGets = node.sleOffer->getFieldAmount (sfTakerGets); + node().offerOwnerAccount_ + = node().sleOffer->getFieldAccount160 (sfAccount); + node().saTakerPays + = node().sleOffer->getFieldAmount (sfTakerPays); + node().saTakerGets + = node().sleOffer->getFieldAmount (sfTakerGets); - const AccountCurrencyIssuer asLine ( - node.offerOwnerAccount_, node.currency_, node.issuer_); + const AccountIssue accountIssue ( + node().offerOwnerAccount_, node().issue_); WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: 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) <= - rippleCalc.mActiveLedger.getLedger ()->getParentCloseTimeNC ())) + << "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) <= + ledger().getLedger ()-> + getParentCloseTimeNC ())) { // Offer is expired. WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: expired offer"; - rippleCalc.mUnfundedOffers.insert(node.offerIndex_); + << "advanceNode: expired offer"; + rippleCalc_.unfundedOffers.insert(node().offerIndex_); continue; } - if (node.saTakerPays <= zero || node.saTakerGets <= zero) + if (node().saTakerPays <= zero || node().saTakerGets <= zero) { // Offer has bad amounts. Offers should never have a bad // amounts. @@ -265,25 +225,25 @@ TER nodeAdvance ( // Past internal error, offer had bad amounts. // This has occurred in production. WriteLog (lsWARNING, RippleCalc) - << "nodeAdvance: PAST INTERNAL ERROR" + << "advanceNode: PAST INTERNAL ERROR" << " REVERSE: OFFER NON-POSITIVE:" - << " node.saTakerPays=" << node.saTakerPays - << " node.saTakerGets=%s" << node.saTakerGets; + << " node.saTakerPays=" << node().saTakerPays + << " node.saTakerGets=%s" << node().saTakerGets; // Mark offer for always deletion. - rippleCalc.mUnfundedOffers.insert (node.offerIndex_); + rippleCalc_.unfundedOffers.insert (node().offerIndex_); } - else if (rippleCalc.mUnfundedOffers.find (node.offerIndex_) - != rippleCalc.mUnfundedOffers.end ()) + else if (rippleCalc_.unfundedOffers.find (node().offerIndex_) + != rippleCalc_.unfundedOffers.end ()) { // Past internal error, offer was found failed to place - // this in mUnfundedOffers. + // this in unfundedOffers. // Just skip it. It will be deleted. WriteLog (lsDEBUG, RippleCalc) - << "nodeAdvance: PAST INTERNAL ERROR " + << "advanceNode: PAST INTERNAL ERROR " << " FORWARD CONFIRM: OFFER NON-POSITIVE:" - << " node.saTakerPays=" << node.saTakerPays - << " node.saTakerGets=" << node.saTakerGets; + << " node.saTakerPays=" << node().saTakerPays + << " node.saTakerGets=" << node().saTakerGets; } else @@ -291,11 +251,11 @@ TER nodeAdvance ( // Reverse should have previously put bad offer in list. // An internal error previously left a bad offer. WriteLog (lsWARNING, RippleCalc) - << "nodeAdvance: INTERNAL ERROR" + << "advanceNode: INTERNAL ERROR" <<" FORWARD NEWLY FOUND: OFFER NON-POSITIVE:" - << " node.saTakerPays=" << node.saTakerPays - << " node.saTakerGets=" << node.saTakerGets; + << " node.saTakerPays=" << node().saTakerPays + << " node.saTakerGets=" << node().saTakerGets; // Don't process at all, things are in an unexpected // state for this transactions. @@ -313,60 +273,62 @@ TER nodeAdvance ( // 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 (asLine); - const bool bFoundForward = itForward != pathState.forward().end (); + 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.issuer_) + itForward->second != nodeIndex_ && + node().offerOwnerAccount_ != node().issue_.account) { // Temporarily unfunded. Another node uses this source, // ignore in this offer. WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: temporarily unfunded offer" + << "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 (asLine); - bool bFoundReverse = itReverse != pathState.reverse().end (); + 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.issuer_) + itReverse->second != nodeIndex_ && + node().offerOwnerAccount_ != node().issue_.account) { // Temporarily unfunded. Another node uses this source, // ignore in this offer. WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: temporarily unfunded offer" + << "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 (asLine); - bool bFoundPast = (itPast != rippleCalc.mumSource.end ()); + auto itPast = rippleCalc_.mumSource.find (accountIssue); + bool bFoundPast = (itPast != rippleCalc_.mumSource.end ()); // Only the current node is allowed to use the source. - node.saOfferFunds = rippleCalc.mActiveLedger.accountFunds - (node.offerOwnerAccount_, node.saTakerGets); // Funds held. + node().saOfferFunds = ledger().accountFunds ( + node().offerOwnerAccount_, node().saTakerGets); + // Funds held. - if (node.saOfferFunds <= zero) + if (node().saOfferFunds <= zero) { // Offer is unfunded. WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: unfunded offer"; + << "advanceNode: unfunded offer"; if (bReverse && !bFoundReverse && !bFoundPast) { @@ -374,7 +336,7 @@ TER nodeAdvance ( // That is, even if this offer fails due to fill or kill // still do deletions. // Mark offer for always deletion. - rippleCalc.mUnfundedOffers.insert (node.offerIndex_); + rippleCalc_.unfundedOffers.insert (node().offerIndex_); } else { @@ -392,32 +354,31 @@ TER nodeAdvance ( { // Consider source mentioned by current path state. WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: remember=" - << to_string (node.offerOwnerAccount_) - << "/" - << to_string (node.currency_) + << "advanceNode: remember=" + << node().offerOwnerAccount_ << "/" - << to_string (node.issuer_); + << node().issue_; - pathState.reverse().insert (std::make_pair (asLine, nodeIndex)); + pathState_.insertReverse (accountIssue, nodeIndex_); } - node.bFundsDirty = false; - node.bEntryAdvance = false; + node().bFundsDirty = false; + node().bEntryAdvance = false; } } } - while (resultCode == tesSUCCESS && (node.bEntryAdvance || node.bDirectAdvance)); + while (resultCode == tesSUCCESS && + (node().bEntryAdvance || node().directory.advanceNeeded)); if (resultCode == tesSUCCESS) { WriteLog (lsTRACE, RippleCalc) - << "nodeAdvance: node.offerIndex_=" << node.offerIndex_; + << "advanceNode: node.offerIndex_=" << node().offerIndex_; } else { WriteLog (lsDEBUG, RippleCalc) - << "nodeAdvance: resultCode=" << transToken (resultCode); + << "advanceNode: resultCode=" << transToken (resultCode); } return resultCode; diff --git a/src/ripple/module/app/paths/CalcNodeDeliverFwd.cpp b/src/ripple/module/app/paths/cursor/DeliverNodeForward.cpp similarity index 53% rename from src/ripple/module/app/paths/CalcNodeDeliverFwd.cpp rename to src/ripple/module/app/paths/cursor/DeliverNodeForward.cpp index 7a028eb1c42..bb54ce59baf 100644 --- a/src/ripple/module/app/paths/CalcNodeDeliverFwd.cpp +++ b/src/ripple/module/app/paths/cursor/DeliverNodeForward.cpp @@ -17,9 +17,7 @@ */ //============================================================================== -#include -#include -#include +#include namespace ripple { namespace path { @@ -27,39 +25,28 @@ namespace path { // For current offer, get input from deliver/limbo and output to next account or // deliver for next offers. // -// <-- node.saFwdDeliver: For computeForwardLiquidityForAccount to know +// <-- node.saFwdDeliver: For forwardLiquidityForAccount to know // how much went through // --> node.saRevDeliver: Do not exceed. -TER nodeDeliverFwd ( - RippleCalc& rippleCalc, - const unsigned int nodeIndex, // 0 < nodeIndex < lastNodeIndex - PathState& pathState, - const bool bMultiQuality, - Account const& uInAccountID, // --> Input owner's account. - const STAmount& saInReq, // --> Amount to deliver. - STAmount& saInAct, // <-- Amount delivered, this invokation. - STAmount& saInFees) // <-- Fees charged, this invokation. +TER PathCursor::deliverNodeForward ( + Account const& uInAccountID, // --> Input owner's account. + STAmount const& saInReq, // --> Amount to deliver. + STAmount& saInAct, // <-- Amount delivered, this invocation. + STAmount& saInFees) const // <-- Fees charged, this invocation. { TER resultCode = tesSUCCESS; - auto& previousNode = pathState.nodes()[nodeIndex - 1]; - auto& node = pathState.nodes()[nodeIndex]; - auto& nextNode = pathState.nodes()[nodeIndex + 1]; - // Don't deliver more than wanted. // Zeroed in reverse pass. - if (bMultiQuality) - node.currentDirectory_ = 0; // Restart book searching. - else - node.bDirectRestart = true; // Restart at same quality. + node().directory.restart(multiQuality_); saInAct.clear (saInReq); saInFees.clear (saInReq); int loopCount = 0; - // XXX Perhaps make sure do not exceed node.saRevDeliver as another way to + // XXX Perhaps make sure do not exceed node().saRevDeliver as another way to // stop? while (resultCode == tesSUCCESS && saInAct + saInFees < saInReq) { @@ -67,95 +54,90 @@ TER nodeDeliverFwd ( if (++loopCount > CALC_NODE_DELIVER_MAX_LOOPS) { WriteLog (lsWARNING, RippleCalc) - << "nodeDeliverFwd: max loops cndf"; - return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : - tecFAILED_PROCESSING; + << "deliverNodeForward: max loops cndf"; + return telFAILED_PROCESSING; } // Determine values for pass to adjust saInAct, saInFees, and - // node.saFwdDeliver. - resultCode = nodeAdvance ( - rippleCalc, - nodeIndex, pathState, bMultiQuality || saInAct == zero, false); + // node().saFwdDeliver. + advanceNode (saInAct, false); + // If needed, advance to next funded offer. if (resultCode != tesSUCCESS) { } - else if (!node.offerIndex_) + else if (!node().offerIndex_) { WriteLog (lsWARNING, RippleCalc) - << "nodeDeliverFwd: INTERNAL ERROR: Ran out of offers."; - return rippleCalc.mOpenLedger ? telFAILED_PROCESSING - : tecFAILED_PROCESSING; + << "deliverNodeForward: INTERNAL ERROR: Ran out of offers."; + return telFAILED_PROCESSING; } else if (resultCode == tesSUCCESS) { // Doesn't charge input. Input funds are in limbo. - STAmount& saOfrRate = node.saOfrRate; - SLE::pointer& sleOffer = node.sleOffer; - bool& bFundsDirty = node.bFundsDirty; - STAmount& saOfferFunds = node.saOfferFunds; - STAmount& saTakerPays = node.saTakerPays; - STAmount& saTakerGets = node.saTakerGets; - // There's no fee if we're transferring XRP, if the sender is the // issuer, or if the receiver is the issuer. - bool noFee = isXRP(previousNode.currency_) - || uInAccountID == previousNode.issuer_ - || node.offerOwnerAccount_ == previousNode.issuer_; + bool noFee = isXRP (previousNode().issue_) + || uInAccountID == previousNode().issue_.account + || node().offerOwnerAccount_ == previousNode().issue_.account; const STAmount saInFeeRate = noFee ? saOne - : previousNode.transferRate_; // Transfer rate of issuer. + : previousNode().transferRate_; // Transfer rate of issuer. // First calculate assuming no output fees: saInPassAct, // saInPassFees, saOutPassAct. // Offer maximum out - limited by funds with out fees. - STAmount saOutFunded = std::min (saOfferFunds, saTakerGets); + auto saOutFunded = std::min ( + node().saOfferFunds, node().saTakerGets); // Offer maximum out - limit by most to deliver. - STAmount saOutPassFunded = std::min ( - saOutFunded, node.saRevDeliver - node.saFwdDeliver); + auto saOutPassFunded = std::min ( + saOutFunded, + node().saRevDeliver - node().saFwdDeliver); // Offer maximum in - Limited by by payout. - STAmount saInFunded = STAmount::mulRound ( - saOutPassFunded, saOfrRate, saTakerPays, true); + auto saInFunded = STAmount::mulRound ( + saOutPassFunded, + node().saOfrRate, + node().saTakerPays, + true); // Offer maximum in with fees. - STAmount saInTotal = STAmount::mulRound ( - saInFunded, saInFeeRate, true); - STAmount saInRemaining = saInReq - saInAct - saInFees; + auto saInTotal = STAmount::mulRound (saInFunded, saInFeeRate, true); + auto saInRemaining = saInReq - saInAct - saInFees; if (saInRemaining < zero) saInRemaining.clear(); // In limited by remaining. - STAmount saInSum = std::min (saInTotal, saInRemaining); + auto saInSum = std::min (saInTotal, saInRemaining); // In without fees. - STAmount saInPassAct = std::min ( - saTakerPays, STAmount::divRound (saInSum, saInFeeRate, true)); + auto saInPassAct = std::min ( + node().saTakerPays, STAmount::divRound ( + saInSum, saInFeeRate, true)); // Out limited by in remaining. auto outPass = STAmount::divRound ( - saInPassAct, saOfrRate, saTakerGets, true); - STAmount saOutPassMax = std::min (saOutPassFunded, outPass); + saInPassAct, node().saOfrRate, node().saTakerGets, true); + STAmount saOutPassMax = std::min (saOutPassFunded, outPass); - STAmount saInPassFeesMax = saInSum - saInPassAct; + STAmount saInPassFeesMax = saInSum - saInPassAct; - // Will be determined by next node. - STAmount saOutPassAct; + // Will be determined by next node(). + STAmount saOutPassAct; // Will be determined by adjusted saInPassAct. - STAmount saInPassFees; + STAmount saInPassFees; WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverFwd:" - << " nodeIndex=" << nodeIndex + << "deliverNodeForward:" + << " nodeIndex_=" << nodeIndex_ << " saOutFunded=" << saOutFunded << " saOutPassFunded=" << saOutPassFunded - << " saOfferFunds=" << saOfferFunds - << " saTakerGets=" << saTakerGets + << " node().saOfferFunds=" << node().saOfferFunds + << " node().saTakerGets=" << node().saTakerGets << " saInReq=" << saInReq << " saInAct=" << saInAct << " saInFees=" << saInFees @@ -166,50 +148,54 @@ TER nodeDeliverFwd ( << " saOutPassMax=" << saOutPassMax; // FIXME: We remove an offer if WE didn't want anything out of it? - if (!saTakerPays || saInSum <= zero) + if (!node().saTakerPays || saInSum <= zero) { WriteLog (lsDEBUG, RippleCalc) - << "nodeDeliverFwd: Microscopic offer unfunded."; + << "deliverNodeForward: Microscopic offer unfunded."; // After math offer is effectively unfunded. - pathState.becameUnfunded().push_back (node.offerIndex_); - node.bEntryAdvance = true; + pathState_.unfundedOffers().push_back (node().offerIndex_); + node().bEntryAdvance = true; continue; } - else if (!saInFunded) + + if (!saInFunded) { // Previous check should catch this. WriteLog (lsWARNING, RippleCalc) - << "nodeDeliverFwd: UNREACHABLE REACHED"; + << "deliverNodeForward: UNREACHABLE REACHED"; // After math offer is effectively unfunded. - pathState.becameUnfunded().push_back (node.offerIndex_); - node.bEntryAdvance = true; + pathState_.unfundedOffers().push_back (node().offerIndex_); + node().bEntryAdvance = true; continue; } - else if (!!nextNode.account_) + + 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; + saOutPassAct = saOutPassMax; + saInPassFees = saInPassFeesMax; WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverFwd: ? --> OFFER --> account:" + << "deliverNodeForward: ? --> OFFER --> account:" << " offerOwnerAccount_=" - << to_string (node.offerOwnerAccount_) - << " nextNode.account_=" - << to_string (nextNode.account_) + << node().offerOwnerAccount_ + << " nextNode().account_=" + << nextNode().account_ << " saOutPassAct=" << saOutPassAct << " saOutFunded=%s" << saOutFunded; // Output: Debit offer owner, send XRP or non-XPR to next // account. - resultCode = rippleCalc.mActiveLedger.accountSend ( - node.offerOwnerAccount_, nextNode.account_, saOutPassAct); + resultCode = ledger().accountSend ( + node().offerOwnerAccount_, + nextNode().account_, + saOutPassAct); if (resultCode != tesSUCCESS) break; @@ -223,19 +209,15 @@ TER nodeDeliverFwd ( // // Output fees: possible if issuer has fees and is not on either // side. - STAmount saOutPassFees; + STAmount saOutPassFees; // Output fees vary as the next nodes offer owners may vary. // Therefore, immediately push through output for current offer. - resultCode = nodeDeliverFwd ( - rippleCalc, - nodeIndex + 1, - pathState, - bMultiQuality, - node.offerOwnerAccount_, // --> Current holder. - saOutPassMax, // --> Amount available. - saOutPassAct, // <-- Amount delivered. - saOutPassFees); // <-- Fees charged. + increment().deliverNodeForward ( + node().offerOwnerAccount_, // --> Current holder. + saOutPassMax, // --> Amount available. + saOutPassAct, // <-- Amount delivered. + saOutPassFees); // <-- Fees charged. if (resultCode != tesSUCCESS) break; @@ -244,7 +226,7 @@ TER nodeDeliverFwd ( { // No fees and entire output amount. - saInPassFees = saInPassFeesMax; + saInPassFees = saInPassFeesMax; } else { @@ -254,8 +236,8 @@ TER nodeDeliverFwd ( assert (saOutPassAct < saOutPassMax); auto inPassAct = STAmount::mulRound ( - saOutPassAct, saOfrRate, saInReq, true); - saInPassAct = std::min (saTakerPays, inPassAct); + saOutPassAct, node().saOfrRate, saInReq, true); + saInPassAct = std::min (node().saTakerPays, inPassAct); auto inPassFees = STAmount::mulRound ( saInPassAct, saInFeeRate, true); saInPassFees = std::min (saInPassFeesMax, inPassFees); @@ -264,41 +246,47 @@ TER nodeDeliverFwd ( // Do outbound debiting. // Send to issuer/limbo total amount including fees (issuer gets // fees). - auto id = !!node.currency_ ? Account(node.issuer_) : xrpAccount(); + auto const& id = isXRP(node().issue_) ? + xrpAccount() : node().issue_.account; auto outPassTotal = saOutPassAct + saOutPassFees; - rippleCalc.mActiveLedger.accountSend (node.offerOwnerAccount_, id, outPassTotal); + ledger().accountSend ( + node().offerOwnerAccount_, + id, + outPassTotal); WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverFwd: ? --> OFFER --> offer:" + << "deliverNodeForward: ? --> OFFER --> offer:" << " saOutPassAct=" << saOutPassAct << " saOutPassFees=" << saOutPassFees; } WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverFwd: " - << " nodeIndex=" << nodeIndex - << " saTakerGets=" << saTakerGets - << " saTakerPays=" << saTakerPays + << "deliverNodeForward: " + << " nodeIndex_=" << nodeIndex_ + << " node().saTakerGets=" << node().saTakerGets + << " node().saTakerPays=" << node().saTakerPays << " saInPassAct=" << saInPassAct << " saInPassFees=" << saInPassFees << " saOutPassAct=" << saOutPassAct << " saOutFunded=" << saOutFunded; // Funds were spent. - bFundsDirty = true; + 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 (!previousNode.currency_ - || uInAccountID != node.offerOwnerAccount_) + if (isXRP (previousNode().issue_.currency) + || uInAccountID != node().offerOwnerAccount_) { - auto id = !isXRP(previousNode.currency_) ? + auto id = !isXRP(previousNode().issue_.currency) ? uInAccountID : xrpAccount(); - resultCode = rippleCalc.mActiveLedger.accountSend ( - id, node.offerOwnerAccount_, saInPassAct); + resultCode = ledger().accountSend ( + id, + node().offerOwnerAccount_, + saInPassAct); if (resultCode != tesSUCCESS) break; @@ -308,62 +296,59 @@ TER nodeDeliverFwd ( // // Fees are considered paid from a seperate budget and are not named // in the offer. - STAmount saTakerGetsNew = saTakerGets - saOutPassAct; - STAmount saTakerPaysNew = saTakerPays - saInPassAct; + STAmount saTakerGetsNew = node().saTakerGets - saOutPassAct; + STAmount saTakerPaysNew = node().saTakerPays - saInPassAct; if (saTakerPaysNew < zero || saTakerGetsNew < zero) { WriteLog (lsWARNING, RippleCalc) - << "nodeDeliverFwd: NEGATIVE:" + << "deliverNodeForward: NEGATIVE:" << " saTakerPaysNew=" << saTakerPaysNew << " saTakerGetsNew=" << saTakerGetsNew; - // If mOpenLedger, then ledger is not final, can vote no. - resultCode = rippleCalc.mOpenLedger - ? telFAILED_PROCESSING - : tecFAILED_PROCESSING; + resultCode = telFAILED_PROCESSING; break; } - sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); - sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); + node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); + node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); - rippleCalc.mActiveLedger.entryModify (sleOffer); + ledger().entryModify (node().sleOffer); if (saOutPassAct == saOutFunded || saTakerGetsNew == zero) { // Offer became unfunded. WriteLog (lsWARNING, RippleCalc) - << "nodeDeliverFwd: unfunded:" + << "deliverNodeForward: unfunded:" << " saOutPassAct=" << saOutPassAct << " saOutFunded=" << saOutFunded; - pathState.becameUnfunded().push_back (node.offerIndex_); - node.bEntryAdvance = true; + pathState_.unfundedOffers().push_back (node().offerIndex_); + node().bEntryAdvance = true; } else { CondLog (saOutPassAct >= saOutFunded, lsWARNING, RippleCalc) - << "nodeDeliverFwd: TOO MUCH:" + << "deliverNodeForward: TOO MUCH:" << " saOutPassAct=" << saOutPassAct << " saOutFunded=" << saOutFunded; assert (saOutPassAct < saOutFunded); } - saInAct += saInPassAct; - saInFees += saInPassFees; + saInAct += saInPassAct; + saInFees += saInPassFees; - // Adjust amount available to next node. - node.saFwdDeliver = std::min (node.saRevDeliver, - node.saFwdDeliver + saOutPassAct); + // Adjust amount available to next node(). + node().saFwdDeliver = std::min (node().saRevDeliver, + node().saFwdDeliver + saOutPassAct); } } WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverFwd<" - << " nodeIndex=" << nodeIndex + << "deliverNodeForward<" + << " nodeIndex_=" << nodeIndex_ << " saInAct=" << saInAct << " saInFees=" << saInFees; diff --git a/src/ripple/module/app/paths/CalcNodeDeliverRev.cpp b/src/ripple/module/app/paths/cursor/DeliverNodeReverse.cpp similarity index 62% rename from src/ripple/module/app/paths/CalcNodeDeliverRev.cpp rename to src/ripple/module/app/paths/cursor/DeliverNodeReverse.cpp index 3b554b4e739..1885166f634 100644 --- a/src/ripple/module/app/paths/CalcNodeDeliverRev.cpp +++ b/src/ripple/module/app/paths/cursor/DeliverNodeReverse.cpp @@ -17,9 +17,7 @@ */ //============================================================================== -#include -#include -#include +#include namespace ripple { namespace path { @@ -34,29 +32,17 @@ namespace path { // spent on fees. Continue processing until the request is satisified as long // as the rate does not increase past the initial rate. -TER nodeDeliverRev ( - RippleCalc& rippleCalc, - const unsigned int nodeIndex, - PathState& pathState, - const bool bMultiQuality, // True, if not constrained to the same - // or better quality. - Account const& uOutAccountID, // --> Output owner's account. - const STAmount& saOutReq, // --> Funds requested to be - // delivered for an increment. - STAmount& saOutAct) // <-- Funds actually delivered for an - // increment. +// To deliver from an order book, when computing +TER PathCursor::deliverNodeReverse ( + Account const& uOutAccountID, // --> Output owner's account. + STAmount const& saOutReq, // --> Funds requested to be + // delivered for an increment. + STAmount& saOutAct) const // <-- Funds actually delivered for an + // increment. { TER resultCode = tesSUCCESS; - auto& previousNode = pathState.nodes()[nodeIndex - 1]; - auto& node = pathState.nodes()[nodeIndex]; - - if (bMultiQuality) - node.currentDirectory_ = 0; // Restart book searching. - else - node.bDirectRestart = true; // Restart at same quality. - - STAmount& saPrvDlvReq = previousNode.saRevDeliver; + node().directory.restart(multiQuality_); // Accumulation of what the previous node must deliver. // Possible optimization: Note this gets zeroed on each increment, ideally @@ -64,10 +50,10 @@ TER nodeDeliverRev ( saOutAct.clear (saOutReq); WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev>" + << "deliverNodeReverse>" << " saOutAct=" << saOutAct << " saOutReq=" << saOutReq - << " saPrvDlvReq=" << saPrvDlvReq; + << " saPrvDlvReq=" << previousNode().saRevDeliver; assert (saOutReq != zero); @@ -79,72 +65,60 @@ TER nodeDeliverRev ( if (++loopCount > CALC_NODE_DELIVER_MAX_LOOPS) { WriteLog (lsFATAL, RippleCalc) << "loop count exceeded"; - return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : - tecFAILED_PROCESSING; + return telFAILED_PROCESSING; } - bool& bEntryAdvance = node.bEntryAdvance; - STAmount& saOfrRate = node.saOfrRate; - SLE::pointer& sleOffer = node.sleOffer; - bool& bFundsDirty = node.bFundsDirty; - STAmount& saOfferFunds = node.saOfferFunds; - STAmount& saTakerPays = node.saTakerPays; - STAmount& saTakerGets = node.saTakerGets; - STAmount& saRateMax = node.saRateMax; - - resultCode = nodeAdvance ( - rippleCalc, - nodeIndex, pathState, bMultiQuality || saOutAct == zero, true); + resultCode = advanceNode (saOutAct, true); // If needed, advance to next funded offer. - if (resultCode != tesSUCCESS || !node.offerIndex_) - { + if (resultCode != tesSUCCESS || !node().offerIndex_) // Error or out of offers. break; - } - auto const hasFee = node.offerOwnerAccount_ == node.issuer_ - || uOutAccountID == node.issuer_; // Issuer sending or receiving. + auto const hasFee = node().offerOwnerAccount_ == node().issue_.account + || uOutAccountID == node().issue_.account; + // Issuer sending or receiving. + const STAmount saOutFeeRate = hasFee ? saOne // No fee. - : node.transferRate_; // Transfer rate of issuer. + : node().transferRate_; // Transfer rate of issuer. WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev:" + << "deliverNodeReverse:" << " offerOwnerAccount_=" - << to_string (node.offerOwnerAccount_) + << node().offerOwnerAccount_ << " uOutAccountID=" - << to_string (uOutAccountID) - << " node.issuer_=" - << to_string (node.issuer_) - << " node.transferRate_=" << node.transferRate_ + << uOutAccountID + << " node().issue_.account=" + << node().issue_.account + << " node().transferRate_=" << node().transferRate_ << " saOutFeeRate=" << saOutFeeRate; - if (bMultiQuality) + if (multiQuality_) { // In multi-quality mode, ignore rate. } - else if (!saRateMax) + else if (!node().saRateMax) { // Set initial rate. - saRateMax = saOutFeeRate; + node().saRateMax = saOutFeeRate; WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev: Set initial rate:" - << " saRateMax=" << saRateMax + << "deliverNodeReverse: Set initial rate:" + << " node().saRateMax=" << node().saRateMax << " saOutFeeRate=" << saOutFeeRate; } - else if (saOutFeeRate > saRateMax) + else if (saOutFeeRate > node().saRateMax) { // Offer exceeds initial rate. WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev: Offer exceeds initial rate:" - << " saRateMax=" << saRateMax + << "deliverNodeReverse: Offer exceeds initial rate:" + << " node().saRateMax=" << node().saRateMax << " saOutFeeRate=" << saOutFeeRate; break; // Done. Don't bother looking for smaller transferRates. } - else if (saOutFeeRate < saRateMax) + else if (saOutFeeRate < node().saRateMax) { // Reducing rate. Additional offers will only considered for this // increment if they are at least this good. @@ -156,16 +130,16 @@ TER nodeDeliverRev ( // The rate would be reduced if the current offer was from the // issuer and the previous offer wasn't. - saRateMax = saOutFeeRate; + node().saRateMax = saOutFeeRate; WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev: Reducing rate:" - << " saRateMax=" << saRateMax; + << "deliverNodeReverse: Reducing rate:" + << " node().saRateMax=" << node().saRateMax; } // Amount that goes to the taker. STAmount saOutPassReq = std::min ( - std::min (saOfferFunds, saTakerGets), + std::min (node().saOfferFunds, node().saTakerGets), saOutReq - saOutAct); // Maximum out - assuming no out fees. @@ -182,19 +156,19 @@ TER nodeDeliverRev ( // Offer out with fees. WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev:" + << "deliverNodeReverse:" << " saOutReq=" << saOutReq << " saOutAct=" << saOutAct - << " saTakerGets=" << saTakerGets + << " node().saTakerGets=" << node().saTakerGets << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees - << " saOfferFunds=" << saOfferFunds; + << " node().saOfferFunds=" << node().saOfferFunds; - if (saOutPlusFees > saOfferFunds) + if (saOutPlusFees > node().saOfferFunds) { // Offer owner can not cover all fees, compute saOutPassAct based on - // saOfferFunds. - saOutPlusFees = saOfferFunds; + // node().saOfferFunds. + saOutPlusFees = node().saOfferFunds; // Round up: prefer liquidity rather than microscopic fees. But, // limit by requested. @@ -202,23 +176,23 @@ TER nodeDeliverRev ( saOutPassAct = std::min (saOutPassReq, fee); WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev: Total exceeds fees:" + << "deliverNodeReverse: Total exceeds fees:" << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees - << " saOfferFunds=" << saOfferFunds; + << " node().saOfferFunds=" << node().saOfferFunds; } // Compute portion of input needed to cover actual output. auto outputFee = STAmount::mulRound ( - saOutPassAct, saOfrRate, saTakerPays, true); - STAmount saInPassReq = std::min (saTakerPays, outputFee); + saOutPassAct, node().saOfrRate, node().saTakerPays, true); + STAmount saInPassReq = std::min (node().saTakerPays, outputFee); STAmount saInPassAct; WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev:" + << "deliverNodeReverse:" << " outputFee=" << outputFee << " saInPassReq=" << saInPassReq - << " saOfrRate=" << saOfrRate + << " node().saOfrRate=" << node().saOfrRate << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees; @@ -226,13 +200,13 @@ TER nodeDeliverRev ( { // After rounding did not want anything. WriteLog (lsDEBUG, RippleCalc) - << "nodeDeliverRev: micro offer is unfunded."; + << "deliverNodeReverse: micro offer is unfunded."; - bEntryAdvance = true; + node().bEntryAdvance = true; continue; } // Find out input amount actually available at current rate. - else if (!!previousNode.account_) + else if (!isXRP(previousNode().account_)) { // account --> OFFER --> ? // Due to node expansion, previous is guaranteed to be the issuer. @@ -249,7 +223,7 @@ TER nodeDeliverRev ( saInPassAct = saInPassReq; WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev: account --> OFFER --> ? :" + << "deliverNodeReverse: account --> OFFER --> ? :" << " saInPassAct=" << saInPassAct; } else @@ -257,17 +231,14 @@ TER nodeDeliverRev ( // offer --> OFFER --> ? // Compute in previous offer node how much could come in. - resultCode = nodeDeliverRev ( - rippleCalc, - nodeIndex - 1, - pathState, - bMultiQuality, - node.offerOwnerAccount_, + // TODO(tom): Fix nasty recursion here! + resultCode = increment(-1).deliverNodeReverse( + node().offerOwnerAccount_, saInPassReq, saInPassAct); WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev: offer --> OFFER --> ? :" + << "deliverNodeReverse: offer --> OFFER --> ? :" << " saInPassAct=" << saInPassAct; } @@ -278,14 +249,14 @@ TER nodeDeliverRev ( { // Adjust output to conform to limited input. auto outputRequirements = STAmount::divRound ( - saInPassAct, saOfrRate, saTakerGets, true); + saInPassAct, node().saOfrRate, node().saTakerGets, true); saOutPassAct = std::min (saOutPassReq, outputRequirements); auto outputFees = STAmount::mulRound ( saOutPassAct, saOutFeeRate, true); - saOutPlusFees = std::min (saOfferFunds, outputFees); + saOutPlusFees = std::min (node().saOfferFunds, outputFees); WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev: adjusted:" + << "deliverNodeReverse: adjusted:" << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees; } @@ -296,7 +267,7 @@ TER nodeDeliverRev ( } // Funds were spent. - bFundsDirty = true; + node().bFundsDirty = true; // Want to deduct output to limit calculations while computing reverse. // Don't actually need to send. @@ -305,54 +276,53 @@ TER nodeDeliverRev ( // visited. However, these deductions and adjustments are tenative. // // Must reset balances when going forward to perform actual transfers. - resultCode = rippleCalc.mActiveLedger.accountSend ( - node.offerOwnerAccount_, node.issuer_, saOutPassAct); + resultCode = ledger().accountSend ( + node().offerOwnerAccount_, node().issue_.account, saOutPassAct); if (resultCode != tesSUCCESS) break; // Adjust offer - STAmount saTakerGetsNew = saTakerGets - saOutPassAct; - STAmount saTakerPaysNew = saTakerPays - saInPassAct; + STAmount saTakerGetsNew = node().saTakerGets - saOutPassAct; + STAmount saTakerPaysNew = node().saTakerPays - saInPassAct; if (saTakerPaysNew < zero || saTakerGetsNew < zero) { WriteLog (lsWARNING, RippleCalc) - << "nodeDeliverRev: NEGATIVE:" - << " saTakerPaysNew=" << saTakerPaysNew - << " saTakerGetsNew=%s" << saTakerGetsNew; + << "deliverNodeReverse: NEGATIVE:" + << " node().saTakerPaysNew=" << saTakerPaysNew + << " node().saTakerGetsNew=%s" << saTakerGetsNew; - // If mOpenLedger then ledger is not final, can vote no. - resultCode = rippleCalc.mOpenLedger ? telFAILED_PROCESSING - : tecFAILED_PROCESSING; + resultCode = telFAILED_PROCESSING; break; } - sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); - sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); + node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); + node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); - rippleCalc.mActiveLedger.entryModify (sleOffer); + ledger().entryModify (node().sleOffer); - if (saOutPassAct == saTakerGets) + if (saOutPassAct == node().saTakerGets) { // Offer became unfunded. WriteLog (lsDEBUG, RippleCalc) - << "nodeDeliverRev: offer became unfunded."; + << "deliverNodeReverse: offer became unfunded."; - bEntryAdvance = true; // XXX When don't we want to set advance? + node().bEntryAdvance = true; + // XXX When don't we want to set advance? } else { - assert (saOutPassAct < saTakerGets); + assert (saOutPassAct < node().saTakerGets); } saOutAct += saOutPassAct; // Accumulate what is to be delivered from previous node. - saPrvDlvReq += saInPassAct; + previousNode().saRevDeliver += saInPassAct; } CondLog (saOutAct > saOutReq, lsWARNING, RippleCalc) - << "nodeDeliverRev: TOO MUCH:" + << "deliverNodeReverse: TOO MUCH:" << " saOutAct=" << saOutAct << " saOutReq=" << saOutReq; @@ -364,10 +334,10 @@ TER nodeDeliverRev ( // Design invariant: if nothing was actually delivered, return tecPATH_DRY. WriteLog (lsTRACE, RippleCalc) - << "nodeDeliverRev<" + << "deliverNodeReverse<" << " saOutAct=" << saOutAct << " saOutReq=" << saOutReq - << " saPrvDlvReq=" << saPrvDlvReq; + << " saPrvDlvReq=" << previousNode().saRevDeliver; return resultCode; } diff --git a/src/ripple/module/app/paths/cursor/ForwardLiquidity.cpp b/src/ripple/module/app/paths/cursor/ForwardLiquidity.cpp new file mode 100644 index 00000000000..2d8822bc464 --- /dev/null +++ b/src/ripple/module/app/paths/cursor/ForwardLiquidity.cpp @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +/* + 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 + +namespace ripple { +namespace path { + +TER PathCursor::forwardLiquidity () const +{ + if (node().isAccount()) + return forwardLiquidityForAccount (); + + // Otherwise, node is an offer. + if (previousNode().account_ == 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); + + 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/module/app/paths/cursor/ForwardLiquidityForAccount.cpp b/src/ripple/module/app/paths/cursor/ForwardLiquidityForAccount.cpp new file mode 100644 index 00000000000..c30f4d0341e --- /dev/null +++ b/src/ripple/module/app/paths/cursor/ForwardLiquidityForAccount.cpp @@ -0,0 +1,499 @@ +//------------------------------------------------------------------------------ +/* + 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 + +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; + + std::uint64_t uRateMax = 0; + + Account const& previousAccountID = + previousNode().isAccount() ? previousNode().account_ : + node().account_; + // Offers are always issue. + Account const& nextAccountID = + nextNode().isAccount() ? nextNode().account_ : node().account_; + + std::uint32_t uQualityIn = nodeIndex_ + ? ledger().rippleQualityIn ( + node().account_, + previousAccountID, + node().issue_.currency) + : QUALITY_ONE; + std::uint32_t uQualityOut = (nodeIndex_ == lastNodeIndex) + ? ledger().rippleQualityOut ( + node().account_, + nextAccountID, + node().issue_.currency) + : QUALITY_ONE; + + // 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 (); + + WriteLog (lsTRACE, RippleCalc) + << "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() >= 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() >= zero) + { + // Limit by send max. + node().saFwdIssue = std::min ( + node().saFwdIssue, + pathState_.inReq() - pathState_.inAct() - node().saFwdRedeem); + } + + pathState_.setInPass (pathState_.inPass() + node().saFwdIssue); + + WriteLog (lsTRACE, RippleCalc) + << "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 --> $ + WriteLog (lsTRACE, RippleCalc) + << "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 = uQualityIn >= QUALITY_ONE + ? previousNode().saFwdIssue // No fee. + : STAmount::mulRound ( + previousNode().saFwdIssue, + STAmount (noIssue(), uQualityIn, -9), + 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 = ledger().rippleCredit ( + previousAccountID, + node().account_, + previousNode().saFwdRedeem + previousNode().saFwdIssue, + false); + } + else + { + // After applying quality, total payment was microscopic. + resultCode = tecPATH_DRY; + } + } + else + { + // account --> ACCOUNT --> account + WriteLog (lsTRACE, RippleCalc) + << "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_, + QUALITY_ONE, + uQualityOut, + 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_, + uQualityIn, + uQualityOut, + 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_, + QUALITY_ONE, + ledger().rippleTransferRate (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_, + uQualityIn, + QUALITY_ONE, + previousNode().saFwdIssue, + node().saRevIssue, + saPrvIssueAct, + node().saFwdIssue, + uRateMax); + } + + STAmount saProvide = node().saFwdRedeem + node().saFwdIssue; + + // Adjust prv --> cur balance : take all inbound + resultCode = saProvide + ? ledger().rippleCredit ( + previousAccountID, + node().account_, + previousNode().saFwdRedeem + previousNode().saFwdIssue, + false) + : 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. + WriteLog (lsTRACE, RippleCalc) + << "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_, + QUALITY_ONE, + ledger().rippleTransferRate (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_, + uQualityIn, + QUALITY_ONE, + previousNode().saFwdIssue, + node().saRevDeliver, + saPrvIssueAct, + node().saFwdDeliver, + uRateMax); + } + + // Adjust prv --> cur balance : take all inbound + resultCode = node().saFwdDeliver + ? ledger().rippleCredit ( + previousAccountID, node().account_, + previousNode().saFwdRedeem + previousNode().saFwdIssue, + false) + : 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() >= 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, + ledger().accountHolds ( + node().account_, + xrpCurrency(), + xrpAccount())); + + } + + // 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. + + WriteLog (lsTRACE, RippleCalc) + << "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 + { + WriteLog (lsTRACE, RippleCalc) + << "forwardLiquidityForAccount: ^ --> " + << "ACCOUNT -- XRP --> offer"; + + // Deliver XRP to limbo. + resultCode = ledger().accountSend ( + node().account_, xrpAccount(), node().saFwdDeliver); + } + } + } + else if (!previousNode().isAccount() && nextNode().isAccount()) + { + if (nodeIndex_ == lastNodeIndex) + { + // offer --> ACCOUNT --> $ + WriteLog (lsTRACE, RippleCalc) + << "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 + WriteLog (lsTRACE, RippleCalc) + << "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_, + QUALITY_ONE, + uQualityOut, + 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_, + QUALITY_ONE, + ledger().rippleTransferRate (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. + WriteLog (lsTRACE, RippleCalc) + << "forwardLiquidityForAccount: offer --> ACCOUNT --> offer"; + + node().saFwdDeliver.clear (node().saRevDeliver); + + if (previousNode().saFwdDeliver + // Previous wants to deliver + && node().saRevIssue) + // Current wants issue. + { + // Rate : 1.0 : transfer_rate + rippleLiquidity ( + rippleCalc_, + QUALITY_ONE, + ledger().rippleTransferRate (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/module/app/paths/cursor/Liquidity.cpp b/src/ripple/module/app/paths/cursor/Liquidity.cpp new file mode 100644 index 00000000000..2ca8fba1e3b --- /dev/null +++ b/src/ripple/module/app/paths/cursor/Liquidity.cpp @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +/* + 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 { + +TER PathCursor::liquidity (LedgerEntrySet const& lesCheckpoint) const +{ + TER resultCode; + PathCursor pc = *this; + + ledger() = lesCheckpoint.duplicate (); + for (pc.nodeIndex_ = pc.nodeSize(); pc.nodeIndex_--; ) + { + WriteLog (lsTRACE, RippleCalc) + << "reverseLiquidity>" + << " nodeIndex=" << pc.nodeIndex_ + << ".issue_.account=" << to_string (pc.node().issue_.account); + + resultCode = pc.reverseLiquidity(); + + WriteLog (lsTRACE, RippleCalc) + << "reverseLiquidity< " + << "nodeIndex=" << pc.nodeIndex_ + << " resultCode=%s" << transToken (resultCode) + << " transferRate_=" << pc.node().transferRate_ + << "/" << resultCode; + + if (resultCode != tesSUCCESS) + break; + } + + WriteLog (lsTRACE, RippleCalc) + << "nextIncrement: Path after reverse: " << pathState_.getJson (); + + if (resultCode != tesSUCCESS) + return resultCode; + + // Do forward. + ledger() = lesCheckpoint.duplicate (); + for (pc.nodeIndex_ = 0; pc.nodeIndex_ < pc.nodeSize(); ++pc.nodeIndex_) + { + WriteLog (lsTRACE, RippleCalc) + << "forwardLiquidity> nodeIndex=" << nodeIndex_; + + resultCode = pc.forwardLiquidity(); + if (resultCode != tesSUCCESS) + return resultCode; + + WriteLog (lsTRACE, RippleCalc) + << "forwardLiquidity<" + << " nodeIndex:" << pc.nodeIndex_ + << " resultCode:" << resultCode; + + if (pathState_.isDry()) + resultCode = tecPATH_DRY; + } + return resultCode; +} + +} // path +} // ripple diff --git a/src/ripple/module/app/paths/cursor/NextIncrement.cpp b/src/ripple/module/app/paths/cursor/NextIncrement.cpp new file mode 100644 index 00000000000..053bb6b83ef --- /dev/null +++ b/src/ripple/module/app/paths/cursor/NextIncrement.cpp @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +/* + 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 + +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 (LedgerEntrySet const& lesCheckpoint) const +{ + // The next state is what is available in preference order. + // This is calculated when referenced accounts changed. + WriteLog (lsTRACE, RippleCalc) + << "nextIncrement: Path In: " << pathState_.getJson (); + + auto status = liquidity(lesCheckpoint); + + if (status == tesSUCCESS) + { + auto isDry = pathState_.isDry(); + CondLog (isDry, lsDEBUG, RippleCalc) + << "nextIncrement: Error forwardLiquidity reported success" + << " on dry path:" + << " saOutPass=" << pathState_.outPass() + << " inPass()=" << pathState_.inPass(); + + if (isDry) + throw std::runtime_error ("Made no progress."); + + // Calculate relative quality. + pathState_.setQuality(STAmount::getRate ( + pathState_.outPass(), pathState_.inPass())); + + WriteLog (lsTRACE, RippleCalc) + << "nextIncrement: Path after forward: " << pathState_.getJson (); + } + else + { + pathState_.setQuality(0); + } + pathState_.setStatus (status); +} + +} // path +} // ripple diff --git a/src/ripple/module/app/paths/cursor/PathCursor.h b/src/ripple/module/app/paths/cursor/PathCursor.h new file mode 100644 index 00000000000..5452c2d43ae --- /dev/null +++ b/src/ripple/module/app/paths/cursor/PathCursor.h @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +/* + 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 RIPPLED_RIPPLE_MODULE_APP_PATHS_PATHCURSOR_H +#define RIPPLED_RIPPLE_MODULE_APP_PATHS_PATHCURSOR_H + +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, + NodeIndex nodeIndex = 0) + : rippleCalc_(rippleCalc), + pathState_(pathState), + multiQuality_(multiQuality), + nodeIndex_(restrict(nodeIndex)) + { + } + + void nextIncrement(LedgerEntrySet const& checkpoint) const; + +private: + PathCursor(PathCursor const&) = default; + + PathCursor increment(int delta = 1) const { + return {rippleCalc_, pathState_, multiQuality_, nodeIndex_ + delta}; + } + + TER liquidity(LedgerEntrySet const& lesCheckpoint) 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) const; + + // To deliver from an order book, when computing + TER deliverNodeReverse ( + Account const& uOutAccountID, + const STAmount& saOutReq, + STAmount& saOutAct) const; + + TER deliverNodeForward ( + Account const& uInAccountID, + const STAmount& saInReq, + STAmount& saInAct, + STAmount& saInFees) const; + + RippleCalc& rippleCalc_; + PathState& pathState_; + bool multiQuality_; + NodeIndex nodeIndex_; + + LedgerEntrySet& ledger() const + { + return rippleCalc_.mActiveLedger; + } + + 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)); + } +}; + +} // path +} // ripple + +#endif diff --git a/src/ripple/module/app/paths/cursor/ReverseLiquidity.cpp b/src/ripple/module/app/paths/cursor/ReverseLiquidity.cpp new file mode 100644 index 00000000000..5e9b380829a --- /dev/null +++ b/src/ripple/module/app/paths/cursor/ReverseLiquidity.cpp @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +/* + 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 + +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. + + // node.transferRate_ caches the output transfer rate for this node. + node().transferRate_ = STAmount::saFromRate ( + ledger().rippleTransferRate (node().issue_.account)); + + if (node().isAccount ()) + return reverseLiquidityForAccount (); + + // Otherwise the node is an Offer. + if (isXRP (nextNode().account_)) + { + WriteLog (lsTRACE, RippleCalc) + << "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; + + WriteLog (lsTRACE, RippleCalc) + << "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/module/app/paths/ComputeAccountLiquidityReverse.cpp b/src/ripple/module/app/paths/cursor/ReverseLiquidityForAccount.cpp similarity index 58% rename from src/ripple/module/app/paths/ComputeAccountLiquidityReverse.cpp rename to src/ripple/module/app/paths/cursor/ReverseLiquidityForAccount.cpp index 56c32c39b95..20e9f241aa2 100644 --- a/src/ripple/module/app/paths/ComputeAccountLiquidityReverse.cpp +++ b/src/ripple/module/app/paths/cursor/ReverseLiquidityForAccount.cpp @@ -17,9 +17,7 @@ */ //============================================================================== -#include -#include -#include +#include namespace ripple { namespace path { @@ -40,71 +38,76 @@ namespace path { // // <-- tesSUCCESS or tecPATH_DRY -TER computeReverseLiquidityForAccount ( - RippleCalc& rippleCalc, - const unsigned int nodeIndex, PathState& pathState, - const bool bMultiQuality) +TER PathCursor::reverseLiquidityForAccount () const { TER terResult = tesSUCCESS; - auto const lastNodeIndex = pathState.nodes().size () - 1; - auto const isFinalNode = (nodeIndex == lastNodeIndex); + auto const lastNodeIndex = nodeSize () - 1; + auto const isFinalNode = (nodeIndex_ == lastNodeIndex); // 0 quality means none has yet been determined. std::uint64_t uRateMax = 0; - auto& previousNode = pathState.nodes()[nodeIndex ? nodeIndex - 1 : 0]; - auto& node = pathState.nodes()[nodeIndex]; - auto& nextNode = pathState.nodes()[isFinalNode ? lastNodeIndex : nodeIndex + 1]; - // Current is allowed to redeem to next. - const bool previousNodeIsAccount = !nodeIndex || previousNode.isAccount(); - const bool nextNodeIsAccount = isFinalNode || nextNode.isAccount(); + const bool previousNodeIsAccount = !nodeIndex_ || + previousNode().isAccount(); + + const bool nextNodeIsAccount = isFinalNode || nextNode().isAccount(); Account const& previousAccountID = previousNodeIsAccount - ? previousNode.account_ : node.account_; - Account const& nextAccountID = nextNodeIsAccount ? nextNode.account_ - : node.account_; // Offers are always issue. + ? previousNode().account_ : node().account_; + Account const& nextAccountID = nextNodeIsAccount ? nextNode().account_ + : node().account_; // Offers are always issue. // This is the quality from from the previous node to this one. const std::uint32_t uQualityIn - = (nodeIndex != 0) - ? rippleCalc.mActiveLedger.rippleQualityIn ( - node.account_, previousAccountID, node.currency_) + = (nodeIndex_ != 0) + ? ledger().rippleQualityIn ( + node().account_, + previousAccountID, + node().issue_.currency) : QUALITY_ONE; // And this is the quality from the next one to this one. const std::uint32_t uQualityOut - = (nodeIndex != lastNodeIndex) - ? rippleCalc.mActiveLedger.rippleQualityOut ( - node.account_, nextAccountID, node.currency_) + = (nodeIndex_ != lastNodeIndex) + ? ledger().rippleQualityOut ( + node().account_, + nextAccountID, + node().issue_.currency) : QUALITY_ONE; // For previousNodeIsAccount: // Previous account is already owed. - const STAmount saPrvOwed = (previousNodeIsAccount && nodeIndex != 0) - ? rippleCalc.mActiveLedger.rippleOwed ( - node.account_, previousAccountID, node.currency_) - : STAmount ({node.currency_, node.account_}); + const STAmount saPrvOwed = (previousNodeIsAccount && nodeIndex_ != 0) + ? ledger().rippleOwed ( + node().account_, + previousAccountID, + node().issue_.currency) + : STAmount (node().issue_); // The limit amount that the previous account may owe. - const STAmount saPrvLimit = (previousNodeIsAccount && nodeIndex != 0) - ? rippleCalc.mActiveLedger.rippleLimit ( - node.account_, previousAccountID, node.currency_) - : STAmount ({node.currency_, node.account_}); + const STAmount saPrvLimit = (previousNodeIsAccount && nodeIndex_ != 0) + ? ledger().rippleLimit ( + node().account_, + previousAccountID, + node().issue_.currency) + : STAmount (node().issue_); // Next account is owed. - const STAmount saNxtOwed = (nextNodeIsAccount && nodeIndex != lastNodeIndex) - ? rippleCalc.mActiveLedger.rippleOwed ( - node.account_, nextAccountID, node.currency_) - : STAmount ({node.currency_, node.account_}); + const STAmount saNxtOwed = (nextNodeIsAccount && nodeIndex_ != lastNodeIndex) + ? ledger().rippleOwed ( + node().account_, + nextAccountID, + node().issue_.currency) + : STAmount (node().issue_); WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount>" - << " nodeIndex=%d/%d" << nodeIndex << "/" << lastNodeIndex + << "reverseLiquidityForAccount>" + << " nodeIndex_=%d/%d" << nodeIndex_ << "/" << lastNodeIndex << " previousAccountID=" << previousAccountID - << " node.account_=" << node.account_ + << " node.account_=" << node().account_ << " nextAccountID=" << nextAccountID - << " currency_=" << node.currency_ + << " currency=" << node().issue_.currency << " uQualityIn=" << uQualityIn << " uQualityOut=" << uQualityOut << " saPrvOwed=" << saPrvOwed @@ -116,67 +119,54 @@ TER computeReverseLiquidityForAccount ( ? saPrvOwed : STAmount (saPrvOwed.issue ()); - // This is the amount we're actually going to be setting for the previous - // node. - STAmount& saPrvRedeemAct = previousNode.saRevRedeem; - // Previous can issue up to limit minus whatever portion of limit already // used (not including redeemable amount) - another "maximum flow". const STAmount saPrvIssueReq = (saPrvOwed < zero) ? saPrvLimit + saPrvOwed : saPrvLimit; - STAmount& saPrvIssueAct = previousNode.saRevIssue; // Precompute these values in case we have an order book. - auto deliverCurrency = previousNode.saRevDeliver.getCurrency (); + auto deliverCurrency = previousNode().saRevDeliver.getCurrency (); const STAmount saPrvDeliverReq ( - {deliverCurrency, previousNode.saRevDeliver.getIssuer ()}, -1); - // Unlimited delivery. - - STAmount& saPrvDeliverAct = previousNode.saRevDeliver; - - // For nextNodeIsAccount - const STAmount& saCurRedeemReq = node.saRevRedeem; + {deliverCurrency, previousNode().saRevDeliver.getIssuer ()}, -1); + // -1 means unlimited delivery. // Set to zero, because we're trying to hit the previous node. - auto saCurRedeemAct = saCurRedeemReq.zeroed(); - - const STAmount& saCurIssueReq = node.saRevIssue; + auto saCurRedeemAct = node().saRevRedeem.zeroed(); // Track the amount we actually redeem. - auto saCurIssueAct = saCurIssueReq.zeroed(); + auto saCurIssueAct = node().saRevIssue.zeroed(); // For !nextNodeIsAccount - const STAmount& saCurDeliverReq = node.saRevDeliver; - auto saCurDeliverAct = saCurDeliverReq.zeroed(); + auto saCurDeliverAct = node().saRevDeliver.zeroed(); WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount:" + << "reverseLiquidityForAccount:" << " saPrvRedeemReq:" << saPrvRedeemReq << " saPrvIssueReq:" << saPrvIssueReq - << " saPrvDeliverAct:" << saPrvDeliverAct + << " previousNode.saRevDeliver:" << previousNode().saRevDeliver << " saPrvDeliverReq:" << saPrvDeliverReq - << " saCurRedeemReq:" << saCurRedeemReq - << " saCurIssueReq:" << saCurIssueReq + << " node.saRevRedeem:" << node().saRevRedeem + << " node.saRevIssue:" << node().saRevIssue << " saNxtOwed:" << saNxtOwed; - WriteLog (lsTRACE, RippleCalc) << pathState.getJson (); + WriteLog (lsTRACE, RippleCalc) << pathState_.getJson (); // Current redeem req can't be more than IOUs on hand. - assert (!saCurRedeemReq || (-saNxtOwed) >= saCurRedeemReq); - assert (!saCurIssueReq // If not issuing, fine. + assert (!node().saRevRedeem || -saNxtOwed >= node().saRevRedeem); + assert (!node().saRevIssue // If not issuing, fine. || saNxtOwed >= zero // saNxtOwed >= 0: Sender not holding next IOUs, saNxtOwed < 0: // Sender holding next IOUs. - || -saNxtOwed == saCurRedeemReq); + || -saNxtOwed == node().saRevRedeem); // If issue req, then redeem req must consume all owed. - if (nodeIndex == 0) + 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! + // 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 @@ -188,12 +178,13 @@ TER computeReverseLiquidityForAccount ( // account --> ACCOUNT --> $ // Overall deliverable. const STAmount saCurWantedReq = std::min ( - pathState.outReq() - pathState.outAct(), + pathState_.outReq() - pathState_.outAct(), saPrvLimit + saPrvOwed); auto saCurWantedAct = saCurWantedReq.zeroed (); WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: account --> ACCOUNT --> $ :" + << "reverseLiquidityForAccount: account --> " + << "ACCOUNT --> $ :" << " saCurWantedReq=" << saCurWantedReq; // Calculate redeem @@ -202,24 +193,25 @@ TER computeReverseLiquidityForAccount ( // Redeem your own IOUs at 1:1 saCurWantedAct = std::min (saPrvRedeemReq, saCurWantedReq); - saPrvRedeemAct = saCurWantedAct; + previousNode().saRevRedeem = saCurWantedAct; uRateMax = STAmount::uRateOne; WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: Redeem at 1:1" + << "reverseLiquidityForAccount: Redeem at 1:1" << " saPrvRedeemReq=" << saPrvRedeemReq - << " (available) saPrvRedeemAct=" << saPrvRedeemAct + << " (available) previousNode.saRevRedeem=" + << previousNode().saRevRedeem << " uRateMax=" << STAmount::saFromRate (uRateMax).getText (); } else { - saPrvRedeemAct.clear (saPrvRedeemReq); + previousNode().saRevRedeem.clear (saPrvRedeemReq); } // Calculate issuing. - saPrvIssueAct.clear (saPrvIssueReq); + previousNode().saRevIssue.clear (saPrvIssueReq); if (saCurWantedReq != saCurWantedAct // Need more. && saPrvIssueReq) // Will accept IOUs from previous. @@ -228,15 +220,20 @@ TER computeReverseLiquidityForAccount ( // If we previously redeemed and this has a poorer rate, this // won't be included the current increment. - computeRippleLiquidity ( - rippleCalc, - uQualityIn, QUALITY_ONE, - saPrvIssueReq, saCurWantedReq, - saPrvIssueAct, saCurWantedAct, uRateMax); + rippleLiquidity ( + rippleCalc_, + uQualityIn, + QUALITY_ONE, + saPrvIssueReq, + saCurWantedReq, + previousNode().saRevIssue, + saCurWantedAct, + uRateMax); WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: Issuing: Rate: quality in : 1.0" - << " saPrvIssueAct:" << saPrvIssueAct + << "reverseLiquidityForAccount: Issuing: Rate: " + << "quality in : 1.0" + << " previousNode.saRevIssue:" << previousNode().saRevIssue << " saCurWantedAct:" << saCurWantedAct; } @@ -250,94 +247,110 @@ TER computeReverseLiquidityForAccount ( { // Not final node. // account --> ACCOUNT --> account - saPrvRedeemAct.clear (saPrvRedeemReq); - saPrvIssueAct.clear (saPrvIssueReq); + previousNode().saRevRedeem.clear (saPrvRedeemReq); + previousNode().saRevIssue.clear (saPrvIssueReq); // redeem (part 1) -> redeem - if (saCurRedeemReq + 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. - computeRippleLiquidity ( - rippleCalc, - QUALITY_ONE, uQualityOut, - saPrvRedeemReq, saCurRedeemReq, - saPrvRedeemAct, saCurRedeemAct, uRateMax); + // Rate : 1.0 : quality out - we must accept our own IOUs + // as 1:1. + rippleLiquidity ( + rippleCalc_, + QUALITY_ONE, + uQualityOut, + saPrvRedeemReq, + node().saRevRedeem, + previousNode().saRevRedeem, + saCurRedeemAct, + uRateMax); WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: " + << "reverseLiquidityForAccount: " << "Rate : 1.0 : quality out" - << " saPrvRedeemAct:" << saPrvRedeemAct + << " previousNode.saRevRedeem:" << previousNode().saRevRedeem << " saCurRedeemAct:" << saCurRedeemAct; } // issue (part 1) -> redeem - if (saCurRedeemReq != saCurRedeemAct + if (node().saRevRedeem != saCurRedeemAct // The current node has more IOUs to redeem. - && saPrvRedeemAct == saPrvRedeemReq) + && previousNode().saRevRedeem == saPrvRedeemReq) // The previous node has no IOUs to redeem remaining, so issues. { // Rate: quality in : quality out - computeRippleLiquidity ( - rippleCalc, - uQualityIn, uQualityOut, - saPrvIssueReq, saCurRedeemReq, - saPrvIssueAct, saCurRedeemAct, uRateMax); + rippleLiquidity ( + rippleCalc_, + uQualityIn, + uQualityOut, + saPrvIssueReq, + node().saRevRedeem, + previousNode().saRevIssue, + saCurRedeemAct, + uRateMax); WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: " + << "reverseLiquidityForAccount: " << "Rate: quality in : quality out:" - << " saPrvIssueAct:" << saPrvIssueAct + << " previousNode.saRevIssue:" << previousNode().saRevIssue << " saCurRedeemAct:" << saCurRedeemAct; } // redeem (part 2) -> issue. - if (saCurIssueReq // Next wants IOUs issued. + if (node().saRevIssue // Next wants IOUs issued. // TODO(tom): this condition seems redundant. - && saCurRedeemAct == saCurRedeemReq + && saCurRedeemAct == node().saRevRedeem // Can only issue if completed redeeming. - && saPrvRedeemAct != saPrvRedeemReq) + && previousNode().saRevRedeem != saPrvRedeemReq) // Did not complete redeeming previous IOUs. { // Rate : 1.0 : transfer_rate - computeRippleLiquidity ( - rippleCalc, + rippleLiquidity ( + rippleCalc_, QUALITY_ONE, - rippleCalc.mActiveLedger.rippleTransferRate (node.account_), - saPrvRedeemReq, saCurIssueReq, - saPrvRedeemAct, saCurIssueAct, uRateMax); + ledger().rippleTransferRate (node().account_), + saPrvRedeemReq, + node().saRevIssue, + previousNode().saRevRedeem, + saCurIssueAct, + uRateMax); WriteLog (lsDEBUG, RippleCalc) - << "computeReverseLiquidityForAccount: " + << "reverseLiquidityForAccount: " << "Rate : 1.0 : transfer_rate:" - << " saPrvRedeemAct:" << saPrvRedeemAct + << " previousNode.saRevRedeem:" << previousNode().saRevRedeem << " saCurIssueAct:" << saCurIssueAct; } // issue (part 2) -> issue - if (saCurIssueReq != saCurIssueAct + if (node().saRevIssue != saCurIssueAct // Need wants more IOUs issued. - && saCurRedeemAct == saCurRedeemReq + && saCurRedeemAct == node().saRevRedeem // Can only issue if completed redeeming. - && saPrvRedeemReq == saPrvRedeemAct + && saPrvRedeemReq == previousNode().saRevRedeem // Previously redeemed all owed IOUs. && saPrvIssueReq) // Previous can issue. { // Rate: quality in : 1.0 - computeRippleLiquidity ( - rippleCalc, - uQualityIn, QUALITY_ONE, - saPrvIssueReq, saCurIssueReq, - saPrvIssueAct, saCurIssueAct, uRateMax); + rippleLiquidity ( + rippleCalc_, + uQualityIn, + QUALITY_ONE, + saPrvIssueReq, + node().saRevIssue, + previousNode().saRevIssue, + saCurIssueAct, + uRateMax); WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: " + << "reverseLiquidityForAccount: " << "Rate: quality in : 1.0:" - << " saPrvIssueAct:" << saPrvIssueAct + << " previousNode.saRevIssue:" << previousNode().saRevIssue << " saCurIssueAct:" << saCurIssueAct; } @@ -348,10 +361,10 @@ TER computeReverseLiquidityForAccount ( } WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: " + << "reverseLiquidityForAccount: " << "^|account --> ACCOUNT --> account :" - << " saCurRedeemReq:" << saCurRedeemReq - << " saCurIssueReq:" << saCurIssueReq + << " node.saRevRedeem:" << node().saRevRedeem + << " node.saRevIssue:" << node().saRevIssue << " saPrvOwed:" << saPrvOwed << " saCurRedeemAct:" << saCurRedeemAct << " saCurIssueAct:" << saCurIssueAct; @@ -363,11 +376,11 @@ TER computeReverseLiquidityForAccount ( // Note: deliver is always issue as ACCOUNT is the issuer for the offer // input. WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: " + << "reverseLiquidityForAccount: " << "account --> ACCOUNT --> offer"; - saPrvRedeemAct.clear (saPrvRedeemReq); - saPrvIssueAct.clear (saPrvIssueReq); + 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. @@ -379,26 +392,35 @@ TER computeReverseLiquidityForAccount ( // TODO(tom): Make sure deliver was cleared, or check actual is zero. // redeem -> deliver/issue. if (saPrvOwed > zero // Previous has IOUs to redeem. - && saCurDeliverReq) // Need some issued. + && node().saRevDeliver) // Need some issued. { // Rate : 1.0 : transfer_rate - computeRippleLiquidity ( - rippleCalc, QUALITY_ONE, - rippleCalc.mActiveLedger.rippleTransferRate (node.account_), - saPrvRedeemReq, saCurDeliverReq, - saPrvRedeemAct, saCurDeliverAct, uRateMax); + rippleLiquidity ( + rippleCalc_, + QUALITY_ONE, + ledger().rippleTransferRate (node().account_), + saPrvRedeemReq, + node().saRevDeliver, + previousNode().saRevRedeem, + saCurDeliverAct, + uRateMax); } // issue -> deliver/issue - if (saPrvRedeemReq == saPrvRedeemAct // Previously redeemed all owed. - && saCurDeliverReq != saCurDeliverAct) // Still need some issued. + if (saPrvRedeemReq == previousNode().saRevRedeem + // Previously redeemed all owed. + && node().saRevDeliver != saCurDeliverAct) // Still need some issued. { // Rate: quality in : 1.0 - computeRippleLiquidity ( - rippleCalc, - uQualityIn, QUALITY_ONE, - saPrvIssueReq, saCurDeliverReq, - saPrvIssueAct, saCurDeliverAct, uRateMax); + rippleLiquidity ( + rippleCalc_, + uQualityIn, + QUALITY_ONE, + saPrvIssueReq, + node().saRevDeliver, + previousNode().saRevIssue, + saCurDeliverAct, + uRateMax); } if (!saCurDeliverAct) @@ -408,8 +430,8 @@ TER computeReverseLiquidityForAccount ( } WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: " - << " saCurDeliverReq:" << saCurDeliverReq + << "reverseLiquidityForAccount: " + << " node.saRevDeliver:" << node().saRevDeliver << " saCurDeliverAct:" << saCurDeliverAct << " saPrvOwed:" << saPrvOwed; } @@ -423,15 +445,15 @@ TER computeReverseLiquidityForAccount ( // 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. const STAmount& saCurWantedReq = - pathState.outReq() - pathState.outAct(); + pathState_.outReq() - pathState_.outAct(); STAmount saCurWantedAct = saCurWantedReq.zeroed(); WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: " + << "reverseLiquidityForAccount: " << "offer --> ACCOUNT --> $ :" << " saCurWantedReq:" << saCurWantedReq - << " saOutAct:" << pathState.outAct() - << " saOutReq:" << pathState.outReq(); + << " saOutAct:" << pathState_.outAct() + << " saOutReq:" << pathState_.outReq(); if (saCurWantedReq <= zero) { @@ -446,7 +468,7 @@ TER computeReverseLiquidityForAccount ( } assert (saCurWantedReq > zero); // FIXME: We got one of these - // The previous node is an offer; we are receiving our own currency; + // 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. @@ -460,11 +482,15 @@ TER computeReverseLiquidityForAccount ( // to a document. // Rate: quality in : 1.0 - computeRippleLiquidity ( - rippleCalc, - uQualityIn, QUALITY_ONE, - saPrvDeliverReq, saCurWantedReq, - saPrvDeliverAct, saCurWantedAct, uRateMax); + rippleLiquidity ( + rippleCalc_, + uQualityIn, + QUALITY_ONE, + saPrvDeliverReq, + saCurWantedReq, + previousNode().saRevDeliver, + saCurWantedAct, + uRateMax); if (!saCurWantedAct) { @@ -473,8 +499,8 @@ TER computeReverseLiquidityForAccount ( } WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount:" - << " saPrvDeliverAct:" << saPrvDeliverAct + << "reverseLiquidityForAccount:" + << " previousNode().saRevDeliver:" << previousNode().saRevDeliver << " saPrvDeliverReq:" << saPrvDeliverReq << " saCurWantedAct:" << saCurWantedAct << " saCurWantedReq:" << saCurWantedReq; @@ -484,51 +510,59 @@ TER computeReverseLiquidityForAccount ( // offer --> ACCOUNT --> account // Note: offer is always delivering(redeeming) as account is issuer. WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: " + << "reverseLiquidityForAccount: " << "offer --> ACCOUNT --> account :" - << " saCurRedeemReq:" << saCurRedeemReq - << " saCurIssueReq:" << saCurIssueReq; + << " 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 (saCurRedeemReq) // Next wants us to redeem. + 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. + // the nxt account's IOUs, then charge the input for quality + // out. // // Rate : 1.0 : quality out - computeRippleLiquidity ( - rippleCalc, - QUALITY_ONE, uQualityOut, - saPrvDeliverReq, saCurRedeemReq, - saPrvDeliverAct, saCurRedeemAct, uRateMax); + rippleLiquidity ( + rippleCalc_, + QUALITY_ONE, + uQualityOut, + saPrvDeliverReq, + node().saRevRedeem, + previousNode().saRevDeliver, + saCurRedeemAct, + uRateMax); } // deliver -> issue. - if (saCurRedeemReq == saCurRedeemAct + if (node().saRevRedeem == saCurRedeemAct // Can only issue if previously redeemed all. - && saCurIssueReq) + && node().saRevIssue) // Need some issued. { // Rate : 1.0 : transfer_rate - computeRippleLiquidity ( - rippleCalc, + rippleLiquidity ( + rippleCalc_, QUALITY_ONE, - rippleCalc.mActiveLedger.rippleTransferRate (node.account_), - saPrvDeliverReq, saCurIssueReq, saPrvDeliverAct, - saCurIssueAct, uRateMax); + ledger().rippleTransferRate (node().account_), + saPrvDeliverReq, + node().saRevIssue, + previousNode().saRevDeliver, + saCurIssueAct, + uRateMax); } WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount:" + << "reverseLiquidityForAccount:" << " saCurRedeemAct:" << saCurRedeemAct - << " saCurRedeemReq:" << saCurRedeemReq - << " saPrvDeliverAct:" << saPrvDeliverAct - << " saCurIssueReq:" << saCurIssueReq; + << " node.saRevRedeem:" << node().saRevRedeem + << " previousNode.saRevDeliver:" << previousNode().saRevDeliver + << " node.saRevIssue:" << node().saRevIssue; - if (!saPrvDeliverAct) + if (!previousNode().saRevDeliver) { // Must want something. terResult = tecPATH_DRY; @@ -540,15 +574,18 @@ TER computeReverseLiquidityForAccount ( // offer --> ACCOUNT --> offer // deliver/redeem -> deliver/issue. WriteLog (lsTRACE, RippleCalc) - << "computeReverseLiquidityForAccount: offer --> ACCOUNT --> offer"; + << "reverseLiquidityForAccount: offer --> ACCOUNT --> offer"; // Rate : 1.0 : transfer_rate - computeRippleLiquidity ( - rippleCalc, + rippleLiquidity ( + rippleCalc_, QUALITY_ONE, - rippleCalc.mActiveLedger.rippleTransferRate (node.account_), - saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct, - saCurDeliverAct, uRateMax); + ledger().rippleTransferRate (node().account_), + saPrvDeliverReq, + node().saRevDeliver, + previousNode().saRevDeliver, + saCurDeliverAct, + uRateMax); if (!saCurDeliverAct) { diff --git a/src/ripple/module/app/paths/ComputeRippleLiquidity.cpp b/src/ripple/module/app/paths/cursor/RippleLiquidity.cpp similarity index 90% rename from src/ripple/module/app/paths/ComputeRippleLiquidity.cpp rename to src/ripple/module/app/paths/cursor/RippleLiquidity.cpp index e3e3ad9ac16..4d13afc3762 100644 --- a/src/ripple/module/app/paths/ComputeRippleLiquidity.cpp +++ b/src/ripple/module/app/paths/cursor/RippleLiquidity.cpp @@ -17,9 +17,7 @@ */ //============================================================================== -#include -#include -#include +#include namespace ripple { namespace path { @@ -46,18 +44,18 @@ namespace path { // it will work and set a rate. If called again, the new work must not worsen // the previous rate. -void computeRippleLiquidity ( +void rippleLiquidity ( RippleCalc& rippleCalc, - const std::uint32_t uQualityIn, - const std::uint32_t uQualityOut, - const STAmount& saPrvReq, // --> in limit including fees, <0 = unlimited - const STAmount& saCurReq, // --> out limit + std::uint32_t const uQualityIn, + std::uint32_t const uQualityOut, + 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) { WriteLog (lsTRACE, RippleCalc) - << "computeRippleLiquidity>" + << "rippleLiquidity>" << " uQualityIn=" << uQualityIn << " uQualityOut=" << uQualityOut << " saPrvReq=" << saPrvReq @@ -84,7 +82,7 @@ void computeRippleLiquidity ( const STAmount saCur = saCurReq - saCurAct; WriteLog (lsTRACE, RippleCalc) - << "computeRippleLiquidity: " + << "rippleLiquidity: " << " bPrvUnlimited=" << bPrvUnlimited << " saPrv=" << saPrv << " saCur=" << saCur; @@ -96,7 +94,7 @@ void computeRippleLiquidity ( if (uQualityIn >= uQualityOut) { // You're getting better quality than you asked for, so no fee. - WriteLog (lsTRACE, RippleCalc) << "computeRippleLiquidity: No fees"; + WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity: No fees"; // Only process if the current rate, 1:1, is not worse than the previous // rate, uRateMax - otherwise there is no flow. @@ -126,7 +124,7 @@ void computeRippleLiquidity ( else { // If the quality is worse than the previous - WriteLog (lsTRACE, RippleCalc) << "computeRippleLiquidity: Fee"; + WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity: Fee"; std::uint64_t uRate = STAmount::getRate ( STAmount (uQualityOut), STAmount (uQualityIn)); @@ -146,7 +144,7 @@ void computeRippleLiquidity ( numerator, uQualityIn, {currency, uCurIssuerID}, true); WriteLog (lsTRACE, RippleCalc) - << "computeRippleLiquidity:" + << "rippleLiquidity:" << " bPrvUnlimited=" << bPrvUnlimited << " saPrv=" << saPrv << " saCurIn=" << saCurIn; @@ -157,7 +155,7 @@ void computeRippleLiquidity ( saCurAct += saCur; saPrvAct += saCurIn; WriteLog (lsTRACE, RippleCalc) - << "computeRippleLiquidity:3c:" + << "rippleLiquidity:3c:" << " saCurReq=" << saCurReq << " saPrvAct=" << saPrvAct; } @@ -179,7 +177,7 @@ void computeRippleLiquidity ( numerator, uQualityOut, issue, true); WriteLog (lsTRACE, RippleCalc) - << "computeRippleLiquidity:4: saCurReq=" << saCurReq; + << "rippleLiquidity:4: saCurReq=" << saCurReq; saCurAct += saCurOut; saPrvAct = saPrvReq; @@ -190,7 +188,7 @@ void computeRippleLiquidity ( } WriteLog (lsTRACE, RippleCalc) - << "computeRippleLiquidity<" + << "rippleLiquidity<" << " uQualityIn=" << uQualityIn << " uQualityOut=" << uQualityOut << " saPrvReq=" << saPrvReq diff --git a/src/ripple/module/app/paths/CalcState.h b/src/ripple/module/app/paths/cursor/RippleLiquidity.h similarity index 55% rename from src/ripple/module/app/paths/CalcState.h rename to src/ripple/module/app/paths/cursor/RippleLiquidity.h index d3be62acbfd..ad7a606155c 100644 --- a/src/ripple/module/app/paths/CalcState.h +++ b/src/ripple/module/app/paths/cursor/RippleLiquidity.h @@ -17,50 +17,29 @@ */ //============================================================================== -#ifndef RIPPLE_CALCSTATE_H -#define RIPPLE_CALCSTATE_H +#ifndef RIPPLE_PATHS_CALCULATORS_H +#define RIPPLE_PATHS_CALCULATORS_H -namespace ripple { - -typedef TER ErrorCode; - -class CalcState -{ - public: - CalcState( - unsigned int nodeIndex, PathState& state, LedgerEntrySet& ledger, bool quality) - : nodeIndex_(nodeIndex), - pathState_(state), - ledger_(ledger), - quality_(quality) - {} - - enum Direction { BACKWARD, FORWARD }; - TER calc(Direction); - TER calcAccount(Direction); - TER calcOffer(Direction); - TER calcDeliver(Direction); - TER calcAdvance(Direction); - void nextPath(LedgerEntrySet const& checkpoint) const; +#include - private: - LedgerEntrySet& ledger() - { - return ledger_; - } - - bool quality() const - { - return quality_; - } - - private: - unsigned int const nodeIndex_; - PathState& pathState_; - LedgerEntrySet& ledger_; - bool const quality_; -}; +#include +#include +#include +namespace ripple { +namespace path { + +void rippleLiquidity ( + RippleCalc&, + const std::uint32_t uQualityIn, + const std::uint32_t uQualityOut, + const STAmount& saPrvReq, + const STAmount& saCurReq, + STAmount& saPrvAct, + STAmount& saCurAct, + std::uint64_t& uRateMax); + +} // path } // ripple #endif diff --git a/src/ripple/module/app/peers/UniqueNodeList.cpp b/src/ripple/module/app/peers/UniqueNodeList.cpp index 2346e5cb8a1..4ae6ab3bf64 100644 --- a/src/ripple/module/app/peers/UniqueNodeList.cpp +++ b/src/ripple/module/app/peers/UniqueNodeList.cpp @@ -654,7 +654,7 @@ class UniqueNodeListImp if (!db->executeSQL ("SELECT * FROM Misc WHERE Magic=1;")) return false; - bool bAvail = !!db->startIterRows (); + bool bAvail = db->startIterRows (); mtpFetchUpdated = ptFromSeconds (bAvail ? db->getInt ("FetchUpdated") : -1); mtpScoreUpdated = ptFromSeconds (bAvail ? db->getInt ("ScoreUpdated") : -1); diff --git a/src/ripple/module/app/shamap/SHAMap.cpp b/src/ripple/module/app/shamap/SHAMap.cpp index c3a7e268263..31a5bf821d8 100644 --- a/src/ripple/module/app/shamap/SHAMap.cpp +++ b/src/ripple/module/app/shamap/SHAMap.cpp @@ -1378,7 +1378,7 @@ class SHAMap_test : public beast::unit_test::suite i = sMap.peekNextItem (i->getTag ()); - unexpected (!!i, "bad traverse"); + unexpected (i, "bad traverse"); sMap.addItem (i4, true, false); sMap.delItem (i2.getTag ()); @@ -1398,7 +1398,7 @@ class SHAMap_test : public beast::unit_test::suite i = sMap.peekNextItem (i->getTag ()); - unexpected (!!i, "bad traverse"); + unexpected (i, "bad traverse"); diff --git a/src/ripple/module/app/shamap/SHAMapTreeNode.h b/src/ripple/module/app/shamap/SHAMapTreeNode.h index ec9fe4df776..93d1513a9cb 100644 --- a/src/ripple/module/app/shamap/SHAMapTreeNode.h +++ b/src/ripple/module/app/shamap/SHAMapTreeNode.h @@ -146,10 +146,12 @@ class SHAMapTreeNode // item node function bool hasItem () const { - return !!mItem; + return bool(mItem); } SHAMapItem::ref peekItem () - { // CAUTION: Do not modify the item + { + // CAUTION: Do not modify the item TODO(tom): a comment in the code does + // nothing - this should return a const reference. return mItem; } bool setItem (SHAMapItem::ref i, TNType type); diff --git a/src/ripple/module/app/transactors/Payment.cpp b/src/ripple/module/app/transactors/Payment.cpp index aeabcdec25c..ccdcc22ba10 100644 --- a/src/ripple/module/app/transactors/Payment.cpp +++ b/src/ripple/module/app/transactors/Payment.cpp @@ -25,9 +25,9 @@ TER Payment::doApply () { // Ripple if source or destination is non-native or if there are paths. std::uint32_t const uTxFlags = mTxn.getFlags (); - bool const bPartialPayment (uTxFlags & tfPartialPayment); - bool const bLimitQuality (uTxFlags & tfLimitQuality); - bool const bNoRippleDirect (uTxFlags & tfNoRippleDirect); + bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; + bool const limitQuality = uTxFlags & tfLimitQuality; + bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); bool const bPaths = mTxn.isFieldPresent (sfPaths); bool const bMax = mTxn.isFieldPresent (sfSendMax); Account const& uDstAccountID = mTxn.getFieldAccount160 (sfDestination); @@ -128,7 +128,7 @@ TER Payment::doApply () return temBAD_SEND_XRP_PATHS; } - else if (bXRPDirect && bPartialPayment) + else if (bXRPDirect && partialPaymentAllowed) { // Consistent but redundant transaction. m_journal.trace << @@ -136,7 +136,7 @@ TER Payment::doApply () return temBAD_SEND_XRP_PARTIAL; } - else if (bXRPDirect && bLimitQuality) + else if (bXRPDirect && limitQuality) { // Consistent but redundant transaction. m_journal.trace << @@ -144,7 +144,7 @@ TER Payment::doApply () return temBAD_SEND_XRP_LIMIT; } - else if (bXRPDirect && bNoRippleDirect) + else if (bXRPDirect && !defaultPathsAllowed) { // Consistent but redundant transaction. m_journal.trace << @@ -166,10 +166,11 @@ TER Payment::doApply () m_journal.trace << "Delay transaction: Destination account does not exist."; - // Another transaction could create the account and then this transaction would succeed. + // Another transaction could create the account and then this + // transaction would succeed. return tecNO_DST; } - else if (mParams & tapOPEN_LEDGER && bPartialPayment) + else if (mParams & tapOPEN_LEDGER && partialPaymentAllowed) { // You cannot fund an account with a partial payment. // Make retry work smaller, by rejecting this. @@ -233,43 +234,31 @@ TER Payment::doApply () // Copy paths into an editable class. STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths); - PathState::List pathStateList; - STAmount maxSourceAmountAct; - STAmount saDstAmountAct; + path::RippleCalc rc( + mEngine->view (), + maxSourceAmount, + saDstAmount, + uDstAccountID, + mTxnAccountID, + spsPaths); + + rc.partialPaymentAllowed_ = partialPaymentAllowed; + rc.limitQuality_ = limitQuality; + rc.defaultPathsAllowed_ = defaultPathsAllowed; + rc.deleteUnfundedOffers_ = true; + rc.isLedgerOpen_ = (mParams & tapOPEN_LEDGER); try { - bool const openLedger = (mParams & tapOPEN_LEDGER); - bool pathTooBig = spsPaths.size () > MaxPathSize; for (auto const& path : spsPaths) if (path.size () > MaxPathLength) pathTooBig = true; - terResult = openLedger && pathTooBig + terResult = rc.isLedgerOpen_ && pathTooBig ? telBAD_PATH_COUNT // Too many paths for proposed ledger. - : path::rippleCalculate ( - mEngine->view (), - maxSourceAmountAct, - saDstAmountAct, - pathStateList, // Vector for saving expanded path. - maxSourceAmount, - saDstAmount, - uDstAccountID, - mTxnAccountID, - spsPaths, - bPartialPayment, - bLimitQuality, - bNoRippleDirect, // Always compute for finalizing ledger. - false, // Not standalone, delete unfundeds. - openLedger); - - // Not standalone means: If we discover an offer that's unfunded, we - // should delete it as soon as we can. - - // If you're not modifying the ledger, you don't need to delete - // unfunded. + : rc.rippleCalculate (); // TODO(tom): what's going on here? if (isTerRetry(terResult)) @@ -277,8 +266,8 @@ TER Payment::doApply () // TODO: is this right? If the amount is the correct amount, was // the delivered amount previously set? - if (terResult == tesSUCCESS && saDstAmountAct != saDstAmount) - mEngine->view().setDeliveredAmount (saDstAmountAct); + if (terResult == tesSUCCESS && rc.actualAmountOut_ != saDstAmount) + mEngine->view().setDeliveredAmount (rc.actualAmountOut_); } catch (std::exception const& e) { diff --git a/src/ripple/module/app/transactors/SetTrust.cpp b/src/ripple/module/app/transactors/SetTrust.cpp index f158df2f3a6..eafcd27ca6f 100644 --- a/src/ripple/module/app/transactors/SetTrust.cpp +++ b/src/ripple/module/app/transactors/SetTrust.cpp @@ -261,13 +261,13 @@ TER SetTrust::doApply () bool const bLowReserveSet = uLowQualityIn || uLowQualityOut || (uFlagsOut & lsfLowNoRipple) || (uFlagsOut & lsfLowFreeze) || - !!saLowLimit || saLowBalance > zero; + saLowLimit || saLowBalance > zero; bool const bLowReserveClear = !bLowReserveSet; bool const bHighReserveSet = uHighQualityIn || uHighQualityOut || (uFlagsOut & lsfHighNoRipple) || (uFlagsOut & lsfHighFreeze) || - !!saHighLimit || saHighBalance > zero; + saHighLimit || saHighBalance > zero; bool const bHighReserveClear = !bHighReserveSet; bool const bDefault = bLowReserveClear && bHighReserveClear; diff --git a/src/ripple/module/app/tx/Transaction.cpp b/src/ripple/module/app/tx/Transaction.cpp index efb0d490b97..fdf05b3cccb 100644 --- a/src/ripple/module/app/tx/Transaction.cpp +++ b/src/ripple/module/app/tx/Transaction.cpp @@ -289,12 +289,14 @@ bool Transaction::convertToTransactions (std::uint32_t firstLedgerSeq, std::uint Transaction::pointer firstTrans, secondTrans; - if (!!first) + if (first) { // transaction in our table - firstTrans = sharedTransaction (first->peekData (), checkFirstTransactions); + firstTrans = sharedTransaction ( + first->peekData (), checkFirstTransactions); - if ((firstTrans->getStatus () == INVALID) || (firstTrans->getID () != id )) + if (firstTrans->getStatus () == INVALID || + firstTrans->getID () != id ) { firstTrans->setStatus (INVALID, firstLedgerSeq); return false; @@ -302,25 +304,36 @@ bool Transaction::convertToTransactions (std::uint32_t firstLedgerSeq, std::uint else firstTrans->setStatus (INCLUDED, firstLedgerSeq); } - if (!!second) + if (second) { // transaction in other table - secondTrans = sharedTransaction (second->peekData (), checkSecondTransactions); + secondTrans = sharedTransaction ( + second->peekData (), checkSecondTransactions); - if ((secondTrans->getStatus () == INVALID) || (secondTrans->getID () != id)) + if (secondTrans->getStatus () == INVALID || + secondTrans->getID () != id) { secondTrans->setStatus (INVALID, secondLedgerSeq); return false; } - else secondTrans->setStatus (INCLUDED, secondLedgerSeq); + else + { + secondTrans->setStatus (INCLUDED, secondLedgerSeq); + } } assert (firstTrans || secondTrans); - if (firstTrans && secondTrans && (firstTrans->getStatus () != INVALID) && (secondTrans->getStatus () != INVALID)) - return false; // one or the other SHAMap is structurally invalid or a miracle has happened + if (firstTrans && secondTrans && + firstTrans->getStatus () != INVALID && + secondTrans->getStatus () != INVALID) + { + // One or the other SHAMap is structurally invalid or a miracle has + // happened. + return false; + } - outMap[id] = std::pair (firstTrans, secondTrans); + outMap[id] = {firstTrans, secondTrans}; } return true; @@ -338,8 +351,8 @@ Json::Value Transaction::getJson (int options, bool binary) const if (options == 1) { - Ledger::pointer ledger = getApp().getLedgerMaster ().getLedgerBySeq (mInLedger); - + auto ledger = getApp().getLedgerMaster (). + getLedgerBySeq (mInLedger); if (ledger) ret["date"] = ledger->getCloseTimeNC (); } diff --git a/src/ripple/module/data/crypto/CKey.h b/src/ripple/module/data/crypto/CKey.h index 9c3283d1840..ddc7074489d 100644 --- a/src/ripple/module/data/crypto/CKey.h +++ b/src/ripple/module/data/crypto/CKey.h @@ -215,8 +215,8 @@ class CKey bool SetPrivateKeyU (uint256 const& key, bool bThrow = false) { // XXX Broken if pkey is not set. - BIGNUM* bn = BN_bin2bn (key.begin (), key.size (), nullptr); - bool bSuccess = !!EC_KEY_set_private_key (pkey, bn); + BIGNUM* bn = BN_bin2bn (key.begin (), key.size (), nullptr); + bool bSuccess = EC_KEY_set_private_key (pkey, bn); BN_clear_free (bn); diff --git a/src/ripple/module/data/protocol/RippleSystem.h b/src/ripple/module/data/protocol/RippleSystem.h index d88ab2b2a02..188e156b4ae 100644 --- a/src/ripple/module/data/protocol/RippleSystem.h +++ b/src/ripple/module/data/protocol/RippleSystem.h @@ -23,7 +23,7 @@ namespace ripple { /** Protocol specific constant globals. */ -// VFALCO NOTE use these from now on instead of the macros!! +// VFALCO NOTE use these from now on instead of the macros! class RippleSystem { public: diff --git a/src/ripple/module/data/protocol/SerializedObject.cpp b/src/ripple/module/data/protocol/SerializedObject.cpp index 598c63daba9..c846af8896c 100644 --- a/src/ripple/module/data/protocol/SerializedObject.cpp +++ b/src/ripple/module/data/protocol/SerializedObject.cpp @@ -860,7 +860,7 @@ const STArray& STObject::getFieldArray (SField::ref field) const return *cf; } -const STPathSet& STObject::getFieldPathSet (SField::ref field) const +STPathSet const& STObject::getFieldPathSet (SField::ref field) const { static STPathSet empty; const SerializedType* rf = peekAtPField (field); @@ -1049,7 +1049,7 @@ void STObject::setFieldAmount (SField::ref field, const STAmount& v) (*cf) = v; } -void STObject::setFieldPathSet (SField::ref field, const STPathSet& v) +void STObject::setFieldPathSet (SField::ref field, STPathSet const& v) { SerializedType* rf = getPField (field, true); diff --git a/src/ripple/module/data/protocol/SerializedObject.h b/src/ripple/module/data/protocol/SerializedObject.h index b55bc4e9153..96926fbf1bc 100644 --- a/src/ripple/module/data/protocol/SerializedObject.h +++ b/src/ripple/module/data/protocol/SerializedObject.h @@ -205,7 +205,7 @@ class STObject Blob getFieldVL (SField::ref field) const; const STAmount& getFieldAmount (SField::ref field) const; - const STPathSet& getFieldPathSet (SField::ref field) const; + STPathSet const& getFieldPathSet (SField::ref field) const; const STVector256& getFieldV256 (SField::ref field) const; const STArray& getFieldArray (SField::ref field) const; @@ -222,7 +222,7 @@ class STObject setFieldAccount (field, addr.getAccountID ()); } void setFieldAmount (SField::ref field, const STAmount&); - void setFieldPathSet (SField::ref field, const STPathSet&); + void setFieldPathSet (SField::ref field, STPathSet const&); void setFieldV256 (SField::ref field, const STVector256 & v); template @@ -345,15 +345,6 @@ static T range_check_cast (const U& value, const T& minimum, const T& maximum) return static_cast (value); } -inline STObject::iterator range_begin (STObject& x) -{ - return x.begin (); -} -inline STObject::iterator range_end (STObject& x) -{ - return x.end (); -} - //------------------------------------------------------------------------------ class STArray @@ -535,15 +526,6 @@ class STArray static STArray* construct (SerializerIterator&, SField::ref); }; -inline STArray::iterator range_begin (STArray& x) -{ - return x.begin (); -} -inline STArray::iterator range_end (STArray& x) -{ - return x.end (); -} - } // ripple #endif diff --git a/src/ripple/module/data/protocol/SerializedTypes.h b/src/ripple/module/data/protocol/SerializedTypes.h index 5b5eb429a68..85745037965 100644 --- a/src/ripple/module/data/protocol/SerializedTypes.h +++ b/src/ripple/module/data/protocol/SerializedTypes.h @@ -327,12 +327,12 @@ class STPath return mPath.end (); } - bool operator== (const STPath& t) const + bool operator== (STPath const& t) const { return mPath == t.mPath; } - void setCanonical (const STPath& spExpanded); + void setCanonical (STPath const& spExpanded); private: friend class STPathSet; @@ -341,26 +341,6 @@ class STPath std::vector mPath; }; -inline std::vector::iterator range_begin (STPath& x) -{ - return x.begin (); -} - -inline std::vector::iterator range_end (STPath& x) -{ - return x.end (); -} - -inline std::vector::const_iterator range_begin (const STPath& x) -{ - return x.begin (); -} - -inline std::vector::const_iterator range_end (const STPath& x) -{ - return x.end (); -} - //------------------------------------------------------------------------------ // A set of zero or more payment paths @@ -408,7 +388,7 @@ class STPathSet : public SerializedType { value.reserve(n); } - const STPath& getPath (int off) const + STPath const& getPath (int off) const { return value[off]; } @@ -424,11 +404,11 @@ class STPathSet : public SerializedType { value.clear (); } - void addPath (const STPath& e) + void addPath (STPath const& e) { value.push_back (e); } - void addUniquePath (const STPath& e) + void addUniquePath (STPath const& e) { for (auto const& p: value) { @@ -500,26 +480,6 @@ class STPathSet : public SerializedType static STPathSet* construct (SerializerIterator&, SField::ref); }; -inline std::vector::iterator range_begin (STPathSet& x) -{ - return x.begin (); -} - -inline std::vector::iterator range_end (STPathSet& x) -{ - return x.end (); -} - -inline std::vector::const_iterator range_begin (const STPathSet& x) -{ - return x.begin (); -} - -inline std::vector::const_iterator range_end (const STPathSet& x) -{ - return x.end (); -} - //------------------------------------------------------------------------------ class STVector256 : public SerializedType diff --git a/src/ripple/module/rpc/handlers/RipplePathFind.cpp b/src/ripple/module/rpc/handlers/RipplePathFind.cpp index ffa7b211744..a8221477abe 100644 --- a/src/ripple/module/rpc/handlers/RipplePathFind.cpp +++ b/src/ripple/module/rpc/handlers/RipplePathFind.cpp @@ -30,17 +30,17 @@ Json::Value doRipplePathFind (RPC::Context& context) context.loadType_ = Resource::feeHighBurdenRPC; - RippleAddress raSrc; - RippleAddress raDst; - STAmount saDstAmount; + RippleAddress raSrc; + RippleAddress raDst; + STAmount saDstAmount; Ledger::pointer lpLedger; - Json::Value jvResult; + Json::Value jvResult; - if (getConfig().RUN_STANDALONE - || context.params_.isMember("ledger") - || context.params_.isMember("ledger_index") - || context.params_.isMember("ledger_hash")) + if (getConfig().RUN_STANDALONE || + context.params_.isMember(jss::ledger) || + context.params_.isMember(jss::ledger_index) || + context.params_.isMember(jss::ledger_hash)) { // The caller specified a ledger jvResult = RPC::lookupLedger ( @@ -51,32 +51,32 @@ Json::Value doRipplePathFind (RPC::Context& context) if (!context.params_.isMember ("source_account")) { - jvResult = rpcError (rpcSRC_ACT_MISSING); + jvResult = rpcError (rpcSRC_ACT_MISSING); } else if (!context.params_["source_account"].isString () || !raSrc.setAccountID ( context.params_["source_account"].asString ())) { - jvResult = rpcError (rpcSRC_ACT_MALFORMED); + jvResult = rpcError (rpcSRC_ACT_MALFORMED); } else if (!context.params_.isMember ("destination_account")) { - jvResult = rpcError (rpcDST_ACT_MISSING); + jvResult = rpcError (rpcDST_ACT_MISSING); } else if (!context.params_["destination_account"].isString () || !raDst.setAccountID ( context.params_["destination_account"].asString ())) { - jvResult = rpcError (rpcDST_ACT_MALFORMED); + jvResult = rpcError (rpcDST_ACT_MALFORMED); } else if ( // Parse saDstAmount. !context.params_.isMember ("destination_amount") || !saDstAmount.bSetJson (context.params_["destination_amount"]) || saDstAmount <= zero - || (!!saDstAmount.getCurrency () && - (!saDstAmount.getIssuer () - || noAccount() == saDstAmount.getIssuer ()))) + || (!isXRP(saDstAmount.getCurrency ()) + && (!saDstAmount.getIssuer () || + noAccount() == saDstAmount.getIssuer ()))) { WriteLog (lsINFO, RPCHandler) << "Bad destination_amount."; jvResult = rpcError (rpcINVALID_PARAMS); @@ -85,8 +85,9 @@ Json::Value doRipplePathFind (RPC::Context& context) // Checks on source_currencies. context.params_.isMember ("source_currencies") && (!context.params_["source_currencies"].isArray () - || !context.params_["source_currencies"].size ())) + || !context.params_["source_currencies"].size ()) // Don't allow empty currencies. + ) { WriteLog (lsINFO, RPCHandler) << "Bad source_currencies."; jvResult = rpcError (rpcINVALID_PARAMS); @@ -173,14 +174,13 @@ Json::Value doRipplePathFind (RPC::Context& context) (noAccount() == uSrcIssuerID))) { WriteLog (lsINFO, RPCHandler) << "Bad issuer."; - return rpcError (rpcSRC_ISR_MALFORMED); } STPathSet spsComputed; bool bValid; - Pathfinder pf ( - cache, raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount, bValid); + Pathfinder pf (cache, raSrc, raDst, uSrcCurrencyID, + uSrcIssuerID, saDstAmount, bValid); int level = getConfig().PATH_SEARCH_OLD; if ((getConfig().PATH_SEARCH_MAX > level) @@ -213,74 +213,51 @@ Json::Value doRipplePathFind (RPC::Context& context) } else { - PathState::List pathStateList; - STAmount saMaxAmountAct; - STAmount saDstAmountAct; - STAmount saMaxAmount ( - {uSrcCurrencyID, - !!uSrcIssuerID - ? uSrcIssuerID // Use specifed issuer. - : !!uSrcCurrencyID // Default to source account. - ? Account(raSrc.getAccountID ()) - : xrpAccount()}, - 1); + auto& issuer = + isXRP (uSrcIssuerID) ? + isXRP (uSrcCurrencyID) ? // Default to source account. + xrpAccount() : + Account (raSrc.getAccountID ()) + : uSrcIssuerID; // Use specifed issuer. + + STAmount saMaxAmount ({uSrcCurrencyID, issuer}, 1); saMaxAmount.negate (); - LedgerEntrySet lesSandbox (lpLedger, tapNONE); - - TER terResult = - path::rippleCalculate ( - lesSandbox, - saMaxAmountAct, // <-- - saDstAmountAct, // <-- - pathStateList, // <-- - saMaxAmount, // --> Amount to send is unlimited to get an estimate. - saDstAmount, // --> Amount to deliver. - raDst.getAccountID (), // --> Account to deliver to. - raSrc.getAccountID (), // --> Account sending from. - spsComputed, // --> Path set. - false, // --> Don't allow partial payment. This is for normal fill or kill payments. - // Must achieve delivery goal. - false, // --> Don't limit quality. Average quality is wanted for normal payments. - false, // --> Allow direct ripple to be added to path set. to path set. - true); // --> Stand alone mode, no point in deleting unfundeds. - - // WriteLog (lsDEBUG, RPCHandler) << "ripple_path_find: PATHS IN: " << spsComputed.size() << " : " << spsComputed.getJson(0); - // WriteLog (lsDEBUG, RPCHandler) << "ripple_path_find: PATHS EXP: " << pathStateList.size(); + LedgerEntrySet lesSandbox (lpLedger, tapNONE); - WriteLog (lsWARNING, RPCHandler) - << boost::str (boost::format ("ripple_path_find: saMaxAmount=%s saDstAmount=%s saMaxAmountAct=%s saDstAmountAct=%s") - % saMaxAmount - % saDstAmount - % saMaxAmountAct - % saDstAmountAct); + path::RippleCalc rc ( + lesSandbox, + saMaxAmount, // --> Amount to send is unlimited + // to get an estimate. + saDstAmount, // --> Amount to deliver. + raDst.getAccountID (), // --> Account to deliver to. + raSrc.getAccountID (), // --> Account sending from. + spsComputed); + TER terResult = rc.rippleCalculate (); // --> Path set. - if ((extraPath.size() > 0) && ((terResult == terNO_LINE) || (terResult == tecPATH_PARTIAL))) + WriteLog (lsWARNING, RPCHandler) + << "ripple_path_find:" + << " saMaxAmount=" << saMaxAmount + << " saDstAmount=" << saDstAmount + << " saMaxAmountAct=" << rc.actualAmountIn_ + << " saDstAmountAct=" << rc.actualAmountOut_; + + if (extraPath.size() > 0 && + (terResult == terNO_LINE || terResult == tecPATH_PARTIAL)) { - WriteLog (lsDEBUG, PathRequest) << "Trying with an extra path element"; + WriteLog (lsDEBUG, PathRequest) + << "Trying with an extra path element"; + spsComputed.addPath(extraPath); - pathStateList.clear (); + rc.pathStateList_.clear (); lesSandbox.clear (); - terResult = path::rippleCalculate ( - lesSandbox, - saMaxAmountAct, - saDstAmountAct, - pathStateList, - saMaxAmount, - saDstAmount, - raDst.getAccountID (), - raSrc.getAccountID (), - spsComputed, - false, - false, - false, - true); + terResult = rc.rippleCalculate (); WriteLog (lsDEBUG, PathRequest) << "Extra path element gives " << transHuman (terResult); } - if (tesSUCCESS == terResult) + if (terResult == tesSUCCESS) { Json::Value jvEntry (Json::objectValue); @@ -290,10 +267,9 @@ Json::Value doRipplePathFind (RPC::Context& context) // anyway to produce the canonical. (At least unless we // make a direct canonical.) - jvEntry["source_amount"] = saMaxAmountAct.getJson (0); - jvEntry["paths_canonical"] - = Json::arrayValue; // spsCanonical.getJson(0); - jvEntry["paths_computed"] = spsComputed.getJson (0); + jvEntry["source_amount"] = rc.actualAmountIn_.getJson (0); + jvEntry["paths_canonical"] = Json::arrayValue; + jvEntry["paths_computed"] = spsComputed.getJson (0); jvArray.append (jvEntry); } diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 7eed243e0df..38af596e852 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -341,7 +341,7 @@ OverlayImpl::onPrepare () auto bootstrapIps (getConfig ().IPS); // If no IPs are specified, use the Ripple Labs round robin - // pool to get some servers to insert into the boot cache. + // pool to get some servers to insert into the boot cache. if (bootstrapIps.empty ()) bootstrapIps.push_back ("r.ripple.com 51235"); @@ -349,7 +349,7 @@ OverlayImpl::onPrepare () { m_resolver.resolve (bootstrapIps, [this]( - std::string const& name, + std::string const& name, std::vector const& addresses) { std::vector ips; @@ -369,7 +369,7 @@ OverlayImpl::onPrepare () { m_resolver.resolve (getConfig ().IPS_FIXED, [this]( - std::string const& name, + std::string const& name, std::vector const& addresses) { if (!addresses.empty ()) @@ -406,7 +406,7 @@ OverlayImpl::onStart () } /** Close all peer connections. - If `graceful` is true then active + If `graceful` is true then active Requirements: Caller must hold the mutex. */ @@ -437,7 +437,7 @@ OverlayImpl::onStop () // Take off the extra count we added in the constructor release(); - close_all (false); + close_all (false); } void @@ -486,9 +486,9 @@ OverlayImpl::onPeerActivated (Peer::ptr const& peer) assert(result.second); } - m_journal.debug << + m_journal.debug << "activated " << peer->getRemoteAddress() << - " (" << peer->getShortId() << + " (" << peer->getShortId() << ":" << RipplePublicKey(peer->getNodePublic()) << ")"; // We just accepted this peer so we have non-zero active peers @@ -537,7 +537,7 @@ OverlayImpl::getActivePeers () BOOST_FOREACH (PeerByPublicKey::value_type const& pair, m_publicKeyMap) { - assert (!!pair.second); + assert (pair.second); ret.push_back (pair.second); } @@ -562,7 +562,7 @@ make_Overlay ( beast::Stoppable& parent, Resource::Manager& resourceManager, SiteFiles::Manager& siteFiles, - beast::File const& pathToDbFileOrDirectory, + beast::File const& pathToDbFileOrDirectory, Resolver& resolver, boost::asio::io_service& io_service, boost::asio::ssl::context& ssl_context) diff --git a/src/ripple/overlay/impl/PeerImp.h b/src/ripple/overlay/impl/PeerImp.h index fb9d596188a..591bff10212 100644 --- a/src/ripple/overlay/impl/PeerImp.h +++ b/src/ripple/overlay/impl/PeerImp.h @@ -610,7 +610,7 @@ class PeerImp ret["complete_ledgers"] = boost::lexical_cast(minSeq) + " - " + boost::lexical_cast(maxSeq); - if (!!m_closedLedgerHash) + if (m_closedLedgerHash != zero) ret["ledger"] = to_string (m_closedLedgerHash); if (mLastStatus.has_newstatus ()) diff --git a/src/ripple/peerfinder/impl/Livecache.cpp b/src/ripple/peerfinder/impl/Livecache.cpp index e9626935318..fbdd9e23b77 100644 --- a/src/ripple/peerfinder/impl/Livecache.cpp +++ b/src/ripple/peerfinder/impl/Livecache.cpp @@ -54,7 +54,7 @@ class Livecache_test : public beast::unit_test::suite add (6, 2, c); add (7, 1, c); - // VFALCO TODO!!! + // VFALCO TODO! pass(); } diff --git a/src/ripple/unity/app8.cpp b/src/ripple/unity/app8.cpp index 65824000458..e258825b96a 100644 --- a/src/ripple/unity/app8.cpp +++ b/src/ripple/unity/app8.cpp @@ -28,10 +28,21 @@ #include +#include #include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/src/ripple/module/app/paths/QualityConstraint.h b/src/ripple/unity/http.h similarity index 69% rename from src/ripple/module/app/paths/QualityConstraint.h rename to src/ripple/unity/http.h index 4c80b3bad90..439d80f8624 100644 --- a/src/ripple/module/app/paths/QualityConstraint.h +++ b/src/ripple/unity/http.h @@ -17,15 +17,19 @@ */ //============================================================================== -#ifndef RIPPLE_QUALITY_CONSTRAINT_H -#define RIPPLE_QUALITY_CONSTRAINT_H +#ifndef RIPPLE_HTTP_H_INCLUDED +#define RIPPLE_HTTP_H_INCLUDED -namespace ripple { -namespace path { +// VFALCO This entire file is deprecated now, I'm working on a replacement -enum class QualityConstraint { SAME_OR_BETTER, UNCONSTRAINED }; +// VFALCO NOTE this sucks that we have to include asio in the header +// just for HTTPMessage! +#include -} // path -} // ripple +#include +#include +#include +#include +#include #endif