diff --git a/src/cpp/ripple/AccountItems.cpp b/src/cpp/ripple/AccountItems.cpp index 1953ff22d9..1cca67ee9e 100644 --- a/src/cpp/ripple/AccountItems.cpp +++ b/src/cpp/ripple/AccountItems.cpp @@ -24,20 +24,18 @@ void AccountItems::fillItems(const uint160& accountID, Ledger::ref ledger) uint256 rootIndex = Ledger::getOwnerDirIndex(accountID); uint256 currentIndex = rootIndex; - LedgerStateParms lspNode = lepNONE; - while (1) { - SLE::pointer ownerDir = ledger->getDirNode(lspNode, currentIndex); + SLE::pointer ownerDir = ledger->getDirNode(currentIndex); if (!ownerDir) return; STVector256 svOwnerNodes = ownerDir->getFieldV256(sfIndexes); BOOST_FOREACH(uint256& uNode, svOwnerNodes.peekValue()) { - SLE::pointer sleCur = ledger->getSLE(uNode); + SLE::pointer sleCur = ledger->getSLEi(uNode); - AccountItem::pointer item=mOfType->makeItem(accountID, sleCur); + AccountItem::pointer item = mOfType->makeItem(accountID, sleCur); if(item) { mItems.push_back(item); diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 20235bc65b..3081fb3743 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -41,7 +41,7 @@ DatabaseCon::~DatabaseCon() Application::Application() : mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mLedgerMaster), - mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), + mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), mSLECache("LedgerEntryCache", 4096, 120), mSNTPClient(mAuxService), mRPCHandler(&mNetOps), mFeeTrack(), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL), mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL), @@ -301,6 +301,7 @@ void Application::sweep() mTempNodeCache.sweep(); mValidations.sweep(); getMasterLedgerAcquire().sweep(); + mSLECache.sweep(); mSweepTimer.expires_from_now(boost::posix_time::seconds(theConfig.getSize(siSweepInterval))); mSweepTimer.async_wait(boost::bind(&Application::sweep, this)); } diff --git a/src/cpp/ripple/Application.h b/src/cpp/ripple/Application.h index 343c9d26c2..b8ef316499 100644 --- a/src/cpp/ripple/Application.h +++ b/src/cpp/ripple/Application.h @@ -29,6 +29,7 @@ class RPCDoor; class PeerDoor; typedef TaggedCache< uint256, std::vector > NodeCache; +typedef TaggedCache< uint256, SLE > SLECache; class DatabaseCon { @@ -60,6 +61,7 @@ class Application ValidationCollection mValidations; SuppressionTable mSuppressions; HashedObjectStore mHashedObjectStore; + SLECache mSLECache; SNTPClient mSNTPClient; JobQueue mJobQueue; RPCHandler mRPCHandler; @@ -118,6 +120,7 @@ public: TXQueue& getTxnQueue() { return mTxnQueue; } PeerDoor& getPeerDoor() { return *mPeerDoor; } OrderBookDB& getOrderBookDB() { return mOrderBookDB; } + SLECache& getSLECache() { return mSLECache; } bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); } diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp index 6f9772f849..169cb4236e 100644 --- a/src/cpp/ripple/Config.cpp +++ b/src/cpp/ripple/Config.cpp @@ -483,7 +483,7 @@ int Config::getSize(SizedItemName item) { SizedItem sizeTable[] = { { siSweepInterval, { 10, 30, 60, 90, 90 } }, - { siLedgerFetch, { 2, 4, 5, 6, 6 } }, + { siLedgerFetch, { 2, 2, 3, 4, 5 } }, { siValidationsSize, { 256, 256, 512, 1024, 1024 } }, { siValidationsAge, { 500, 500, 500, 500, 500 } }, { siNodeCacheSize, { 8192, 32768, 131072, 1048576, 0 } }, diff --git a/src/cpp/ripple/ConnectionPool.cpp b/src/cpp/ripple/ConnectionPool.cpp index ae727e3329..7bbfc9e158 100644 --- a/src/cpp/ripple/ConnectionPool.cpp +++ b/src/cpp/ripple/ConnectionPool.cpp @@ -98,7 +98,7 @@ bool ConnectionPool::savePeer(const std::string& strIp, int iPort, char code) Peer::pointer ConnectionPool::getPeerById(const uint64& id) { - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); const boost::unordered_map::iterator& it = mPeerIdMap.find(id); if (it == mPeerIdMap.end()) return Peer::pointer(); @@ -107,7 +107,7 @@ Peer::pointer ConnectionPool::getPeerById(const uint64& id) bool ConnectionPool::hasPeer(const uint64& id) { - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); return mPeerIdMap.find(id) != mPeerIdMap.end(); } @@ -122,7 +122,7 @@ bool ConnectionPool::peerAvailable(std::string& strIp, int& iPort) // Convert mIpMap (list of open connections) to a vector of " ". { - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); vstrIpPort.reserve(mIpMap.size()); @@ -235,7 +235,7 @@ void ConnectionPool::policyHandler(const boost::system::error_code& ecResult) int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg) { int sentTo = 0; - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); BOOST_FOREACH(const vtConMap& pair, mConnectedMap) { @@ -254,7 +254,7 @@ int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& m void ConnectionPool::relayMessageBut(const std::set& fromPeers, const PackedMessage::pointer& msg) { // Relay message to all but the specified peers - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); BOOST_FOREACH(const vtConMap& pair, mConnectedMap) { @@ -267,7 +267,7 @@ void ConnectionPool::relayMessageBut(const std::set& fromPeers, const Pa void ConnectionPool::relayMessageTo(const std::set& fromPeers, const PackedMessage::pointer& msg) { // Relay message to the specified peers - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); BOOST_FOREACH(const uint64& peerID, fromPeers) { @@ -305,34 +305,22 @@ Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort) ipPort pipPeer = make_pair(strIp, iPort); Peer::pointer ppResult; - boost::unordered_map::iterator it; { - boost::mutex::scoped_lock sl(mPeerLock); - - if ((it = mIpMap.find(pipPeer)) == mIpMap.end()) + boost::recursive_mutex::scoped_lock sl(mPeerLock); + if (mIpMap.find(pipPeer) == mIpMap.end()) { - Peer::pointer ppNew(Peer::create(theApp->getIOService(), theApp->getPeerDoor().getSSLContext(), - ++mLastPeer, false)); + ppResult = Peer::create(theApp->getIOService(), theApp->getPeerDoor().getSSLContext(), + ++mLastPeer, false); - // Did not find it. Not already connecting or connected. - ppNew->connect(strIp, iPort); - - mIpMap[pipPeer] = ppNew; - - ppResult = ppNew; + mIpMap[pipPeer] = ppResult; // ++miConnectStarting; } - else - { - // Found it. Already connected. - - nothing(); - } } if (ppResult) { + ppResult->connect(strIp, iPort); //cLog(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort; } else @@ -359,7 +347,7 @@ Json::Value ConnectionPool::getPeersJson() int ConnectionPool::getPeerCount() { - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); return mConnectedMap.size(); } @@ -368,7 +356,7 @@ std::vector ConnectionPool::getPeerVector() { std::vector ret; - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); ret.reserve(mConnectedMap.size()); @@ -383,7 +371,7 @@ std::vector ConnectionPool::getPeerVector() uint64 ConnectionPool::assignPeerId() { - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); return ++mLastPeer; } @@ -402,7 +390,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer, } else { - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); const boost::unordered_map::iterator& itCm = mConnectedMap.find(naPeer); if (itCm == mConnectedMap.end()) @@ -453,7 +441,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer, // We maintain a map of public key to peer for connected and verified peers. Maintain it. void ConnectionPool::peerDisconnected(Peer::ref peer, const RippleAddress& naPeer) { - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); if (naPeer.isValid()) { @@ -556,7 +544,7 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP // Determine if closed peer was redundant. bool bRedundant = true; { - boost::mutex::scoped_lock sl(mPeerLock); + boost::recursive_mutex::scoped_lock sl(mPeerLock); const boost::unordered_map::iterator& itIp = mIpMap.find(ipPeer); if (itIp == mIpMap.end()) diff --git a/src/cpp/ripple/ConnectionPool.h b/src/cpp/ripple/ConnectionPool.h index 3cdc773178..3d1e3d3a4d 100644 --- a/src/cpp/ripple/ConnectionPool.h +++ b/src/cpp/ripple/ConnectionPool.h @@ -16,8 +16,8 @@ class ConnectionPool { private: - boost::mutex mPeerLock; - uint64 mLastPeer; + boost::recursive_mutex mPeerLock; + uint64 mLastPeer; typedef std::pair naPeer; typedef std::pair pipPeer; diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index 3f3d0a3e2d..cc2aadb40b 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -23,7 +23,7 @@ JobQueue::JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false) mJobLoads[jtTRANSACTION_l].setTargetLatency(100, 500); mJobLoads[jtPROPOSAL_t].setTargetLatency(100, 500); - mJobLoads[jtCLIENT].setTargetLatency(250, 1000); + mJobLoads[jtCLIENT].setTargetLatency(2000, 5000); mJobLoads[jtPEER].setTargetLatency(200, 1250); mJobLoads[jtDISK].setTargetLatency(500, 1000); mJobLoads[jtRPC].setTargetLatency(250, 750); diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index e0f9589ebc..f0c35a8d36 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -200,8 +200,8 @@ AccountState::pointer Ledger::getAccountState(const RippleAddress& accountID) // std::cerr << "Ledger:getAccountState(" << accountID.humanAccountID() << ")" << std::endl; #endif - SHAMapItem::pointer item = mAccountStateMap->peekItem(Ledger::getAccountRootIndex(accountID)); - if (!item) + SLE::pointer sle = getSLEi(Ledger::getAccountRootIndex(accountID)); + if (!sle) { cLog(lsDEBUG) << boost::str(boost::format("Ledger:getAccountState: not found: %s: %s") % accountID.humanAccountID() @@ -210,8 +210,6 @@ AccountState::pointer Ledger::getAccountState(const RippleAddress& accountID) return AccountState::pointer(); } - SerializedLedgerEntry::pointer sle = - boost::make_shared(item->peekSerializer(), item->getTag()); if (sle->getType() != ltACCOUNT_ROOT) return AccountState::pointer(); @@ -395,7 +393,7 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) static boost::format transExists("SELECT Status FROM Transactions WHERE TransID = '%s';"); static boost::format updateTx("UPDATE Transactions SET LedgerSeq = %d, Status = '%c', TxnMeta = %s WHERE TransID = '%s';"); - static boost::format addLedger("INSERT INTO Ledgers " + static boost::format addLedger("INSERT OR REPLACE INTO Ledgers " "(LedgerHash,LedgerSeq,PrevHash,TotalCoins,ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags," "AccountSetHash,TransSetHash) VALUES ('%s','%u','%s','%s','%u','%u','%d','%u','%s','%s');"); @@ -819,6 +817,7 @@ Json::Value Ledger::getJson(int options) { Json::Value txns(Json::arrayValue); SHAMapTreeNode::TNType type; + ScopedLock l(mTransactionMap->Lock()); for (SHAMapItem::pointer item = mTransactionMap->peekFirstItem(type); !!item; item = mTransactionMap->peekNextItem(item->getTag(), type)) { @@ -858,6 +857,7 @@ Json::Value Ledger::getJson(int options) if (mAccountStateMap && (bFull || ((options & LEDGER_JSON_DUMP_STATE) != 0))) { Json::Value state(Json::arrayValue); + ScopedLock l(mAccountStateMap->Lock()); for (SHAMapItem::pointer item = mAccountStateMap->peekFirstItem(); !!item; item = mAccountStateMap->peekNextItem(item->getTag())) { @@ -954,6 +954,26 @@ SLE::pointer Ledger::getSLE(const uint256& uHash) return boost::make_shared(node->peekSerializer(), node->getTag()); } +SLE::pointer Ledger::getSLEi(const uint256& uId) +{ + uint256 hash; + + ScopedLock sl(mAccountStateMap->Lock()); + + SHAMapItem::pointer node = mAccountStateMap->peekItem(uId, hash); + if (!node) + return SLE::pointer(); + + SLE::pointer ret = theApp->getSLECache().fetch(hash); + if (!ret) + { + ret = boost::make_shared(node->peekSerializer(), node->getTag()); + ret->setImmutable(); + theApp->getSLECache().canonicalize(hash, ret); + } + return ret; +} + uint256 Ledger::getFirstLedgerIndex() { SHAMapItem::pointer node = mAccountStateMap->peekFirstItem(); @@ -994,6 +1014,14 @@ uint256 Ledger::getPrevLedgerIndex(const uint256& uHash, const uint256& uBegin) return node->getTag(); } +SLE::pointer Ledger::getASNodeI(const uint256& nodeID, LedgerEntryType let) +{ + SLE::pointer node = getSLEi(nodeID); + if (node && (node->getType() != let)) + node.reset(); + return node; +} + SLE::pointer Ledger::getASNode(LedgerStateParms& parms, const uint256& nodeID, LedgerEntryType let ) { @@ -1030,43 +1058,39 @@ SLE::pointer Ledger::getASNode(LedgerStateParms& parms, const uint256& nodeID, SLE::pointer Ledger::getAccountRoot(const uint160& accountID) { - LedgerStateParms qry = lepNONE; - - return getASNode(qry, getAccountRootIndex(accountID), ltACCOUNT_ROOT); + return getASNodeI(getAccountRootIndex(accountID), ltACCOUNT_ROOT); } SLE::pointer Ledger::getAccountRoot(const RippleAddress& naAccountID) { - LedgerStateParms qry = lepNONE; - - return getASNode(qry, getAccountRootIndex(naAccountID.getAccountID()), ltACCOUNT_ROOT); + return getASNodeI(getAccountRootIndex(naAccountID.getAccountID()), ltACCOUNT_ROOT); } // // Directory // -SLE::pointer Ledger::getDirNode(LedgerStateParms& parms, const uint256& uNodeIndex) +SLE::pointer Ledger::getDirNode(const uint256& uNodeIndex) { - return getASNode(parms, uNodeIndex, ltDIR_NODE); + return getASNodeI(uNodeIndex, ltDIR_NODE); } // // Generator Map // -SLE::pointer Ledger::getGenerator(LedgerStateParms& parms, const uint160& uGeneratorID) +SLE::pointer Ledger::getGenerator(const uint160& uGeneratorID) { - return getASNode(parms, getGeneratorIndex(uGeneratorID), ltGENERATOR_MAP); + return getASNodeI(getGeneratorIndex(uGeneratorID), ltGENERATOR_MAP); } // // Nickname // -SLE::pointer Ledger::getNickname(LedgerStateParms& parms, const uint256& uNickname) +SLE::pointer Ledger::getNickname(const uint256& uNickname) { - return getASNode(parms, uNickname, ltNICKNAME); + return getASNodeI(uNickname, ltNICKNAME); } // @@ -1074,18 +1098,18 @@ SLE::pointer Ledger::getNickname(LedgerStateParms& parms, const uint256& uNickna // -SLE::pointer Ledger::getOffer(LedgerStateParms& parms, const uint256& uIndex) +SLE::pointer Ledger::getOffer(const uint256& uIndex) { - return getASNode(parms, uIndex, ltOFFER); + return getASNodeI(uIndex, ltOFFER); } // // Ripple State // -SLE::pointer Ledger::getRippleState(LedgerStateParms& parms, const uint256& uNode) +SLE::pointer Ledger::getRippleState(const uint256& uNode) { - return getASNode(parms, uNode, ltRIPPLE_STATE); + return getASNodeI(uNode, ltRIPPLE_STATE); } // For an entry put in the 64 bit index or quality. @@ -1179,7 +1203,7 @@ uint256 Ledger::getLedgerHash(uint32 ledgerIndex) int diff = mLedgerSeq - ledgerIndex; if (diff <= 256) { - SLE::pointer hashIndex = getSLE(getLedgerHashIndex()); + SLE::pointer hashIndex = getSLEi(getLedgerHashIndex()); if (hashIndex) { assert(hashIndex->getFieldU32(sfLastLedgerSequence) == (mLedgerSeq - 1)); @@ -1199,7 +1223,7 @@ uint256 Ledger::getLedgerHash(uint32 ledgerIndex) } // in skiplist - SLE::pointer hashIndex = getSLE(getLedgerHashIndex(ledgerIndex)); + SLE::pointer hashIndex = getSLEi(getLedgerHashIndex(ledgerIndex)); if (hashIndex) { int lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence); @@ -1219,7 +1243,7 @@ uint256 Ledger::getLedgerHash(uint32 ledgerIndex) std::vector< std::pair > Ledger::getLedgerHashes() { std::vector< std::pair > ret; - SLE::pointer hashIndex = getSLE(getLedgerHashIndex()); + SLE::pointer hashIndex = getSLEi(getLedgerHashIndex()); if (hashIndex) { STVector256 vec = hashIndex->getFieldV256(sfHashes); diff --git a/src/cpp/ripple/Ledger.h b/src/cpp/ripple/Ledger.h index 20da8705f6..563921d2b2 100644 --- a/src/cpp/ripple/Ledger.h +++ b/src/cpp/ripple/Ledger.h @@ -97,6 +97,9 @@ private: protected: SLE::pointer getASNode(LedgerStateParms& parms, const uint256& nodeID, LedgerEntryType let); + // returned SLE is immutable + SLE::pointer getASNodeI(const uint256& nodeID, LedgerEntryType let); + static void incPendingSaves(); static void decPendingSaves(); void saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer); @@ -201,7 +204,8 @@ public: void pendSave(bool fromConsensus); // next/prev function - SLE::pointer getSLE(const uint256& uHash); + SLE::pointer getSLE(const uint256& uHash); // SLE is mutable + SLE::pointer getSLEi(const uint256& uHash); // SLE is immutable uint256 getFirstLedgerIndex(); uint256 getLastLedgerIndex(); uint256 getNextLedgerIndex(const uint256& uHash); // first node >hash @@ -230,7 +234,7 @@ public: // Generator Map functions // - SLE::pointer getGenerator(LedgerStateParms& parms, const uint160& uGeneratorID); + SLE::pointer getGenerator(const uint160& uGeneratorID); static uint256 getGeneratorIndex(const uint160& uGeneratorID); @@ -245,9 +249,8 @@ public: NicknameState::pointer getNicknameState(const std::string& strNickname) { return getNicknameState(getNicknameHash(strNickname)); } - SLE::pointer getNickname(LedgerStateParms& parms, const uint256& uNickname); - SLE::pointer getNickname(LedgerStateParms& parms, const std::string& strNickname) - { return getNickname(parms, getNicknameHash(strNickname)); } + SLE::pointer getNickname(const uint256& uNickname); + SLE::pointer getNickname(const std::string& strNickname) { return getNickname(getNicknameHash(strNickname)); } static uint256 getNicknameIndex(const uint256& uNickname); @@ -263,16 +266,10 @@ public: // Offer functions // - SLE::pointer getOffer(LedgerStateParms& parms, const uint256& uIndex); + SLE::pointer getOffer(const uint256& uIndex); - SLE::pointer getOffer(const uint256& uIndex) - { - LedgerStateParms qry = lepNONE; - return getOffer(qry, uIndex); - } - - SLE::pointer getOffer(LedgerStateParms& parms, const uint160& uAccountID, uint32 uSequence) - { return getOffer(parms, getOfferIndex(uAccountID, uSequence)); } + SLE::pointer getOffer(const uint160& uAccountID, uint32 uSequence) + { return getOffer(getOfferIndex(uAccountID, uSequence)); } // The index of an offer. static uint256 getOfferIndex(const uint160& uAccountID, uint32 uSequence); @@ -293,7 +290,7 @@ public: static void ownerDirDescriber(SLE::ref, const uint160& owner); // Return a node: root or normal - SLE::pointer getDirNode(LedgerStateParms& parms, const uint256& uNodeIndex); + SLE::pointer getDirNode(const uint256& uNodeIndex); // // Quality @@ -316,13 +313,7 @@ public: static uint256 getRippleStateIndex(const uint160& uiA, const uint160& uiB, const uint160& uCurrency) { return getRippleStateIndex(RippleAddress::createAccountID(uiA), RippleAddress::createAccountID(uiB), uCurrency); } - SLE::pointer getRippleState(LedgerStateParms& parms, const uint256& uNode); - - SLE::pointer getRippleState(const uint256& uNode) - { - LedgerStateParms qry = lepNONE; - return getRippleState(qry, uNode); - } + SLE::pointer getRippleState(const uint256& uNode); SLE::pointer getRippleState(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency) { return getRippleState(getRippleStateIndex(naA, naB, uCurrency)); } diff --git a/src/cpp/ripple/LedgerAcquire.cpp b/src/cpp/ripple/LedgerAcquire.cpp index 84b3a5a85e..5d1e7c96e2 100644 --- a/src/cpp/ripple/LedgerAcquire.cpp +++ b/src/cpp/ripple/LedgerAcquire.cpp @@ -71,6 +71,8 @@ void PeerSet::TimerEntry(boost::weak_ptr wptr, const boost::system::err { if (result == boost::asio::error::operation_aborted) return; + + ScopedLock sl(theApp->getMasterLock()); boost::shared_ptr ptr = wptr.lock(); if (ptr) ptr->invokeOnTimer(); @@ -920,15 +922,21 @@ void LedgerAcquireMaster::sweep() } } -int LedgerAcquireMaster::getFetchCount() +int LedgerAcquireMaster::getFetchCount(int& timeoutCount) { + timeoutCount = 0; int ret = 0; { typedef std::pair u256_acq_pair; boost::mutex::scoped_lock sl(mLock); BOOST_FOREACH(const u256_acq_pair& it, mLedgers) + { if (it.second->isActive()) + { ++ret; + timeoutCount += it.second->getTimeouts(); + } + } } return ret; } diff --git a/src/cpp/ripple/LedgerAcquire.h b/src/cpp/ripple/LedgerAcquire.h index b6d60f0f37..28c8434a02 100644 --- a/src/cpp/ripple/LedgerAcquire.h +++ b/src/cpp/ripple/LedgerAcquire.h @@ -149,7 +149,7 @@ public: void dropLedger(const uint256& ledgerHash); SMAddNode gotLedgerData(ripple::TMLedgerData& packet, Peer::ref); - int getFetchCount(); + int getFetchCount(int& timeoutCount); void logFailure(const uint256& h) { mRecentFailures.add(h); } bool isFailure(const uint256& h) { return mRecentFailures.isPresent(h, false); } diff --git a/src/cpp/ripple/LedgerEntrySet.cpp b/src/cpp/ripple/LedgerEntrySet.cpp index 43f0fc4515..860247768a 100644 --- a/src/cpp/ripple/LedgerEntrySet.cpp +++ b/src/cpp/ripple/LedgerEntrySet.cpp @@ -107,6 +107,7 @@ LedgerEntryAction LedgerEntrySet::hasEntry(const uint256& index) const void LedgerEntrySet::entryCache(SLE::ref sle) { + assert(sle->isMutable()); std::map::iterator it = mEntries.find(sle->getIndex()); if (it == mEntries.end()) { @@ -128,6 +129,7 @@ void LedgerEntrySet::entryCache(SLE::ref sle) void LedgerEntrySet::entryCreate(SLE::ref sle) { + assert(sle->isMutable()); std::map::iterator it = mEntries.find(sle->getIndex()); if (it == mEntries.end()) { @@ -161,6 +163,7 @@ void LedgerEntrySet::entryCreate(SLE::ref sle) void LedgerEntrySet::entryModify(SLE::ref sle) { + assert(sle->isMutable()); std::map::iterator it = mEntries.find(sle->getIndex()); if (it == mEntries.end()) { @@ -193,6 +196,7 @@ void LedgerEntrySet::entryModify(SLE::ref sle) void LedgerEntrySet::entryDelete(SLE::ref sle) { + assert(sle->isMutable()); std::map::iterator it = mEntries.find(sle->getIndex()); if (it == mEntries.end()) { @@ -399,7 +403,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result, uint32 index) if (type == &sfGeneric) continue; - SLE::pointer origNode = mLedger->getSLE(it.first); + SLE::pointer origNode = mLedger->getSLEi(it.first); SLE::pointer curNode = it.second.mEntry; if ((type == &sfModifiedNode) && (*curNode == *origNode)) diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index 5176d58795..fbcd2e9567 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -180,7 +180,10 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l } if (theApp->getMasterLedgerAcquire().isFailure(ledgerHash)) + { + cLog(lsTRACE) << "Already failed to acquire " << ledgerSeq; return false; + } mMissingLedger = theApp->getMasterLedgerAcquire().findCreate(ledgerHash); if (mMissingLedger->isComplete()) @@ -203,20 +206,28 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l theApp->getIOService().post(boost::bind(&LedgerMaster::missingAcquireComplete, this, mMissingLedger)); } - int fetch = theConfig.getSize(siLedgerFetch); - if (theApp->getMasterLedgerAcquire().getFetchCount() < fetch) - { - int count = 0; - typedef std::pair u_pair; + int fetchMax = theConfig.getSize(siLedgerFetch); + int timeoutCount; + int fetchCount = theApp->getMasterLedgerAcquire().getFetchCount(timeoutCount); - std::vector vec = origLedger->getLedgerHashes(); - BOOST_REVERSE_FOREACH(const u_pair& it, vec) + if (fetchCount < fetchMax) + { + if (timeoutCount > 2) { - if ((count < fetch) && (it.first < ledgerSeq) && - !mCompleteLedgers.hasValue(it.first) && !theApp->getMasterLedgerAcquire().find(it.second)) + cLog(lsDEBUG) << "Not acquiring due to timeouts"; + } + else + { + typedef std::pair u_pair; + std::vector vec = origLedger->getLedgerHashes(); + BOOST_REVERSE_FOREACH(const u_pair& it, vec) { - ++count; - theApp->getMasterLedgerAcquire().findCreate(it.second); + if ((fetchCount < fetchMax) && (it.first < ledgerSeq) && + !mCompleteLedgers.hasValue(it.first) && !theApp->getMasterLedgerAcquire().find(it.second)) + { + ++fetchCount; + theApp->getMasterLedgerAcquire().findCreate(it.second); + } } } } @@ -249,7 +260,7 @@ bool LedgerMaster::shouldAcquire(uint32 currentLedger, uint32 ledgerHistory, uin if (candidateLedger >= currentLedger) ret = true; else ret = (currentLedger - candidateLedger) <= ledgerHistory; - cLog(lsTRACE) << "Missing ledger " << candidateLedger << (ret ? " will" : " will NOT") << " be acquired"; + cLog(lsTRACE) << "Missing ledger " << candidateLedger << (ret ? " should" : " should NOT") << " be acquired"; return ret; } @@ -362,7 +373,10 @@ void LedgerMaster::setFullLedger(Ledger::ref ledger) if (!mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1)) { if (!shouldAcquire(mCurrentLedger->getLedgerSeq(), theConfig.LEDGER_HISTORY, ledger->getLedgerSeq() - 1)) + { + cLog(lsTRACE) << "Don't need any ledgers"; return; + } cLog(lsDEBUG) << "We need the ledger before the ledger we just accepted: " << ledger->getLedgerSeq() - 1; acquireMissingLedger(ledger, ledger->getParentHash(), ledger->getLedgerSeq() - 1); } @@ -387,6 +401,8 @@ void LedgerMaster::setFullLedger(Ledger::ref ledger) cLog(lsWARNING) << "We have a gap we can't fix: " << prevMissing + 1; } } + else + cLog(lsTRACE) << "Shouldn't acquire"; } } diff --git a/src/cpp/ripple/LoadManager.cpp b/src/cpp/ripple/LoadManager.cpp index 1a03a92bd2..a476c37e19 100644 --- a/src/cpp/ripple/LoadManager.cpp +++ b/src/cpp/ripple/LoadManager.cpp @@ -266,7 +266,7 @@ bool LoadFeeTrack::lowerLocalFee() boost::mutex::scoped_lock sl(mLock); uint32 origFee = mLocalTxnLoadFee; - mLocalTxnLoadFee -= (mLocalTxnLoadFee / lftFeeDecFraction ); // reduce by 1/16th + mLocalTxnLoadFee -= (mLocalTxnLoadFee / lftFeeDecFraction ); // reduce by 1/4 if (mLocalTxnLoadFee < lftNormalFee) mLocalTxnLoadFee = lftNormalFee; diff --git a/src/cpp/ripple/LoadManager.h b/src/cpp/ripple/LoadManager.h index 825e8f7605..b175ddfdd3 100644 --- a/src/cpp/ripple/LoadManager.h +++ b/src/cpp/ripple/LoadManager.h @@ -134,7 +134,7 @@ protected: static const int lftNormalFee = 256; // 256 is the minimum/normal load factor static const int lftFeeIncFraction = 16; // increase fee by 1/16 - static const int lftFeeDecFraction = 16; // decrease fee by 1/16 + static const int lftFeeDecFraction = 4; // decrease fee by 1/4 static const int lftFeeMax = lftNormalFee * 1000000; uint32 mLocalTxnLoadFee; // Scale factor, lftNormalFee = normal fee diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 4c798660bf..86110607e6 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -455,12 +455,9 @@ AccountState::pointer NetworkOPs::getAccountState(Ledger::ref lrLedger, const Ri SLE::pointer NetworkOPs::getGenerator(Ledger::ref lrLedger, const uint160& uGeneratorID) { - LedgerStateParms qry = lepNONE; - if (!lrLedger) return SLE::pointer(); - else - return lrLedger->getGenerator(qry, uGeneratorID); + return lrLedger->getGenerator(uGeneratorID); } // @@ -475,8 +472,7 @@ STVector256 NetworkOPs::getDirNodeInfo( uint64& uNodeNext) { STVector256 svIndexes; - LedgerStateParms lspNode = lepNONE; - SLE::pointer sleNode = lrLedger->getDirNode(lspNode, uNodeIndex); + SLE::pointer sleNode = lrLedger->getDirNode(uNodeIndex); if (sleNode) { @@ -524,8 +520,7 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre uint256 uRootIndex = lpLedger->getOwnerDirIndex(naAccount.getAccountID()); - LedgerStateParms lspNode = lepNONE; - SLE::pointer sleNode = lpLedger->getDirNode(lspNode, uRootIndex); + SLE::pointer sleNode = lpLedger->getDirNode(uRootIndex); if (sleNode) { @@ -538,7 +533,7 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre BOOST_FOREACH(const uint256& uDirEntry, vuiIndexes) { - SLE::pointer sleCur = lpLedger->getSLE(uDirEntry); + SLE::pointer sleCur = lpLedger->getSLEi(uDirEntry); switch (sleCur->getType()) { @@ -569,9 +564,7 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre uNodeDir = sleNode->getFieldU64(sfIndexNext); if (uNodeDir) { - lspNode = lepNONE; - sleNode = lpLedger->getDirNode(lspNode, Ledger::getDirNodeIndex(uRootIndex, uNodeDir)); - + sleNode = lpLedger->getDirNode(Ledger::getDirNodeIndex(uRootIndex, uNodeDir)); assert(sleNode); } } while (uNodeDir); @@ -1070,7 +1063,7 @@ std::vector< std::pair > std::string sql = str(boost::format("SELECT LedgerSeq,Status,RawTxn,TxnMeta FROM Transactions where TransID in (SELECT TransID from AccountTransactions " - " WHERE Account = '%s' AND LedgerSeq <= '%d' AND LedgerSeq >= '%d' LIMIT 1000) ORDER BY LedgerSeq;") + " WHERE Account = '%s' AND LedgerSeq <= '%d' AND LedgerSeq >= '%d' LIMIT 200) ORDER BY LedgerSeq DESC;") % account.humanAccountID() % maxLedger % minLedger); { @@ -1092,7 +1085,7 @@ std::vector< std::pair > }else rawMeta.resize(metaSize); TransactionMetaSet::pointer meta= boost::make_shared(txn->getID(), txn->getLedger(), rawMeta.getData()); - ret.push_back(std::pair(txn,meta)); + ret.push_back(std::pair(txn,meta)); } } @@ -1212,7 +1205,16 @@ Json::Value NetworkOPs::getServerInfo(bool human, bool admin) static_cast(Json::UInt(lpClosed->getReserve(0) * baseFee / baseRef)) / SYSTEM_CURRENCY_PARTS; l["reserve_inc_xrp"] = static_cast(Json::UInt(lpClosed->getReserveInc() * baseFee / baseRef)) / SYSTEM_CURRENCY_PARTS; - l["age"] = Json::UInt(getCloseTimeNC() - lpClosed->getCloseTimeNC()); + + uint32 closeTime = getCloseTimeNC(); + uint32 lCloseTime = lpClosed->getCloseTimeNC(); + + if (lCloseTime <= closeTime) + { + uint32 age = closeTime - lCloseTime; + if (age < 1000000) + l["age"] = Json::UInt(age); + } } info["closed_ledger"] = l; } diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 8940d55cb8..1c1ba558c0 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -165,6 +165,7 @@ public: void setLastValidation(SerializedValidation::ref v) { mLastValidation = v; } SLE::pointer getSLE(Ledger::pointer lpLedger, const uint256& uHash) { return lpLedger->getSLE(uHash); } + SLE::pointer getSLEi(Ledger::pointer lpLedger, const uint256& uHash) { return lpLedger->getSLEi(uHash); } // // Transaction operations diff --git a/src/cpp/ripple/OrderBookDB.cpp b/src/cpp/ripple/OrderBookDB.cpp index 7113abe243..64ff9804f9 100644 --- a/src/cpp/ripple/OrderBookDB.cpp +++ b/src/cpp/ripple/OrderBookDB.cpp @@ -39,7 +39,7 @@ void OrderBookDB::setup(Ledger::ref ledger) while (currentIndex.isNonZero()) { - SLE::pointer entry=ledger->getSLE(currentIndex); + SLE::pointer entry=ledger->getSLEi(currentIndex); OrderBook::pointer book = OrderBook::newOrderBook(entry); if (book) diff --git a/src/cpp/ripple/Peer.cpp b/src/cpp/ripple/Peer.cpp index 0d6068a47f..37e9bcb210 100644 --- a/src/cpp/ripple/Peer.cpp +++ b/src/cpp/ripple/Peer.cpp @@ -24,11 +24,14 @@ DECLARE_INSTANCE(Peer); // Node has this long to verify its identity from connection accepted or connection attempt. #define NODE_VERIFY_SECONDS 15 +// Idle nodes are probed this often +#define NODE_IDLE_SECONDS 120 + Peer::Peer(boost::asio::io_service& io_service, boost::asio::ssl::context& ctx, uint64 peerID, bool inbound) : mInbound(inbound), mHelloed(false), mDetaching(false), - mActive(true), + mActive(2), mCluster(false), mPeerId(peerID), mSocketSsl(io_service, ctx), @@ -123,6 +126,33 @@ void Peer::detach(const char *rsn) } } +void Peer::handlePingTimer(const boost::system::error_code& ecResult) +{ + if (ecResult || mDetaching) + return; + + if (mActive == 1) + { // ping out + detach("pto"); + return; + } + + if (mActive == 0) + { // idle->pingsent + mActive = 1; + ripple::TMPing packet; + packet.set_type(ripple::TMPing::ptPING); + sendPacket(boost::make_shared(packet, ripple::mtPING)); + } + else // active->idle + mActive = 0; + + mActivityTimer.expires_from_now(boost::posix_time::seconds(NODE_IDLE_SECONDS)); + mActivityTimer.async_wait(boost::bind(&Peer::handlePingTimer, shared_from_this(), + boost::asio::placeholders::error)); +} + + void Peer::handleVerifyTimer(const boost::system::error_code& ecResult) { if (ecResult == boost::asio::error::operation_aborted) @@ -631,8 +661,10 @@ void Peer::recvHello(ripple::TMHello& packet) { bool bDetach = true; - // Cancel verification timeout. - FIXME Start ping/pong timer (void) mActivityTimer.cancel(); + mActivityTimer.expires_from_now(boost::posix_time::seconds(NODE_IDLE_SECONDS)); + mActivityTimer.async_wait(boost::bind(&Peer::handlePingTimer, shared_from_this(), + boost::asio::placeholders::error)); uint32 ourTime = theApp->getOPs().getNetworkTimeNC(); uint32 minTime = ourTime - 20; @@ -1197,7 +1229,7 @@ void Peer::recvPing(ripple::TMPing& packet) } else if (packet.type() == ripple::TMPing::ptPONG) { - mActive = true; + mActive = 2; } } diff --git a/src/cpp/ripple/Peer.h b/src/cpp/ripple/Peer.h index f21e96b30d..06a07e5d9e 100644 --- a/src/cpp/ripple/Peer.h +++ b/src/cpp/ripple/Peer.h @@ -37,7 +37,7 @@ private: bool mClientConnect; // In process of connecting as client. bool mHelloed; // True, if hello accepted. bool mDetaching; // True, if detaching. - bool mActive; + int mActive; // 0=idle, 1=pingsent, 2=active bool mCluster; // Node in our cluster RippleAddress mNodePublic; // Node public key of peer. std::string mNodeName; @@ -58,6 +58,7 @@ private: void handleStart(const boost::system::error_code& ecResult); void handleVerifyTimer(const boost::system::error_code& ecResult); + void handlePingTimer(const boost::system::error_code& ecResult); protected: diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 038de7c672..68817cc97d 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -217,7 +217,7 @@ Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit) if (!txJSON.isMember("Flags")) txJSON["Flags"] = 0; Ledger::pointer lpCurrent = mNetOps->getCurrentLedger(); - SLE::pointer sleAccountRoot = mNetOps->getSLE(lpCurrent, Ledger::getAccountRootIndex(raSrcAddressID.getAccountID())); + SLE::pointer sleAccountRoot = mNetOps->getSLEi(lpCurrent, Ledger::getAccountRootIndex(raSrcAddressID.getAccountID())); if (!sleAccountRoot) { @@ -899,7 +899,7 @@ Json::Value RPCHandler::doAccountLines(Json::Value jvRequest) if (!lpLedger) return jvResult; - ScopedUnlock su(theApp->getMasterLock(), lpLedger->isFixed()); + ScopedUnlock su(theApp->getMasterLock()); if (!jvRequest.isMember("account")) return rpcError(rpcINVALID_PARAMS); @@ -980,7 +980,7 @@ Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest) if (!lpLedger) return jvResult; - ScopedUnlock su(theApp->getMasterLock(), lpLedger->isClosed() || lpLedger->isImmutable()); + ScopedUnlock su(theApp->getMasterLock()); if (!jvRequest.isMember("account")) return rpcError(rpcINVALID_PARAMS); @@ -1526,6 +1526,7 @@ Json::Value RPCHandler::doLedger(Json::Value jvRequest) Json::Value ret(Json::objectValue); + ScopedUnlock(theApp->getMasterLock()); ledger->addJson(ret, full ? LEDGER_JSON_FULL : 0); return ret; @@ -2147,7 +2148,7 @@ Json::Value RPCHandler::lookupLedger(Json::Value jvRequest, Ledger::pointer& lpL if (-3 == iLedgerIndex) { // Last fully-validated ledger lpLedger = mNetOps->getValidatedLedger(); - iLedgerIndex = lpLedger->getLedgerSeq(); + iLedgerIndex = lpLedger->getLedgerSeq(); } if (iLedgerIndex <= 0) @@ -2364,7 +2365,7 @@ Json::Value RPCHandler::doLedgerEntry(Json::Value jvRequest) if (!!uNodeIndex) { - SLE::pointer sleNode = mNetOps->getSLE(lpLedger, uNodeIndex); + SLE::pointer sleNode = mNetOps->getSLEi(lpLedger, uNodeIndex); if (!sleNode) { @@ -2833,7 +2834,8 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) return rpcError(rpcNO_PERMISSION); } - // XXX Need the master lock for getOperatingMode + boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock()); + if (commandsA[i].iOptions & optNetwork && mNetOps->getOperatingMode() != NetworkOPs::omTRACKING && mNetOps->getOperatingMode() != NetworkOPs::omFULL) @@ -2842,7 +2844,6 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) } // XXX Should verify we have a current ledger. - boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock()); if ((commandsA[i].iOptions & optCurrent) && false) { return rpcError(rpcNO_CURRENT); diff --git a/src/cpp/ripple/RippleAddress.cpp b/src/cpp/ripple/RippleAddress.cpp index 701faa81cd..aac1695e19 100644 --- a/src/cpp/ripple/RippleAddress.cpp +++ b/src/cpp/ripple/RippleAddress.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include @@ -293,6 +295,9 @@ uint160 RippleAddress::getAccountID() const } } +static boost::mutex rncLock; +static boost::unordered_map< std::vector, std::string > rncMap; + std::string RippleAddress::humanAccountID() const { switch (nVersion) { @@ -300,7 +305,13 @@ std::string RippleAddress::humanAccountID() const throw std::runtime_error("unset source - humanAccountID"); case VER_ACCOUNT_ID: - return ToString(); + { + boost::mutex::scoped_lock sl(rncLock); + boost::unordered_map< std::vector, std::string >::iterator it = rncMap.find(vchData); + if (it != rncMap.end()) + return it->second; + return rncMap[vchData] = ToString(); + } case VER_ACCOUNT_PUBLIC: { diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp index 1ae98ed143..2e7c621bee 100644 --- a/src/cpp/ripple/SHAMap.cpp +++ b/src/cpp/ripple/SHAMap.cpp @@ -485,6 +485,17 @@ SHAMapItem::pointer SHAMap::peekItem(const uint256& id, SHAMapTreeNode::TNType& return leaf->peekItem(); } +SHAMapItem::pointer SHAMap::peekItem(const uint256& id, uint256& hash) +{ + boost::recursive_mutex::scoped_lock sl(mLock); + SHAMapTreeNode* leaf = walkToPointer(id); + if (!leaf) + return no_item; + hash = leaf->getNodeHash(); + return leaf->peekItem(); +} + + bool SHAMap::hasItem(const uint256& id) { // does the tree have an item with this ID boost::recursive_mutex::scoped_lock sl(mLock); diff --git a/src/cpp/ripple/SHAMap.h b/src/cpp/ripple/SHAMap.h index 18919c7d81..ada464b016 100644 --- a/src/cpp/ripple/SHAMap.h +++ b/src/cpp/ripple/SHAMap.h @@ -406,6 +406,7 @@ public: // save a copy if you only need a temporary SHAMapItem::pointer peekItem(const uint256& id); + SHAMapItem::pointer peekItem(const uint256& id, uint256& hash); SHAMapItem::pointer peekItem(const uint256& id, SHAMapTreeNode::TNType& type); // traverse functions diff --git a/src/cpp/ripple/SerializedLedger.cpp b/src/cpp/ripple/SerializedLedger.cpp index 8d6804a4de..8f07ec715a 100644 --- a/src/cpp/ripple/SerializedLedger.cpp +++ b/src/cpp/ripple/SerializedLedger.cpp @@ -9,7 +9,7 @@ DECLARE_INSTANCE(SerializedLedgerEntry) SETUP_LOG(); SerializedLedgerEntry::SerializedLedgerEntry(SerializerIterator& sit, const uint256& index) - : STObject(sfLedgerEntry), mIndex(index) + : STObject(sfLedgerEntry), mIndex(index), mMutable(true) { set(sit); uint16 type = getFieldU16(sfLedgerEntryType); @@ -22,7 +22,7 @@ SerializedLedgerEntry::SerializedLedgerEntry(SerializerIterator& sit, const uint } SerializedLedgerEntry::SerializedLedgerEntry(const Serializer& s, const uint256& index) - : STObject(sfLedgerEntry), mIndex(index) + : STObject(sfLedgerEntry), mIndex(index), mMutable(true) { SerializerIterator sit(const_cast(s)); // we know 's' isn't going away set(sit); @@ -41,7 +41,7 @@ SerializedLedgerEntry::SerializedLedgerEntry(const Serializer& s, const uint256& } SerializedLedgerEntry::SerializedLedgerEntry(LedgerEntryType type, const uint256& index) : - STObject(sfLedgerEntry), mIndex(index), mType(type) + STObject(sfLedgerEntry), mIndex(index), mType(type), mMutable(true) { mFormat = LedgerEntryFormat::getLgrFormat(type); if (mFormat == NULL) throw std::runtime_error("invalid ledger entry type"); @@ -49,6 +49,13 @@ SerializedLedgerEntry::SerializedLedgerEntry(LedgerEntryType type, const uint256 setFieldU16(sfLedgerEntryType, static_cast(mFormat->t_type)); } +SerializedLedgerEntry::pointer SerializedLedgerEntry::getMutable() const +{ + SerializedLedgerEntry::pointer ret = boost::make_shared(boost::ref(*this)); + ret->mMutable = true; + return ret; +} + std::string SerializedLedgerEntry::getFullText() const { std::string ret = "\""; diff --git a/src/cpp/ripple/SerializedLedger.h b/src/cpp/ripple/SerializedLedger.h index ec344d9b91..2abdd1aaa1 100644 --- a/src/cpp/ripple/SerializedLedger.h +++ b/src/cpp/ripple/SerializedLedger.h @@ -18,6 +18,7 @@ protected: uint256 mIndex; LedgerEntryType mType; const LedgerEntryFormat* mFormat; + bool mMutable; SerializedLedgerEntry* duplicate() const { return new SerializedLedgerEntry(*this); } @@ -34,6 +35,10 @@ public: const uint256& getIndex() const { return mIndex; } void setIndex(const uint256& i) { mIndex = i; } + void setImmutable() { mMutable = false; } + bool isMutable() { return mMutable; } + SerializedLedgerEntry::pointer getMutable() const; + LedgerEntryType getType() const { return mType; } uint16 getVersion() const { return getFieldU16(sfLedgerEntryType); } const LedgerEntryFormat* getFormat() { return mFormat; } diff --git a/src/cpp/ripple/SerializedObject.cpp b/src/cpp/ripple/SerializedObject.cpp index fc5e710c26..cff7a32724 100644 --- a/src/cpp/ripple/SerializedObject.cpp +++ b/src/cpp/ripple/SerializedObject.cpp @@ -147,16 +147,18 @@ void STObject::set(const std::vector& type) bool STObject::setType(const std::vector &type) { - boost::ptr_vector newData; + boost::ptr_vector newData(type.size()); bool valid = true; mType.clear(); + mType.reserve(type.size()); + BOOST_FOREACH(SOElement::ref elem, type) { bool match = false; for (boost::ptr_vector::iterator it = mData.begin(); it != mData.end(); ++it) if (it->getFName() == elem->e_field) - { + { // matching entry, move to new vector match = true; newData.push_back(mData.release(it).release()); if ((elem->flags == SOE_DEFAULT) && it->isDefault()) @@ -169,7 +171,7 @@ bool STObject::setType(const std::vector &type) } if (!match) - { + { // no match found if (elem->flags == SOE_REQUIRED) { cLog(lsWARNING) << "setType( " << getFName().getName() << ") invalid missing " @@ -181,18 +183,17 @@ bool STObject::setType(const std::vector &type) mType.push_back(elem); } - if (mData.size() != 0) - { - BOOST_FOREACH(const SerializedType& t, mData) + + BOOST_FOREACH(const SerializedType& t, mData) + { // Anything left over must be discardable + if (!t.getFName().isDiscardable()) { - if (!t.getFName().isDiscardable()) - { - cLog(lsWARNING) << "setType( " << getFName().getName() << ") invalid leftover " - << t.getFName().getName(); - valid = false; - } + cLog(lsWARNING) << "setType( " << getFName().getName() << ") invalid leftover " + << t.getFName().getName(); + valid = false; } } + mData.swap(newData); return valid; } diff --git a/src/cpp/ripple/SerializedTypes.cpp b/src/cpp/ripple/SerializedTypes.cpp index 46e599b4ff..d7d1327fc8 100644 --- a/src/cpp/ripple/SerializedTypes.cpp +++ b/src/cpp/ripple/SerializedTypes.cpp @@ -282,22 +282,23 @@ STAccount* STAccount::construct(SerializerIterator& u, SField::ref name) STVector256* STVector256::construct(SerializerIterator& u, SField::ref name) { std::vector data = u.getVL(); - std::vector value; + + std::auto_ptr vec(new STVector256(name)); int count = data.size() / (256 / 8); - value.reserve(count); + vec->mValue.reserve(count); unsigned int uStart = 0; for (unsigned int i = 0; i != count; i++) { unsigned int uEnd = uStart+(256/8); - value.push_back(uint256(std::vector(data.begin()+uStart, data.begin()+(uStart+32)))); - + // This next line could be optimized to construct a default uint256 in the vector and then copy into it + vec->mValue.push_back(uint256(std::vector(data.begin()+uStart, data.begin()+uEnd))); uStart = uEnd; } - return new STVector256(name, value); + return vec.release(); } void STVector256::add(Serializer& s) const diff --git a/src/cpp/ripple/WSConnection.h b/src/cpp/ripple/WSConnection.h index c09c1553b2..bf179b2518 100644 --- a/src/cpp/ripple/WSConnection.h +++ b/src/cpp/ripple/WSConnection.h @@ -5,6 +5,7 @@ #include "../json/value.h" #include +#include #include "WSDoor.h" #include "Application.h" @@ -17,7 +18,7 @@ DEFINE_INSTANCE(WebSocketConnection); #ifndef WEBSOCKET_PING_FREQUENCY -#define WEBSOCKET_PING_FREQUENCY 120 +#define WEBSOCKET_PING_FREQUENCY (5*60) #endif template @@ -53,7 +54,7 @@ public: WSConnection(WSServerHandler* wshpHandler, const connection_ptr& cpConnection) : mHandler(wshpHandler), mConnection(cpConnection), mNetwork(theApp->getOPs()), - mPingTimer(theApp->getAuxService()), mPinged(false) + mPingTimer(cpConnection->get_io_service()), mPinged(false) { mRemoteIP = cpConnection->get_socket().lowest_layer().remote_endpoint().address().to_string(); cLog(lsDEBUG) << "Websocket connection from " << mRemoteIP; @@ -62,6 +63,7 @@ public: void preDestroy() { // sever connection + mPingTimer.cancel(); mConnection.reset(); } @@ -143,10 +145,10 @@ public: bool onPingTimer() { if (mPinged) - return true; + return true; // causes connection to close mPinged = true; setPingTimer(); - return false; + return false; // causes ping to be sent } void onPong() @@ -154,8 +156,11 @@ public: mPinged = false; } - static void pingTimer(weak_connection_ptr c, WSServerHandler* h) + static void pingTimer(weak_connection_ptr c, WSServerHandler* h, const boost::system::error_code& e) { + if (e) + return; + connection_ptr ptr = c.lock(); if (ptr) h->pingTimer(ptr); @@ -164,10 +169,10 @@ public: void setPingTimer() { mPingTimer.expires_from_now(boost::posix_time::seconds(WEBSOCKET_PING_FREQUENCY)); - mPingTimer.async_wait(boost::bind(&WSConnection::pingTimer, mConnection, mHandler)); + mPingTimer.async_wait(boost::bind( + &WSConnection::pingTimer, mConnection, mHandler, boost::asio::placeholders::error)); } }; - // vim:ts=4 diff --git a/src/cpp/ripple/base58.h b/src/cpp/ripple/base58.h index e2fa1c3673..5f08f48718 100644 --- a/src/cpp/ripple/base58.h +++ b/src/cpp/ripple/base58.h @@ -176,7 +176,7 @@ protected: memset(&vchData[0], 0, vchData.size()); } - void SetData(int nVersionIn, std::vector vchDataIn) + void SetData(int nVersionIn, const std::vector& vchDataIn) { nVersion = nVersionIn; vchData = vchDataIn; @@ -186,7 +186,7 @@ protected: { nVersion = nVersionIn; vchData.resize(nSize); - if (!vchData.empty()) + if (nSize) memcpy(&vchData[0], pdata, nSize); } diff --git a/src/js/orderbook.js b/src/js/orderbook.js new file mode 100644 index 0000000000..c78674e079 --- /dev/null +++ b/src/js/orderbook.js @@ -0,0 +1,104 @@ +// Routines for working with an orderbook. +// +// Events: + +// var network = require("./network.js"); + +var EventEmitter = require('events').EventEmitter; +var Amount = require('./amount').Amount; +var UInt160 = require('./uint160').UInt160; +var Currency = require('./currency').Currency; + +var extend = require('extend'); + +var OrderBook = function (remote, + currency_out, issuer_out, + currency_in, issuer_in) { + var self = this; + + this._remote = remote; + this._currency_out = currency_out; + this._issuer_out = issuer_out; + this._currency_in = currency_in; + this._issuer_in = issuer_in; + + this._subs = 0; + + // Ledger entry object + // Important: This must never be overwritten, only extend()-ed + this._entry = {}; + + this.on('newListener', function (type, listener) { + if (OrderBook.subscribe_events.indexOf(type) !== -1) { + if (!self._subs && 'open' === self._remote._online_state) { + self._remote.request_subscribe() + .books([self.to_json()]) + .request(); + } + self._subs += 1; + } + }); + + this.on('removeListener', function (type, listener) { + if (OrderBook.subscribe_events.indexOf(type) !== -1) { + self._subs -= 1; + + if (!self._subs && 'open' === self._remote._online_state) { + self._remote.request_unsubscribe() + .books([self.to_json()]) + .request(); + } + } + }); + + this._remote.on('connect', function () { + if (self._subs) { + self._remote.request_subscribe() + .books([self.to_json()]) + .request(); + } + }); + + return this; +}; + +OrderBook.prototype = new EventEmitter; + +/** + * List of events that require a remote subscription to the orderbook. + */ +OrderBook.subscribe_events = ['transaction']; + +OrderBook.prototype.to_json = function () +{ + var json = { + "CurrencyOut": this._currency_out, + "CurrencyIn": this._currency_in + }; + + if (json["CurrencyOut"] !== "XRP") json["IssuerOut"] = this._issuer_out; + if (json["CurrencyIn"] !== "XRP") json["IssuerIn"] = this._issuer_in; + + return json; +}; + +/** + * Whether the OrderBook is valid. + * + * Note: This only checks whether the parameters (currencies and issuer) are + * syntactically valid. It does not check anything against the ledger. + */ +OrderBook.prototype.is_valid = function () +{ + return ( + Currency.is_valid(this._currency_in) && + (this._currency_in !== "XRP" && UInt160.is_valid(this._issuer_in)) && + Currency.is_valid(this._currency_out) && + (this._currency_out !== "XRP" && UInt160.is_valid(this._issuer_out)) && + !(this._currency_in === "XRP" && this._currency_out === "XRP") + ); +}; + +exports.OrderBook = OrderBook; + +// vim:sw=2:sts=2:ts=8:et diff --git a/src/js/remote.js b/src/js/remote.js index 9fdd70f667..8507c4a3f5 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -22,6 +22,7 @@ var UInt160 = require('./amount').UInt160; var Transaction = require('./transaction').Transaction; var Account = require('./account').Account; var Meta = require('./meta').Meta; +var OrderBook = require('./orderbook').OrderBook; var utils = require('./utils'); var config = require('./config'); @@ -181,6 +182,31 @@ Request.prototype.rt_accounts = function (accounts) { return this.accounts(accounts, true); }; +Request.prototype.books = function (books) { + var procBooks = []; + + for (var i = 0, l = books.length; i < l; i++) { + var book = books[i]; + + var json = { + "CurrencyOut": Currency.json_rewrite(book["CurrencyOut"]), + "CurrencyIn": Currency.json_rewrite(book["CurrencyIn"]) + }; + + if (json["CurrencyOut"] !== "XRP") { + json["IssuerOut"] = UInt160.json_rewrite(book["IssuerOut"]); + } + if (json["CurrencyIn"] !== "XRP") { + json["IssuerIn"] = UInt160.json_rewrite(book["IssuerIn"]); + } + + procBooks.push(json); + } + this.message.books = procBooks; + + return this; +}; + // // Remote - access to a remote Ripple server via websocket. // @@ -1036,6 +1062,15 @@ Remote.prototype.account = function (accountId) { return this._accounts[account.to_json()] = account; }; +Remote.prototype.book = function (currency_out, issuer_out, + currency_in, issuer_in) { + var book = new OrderBook(this, + currency_out, issuer_out, + currency_in, issuer_in); + + return book; +} + // Return the next account sequence if possible. // <-- undefined or Sequence Remote.prototype.account_seq = function (account, advance) { diff --git a/test/amount-test.js b/test/amount-test.js index 550dc29634..42674b91b3 100644 --- a/test/amount-test.js +++ b/test/amount-test.js @@ -218,6 +218,11 @@ buster.testCase("Amount", { "Divide EUR by XRP, neg, <1" : function () { buster.assert.equals("-0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").divide(Amount.from_json("2000")).to_text_full()); } + }, + "Amount comparisons" : { + "10 USD != 100 USD" : function () { + buster.refute(Amount.from_json("10/USD/rNDKeo9RrCiRdfsMG8AdoZvNZxHASGzbZL").equals(Amount.from_json("100/USD/rNDKeo9RrCiRdfsMG8AdoZvNZxHASGzbZL"))); + } } });