diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index f9fa85f386..c8e1a1453d 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -50,7 +50,8 @@ Application::Application() : mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mLedgerMaster), mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), mSLECache("LedgerEntryCache", 4096, 120), mSNTPClient(mAuxService), mJobQueue(mIOService), mFeeTrack(), - mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL), + mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), + mHashNodeDB(NULL), mNetNodeDB(NULL), mPathFindDB(NULL), mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL), mSweepTimer(mAuxService), mShutdown(false) { @@ -58,8 +59,10 @@ Application::Application() : getRand(reinterpret_cast(&mNonceST), sizeof(mNonceST)); } -extern const char *RpcDBInit[], *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[], *NetNodeDBInit[]; -extern int RpcDBCount, TxnDBCount, LedgerDBCount, WalletDBCount, HashNodeDBCount, NetNodeDBCount; +extern const char *RpcDBInit[], *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[], + *NetNodeDBInit[], *PathFindDBInit[]; +extern int RpcDBCount, TxnDBCount, LedgerDBCount, WalletDBCount, HashNodeDBCount, + NetNodeDBCount, PathFindDBCount; bool Instance::running = true; void Application::stop() @@ -135,10 +138,14 @@ void Application::setup() boost::thread t1(boost::bind(&InitDB, &mRpcDB, "rpc.db", RpcDBInit, RpcDBCount)); boost::thread t2(boost::bind(&InitDB, &mTxnDB, "transaction.db", TxnDBInit, TxnDBCount)); boost::thread t3(boost::bind(&InitDB, &mLedgerDB, "ledger.db", LedgerDBInit, LedgerDBCount)); + t1.join(); t2.join(); t3.join(); + boost::thread t4(boost::bind(&InitDB, &mWalletDB, "wallet.db", WalletDBInit, WalletDBCount)); boost::thread t5(boost::bind(&InitDB, &mHashNodeDB, "hashnode.db", HashNodeDBInit, HashNodeDBCount)); boost::thread t6(boost::bind(&InitDB, &mNetNodeDB, "netnode.db", NetNodeDBInit, NetNodeDBCount)); - t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); t6.join(); + boost::thread t7(boost::bind(&InitDB, &mPathFindDB, "pathfind.db", PathFindDBInit, PathFindDBCount)); + t4.join(); t5.join(); t6.join(); t7.join(); + mTxnDB->getDB()->setupCheckpointing(&mJobQueue); mLedgerDB->getDB()->setupCheckpointing(&mJobQueue); mHashNodeDB->getDB()->setupCheckpointing(&mJobQueue); @@ -340,6 +347,7 @@ Application::~Application() delete mWalletDB; delete mHashNodeDB; delete mNetNodeDB; + delete mPathFindDB; } void Application::startNewLedger() diff --git a/src/cpp/ripple/Application.h b/src/cpp/ripple/Application.h index ad09c620d7..6318e4972d 100644 --- a/src/cpp/ripple/Application.h +++ b/src/cpp/ripple/Application.h @@ -72,7 +72,7 @@ class Application TXQueue mTxnQueue; OrderBookDB mOrderBookDB; - DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB; + DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB, *mPathFindDB; ConnectionPool mConnectionPool; PeerDoor* mPeerDoor; @@ -139,6 +139,7 @@ public: DatabaseCon* getWalletDB() { return mWalletDB; } DatabaseCon* getHashNodeDB() { return mHashNodeDB; } DatabaseCon* getNetNodeDB() { return mNetNodeDB; } + DatabaseCon* getPathFindDB() { return mPathFindDB; } uint256 getNonce256() { return mNonce256; } std::size_t getNonceST() { return mNonceST; } diff --git a/src/cpp/ripple/DBInit.cpp b/src/cpp/ripple/DBInit.cpp index 6ff9c0de72..1f840613ef 100644 --- a/src/cpp/ripple/DBInit.cpp +++ b/src/cpp/ripple/DBInit.cpp @@ -294,7 +294,38 @@ const char *NetNodeDBInit[] = { );" }; - int NetNodeDBCount = NUMBER(NetNodeDBInit); +const char *PathFindDBInit[] = { + "PRAGMA synchronous = OFF; ", + + "DROP TABLE TrustLines; ", + + "CREATE TABLE TrustLines { " + "To CHARACTER(40), " // Hex of account trusted + "By CHARACTER(40), " // Hex of account trusting + "Currency CHARACTER(80), " // Hex currency, hex issuer + "Use INTEGER, " // Use count + "Seq BIGINT UNSIGNED " // Sequence when use count was updated + "}; ", + + "CREATE INDEX TLBy ON TrustLines(By, Currency, Use);", + "CREATE INDEX TLTo ON TrustLines(To, Currency, Use);", + + "DROP TABLE Exchanges;", + + "CREATE TABLE Exchanges { " + "From CHARACTER(80), " + "To CHARACTER(80), " + "Currency CHARACTER(80), " + "Use INTEGER, " + "Seq BIGINT UNSIGNED " + "}; ", + + "CREATE INDEX ExBy ON Exchanges(By, Currency, Use);", + "CREATE INDEX ExTo ON Exchanges(To, Currency, Use);", +}; + +int PathFindDBCount = NUMBER(PathFindDBInit); + // vim:ts=4 diff --git a/src/cpp/ripple/OrderBookDB.cpp b/src/cpp/ripple/OrderBookDB.cpp index 8e76b75791..4ebac57497 100644 --- a/src/cpp/ripple/OrderBookDB.cpp +++ b/src/cpp/ripple/OrderBookDB.cpp @@ -17,7 +17,6 @@ void OrderBookDB::invalidate() mSeq = 0; } -// TODO: this would be way faster if we could just look under the order dirs void OrderBookDB::setup(Ledger::ref ledger) { boost::unordered_set mSeen; @@ -30,14 +29,13 @@ void OrderBookDB::setup(Ledger::ref ledger) LoadEvent::autoptr ev = theApp->getJobQueue().getLoadEventAP(jtOB_SETUP, "OrderBookDB::setup"); - mXRPOrders.clear(); - mIssuerMap.clear(); - - // walk through the entire ledger looking for orderbook entries - uint256 currentIndex = ledger->getFirstLedgerIndex(); + mDestMap.clear(); + mSourceMap.clear(); cLog(lsDEBUG) << "OrderBookDB>"; + // walk through the entire ledger looking for orderbook entries + uint256 currentIndex = ledger->getFirstLedgerIndex(); while (currentIndex.isNonZero()) { SLE::pointer entry = ledger->getSLEi(currentIndex); @@ -55,10 +53,8 @@ void OrderBookDB::setup(Ledger::ref ledger) OrderBook::pointer book = boost::make_shared(boost::cref(index), boost::cref(ci), boost::cref(co), boost::cref(ii), boost::cref(io)); - if (!book->getCurrencyIn()) // XRP - mXRPOrders.push_back(book); - else - mIssuerMap[book->getIssuerIn()].push_back(book); + mSourceMap[currencyIssuer_ct(ci, ii)].push_back(book); + mDestMap[currencyIssuer_ct(co, io)].push_back(book); } } @@ -68,29 +64,30 @@ void OrderBookDB::setup(Ledger::ref ledger) cLog(lsDEBUG) << "OrderBookDB<"; } -// return list of all orderbooks that want IssuerID -std::vector& OrderBookDB::getBooks(const uint160& issuerID) +// return list of all orderbooks that want this issuerID and currencyID +void OrderBookDB::getBooksByTakerPays(const uint160& issuerID, const uint160& currencyID, + std::vector& bookRet) { boost::recursive_mutex::scoped_lock sl(mLock); - boost::unordered_map< uint160, std::vector >::iterator it = mIssuerMap.find(issuerID); - return (it == mIssuerMap.end()) - ? mEmptyVector - : it->second; + boost::unordered_map< currencyIssuer_t, std::vector >::const_iterator + it = mSourceMap.find(currencyIssuer_ct(currencyID, issuerID)); + if (it != mSourceMap.end()) + bookRet = it->second; + else + bookRet.clear(); } -// return list of all orderbooks that want this issuerID and currencyID -void OrderBookDB::getBooks(const uint160& issuerID, const uint160& currencyID, std::vector& bookRet) +// return list of all orderbooks that give this issuerID and currencyID +void OrderBookDB::getBooksByTakerGets(const uint160& issuerID, const uint160& currencyID, + std::vector& bookRet) { boost::recursive_mutex::scoped_lock sl(mLock); - boost::unordered_map< uint160, std::vector >::iterator it = mIssuerMap.find(issuerID); - if (it != mIssuerMap.end()) - { - BOOST_FOREACH(OrderBook::ref book, it->second) - { - if (book->getCurrencyIn() == currencyID) - bookRet.push_back(book); - } - } + boost::unordered_map< currencyIssuer_t, std::vector >::const_iterator + it = mDestMap.find(currencyIssuer_ct(currencyID, issuerID)); + if (it != mDestMap.end()) + bookRet = it->second; + else + bookRet.clear(); } BookListeners::pointer OrderBookDB::makeBookListeners(const uint160& currencyPays, const uint160& currencyGets, @@ -133,53 +130,6 @@ BookListeners::pointer OrderBookDB::getBookListeners(const uint160& currencyPays return (*it3).second; } -/* -"CreatedNode" : { -"LedgerEntryType" : "Offer", -"LedgerIndex" : "F353BF8A7DCE35EA2985596F4C8421E30EF3B9A21618566BFE0ED00B62A8A5AB", -"NewFields" : { -"Account" : "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY", -"BookDirectory" : "FF26BE244767D0EA9EFD523941439009E4185E4CBB918F714C08E1BC9BF04000", -"Sequence" : 112, -"TakerGets" : "400000000", -"TakerPays" : { -"currency" : "BTC", -"issuer" : "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV", -"value" : "1" -} -} -} - -"ModifiedNode" : { -"FinalFields" : { -"Account" : "rHTxKLzRbniScyQFGMb3NodmxA848W8dKM", -"BookDirectory" : "407AF8FFDE71114B1981574FDDA9B0334572D56FC579735B4B0BD7A625405555", -"BookNode" : "0000000000000000", -"Flags" : 0, -"OwnerNode" : "0000000000000000", -"Sequence" : 32, -"TakerGets" : "149900000000", -"TakerPays" : { -"currency" : "USD", -"issuer" : "r9vbV3EHvXWjSkeQ6CAcYVPGeq7TuiXY2X", -"value" : "49.96666666666667" -} -}, -"LedgerEntryType" : "Offer", -"LedgerIndex" : "C60F8CC514208FA5F7BD03CF1B64B38B7183CD52318FCBBD3726350D4FE693B0", -"PreviousFields" : { -"TakerGets" : "150000000000", -"TakerPays" : { -"currency" : "USD", -"issuer" : "r9vbV3EHvXWjSkeQ6CAcYVPGeq7TuiXY2X", -"value" : "50" -} -}, -"PreviousTxnID" : "1A6AAE3F1AC5A8A7554A5ABC395D17FED5BF62CD90181AA8E4315EDFED4EDEB3", -"PreviousTxnLgrSeq" : 140734 -} - -*/ // Based on the meta, send the meta to the streams that are listening // We need to determine which streams a given meta effects void OrderBookDB::processTxn(Ledger::ref ledger, const ALTransaction& alTx, Json::Value& jvObj) diff --git a/src/cpp/ripple/OrderBookDB.h b/src/cpp/ripple/OrderBookDB.h index 15ebd24d04..e90f377685 100644 --- a/src/cpp/ripple/OrderBookDB.h +++ b/src/cpp/ripple/OrderBookDB.h @@ -15,6 +15,9 @@ // But, for now it is probably faster to just generate it each time. // +typedef std::pair currencyIssuer_t; +typedef std::pair currencyIssuer_ct; + class BookListeners { boost::unordered_map mListeners; @@ -30,10 +33,8 @@ public: class OrderBookDB { - std::vector mEmptyVector; - std::vector mXRPOrders; - boost::unordered_map > mIssuerMap; - //std::vector mAllOrderBooks; + boost::unordered_map< currencyIssuer_t, std::vector > mSourceMap; // by ci/ii + boost::unordered_map< currencyIssuer_t, std::vector > mDestMap; // by co/io // issuerPays, issuerGets, currencyPays, currencyGets std::map > > > mListeners; @@ -46,18 +47,11 @@ public: void setup(Ledger::ref ledger); void invalidate(); - // return list of all orderbooks that want XRP - std::vector& getXRPInBooks(){ return mXRPOrders; } - - // return list of all orderbooks that want IssuerID - std::vector& getBooks(const uint160& issuerID); - // return list of all orderbooks that want this issuerID and currencyID - void getBooks(const uint160& issuerID, const uint160& currencyID, std::vector& bookRet); - - // returns the best rate we can find - float getPrice(uint160& currencyPays,uint160& currencyGets); - + void getBooksByTakerPays(const uint160& issuerID, const uint160& currencyID, + std::vector& bookRet); + void getBooksByTakerGets(const uint160& issuerID, const uint160& currencyID, + std::vector& bookRet); BookListeners::pointer getBookListeners(const uint160& currencyPays, const uint160& currencyGets, const uint160& issuerPays, const uint160& issuerGets); @@ -66,7 +60,6 @@ public: // see if this txn effects any orderbook void processTxn(Ledger::ref ledger, const ALTransaction& alTx, Json::Value& jvObj); - }; #endif diff --git a/src/cpp/ripple/PathDB.cpp b/src/cpp/ripple/PathDB.cpp new file mode 100644 index 0000000000..4c9f8a47df --- /dev/null +++ b/src/cpp/ripple/PathDB.cpp @@ -0,0 +1 @@ +#include "PathDB.h" diff --git a/src/cpp/ripple/PathDB.h b/src/cpp/ripple/PathDB.h new file mode 100644 index 0000000000..138e78bf66 --- /dev/null +++ b/src/cpp/ripple/PathDB.h @@ -0,0 +1,78 @@ +#ifndef PATHDB__H +#define PATHBD__H + +#include "uint256.h" +#include "TaggedCache.h" + +typedef std::pair currencyIssuer_t; +typedef std::pair currencyIssuer_ct; + +class PathDBEntry +{ +public: + typedef boost::shared_ptr pointer; + typedef const pointer& ref; + + const unsigned int sIsExchange = 0x00001; + const unsigned int sIsOffer = 0x00002; + const unsigned int sIsDirty = 0x10000; + +protected: + currencyIssuer_t mIn; + currencyIssuer_t mOut; + uint32 mLastSeq; + int mUseCount; + unsigned mFlags; + std::size_t mHash; + +public: + + void updateSeq(uint32); + + const uint160& getCurrencyIn() const { return mIn.first; } + const uint160& getIssuerIn() const { return mIn.second; } + const uint160& getCurrencyOut() const { return mOut.first; } + const uint160& getIssuerOut() const { return mOut.second; } + + bool isExchange() const; + bool isOffer() const; + bool isDirty() const; +}; + +class PathDB +{ +protected: + boost::recursive_mutex mLock; + TaggedCache mFromCache; + TaggedCache mToCache; + std::set mDirtyPaths; + +public: + + PathDB(); + + std::vector getPathsFrom(const uint160& currency, const uint160& issuer, + int maxBestPaths = 10, int maxRandPaths = 10); + + std::vector getPathsTo(const uint160& currency, const uint160& issuer, + int maxBestPaths = 10, int maxRandPaths = 10); + + void usedLine(const uint160& currency, const uint160& accountIn, const uint160& accountOut); + void usedExchange(const uint160& currencyFrom, const uint160& issuerFrom, + const uint160& currencyTo, const uint160& issuerTo); +}; + +extern std::size_t hash_value(const currencyIssuer_ct& ci) +{ + std::size_t r = hash_value(ci.second); + return ci.first.hash_combine(r); +} + +static inline std::size_t hash_value(const currencyIssuer_t& ci) +{ + std::size_t r = hash_value(ci.second); + return ci.first.hash_combine(r); +} + + +#endif diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index 62e44c4f57..0a3a1b0400 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -389,7 +389,9 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax // XXX Might restrict the number of times bridging through XRP. // Cursor is for XRP, continue with qualifying books: XRP -> non-XRP - BOOST_FOREACH(OrderBook::ref book, theApp->getOrderBookDB().getXRPInBooks()) + std::vector xrpBooks; + theApp->getOrderBookDB().getBooksByTakerPays(ACCOUNT_XRP, CURRENCY_XRP, xrpBooks); + BOOST_FOREACH(OrderBook::ref book, xrpBooks) { // New end is an order book with the currency and issuer. @@ -504,11 +506,10 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax } } - // Every book that wants the source currency. - std::vector books; - // XXX Need to flip getBooks argument order to be in normal order: currency then issuer. - theApp->getOrderBookDB().getBooks(speEnd.mIssuerID, speEnd.mCurrencyID, books); + // XXX Flip argument order to norm. (currency, issuer) + std::vector books; + theApp->getOrderBookDB().getBooksByTakerPays(speEnd.mIssuerID, speEnd.mCurrencyID, books); BOOST_FOREACH(OrderBook::ref book, books) { @@ -718,7 +719,9 @@ void Pathfinder::addOptions(PathOption::pointer tail) { if (!tail->mCurrencyID) { // source XRP - BOOST_FOREACH(OrderBook::ref book, theApp->getOrderBookDB().getXRPInBooks()) + std::vector xrpBooks; + theApp->getOrderBookDB().getBooksByTakerPays(ISSUER_XRP, CURRENCY_XRP, xrpBooks); + BOOST_FOREACH(OrderBook::ref book, xrpBooks) { PathOption::pointer pathOption(new PathOption(tail)); @@ -750,11 +753,10 @@ void Pathfinder::addOptions(PathOption::pointer tail) } } - // every offer that wants the source currency + // Every offer that can take this currency in std::vector books; - theApp->getOrderBookDB().getBooks(tail->mCurrentAccount, tail->mCurrencyID, books); - - BOOST_FOREACH(OrderBook::ref book,books) + theApp->getOrderBookDB().getBooksByTakerPays(tail->mCurrentAccount, tail->mCurrencyID, books); + BOOST_FOREACH(OrderBook::ref book, books) { PathOption::pointer pathOption(new PathOption(tail));