diff --git a/LICENSE b/LICENSE index c15999ab1..0388cedae 100644 Binary files a/LICENSE and b/LICENSE differ diff --git a/src/Application.cpp b/src/Application.cpp index a5d458dc1..11b19895b 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -208,17 +208,25 @@ void Application::startNewLedger() void Application::loadOldLedger() { - Ledger::pointer lastLedger = Ledger::getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;"); - - if (!lastLedger) + try { - std::cout << "No Ledger found?" << std::endl; + Ledger::pointer lastLedger = Ledger::getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;"); + + if (!lastLedger) + { + std::cout << "No Ledger found?" << std::endl; + exit(-1); + } + lastLedger->setClosed(); + + Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); + mMasterLedger.switchLedgers(lastLedger, openLedger); + mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC()); + } + catch (SHAMapMissingNode& mn) + { + Log(lsFATAL) << "Cannot load ledger. " << mn; exit(-1); } - - lastLedger->setClosed(); - Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); - mMasterLedger.switchLedgers(lastLedger, openLedger); - mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC()); } // vim:ts=4 diff --git a/src/Ledger.cpp b/src/Ledger.cpp index f52e1f1e7..5be6eb02b 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -24,7 +24,8 @@ SETUP_LOG(); Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(startAmount), mLedgerSeq(1), mCloseTime(0), mParentCloseTime(0), mCloseResolution(LEDGER_TIME_ACCURACY), mCloseFlags(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false), - mTransactionMap(new SHAMap()), mAccountStateMap(new SHAMap()) + mTransactionMap(boost::make_shared(smtTRANSACTION)), + mAccountStateMap(boost::make_shared(smtSTATE)) { // special case: put coins in root account AccountState::pointer startAccount = boost::make_shared(masterID); @@ -38,13 +39,21 @@ Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(s } Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, - uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, uint32 ledgerSeq,bool isMutable) + uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, uint32 ledgerSeq) : mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash), mTotCoins(totCoins), mLedgerSeq(ledgerSeq), mCloseTime(closeTime), mParentCloseTime(parentCloseTime), mCloseResolution(closeResolution), mCloseFlags(closeFlags), - mClosed(false), mValidHash(false), mAccepted(false), mImmutable(isMutable) -{ + mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true), + mTransactionMap(boost::make_shared(smtTRANSACTION, transHash)), + mAccountStateMap(boost::make_shared(smtSTATE, accountHash)) +{ // This will throw if the root nodes are not available locally updateHash(); + if (mTransHash.isNonZero()) + mTransactionMap->fetchRoot(mTransHash); + if (mAccountHash.isNonZero()) + mAccountStateMap->fetchRoot(mAccountHash); + mTransactionMap->setImmutable(); + mAccountStateMap->setImmutable(); } Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), mLedgerSeq(ledger.mLedgerSeq), @@ -62,7 +71,8 @@ Ledger::Ledger(bool /* dummy */, Ledger& prevLedger) : mTotCoins(prevLedger.mTotCoins), mLedgerSeq(prevLedger.mLedgerSeq + 1), mParentCloseTime(prevLedger.mCloseTime), mCloseResolution(prevLedger.mCloseResolution), mCloseFlags(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false), - mTransactionMap(new SHAMap()), mAccountStateMap(prevLedger.mAccountStateMap->snapShot(true)) + mTransactionMap(boost::make_shared(smtTRANSACTION)), + mAccountStateMap(prevLedger.mAccountStateMap->snapShot(true)) { // Create a new ledger that follows this one prevLedger.updateHash(); mParentHash = prevLedger.getHash(); @@ -123,8 +133,8 @@ void Ledger::setRaw(const Serializer &s) updateHash(); if(mValidHash) { - mTransactionMap = boost::make_shared(mTransHash); - mAccountStateMap = boost::make_shared(mAccountHash); + mTransactionMap = boost::make_shared(smtTRANSACTION, mTransHash); + mAccountStateMap = boost::make_shared(smtSTATE, mAccountHash); } } @@ -328,10 +338,11 @@ void Ledger::saveAcceptedLedger(Ledger::ref ledger) ledger->mAccountHash.GetHex() % ledger->mTransHash.GetHex())); // write out dirty nodes - while(ledger->mTransactionMap->flushDirty(256, hotTRANSACTION_NODE, ledger->mLedgerSeq)) - { ; } - while(ledger->mAccountStateMap->flushDirty(256, hotACCOUNT_NODE, ledger->mLedgerSeq)) - { ; } + int fc; + while ((fc = ledger->mTransactionMap->flushDirty(256, hotTRANSACTION_NODE, ledger->mLedgerSeq)) > 0) + { cLog(lsINFO) << "Flushed " << fc << " dirty transaction nodes"; } + while ((fc = ledger->mAccountStateMap->flushDirty(256, hotACCOUNT_NODE, ledger->mLedgerSeq)) > 0) + { cLog(lsINFO) << "Flushed " << fc << " dirty state nodes"; } ledger->disarmDirty(); SHAMap& txSet = *ledger->peekTransactionMap(); @@ -425,7 +436,7 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) } Ledger::pointer ret = Ledger::pointer(new Ledger(prevHash, transHash, accountHash, totCoins, - closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, true)); + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq)); if (ret->getHash() != ledgerHash) { if (sLog(lsERROR)) diff --git a/src/Ledger.h b/src/Ledger.h index 41e30d2db..ffb9dbd81 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -93,7 +93,7 @@ public: Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, - uint32 ledgerSeq, bool immutable); // used for database ledgers + uint32 ledgerSeq); // used for database ledgers Ledger(const std::vector& rawLedger); diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp index d9056fdad..95cf41415 100644 --- a/src/LedgerAcquire.cpp +++ b/src/LedgerAcquire.cpp @@ -96,6 +96,45 @@ LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE #endif } +bool LedgerAcquire::tryLocal() +{ // return value: true = no more work to do + HashedObject::pointer node = theApp->getHashedObjectStore().retrieve(mHash); + if (!node) + return false; + + mLedger = boost::make_shared(strCopy(node->getData())); + assert(mLedger->getHash() == mHash); + mHaveBase = true; + + if (!mLedger->getTransHash()) + mHaveTransactions = true; + else + { + try + { + mLedger->peekTransactionMap()->fetchRoot(mLedger->getTransHash()); + } + catch (SHAMapMissingNode&) + { + } + } + + if (!mLedger->getAccountHash()) + mHaveState = true; + else + { + try + { + mLedger->peekAccountStateMap()->fetchRoot(mLedger->getAccountHash()); + } + catch (SHAMapMissingNode&) + { + } + } + + return mHaveTransactions && mHaveState; +} + void LedgerAcquire::onTimer() { if (getTimeouts() > 6) @@ -124,7 +163,7 @@ void LedgerAcquire::done() setComplete(); mLock.lock(); triggers = mOnComplete; - mOnComplete.empty(); + mOnComplete.clear(); mLock.unlock(); if (mLedger) diff --git a/src/LedgerAcquire.h b/src/LedgerAcquire.h index b58961829..7e2da168d 100644 --- a/src/LedgerAcquire.h +++ b/src/LedgerAcquire.h @@ -94,6 +94,7 @@ public: bool takeAsNode(const std::list& IDs, const std::list >& data); bool takeAsRootNode(const std::vector& data); void trigger(Peer::ref, bool timer); + bool tryLocal(); }; class LedgerAcquireMaster diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index a07965ae6..c915faad7 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -27,7 +27,7 @@ SETUP_LOG(); TransactionAcquire::TransactionAcquire(const uint256& hash) : PeerSet(hash, TX_ACQUIRE_TIMEOUT), mHaveRoot(false) { - mMap = boost::make_shared(hash); + mMap = boost::make_shared(smtTRANSACTION, hash); } void TransactionAcquire::done() @@ -759,7 +759,7 @@ SHAMap::pointer LedgerConsensus::getTransactionTree(const uint256& hash, bool do { if (!hash) { - SHAMap::pointer empty = boost::make_shared(); + SHAMap::pointer empty = boost::make_shared(smtTRANSACTION); mapComplete(hash, empty, false); return empty; } diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index 855128bdf..6a01d4268 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -585,7 +585,6 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis if (!mAcquiringLedger->isComplete()) { // add more peers int count = 0; - std::vector peers=theApp->getConnectionPool().getPeerVector(); BOOST_FOREACH(Peer::ref it, peerList) { if (it->getClosedLedgerHash() == closedLedger) diff --git a/src/PubKeyCache.cpp b/src/PubKeyCache.cpp index 503fefc3d..4ca514039 100644 --- a/src/PubKeyCache.cpp +++ b/src/PubKeyCache.cpp @@ -69,6 +69,6 @@ CKey::pointer PubKeyCache::store(const NewcoinAddress& id, const CKey::pointer& void PubKeyCache::clear() { boost::mutex::scoped_lock sl(mLock); - mCache.empty(); + mCache.clear(); } // vim:ts=4 diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 798dd0c1a..5d596012a 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -2711,8 +2711,10 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params try { return (this->*(commandsA[i].dfpFunc))(params); } - catch (...) + catch (std::exception& e) { + cLog(lsINFO) << "Caught throw: " << e.what(); + return RPCError(rpcINTERNAL); } } diff --git a/src/RippleCalc.cpp b/src/RippleCalc.cpp index 817660d05..2de815e3d 100644 --- a/src/RippleCalc.cpp +++ b/src/RippleCalc.cpp @@ -478,6 +478,8 @@ TER RippleCalc::calcNodeDeliverFwd( const uint160& uPrvIssuerID = pnPrv.uIssuerID; const STAmount& saTransferRate = pnPrv.saTransferRate; + STAmount& saCurDeliverAct = pnCur.saFwdDeliver; + saInAct = 0; saInFees = 0; @@ -594,8 +596,11 @@ TER RippleCalc::calcNodeDeliverFwd( bEntryAdvance = true; } - saInAct += saInPassAct; - saInFees += saInPassFees; + saInAct += saInPassAct; + saInFees += saInPassFees; + + // Adjust amount available to next node. + saCurDeliverAct += saOutPassAct; } } @@ -856,18 +861,19 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, const PathState::p const STAmount& saCurDeliverReq = pnCur.saRevDeliver; STAmount saCurDeliverAct(saCurDeliverReq.getCurrency(), saCurDeliverReq.getIssuer()); - cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: saPrvRedeemReq=%s saPrvIssueReq=%s saCurRedeemReq=%s saNxtOwed=%s") + cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: saPrvRedeemReq=%s saPrvIssueReq=%s saCurRedeemReq=%s saCurIssueReq=%s saNxtOwed=%s") % saPrvRedeemReq.getFullText() % saPrvIssueReq.getFullText() % saCurRedeemReq.getFullText() + % saCurIssueReq.getFullText() % saNxtOwed.getFullText()); cLog(lsINFO) << pspCur->getJson(); assert(!saCurRedeemReq || (-saNxtOwed) >= saCurRedeemReq); // Current redeem req can't be more than IOUs on hand. assert(!saCurIssueReq // If not issuing, fine. - || !saNxtOwed.isNegative() // Not hold next IOUs: owed is >= 0 - || saNxtOwed == saCurRedeemReq); // If issue req, then redeem req must consume all owed. + || !saNxtOwed.isNegative() // saNxtOwed >= 0: Sender not holding next IOUs, saNxtOwed < 0: Sender holding next IOUs. + || -saNxtOwed == saCurRedeemReq); // If issue req, then redeem req must consume all owed. if (bPrvAccount && bNxtAccount) { @@ -882,8 +888,8 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, const PathState::p // account --> ACCOUNT --> $ // Overall deliverable. const STAmount& saCurWantedReq = bPrvAccount - ? std::min(pspCur->saOutReq, saPrvLimit+saPrvOwed) // If previous is an account, limit. - : pspCur->saOutReq; // Previous is an offer, no limit: redeem own IOUs. + ? std::min(pspCur->saOutReq-pspCur->saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit. + : pspCur->saOutReq-pspCur->saOutAct; // Previous is an offer, no limit: redeem own IOUs. STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer()); cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: account --> ACCOUNT --> $ : saCurWantedReq=%s") @@ -1017,8 +1023,8 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, const PathState::p { // offer --> ACCOUNT --> $ const STAmount& saCurWantedReq = bPrvAccount - ? std::min(pspCur->saOutReq, saPrvLimit+saPrvOwed) // If previous is an account, limit. - : pspCur->saOutReq; // Previous is an offer, no limit: redeem own IOUs. + ? std::min(pspCur->saOutReq-pspCur->saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit. + : pspCur->saOutReq-pspCur->saOutAct; // Previous is an offer, no limit: redeem own IOUs. STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer()); cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> $ : saCurWantedReq=%s") @@ -1086,10 +1092,10 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, const PathState::p return terResult; } -// Perfrom balance adjustments between previous and current node. +// 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 output for next node. +// 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. TER RippleCalc::calcNodeAccountFwd( @@ -1159,16 +1165,12 @@ TER RippleCalc::calcNodeAccountFwd( // ^ --> ACCOUNT --> account // First node, calculate amount to send. - // XXX Use stamp/ripple balance - PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + // XXX Limit by stamp/ripple balance - const STAmount& saCurRedeemReq = pnCur.saRevRedeem; - STAmount& saCurRedeemAct = pnCur.saFwdRedeem; - const STAmount& saCurIssueReq = pnCur.saRevIssue; - STAmount& saCurIssueAct = pnCur.saFwdIssue; - - const STAmount& saCurSendMaxReq = pspCur->saInReq; // Negative for no limit, doing a calculation. - STAmount& saCurSendMaxAct = pspCur->saInAct; // Report to user how much this sends. + const STAmount& saCurSendMaxReq = pspCur->saInReq.isNegative() + ? pspCur->saInReq // Negative for no limit, doing a calculation. + : pspCur->saInReq-pspCur->saInAct; // request - done. + STAmount& saCurSendMaxPass = pspCur->saInPass; // Report how much pass sends. if (saCurRedeemReq) { @@ -1179,28 +1181,35 @@ TER RippleCalc::calcNodeAccountFwd( } else { - saCurRedeemAct = STAmount(saCurRedeemReq); - } - saCurSendMaxAct = saCurRedeemAct; + // No redeeming. - if (saCurIssueReq && (saCurSendMaxReq.isNegative() || saCurSendMaxReq != saCurRedeemAct)) + saCurRedeemAct = saCurRedeemReq; + } + saCurSendMaxPass = saCurRedeemAct; + + if (saCurIssueReq && (saCurSendMaxReq.isNegative() || saCurSendMaxPass != saCurSendMaxReq)) { - // Issue requested and not over budget. + // Issue requested and pass does not meet max. saCurIssueAct = saCurSendMaxReq.isNegative() ? saCurIssueReq : std::min(saCurSendMaxReq-saCurRedeemAct, saCurIssueReq); } else { + // No issuing. + saCurIssueAct = STAmount(saCurIssueReq); } - saCurSendMaxAct += saCurIssueAct; + saCurSendMaxPass += saCurIssueAct; - cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT --> account : saCurSendMaxReq=%s saCurRedeemAct=%s saCurIssueReq=%s saCurIssueAct=%s") + cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT --> account : saInReq=%s saInAct=%s saCurSendMaxReq=%s saCurRedeemAct=%s saCurIssueReq=%s saCurIssueAct=%s saCurSendMaxPass=%s") + % pspCur->saInReq.getFullText() + % pspCur->saInAct.getFullText() % saCurSendMaxReq.getFullText() % saCurRedeemAct.getFullText() % saCurIssueReq.getFullText() - % saCurIssueAct.getFullText()); + % saCurIssueAct.getFullText() + % saCurSendMaxPass.getFullText()); } else if (uIndex == uLast) { @@ -1213,7 +1222,7 @@ TER RippleCalc::calcNodeAccountFwd( // Last node. Accept all funds. Calculate amount actually to credit. - STAmount& saCurReceive = pspCur->saOutAct; + STAmount& saCurReceive = pspCur->saOutPass; STAmount saIssueCrd = uQualityIn >= QUALITY_ONE ? saPrvIssueReq // No fee. @@ -1299,12 +1308,12 @@ TER RippleCalc::calcNodeAccountFwd( if (uIndex == uLast) { // offer --> ACCOUNT --> $ - cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> $")); + cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> $ : %s") % saPrvDeliverReq.getFullText()); - STAmount& saCurReceive = pspCur->saOutAct; + STAmount& saCurReceive = pspCur->saOutPass; // Amount to credit. - saCurReceive = saPrvDeliverAct; + saCurReceive = saPrvDeliverReq; // No income balance adjustments necessary. The paying side inside the offer paid to this account. } @@ -1359,8 +1368,8 @@ bool PathState::lessPriority(const PathState::pointer& lhs, const PathState::poi return lhs->uQuality > rhs->uQuality; // Bigger is worse. // Best quanity is second rank. - if (lhs->saOutAct != rhs->saOutAct) - return lhs->saOutAct < rhs->saOutAct; // Smaller is worse. + if (lhs->saOutPass != rhs->saOutPass) + return lhs->saOutPass < rhs->saOutPass; // Smaller is worse. // Path index is third rank. return lhs->mIndex > rhs->mIndex; // Bigger is worse. @@ -1549,7 +1558,11 @@ PathState::PathState( const STAmount& saSend, const STAmount& saSendMax ) - : mLedger(lesSource.getLedgerRef()), mIndex(iIndex), uQuality(0) + : mLedger(lesSource.getLedgerRef()), + mIndex(iIndex), + uQuality(1), // Mark path as active. + saInReq(saSendMax), + saOutReq(saSend) { const uint160 uInCurrencyID = saSendMax.getCurrency(); const uint160 uOutCurrencyID = saSend.getCurrency(); @@ -1558,9 +1571,6 @@ PathState::PathState( lesEntries = lesSource.duplicate(); - saInReq = saSendMax; - saOutReq = saSend; - // Push sending node. terStatus = pushNode( STPathElement::typeAccount @@ -1684,12 +1694,18 @@ Json::Value PathState::getJson() const if (saInAct) jvPathState["in_act"] = saInAct.getJson(0); + if (saInPass) + jvPathState["in_pass"] = saInPass.getJson(0); + if (saOutReq) jvPathState["out_req"] = saOutReq.getJson(0); if (saOutAct) jvPathState["out_act"] = saOutAct.getJson(0); + if (saOutPass) + jvPathState["out_pass"] = saOutPass.getJson(0); + if (uQuality) jvPathState["uQuality"] = Json::Value::UInt(uQuality); @@ -1775,13 +1791,17 @@ void RippleCalc::pathNext(const PathState::pointer& pspCur, const int iPaths, co const bool bMultiQuality = iPaths == 1; const unsigned int uLast = pspCur->vpnNodes.size() - 1; - cLog(lsINFO) << "Path In: " << pspCur->getJson(); - - assert(pspCur->vpnNodes.size() >= 2); + // YYY This clearing should only be needed for nice logging. + pspCur->saInPass = STAmount(pspCur->saInReq.getCurrency(), pspCur->saInReq.getIssuer()); + pspCur->saOutPass = STAmount(pspCur->saOutReq.getCurrency(), pspCur->saOutReq.getIssuer()); pspCur->vUnfundedBecame.clear(); pspCur->umReverse.clear(); + cLog(lsINFO) << "Path In: " << pspCur->getJson(); + + assert(pspCur->vpnNodes.size() >= 2); + lesCurrent = lesCheckpoint; // Restore from checkpoint. lesCurrent.bumpSeq(); // Begin ledger varance. @@ -1797,9 +1817,17 @@ void RippleCalc::pathNext(const PathState::pointer& pspCur, const int iPaths, co pspCur->terStatus = calcNodeFwd(0, pspCur, bMultiQuality); + tLog(tesSUCCESS == pspCur->terStatus, lsDEBUG) + << boost::str(boost::format("saOutPass=%s saInPass=%s") + % pspCur->saOutPass.getFullText() + % pspCur->saInPass.getFullText()); + + // Make sure we have a quality. + assert(tesSUCCESS != pspCur->terStatus || (!!pspCur->saOutPass && !!pspCur->saInPass)); + pspCur->uQuality = tesSUCCESS == pspCur->terStatus - ? STAmount::getRate(pspCur->saOutAct, pspCur->saInAct) // Calculate relative quality. - : 0; // Mark path as inactive. + ? STAmount::getRate(pspCur->saOutPass, pspCur->saInPass) // Calculate relative quality. + : 0; // Mark path as inactive. cLog(lsINFO) << "Path after forward: " << pspCur->getJson(); } @@ -1913,8 +1941,8 @@ TER RippleCalc::rippleCalc( terResult = temUNCERTAIN; } - STAmount saPaid; - STAmount saWanted; + STAmount saInAct; + STAmount saOutAct; const LedgerEntrySet lesBase = lesActive; // Checkpoint with just fees paid. const uint64 uQualityLimit = bLimitQuality ? STAmount::getRate(saDstAmountReq, saMaxAmountReq) : 0; // When processing, don't want to complicate directory walking with deletion. @@ -1928,15 +1956,26 @@ TER RippleCalc::rippleCalc( // Find the best path. BOOST_FOREACH(PathState::pointer& pspCur, vpsPaths) { - rc.pathNext(pspCur, vpsPaths.size(), lesCheckpoint, lesActive); // Compute increment. + if (pspCur->uQuality) + { + pspCur->saInAct = saInAct; // Update to current amount processed. + pspCur->saOutAct = saOutAct; - if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality. - || !pspBest // Best is not yet set. - || (pspCur->uQuality && PathState::lessPriority(pspBest, pspCur))) // Current is better than set. - { - lesActive.swapWith(pspCur->lesEntries); // For the path, save ledger state. - pspBest = pspCur; - } + rc.pathNext(pspCur, vpsPaths.size(), lesCheckpoint, lesActive); // Compute increment. + + if (!pspCur->uQuality) { + // Path was dry. + + nothing(); + } + else if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality. + || !pspBest // Best is not yet set. + || PathState::lessPriority(pspBest, pspCur)) // Current is better than set. + { + lesActive.swapWith(pspCur->lesEntries); // For the path, save ledger state. + pspBest = pspCur; + } + } } if (pspBest) @@ -1949,18 +1988,35 @@ TER RippleCalc::rippleCalc( // Record best pass' LedgerEntrySet to build off of and potentially return. lesActive.swapWith(pspBest->lesEntries); - // Figure out if done. - if (temUNCERTAIN == terResult && saPaid == saWanted) + saInAct += pspBest->saInPass; + saOutAct += pspBest->saOutPass; + + if (temUNCERTAIN == terResult && saOutAct == saDstAmountReq) { + // Done. Delivered requested amount. + terResult = tesSUCCESS; } - else + else if (saInAct != saMaxAmountReq) { - // Prepare for next pass. + // Have not met requested amount or max send. Prepare for next pass. // Merge best pass' umReverse. rc.mumSource.insert(pspBest->umReverse.begin(), pspBest->umReverse.end()); } + else if (!bPartialPayment) + { + // Have sent maximum allowed. Partial payment not allowed. + + terResult = tepPATH_PARTIAL; + lesActive = lesBase; // Revert to just fees charged. + } + else + { + // Have sent maximum allowed. Partial payment allowed. Success. + + terResult = tesSUCCESS; + } } // Not done and ran out of paths. else if (!bPartialPayment) @@ -1970,7 +2026,7 @@ TER RippleCalc::rippleCalc( lesActive = lesBase; // Revert to just fees charged. } // Partial payment ok. - else if (!saPaid) + else if (!saOutAct) { // No payment at all. terResult = tepPATH_DRY; diff --git a/src/RippleCalc.h b/src/RippleCalc.h index 673393b5d..14d07f4aa 100644 --- a/src/RippleCalc.h +++ b/src/RippleCalc.h @@ -91,10 +91,12 @@ public: int mIndex; uint64 uQuality; // 0 = none. - STAmount saInReq; // Max amount to spend by sender - STAmount saInAct; // Amount spent by sender (calc output) - STAmount saOutReq; // Amount to send (calc input) - STAmount saOutAct; // Amount actually sent (calc output). + 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. PathState( const int iIndex, diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index 5dd31f565..09877fa40 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -41,14 +41,14 @@ std::size_t hash_value(const uint160& u) } -SHAMap::SHAMap(uint32 seq) : mSeq(seq), mState(smsModifying) +SHAMap::SHAMap(SHAMapType t, uint32 seq) : mSeq(seq), mState(smsModifying), mType(t) { root = boost::make_shared(mSeq, SHAMapNode(0, uint256())); root->makeInner(); mTNByID[*root] = root; } -SHAMap::SHAMap(const uint256& hash) : mSeq(0), mState(smsSynching) +SHAMap::SHAMap(SHAMapType t, const uint256& hash) : mSeq(0), mState(smsSynching), mType(t) { // FIXME: Need to acquire root node root = boost::make_shared(mSeq, SHAMapNode(0, uint256())); root->makeInner(); @@ -58,7 +58,7 @@ SHAMap::SHAMap(const uint256& hash) : mSeq(0), mState(smsSynching) SHAMap::pointer SHAMap::snapShot(bool isMutable) { // Return a new SHAMap that is an immutable snapshot of this one // Initially nodes are shared, but CoW is forced on both ledgers - SHAMap::pointer ret = boost::make_shared(); + SHAMap::pointer ret = boost::make_shared(mType); SHAMap& newMap = *ret; newMap.mSeq = ++mSeq; newMap.mTNByID = mTNByID; @@ -93,7 +93,8 @@ std::stack SHAMap::getStack(const uint256& id, bool inc { if (partialOk) return stack; - throw mn; + mn.setTargetNode(id); + throw; } } @@ -153,10 +154,15 @@ SHAMapTreeNode::pointer SHAMap::walkTo(const uint256& id, bool modify) return inNode; uint256 childHash = inNode->getChildHash(branch); - SHAMapTreeNode::pointer nextNode = getNode(inNode->getChildNodeID(branch), childHash, false); - if (!nextNode) - throw SHAMapMissingNode(inNode->getChildNodeID(branch), childHash); - inNode = nextNode; + try + { + inNode = getNode(inNode->getChildNodeID(branch), childHash, false); + } + catch (SHAMapMissingNode& mn) + { + mn.setTargetNode(id); + throw; + } } if (inNode->getTag() != id) return SHAMapTreeNode::pointer(); @@ -175,7 +181,7 @@ SHAMapTreeNode* SHAMap::walkToPointer(const uint256& id) if (nextHash.isZero()) return NULL; inNode = getNodePointer(inNode->getChildNodeID(branch), nextHash); if (!inNode) - throw SHAMapMissingNode(inNode->getChildNodeID(branch), nextHash); + throw SHAMapMissingNode(mType, inNode->getChildNodeID(branch), nextHash, id); } return (inNode->getTag() == id) ? inNode : NULL; } @@ -680,11 +686,14 @@ void SHAMapItem::dump() SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const uint256& hash) { if (!theApp->running()) - throw SHAMapMissingNode(id, hash); + throw SHAMapMissingNode(mType, id, hash); HashedObject::pointer obj(theApp->getHashedObjectStore().retrieve(hash)); if (!obj) - throw SHAMapMissingNode(id, hash); + { + Log(lsTRACE) << "fetchNodeExternal: missing " << hash; + throw SHAMapMissingNode(mType, id, hash); + } assert(Serializer::getSHA512Half(obj->getData()) == hash); try @@ -698,10 +707,17 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui catch (...) { cLog(lsWARNING) << "fetchNodeExternal gets an invalid node: " << hash; - throw SHAMapMissingNode(id, hash); + throw SHAMapMissingNode(mType, id, hash); } } +void SHAMap::fetchRoot(const uint256& hash) +{ + root = fetchNodeExternal(SHAMapNode(), hash); + root->makeInner(); + mTNByID[*root] = root; +} + void SHAMap::armDirty() { // begin saving dirty nodes ++mSeq; @@ -719,6 +735,8 @@ int SHAMap::flushDirty(int maxNodes, HashedObjectType t, uint32 seq) boost::unordered_map::iterator it = dirtyNodes.begin(); while (it != dirtyNodes.end()) { + tLog(mType == smtTRANSACTION, lsDEBUG) << "TX node write " << it->first; + tLog(mType == smtSTATE, lsDEBUG) << "STATE node write " << it->first; s.erase(); it->second->addRaw(s, snfPREFIX); theApp->getHashedObjectStore().store(t, seq, s.peekData(), s.getSHA512Half()); @@ -802,7 +820,7 @@ BOOST_AUTO_TEST_CASE( SHAMap_test ) h4.SetHex("b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8"); h5.SetHex("a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7"); - SHAMap sMap; + SHAMap sMap(smtFREE); SHAMapItem i1(h1, IntToVUC(1)), i2(h2, IntToVUC(2)), i3(h3, IntToVUC(3)), i4(h4, IntToVUC(4)), i5(h5, IntToVUC(5)); if (!sMap.addItem(i2, true, false)) BOOST_FAIL("no add"); diff --git a/src/SHAMap.h b/src/SHAMap.h index c239bad30..f144b5ca9 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -128,6 +128,13 @@ enum SHANodeFormat snfWIRE = 2, // Compressed form used on the wire }; +enum SHAMapType +{ + smtTRANSACTION =1, // A tree of transactions + smtSTATE =2, // A tree of state nodes + smtFREE =3, // A tree not part of a ledger +}; + class SHAMapTreeNode : public SHAMapNode { friend class SHAMap; @@ -239,20 +246,35 @@ public: class SHAMapMissingNode : public std::runtime_error { protected: + SHAMapType mType; SHAMapNode mNodeID; uint256 mNodeHash; + uint256 mTargetIndex; public: - SHAMapMissingNode(const SHAMapNode& nodeID, const uint256& nodeHash) : - std::runtime_error(nodeID.getString()), mNodeID(nodeID), mNodeHash(nodeHash) + SHAMapMissingNode(SHAMapType t, const SHAMapNode& nodeID, const uint256& nodeHash) : + std::runtime_error(nodeID.getString()), mType(t), mNodeID(nodeID), mNodeHash(nodeHash) { ; } + + SHAMapMissingNode(SHAMapType t, const SHAMapNode& nodeID, const uint256& nodeHash, const uint256& targetIndex) : + std::runtime_error(nodeID.getString()), mType(t), + mNodeID(nodeID), mNodeHash(nodeHash), mTargetIndex(targetIndex) + { ; } + virtual ~SHAMapMissingNode() throw() { ; } - const SHAMapNode& getNodeID() const { return mNodeID; } - const uint256& getNodeHash() const { return mNodeHash; } + void setTargetNode(const uint256& tn) { mTargetIndex = tn; } + + SHAMapType getMapType() const { return mType; } + const SHAMapNode& getNodeID() const { return mNodeID; } + const uint256& getNodeHash() const { return mNodeHash; } + const uint256& getTargetIndex() const { return mTargetIndex; } + bool hasTargetIndex() const { return !mTargetIndex.isZero(); } }; +extern std::ostream& operator<<(std::ostream&, const SHAMapMissingNode&); + class SHAMap { public: @@ -272,6 +294,8 @@ private: SHAMapState mState; + SHAMapType mType; + protected: void dirtyUp(std::stack& stack, const uint256& target, uint256 prevHash); @@ -296,8 +320,8 @@ protected: public: // build new map - SHAMap(uint32 seq = 0); - SHAMap(const uint256& hash); + SHAMap(SHAMapType t, uint32 seq = 0); + SHAMap(SHAMapType t, const uint256& hash); ~SHAMap() { mState = smsInvalid; } @@ -308,6 +332,7 @@ public: ScopedLock Lock() const { return ScopedLock(mLock); } bool hasNode(const SHAMapNode& id); + void fetchRoot(const uint256& hash); // normal hash access functions bool hasItem(const uint256& id); diff --git a/src/SHAMapDiff.cpp b/src/SHAMapDiff.cpp index fa1ae6a64..d0e78e8f9 100644 --- a/src/SHAMapDiff.cpp +++ b/src/SHAMapDiff.cpp @@ -120,7 +120,7 @@ bool SHAMap::compare(SHAMap::ref otherMap, SHAMapDiff& differences, int maxCount if (!ourNode || !otherNode) { assert(false); - throw SHAMapMissingNode(dNode.mNodeID, uint256()); + throw SHAMapMissingNode(mType, dNode.mNodeID, uint256()); } if (ourNode->isLeaf() && otherNode->isLeaf()) diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index 746176aba..ad1e27749 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -18,6 +18,9 @@ std::string SHAMapNode::getString() const { + if ((mDepth == 0) && (mNodeID.isZero())) + return "NodeID(root)"; + return str(boost::format("NodeID(%s,%s)") % boost::lexical_cast(mDepth) % mNodeID.GetHex()); @@ -507,4 +510,16 @@ bool SHAMapTreeNode::setChildHash(int m, const uint256 &hash) return updateHash(); } +std::ostream& operator<<(std::ostream& out, const SHAMapMissingNode& mn) +{ + if (mn.getMapType() == smtTRANSACTION) + out << "Missing/TXN(" << mn.getNodeID() << ")"; + else if (mn.getMapType() == smtSTATE) + out << "Missing/STA(" << mn.getNodeID() << ")"; + else + out << "Missing/" << mn.getNodeID(); +} + + + // vim:ts=4 diff --git a/src/SHAMapSync.cpp b/src/SHAMapSync.cpp index fef3cf04a..7fdfac494 100644 --- a/src/SHAMapSync.cpp +++ b/src/SHAMapSync.cpp @@ -53,7 +53,7 @@ void SHAMap::getMissingNodes(std::vector& nodeIDs, std::vectorisInner() && !nextNode->isFullBelow()) return true; } - catch (SHAMapMissingNode) + catch (SHAMapMissingNode&) { return true; } } iNode->setFullBelow(); } while (!stack.empty()); - if (root->isFullBelow()) clearSynching(); + if (root->isFullBelow()) + clearSynching(); return true; } @@ -420,7 +421,7 @@ BOOST_AUTO_TEST_CASE( SHAMapSync_test ) srand(seed); cLog(lsTRACE) << "Constructing maps"; - SHAMap source, destination; + SHAMap source(smtFREE), destination(smtFREE); // add random data to the source map cLog(lsTRACE) << "Adding random data"; diff --git a/src/SerializedObject.cpp b/src/SerializedObject.cpp index fb30248f6..8e6e9df60 100644 --- a/src/SerializedObject.cpp +++ b/src/SerializedObject.cpp @@ -120,8 +120,8 @@ std::auto_ptr STObject::makeDeserializedObject(SerializedTypeID void STObject::set(const std::vector& type) { - mData.empty(); - mType.empty(); + mData.clear(); + mType.clear(); BOOST_FOREACH(const SOElement::ptr& elem, type) { @@ -138,7 +138,7 @@ bool STObject::setType(const std::vector &type) boost::ptr_vector newData; bool valid = true; - mType.empty(); + mType.clear(); BOOST_FOREACH(const SOElement::ptr& elem, type) { bool match = false; @@ -204,7 +204,7 @@ bool STObject::isFieldAllowed(SField::ref field) bool STObject::set(SerializerIterator& sit, int depth) { // return true = terminated with end-of-object - mData.empty(); + mData.clear(); while (!sit.empty()) { int type, field; diff --git a/src/TransactionAction.cpp b/src/TransactionAction.cpp index 920b65742..e0c61014e 100644 --- a/src/TransactionAction.cpp +++ b/src/TransactionAction.cpp @@ -504,9 +504,15 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn, const Transac return temDST_NEEDED; } + else if (bMax && !saMaxAmount.isPositive()) + { + Log(lsINFO) << "doPayment: Invalid transaction: bad max amount: " << saMaxAmount.getFullText(); + + return temBAD_AMOUNT; + } else if (!saDstAmount.isPositive()) { - Log(lsINFO) << "doPayment: Invalid transaction: bad amount: " << saDstAmount.getHumanCurrency() << " " << saDstAmount.getText(); + Log(lsINFO) << "doPayment: Invalid transaction: bad dst amount: " << saDstAmount.getFullText(); return temBAD_AMOUNT; }