From f88d6804a3f0a32a40b126c5794715890e103376 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 10:53:34 -0700 Subject: [PATCH 01/45] Cache the JSON of an ALTransaction. --- src/cpp/ripple/AcceptedLedger.cpp | 22 ++++++++++++---------- src/cpp/ripple/AcceptedLedger.h | 5 ++++- src/cpp/ripple/NetworkOPs.cpp | 4 ++-- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/cpp/ripple/AcceptedLedger.cpp b/src/cpp/ripple/AcceptedLedger.cpp index 0043d332d5..6226c3ebcb 100644 --- a/src/cpp/ripple/AcceptedLedger.cpp +++ b/src/cpp/ripple/AcceptedLedger.cpp @@ -14,17 +14,21 @@ ALTransaction::ALTransaction(uint32 seq, SerializerIterator& sit) mMeta = boost::make_shared(mTxn->getTransactionID(), seq, mRawMeta); mAffected = mMeta->getAffectedAccounts(); mResult = mMeta->getResultTER(); + buildJson(); } ALTransaction::ALTransaction(SerializedTransaction::ref txn, TransactionMetaSet::ref met) : mTxn(txn), mMeta(met), mAffected(met->getAffectedAccounts()) { mResult = mMeta->getResultTER(); + buildJson(); } ALTransaction::ALTransaction(SerializedTransaction::ref txn, TER result) : mTxn(txn), mResult(result), mAffected(txn->getMentionedAccounts()) -{ ; } +{ + buildJson(); +} std::string ALTransaction::getEscMeta() const { @@ -32,16 +36,16 @@ std::string ALTransaction::getEscMeta() const return sqlEscape(mRawMeta); } -Json::Value ALTransaction::getJson(int j) const +void ALTransaction::buildJson() { - Json::Value ret(Json::objectValue); - ret["transaction"] = mTxn->getJson(j); + mJson = Json::objectValue; + mJson["transaction"] = mTxn->getJson(0); if (mMeta) { - ret["meta"] = mMeta->getJson(j); - ret["raw_meta"] = strHex(mRawMeta); + mJson["meta"] = mMeta->getJson(0); + mJson["raw_meta"] = strHex(mRawMeta); } - ret["result"] = transHuman(mResult); + mJson["result"] = transHuman(mResult); if (!mAffected.empty()) { @@ -50,10 +54,8 @@ Json::Value ALTransaction::getJson(int j) const { affected.append(ra.humanAccountID()); } - ret["affected"] = affected; + mJson["affected"] = affected; } - - return ret; } AcceptedLedger::AcceptedLedger(Ledger::ref ledger) : mLedger(ledger) diff --git a/src/cpp/ripple/AcceptedLedger.h b/src/cpp/ripple/AcceptedLedger.h index 4b6297e559..dcc662b959 100644 --- a/src/cpp/ripple/AcceptedLedger.h +++ b/src/cpp/ripple/AcceptedLedger.h @@ -14,6 +14,9 @@ protected: TER mResult; std::vector mAffected; std::vector mRawMeta; + Json::Value mJson; + + void buildJson(); public: @@ -32,7 +35,7 @@ public: bool isApplied() const { return !!mMeta; } int getIndex() const { return mMeta ? mMeta->getIndex() : 0; } std::string getEscMeta() const; - Json::Value getJson(int) const; + Json::Value getJson() const { return mJson; } }; class AcceptedLedger diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 8b3e8aa98c..4ad0042228 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1375,7 +1375,7 @@ void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, SerializedTransac } } ALTransaction alt(stTxn, terResult); - cLog(lsTRACE) << "pubProposed: " << alt.getJson(0); + cLog(lsTRACE) << "pubProposed: " << alt.getJson(); pubAccountTransaction(lpCurrent, ALTransaction(stTxn, terResult), false); } @@ -1429,7 +1429,7 @@ void NetworkOPs::pubLedger(Ledger::ref accepted) { BOOST_FOREACH(const AcceptedLedger::value_type& vt, alpAccepted->getMap()) { - cLog(lsTRACE) << "pubAccepted: " << vt.second.getJson(0); + cLog(lsTRACE) << "pubAccepted: " << vt.second.getJson(); pubValidatedTransaction(lpAccepted, vt.second); } } From 32c133fe2698034f6ae7acaa1e9f0a1a99793047 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 14:16:19 -0700 Subject: [PATCH 02/45] Move winsock include to before boost includes. --- src/cpp/ripple/utils.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/utils.cpp b/src/cpp/ripple/utils.cpp index fb488f19ff..4c6d5707b4 100644 --- a/src/cpp/ripple/utils.cpp +++ b/src/cpp/ripple/utils.cpp @@ -4,11 +4,17 @@ #include #include #endif + #ifdef __FreeBSD__ #include #include #endif +#ifdef WIN32 +#define _WINSOCK_ +#include +#endif + #include #include @@ -307,8 +313,6 @@ int strIPtoInt(std::string& ipStr) } */ #ifdef WIN32 -#define _WINSOCK_ -#include //#include "Winsock2.h" //#include From e29e6fa13a2d981de3e807df89efd6c87e8ce294 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 14:19:35 -0700 Subject: [PATCH 03/45] newcoin->ripple --- src/cpp/ripple/RippleAddress.h | 4 ++-- src/cpp/ripple/uint256.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cpp/ripple/RippleAddress.h b/src/cpp/ripple/RippleAddress.h index 61a71a28e0..d12cf101b4 100644 --- a/src/cpp/ripple/RippleAddress.h +++ b/src/cpp/ripple/RippleAddress.h @@ -1,5 +1,5 @@ -#ifndef __NEWCOIN_ADDRESS__ -#define __NEWCOIN_ADDRESS__ +#ifndef __RIPPLE_ADDRESS__ +#define __RIPPLE_ADDRESS__ #include "base58.h" #include "uint256.h" diff --git a/src/cpp/ripple/uint256.h b/src/cpp/ripple/uint256.h index 88048bb699..a65f91c43d 100644 --- a/src/cpp/ripple/uint256.h +++ b/src/cpp/ripple/uint256.h @@ -2,8 +2,8 @@ // Copyright (c) 2011 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. -#ifndef NEWCOIN_UINT256_H -#define NEWCOIN_UINT256_H +#ifndef RIPPLE_UINT256_H +#define RIPPLE_UINT256_H #include #include From 62223a7b880abd7b1c685ab98cbc4f275a5b848b Mon Sep 17 00:00:00 2001 From: jed Date: Fri, 29 Mar 2013 14:48:51 -0700 Subject: [PATCH 04/45] windows --- src/cpp/ripple/RippleAddress.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cpp/ripple/RippleAddress.cpp b/src/cpp/ripple/RippleAddress.cpp index 7ef204277e..e2c0efcfa0 100644 --- a/src/cpp/ripple/RippleAddress.cpp +++ b/src/cpp/ripple/RippleAddress.cpp @@ -1,3 +1,4 @@ +#include #include "RippleAddress.h" #include From f96df4d84f477a08cdf7ea0fd8a1ece2171d33ab Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 15:48:54 -0700 Subject: [PATCH 05/45] Add the IP address to a log message. --- src/cpp/ripple/Peer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/Peer.cpp b/src/cpp/ripple/Peer.cpp index 55bb4f4e09..ea3474bbdb 100644 --- a/src/cpp/ripple/Peer.cpp +++ b/src/cpp/ripple/Peer.cpp @@ -1441,7 +1441,7 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) selectedPeer->sendPacket(boost::make_shared(packet, ripple::mtGET_LEDGER), false); return; } - cLog(lsERROR) << "We do not have the map our peer wants"; + cLog(lsERROR) << "We do not have the map our peer wants " << getIP(); punishPeer(LT_InvalidRequest); return; } From 3ba356716591c8bff8258bcab710c00f56c87793 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 15:51:41 -0700 Subject: [PATCH 06/45] Fix a bug that can cause a payment not to work if too many cheap "dust" paths clog out the paths with real liquidity. This is not a great fix, but it's a good fix. --- src/cpp/ripple/Pathfinder.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index ab829d2512..bbc3f5b446 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -78,18 +78,19 @@ PathOption::PathOption(PathOption::pointer other) } #endif +// quality, length, liquidity, index +typedef boost::tuple path_LQ_t; + // Lower numbers have better quality. Sort higher quality first. -static bool bQualityCmp( - std::pair< std::pair, unsigned int> a, - std::pair< std::pair, unsigned int> b) +static bool bQualityCmp(const path_LQ_t& a, const path_LQ_t&b) { - if (a.first.first != b.first.first) - return a.first.first < b.first.first; + if (a.get<0>() != b.get<0>()) + return a.get<0>() < b.get<0>(); - if (a.first.second != b.first.second) - return a.first.second < b.first.second; + if (a.get<1>() != b.get<1>()) + return a.get<1>() < b.get<1>(); - return a.second < b.second; // FIXME: this biases accounts + return a.get<2>() > b.get<2>(); } // Return true, if path is a default path with an element. @@ -537,7 +538,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax // Only filter, sort, and limit if have non-default paths. if (iLimit) { - std::vector< std::pair< std::pair, unsigned int> > vMap; + std::vector vMap; // Build map of quality to entry. for (int i = vspResults.size(); i--;) @@ -586,7 +587,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax % uQuality % spCurrent.getJson(0)); - vMap.push_back(std::make_pair(std::make_pair(uQuality, spCurrent.mPath.size()), i)); + vMap.push_back(path_LQ_t(uQuality, spCurrent.mPath.size(), saDstAmountAct, i)); } else { @@ -606,9 +607,12 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax std::sort(vMap.begin(), vMap.end(), bQualityCmp); // Lower is better and should be first. // Output best quality entries. - for (int i = 0; i != iLimit; ++i) + STAmount remaining = mDstAmount; + for (int i = 0; ((i < vMap.size()) && ((i < iLimit) || remaining.isGEZero())); ++i) { - spsDst.addPath(vspResults[vMap[i].second]); + path_LQ_t& lqt = vMap[i]; + remaining -= lqt.get<2>(); + spsDst.addPath(vspResults[lqt.get<3>()]); } cLog(lsDEBUG) << boost::str(boost::format("findPaths: RESULTS: %s") % spsDst.getJson(0)); From 064712b4f0659d3496b978e8b83e19b4ccc941b0 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 16:00:05 -0700 Subject: [PATCH 07/45] A more elegant fix. --- src/cpp/ripple/Pathfinder.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index bbc3f5b446..fe2e9c9ade 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -79,7 +79,7 @@ PathOption::PathOption(PathOption::pointer other) #endif // quality, length, liquidity, index -typedef boost::tuple path_LQ_t; +typedef boost::tuple path_LQ_t; // Lower numbers have better quality. Sort higher quality first. static bool bQualityCmp(const path_LQ_t& a, const path_LQ_t&b) @@ -587,7 +587,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax % uQuality % spCurrent.getJson(0)); - vMap.push_back(path_LQ_t(uQuality, spCurrent.mPath.size(), saDstAmountAct, i)); + vMap.push_back(path_LQ_t(uQuality, spCurrent.mPath.size(), saDstAmountAct, false, i)); } else { @@ -608,11 +608,25 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax // Output best quality entries. STAmount remaining = mDstAmount; - for (int i = 0; ((i < vMap.size()) && ((i < iLimit) || remaining.isGEZero())); ++i) + for (int i = 0; i < iLimit; ++i) { path_LQ_t& lqt = vMap[i]; remaining -= lqt.get<2>(); - spsDst.addPath(vspResults[lqt.get<3>()]); + lqt.get<3>() = true; + spsDst.addPath(vspResults[lqt.get<4>()]); + } + + if (remaining.isGEZero()) + { // we need an extra path to ensure liquidity + for (int i = 0; i < vMap.size(); ++i) + { + path_LQ_t& lqt = vMap[i]; + if (!lqt.get<3>() && (lqt.get<2>() >= remaining)) + { + spsDst.addPath(vspResults[lqt.get<4>()]); + break; + } + } } cLog(lsDEBUG) << boost::str(boost::format("findPaths: RESULTS: %s") % spsDst.getJson(0)); From 0578034d748627e136bb266b3d6b20333c2f6169 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 16:15:48 -0700 Subject: [PATCH 08/45] Begin supporting more paths. --- src/cpp/ripple/PaymentTransactor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/PaymentTransactor.cpp b/src/cpp/ripple/PaymentTransactor.cpp index 707b0ecf02..3a5f12a504 100644 --- a/src/cpp/ripple/PaymentTransactor.cpp +++ b/src/cpp/ripple/PaymentTransactor.cpp @@ -3,7 +3,7 @@ #include "RippleCalc.h" #include "Application.h" -#define RIPPLE_PATHS_MAX 3 +#define RIPPLE_PATHS_MAX 6 SETUP_LOG(); From 89fb28ff1dac0d48a9956f1ebb99125add650919 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 16:15:56 -0700 Subject: [PATCH 09/45] Missing timeout limit. # ../../../RippleLedger265050/ --- src/cpp/ripple/TransactionAcquire.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cpp/ripple/TransactionAcquire.cpp b/src/cpp/ripple/TransactionAcquire.cpp index 69630d4d55..662acf47d7 100644 --- a/src/cpp/ripple/TransactionAcquire.cpp +++ b/src/cpp/ripple/TransactionAcquire.cpp @@ -45,6 +45,13 @@ void TransactionAcquire::done() void TransactionAcquire::onTimer(bool progress) { + if (getTimeouts() > 10) + { + cLog(lsWARNING) << "Giving up on TX set " << getHash(); + mFailed = true; + done(); + return; + } if (!getPeerCount()) { // out of peers cLog(lsWARNING) << "Out of peers for TX set " << getHash(); From 159ba3f8480721fe7e688711b67fa163251cff13 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 16:16:14 -0700 Subject: [PATCH 10/45] Improve log message. --- src/cpp/ripple/LedgerAcquire.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/LedgerAcquire.cpp b/src/cpp/ripple/LedgerAcquire.cpp index 30b61e0af5..2ca423e981 100644 --- a/src/cpp/ripple/LedgerAcquire.cpp +++ b/src/cpp/ripple/LedgerAcquire.cpp @@ -196,7 +196,7 @@ void LedgerAcquire::onTimer(bool progress) if (getTimeouts() > LEDGER_TIMEOUT_COUNT) { - cLog(lsWARNING) << "Too many timeouts for ledger " << mHash; + cLog(lsWARNING) << "Too many timeouts( " << getTimeouts() << ") for ledger " << mHash; setFailed(); done(); return; From e8e927a0fb774d1e974eff339694f82e32a71e2a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 16:45:31 -0700 Subject: [PATCH 11/45] Last path must provide adequate liquidity. --- src/cpp/ripple/Pathfinder.cpp | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index fe2e9c9ade..78280190cb 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -79,7 +79,7 @@ PathOption::PathOption(PathOption::pointer other) #endif // quality, length, liquidity, index -typedef boost::tuple path_LQ_t; +typedef boost::tuple path_LQ_t; // Lower numbers have better quality. Sort higher quality first. static bool bQualityCmp(const path_LQ_t& a, const path_LQ_t&b) @@ -587,7 +587,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax % uQuality % spCurrent.getJson(0)); - vMap.push_back(path_LQ_t(uQuality, spCurrent.mPath.size(), saDstAmountAct, false, i)); + vMap.push_back(path_LQ_t(uQuality, spCurrent.mPath.size(), saDstAmountAct, i)); } else { @@ -600,35 +600,25 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax if (vMap.size()) { - iLimit = std::min(iMaxPaths, (unsigned int) vMap.size()); - bFound = true; std::sort(vMap.begin(), vMap.end(), bQualityCmp); // Lower is better and should be first. // Output best quality entries. STAmount remaining = mDstAmount; - for (int i = 0; i < iLimit; ++i) + for (int i = 0, iPathsLeft = iMaxPaths; (iPathsLeft > 0) && (i < vMap.size()); ++i) { path_LQ_t& lqt = vMap[i]; - remaining -= lqt.get<2>(); - lqt.get<3>() = true; - spsDst.addPath(vspResults[lqt.get<4>()]); - } - - if (remaining.isGEZero()) - { // we need an extra path to ensure liquidity - for (int i = 0; i < vMap.size(); ++i) - { - path_LQ_t& lqt = vMap[i]; - if (!lqt.get<3>() && (lqt.get<2>() >= remaining)) - { - spsDst.addPath(vspResults[lqt.get<4>()]); - break; - } + if ((iPathsLeft != 1) || (lqt.get<2>() >= remaining)) + { // last path must fill + --iPathsLeft; + remaining -= lqt.get<2>(); + spsDst.addPath(vspResults[lqt.get<3>()]); } } + bFound = !remaining.isGEZero(); + cLog(lsDEBUG) << boost::str(boost::format("findPaths: RESULTS: %s") % spsDst.getJson(0)); } else From 1de7ef20581dd5634d2366cc438b2136b18df3b4 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 16:53:15 -0700 Subject: [PATCH 12/45] Small tweak. --- src/cpp/ripple/Pathfinder.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index 78280190cb..50e5fd76a4 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -84,13 +84,20 @@ typedef boost::tuple path_LQ_t; // Lower numbers have better quality. Sort higher quality first. static bool bQualityCmp(const path_LQ_t& a, const path_LQ_t&b) { + // 1) Higher quality (lower cost) is better if (a.get<0>() != b.get<0>()) return a.get<0>() < b.get<0>(); + // 2) More liquidity (higher volume) is better + if (a.get<2>() != b.get<2>()) + return a.get<2>() > b.get<2>(); + + // 3) Shorter paths are better if (a.get<1>() != b.get<1>()) return a.get<1>() < b.get<1>(); - return a.get<2>() > b.get<2>(); + // 4) Tie breaker + return a.get<3>() > b.get<3>(); } // Return true, if path is a default path with an element. From 75dd924f3aa42eac7eb1a397854c5475f4c24df3 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 17:07:42 -0700 Subject: [PATCH 13/45] Don't spin when an IO+ thread returns to the job queue. --- src/cpp/ripple/JobQueue.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index d169a9ddad..ccd992da2c 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -291,12 +291,19 @@ void JobQueue::threadEntry() while (1) { NameThread("waiting"); + bool didIO = false; while (mJobSet.empty() && !mShuttingDown) { - if ((mIOThreadCount < mMaxIOThreadCount) && !theApp->isShutdown()) + if ((mIOThreadCount < mMaxIOThreadCount) && !didIO && !theApp->isShutdown()) + { IOThread(sl); + didIO = true; + } else + { mJobCond.wait(sl); + didIO = false; + } } if (mShuttingDown) From 1b8be99f89f8746e10dd2d213e3225a644230a67 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 17:12:45 -0700 Subject: [PATCH 14/45] Log when we skip a non-filling path. --- src/cpp/ripple/Pathfinder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index 50e5fd76a4..2cff853af0 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -622,6 +622,8 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax remaining -= lqt.get<2>(); spsDst.addPath(vspResults[lqt.get<3>()]); } + else + cLog(lsDEBUG) << "Skipping a non-filling path: " << vspResults[lqt.get<3>()].getJson(0); } bFound = !remaining.isGEZero(); From fc04d62bae81a5a25e4e4de43b192e0946b4d43a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 17:14:34 -0700 Subject: [PATCH 15/45] Temporarily comment out this check. --- src/cpp/ripple/Pathfinder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index 2cff853af0..841db618bb 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -626,7 +626,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax cLog(lsDEBUG) << "Skipping a non-filling path: " << vspResults[lqt.get<3>()].getJson(0); } - bFound = !remaining.isGEZero(); +// bFound = !remaining.isGEZero(); cLog(lsDEBUG) << boost::str(boost::format("findPaths: RESULTS: %s") % spsDst.getJson(0)); } From 3bfd62971a2b5090c9b1a6990dd52ae5a177bc17 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 17:37:30 -0700 Subject: [PATCH 16/45] Comment out code that breaks a unit test. --- src/cpp/ripple/Pathfinder.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index 841db618bb..73cbb0cf4b 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -607,8 +607,6 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax if (vMap.size()) { - bFound = true; - std::sort(vMap.begin(), vMap.end(), bQualityCmp); // Lower is better and should be first. // Output best quality entries. @@ -626,7 +624,12 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax cLog(lsDEBUG) << "Skipping a non-filling path: " << vspResults[lqt.get<3>()].getJson(0); } -// bFound = !remaining.isGEZero(); + bFound = true; + + // The lines below break the unit test "Issues Path negative: ripple-client issue #23: smaller" +// bFound = remaining.isZero() || remaining.isNegative(); +// if (!bFound) +// cLog(lsWARNING) << "Paths could not send " << remaining << " of " << mDstAmount; cLog(lsDEBUG) << boost::str(boost::format("findPaths: RESULTS: %s") % spsDst.getJson(0)); } From 4f029e25b67ca8acebe3d138edceaa2515222966 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 17:56:03 -0700 Subject: [PATCH 17/45] Temporarily back out the thread migration code until I can figure out if Boost's IO service is broken. --- src/cpp/ripple/JobQueue.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index ccd992da2c..05e5a2a634 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -291,19 +291,19 @@ void JobQueue::threadEntry() while (1) { NameThread("waiting"); - bool didIO = false; +// bool didIO = false; while (mJobSet.empty() && !mShuttingDown) { - if ((mIOThreadCount < mMaxIOThreadCount) && !didIO && !theApp->isShutdown()) - { - IOThread(sl); - didIO = true; - } - else - { +// if ((mIOThreadCount < mMaxIOThreadCount) && !didIO && !theApp->isShutdown()) +// { +// IOThread(sl); +// didIO = true; +// } +// else +// { mJobCond.wait(sl); - didIO = false; - } +// didIO = false; +// } } if (mShuttingDown) From 8215463879c5420f02e3968bf7ee4bf469c3fb27 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 18:28:16 -0700 Subject: [PATCH 18/45] Thread dispatch fixes. (code is commented out) --- src/cpp/ripple/Application.cpp | 1 + src/cpp/ripple/JobQueue.cpp | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 79e179d8b6..a9cef25392 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -46,6 +46,7 @@ DatabaseCon::~DatabaseCon() } Application::Application() : +// mIOService(2), 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(), diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index 05e5a2a634..8ba9ca548d 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -272,9 +272,7 @@ void JobQueue::IOThread(boost::mutex::scoped_lock& sl) NameThread("IO+"); try { - do - NameThread("IO+"); - while ((mIOService.poll_one() == 1) && !theApp->isShutdown()); + mIOService.poll(); } catch (...) { From 6af550800ae445f1b123ad3f8c27eaf4d46f91c7 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 19:41:15 -0700 Subject: [PATCH 19/45] Explain the issue. --- src/cpp/ripple/Pathfinder.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index 73cbb0cf4b..d3c1be196a 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -609,7 +609,10 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax { std::sort(vMap.begin(), vMap.end(), bQualityCmp); // Lower is better and should be first. - // Output best quality entries. +// if (bFound) +// { // must subtract liquidity in default path from remaining amount WRITEME +// } + STAmount remaining = mDstAmount; for (int i = 0, iPathsLeft = iMaxPaths; (iPathsLeft > 0) && (i < vMap.size()); ++i) { @@ -624,12 +627,11 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax cLog(lsDEBUG) << "Skipping a non-filling path: " << vspResults[lqt.get<3>()].getJson(0); } - bFound = true; - - // The lines below break the unit test "Issues Path negative: ripple-client issue #23: smaller" -// bFound = remaining.isZero() || remaining.isNegative(); -// if (!bFound) +// if (remaining.isPositive()) +// { +// bFound = false; // cLog(lsWARNING) << "Paths could not send " << remaining << " of " << mDstAmount; +// | cLog(lsDEBUG) << boost::str(boost::format("findPaths: RESULTS: %s") % spsDst.getJson(0)); } From fc435fa75f8ef81a213b270ac941c40474a6ede5 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 29 Mar 2013 20:19:47 -0700 Subject: [PATCH 20/45] Correctly account for liquidity in the implied default path. --- src/cpp/ripple/Pathfinder.cpp | 55 +++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index d3c1be196a..9628bb213d 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -609,11 +609,46 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax { std::sort(vMap.begin(), vMap.end(), bQualityCmp); // Lower is better and should be first. -// if (bFound) -// { // must subtract liquidity in default path from remaining amount WRITEME -// } - STAmount remaining = mDstAmount; + if (bFound) + { // must subtract liquidity in default path from remaining amount + try + { + STAmount saMaxAmountAct, saDstAmountAct; + std::vector vpsExpanded; + LedgerEntrySet lesSandbox(lesActive.duplicate()); + + TER result = RippleCalc::rippleCalc( + lesSandbox, + saMaxAmountAct, + saDstAmountAct, + vpsExpanded, + mSrcAmount, + mDstAmount, + mDstAccountID, + mSrcAccountID, + STPathSet(), + true, // allow partial payment + false, + false, // don't suppress default paths, that's the point + true); + + if (tesSUCCESS == result) + { + cLog(lsDEBUG) << "Default path contributes: " << saDstAmountAct; + remaining -= saDstAmountAct; + } + else + { + cLog(lsDEBUG) << "Default path fails: " << transToken(result); + } + } + catch (...) + { + cLog(lsDEBUG) << "Default path causes exception"; + } + } + for (int i = 0, iPathsLeft = iMaxPaths; (iPathsLeft > 0) && (i < vMap.size()); ++i) { path_LQ_t& lqt = vMap[i]; @@ -627,11 +662,13 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax cLog(lsDEBUG) << "Skipping a non-filling path: " << vspResults[lqt.get<3>()].getJson(0); } -// if (remaining.isPositive()) -// { -// bFound = false; -// cLog(lsWARNING) << "Paths could not send " << remaining << " of " << mDstAmount; -// | + if (remaining.isPositive()) + { + bFound = false; + cLog(lsINFO) << "Paths could not send " << remaining << " of " << mDstAmount; + } + else + bFound = true; cLog(lsDEBUG) << boost::str(boost::format("findPaths: RESULTS: %s") % spsDst.getJson(0)); } From 58b3cc1dd666fda42902dd34d42a6e37a0d18da1 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 30 Mar 2013 00:34:49 -0700 Subject: [PATCH 21/45] Add nexmo SMS support. --- src/cpp/ripple/CallRPC.cpp | 11 +++++++++ src/cpp/ripple/CallRPC.h | 1 + src/cpp/ripple/Config.cpp | 11 +++++++++ src/cpp/ripple/Config.h | 6 +++++ src/cpp/ripple/HttpsClient.cpp | 44 ++++++++++++++++++++++++++++++++++ src/cpp/ripple/HttpsClient.h | 2 ++ src/cpp/ripple/RPCHandler.cpp | 10 ++++++++ src/cpp/ripple/RPCHandler.h | 1 + 8 files changed, 86 insertions(+) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 6224869405..41ba8a749b 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -505,6 +505,16 @@ Json::Value RPCParser::parseSignSubmit(const Json::Value& jvParams) return rpcError(rpcINVALID_PARAMS); } +// sms +Json::Value RPCParser::parseSMS(const Json::Value& jvParams) +{ + Json::Value jvRequest; + + jvRequest["text"] = jvParams[0u].asString(); + + return jvRequest; +} + // tx Json::Value RPCParser::parseTx(const Json::Value& jvParams) { @@ -669,6 +679,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "random", &RPCParser::parseAsIs, 0, 0 }, { "ripple_path_find", &RPCParser::parseRipplePathFind, 1, 2 }, { "sign", &RPCParser::parseSignSubmit, 2, 2 }, + { "sms", &RPCParser::parseSMS, 1, 1 }, { "submit", &RPCParser::parseSignSubmit, 1, 2 }, { "server_info", &RPCParser::parseAsIs, 0, 0 }, { "server_state", &RPCParser::parseAsIs, 0, 0 }, diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index 4bc3e8439d..5860069418 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -33,6 +33,7 @@ protected: Json::Value parseOwnerInfo(const Json::Value& jvParams); Json::Value parseRandom(const Json::Value& jvParams); Json::Value parseRipplePathFind(const Json::Value& jvParams); + Json::Value parseSMS(const Json::Value& jvParams); Json::Value parseSignSubmit(const Json::Value& jvParams); Json::Value parseTx(const Json::Value& jvParams); Json::Value parseTxHistory(const Json::Value& jvParams); diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp index eb904eb594..d1e166cee5 100644 --- a/src/cpp/ripple/Config.cpp +++ b/src/cpp/ripple/Config.cpp @@ -45,6 +45,11 @@ #define SECTION_RPC_USER "rpc_user" #define SECTION_RPC_PASSWORD "rpc_password" #define SECTION_RPC_STARTUP "rpc_startup" +#define SECTION_SMS_FROM "sms_from" +#define SECTION_SMS_KEY "sms_key" +#define SECTION_SMS_SECRET "sms_secret" +#define SECTION_SMS_TO "sms_to" +#define SECTION_SMS_URL "sms_url" #define SECTION_SNTP "sntp_servers" #define SECTION_SSL_VERIFY "ssl_verify" #define SECTION_SSL_VERIFY_FILE "ssl_verify_file" @@ -473,6 +478,12 @@ void Config::load() if (sectionSingleB(secConfig, SECTION_ACCOUNT_PROBE_MAX, strTemp)) ACCOUNT_PROBE_MAX = boost::lexical_cast(strTemp); + (void) sectionSingleB(secConfig, SECTION_SMS_FROM, SMS_FROM); + (void) sectionSingleB(secConfig, SECTION_SMS_KEY, SMS_KEY); + (void) sectionSingleB(secConfig, SECTION_SMS_SECRET, SMS_SECRET); + (void) sectionSingleB(secConfig, SECTION_SMS_TO, SMS_TO); + (void) sectionSingleB(secConfig, SECTION_SMS_URL, SMS_URL); + if (sectionSingleB(secConfig, SECTION_VALIDATORS_FILE, strTemp)) VALIDATORS_FILE = strTemp; diff --git a/src/cpp/ripple/Config.h b/src/cpp/ripple/Config.h index f6e65856f5..2f31731e61 100644 --- a/src/cpp/ripple/Config.h +++ b/src/cpp/ripple/Config.h @@ -185,6 +185,12 @@ public: std::string SSL_VERIFY_FILE; std::string SSL_VERIFY_DIR; + std::string SMS_FROM; + std::string SMS_KEY; + std::string SMS_SECRET; + std::string SMS_TO; + std::string SMS_URL; + Config(); int getSize(SizedItemName); diff --git a/src/cpp/ripple/HttpsClient.cpp b/src/cpp/ripple/HttpsClient.cpp index 1144d98596..a2c5bb385b 100644 --- a/src/cpp/ripple/HttpsClient.cpp +++ b/src/cpp/ripple/HttpsClient.cpp @@ -443,4 +443,48 @@ void HttpsClient::httpsRequest( client->httpsRequest(bSSL, deqSites, setRequest, timeout, complete); } +#define SMS_TIMEOUT 30 + +bool responseSMS(const boost::system::error_code& ecResult, int iStatus, const std::string& strData) { + cLog(lsINFO) << "SMS: Response:" << iStatus << " :" << strData; + + return true; +} + +void HttpsClient::sendSMS(boost::asio::io_service& io_service, const std::string& strText) { + std::string strScheme; + std::string strDomain; + int iPort; + std::string strPath; + + if (theConfig.SMS_URL == "" || !parseUrl(theConfig.SMS_URL, strScheme, strDomain, iPort, strPath)) + { + cLog(lsWARNING) << "SMSRequest: Bad URL:" << theConfig.SMS_URL; + } + else + { + bool bSSL = strScheme == "https"; + + std::deque deqSites(1, strDomain); + std::string strURI = + boost::str(boost::format("%s?from=%s&to=%s&api_key=%s&api_secret=%s&text=%s") + % (strPath.empty() ? "/" : strPath) + % theConfig.SMS_FROM + % theConfig.SMS_TO + % theConfig.SMS_KEY + % theConfig.SMS_SECRET + % strText); + + // cLog(lsINFO) << "SMS: Request:" << strURI; + cLog(lsINFO) << "SMS: Request: '" << strText << "'"; + + if (iPort < 0) + iPort = bSSL ? 443 : 80; + + boost::shared_ptr client(new HttpsClient(io_service, iPort, CLIENT_MAX_HEADER)); + + client->httpsGet(bSSL, deqSites, strURI, boost::posix_time::seconds(SMS_TIMEOUT), + BIND_TYPE(&responseSMS, P_1, P_2, P_3)); + } +} // vim:ts=4 diff --git a/src/cpp/ripple/HttpsClient.h b/src/cpp/ripple/HttpsClient.h index 909594efe1..a433c0a9d2 100644 --- a/src/cpp/ripple/HttpsClient.h +++ b/src/cpp/ripple/HttpsClient.h @@ -116,6 +116,8 @@ public: std::size_t responseMax, boost::posix_time::time_duration timeout, FUNCTION_TYPE complete); + + static void sendSMS(boost::asio::io_service& io_service, const std::string& strText); }; #endif // vim:ts=4 diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index dc798aefa3..617ac3bc82 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2235,6 +2235,15 @@ Json::Value RPCHandler::doUnlScore(Json::Value, int& cost) return "scoring requested"; } +Json::Value RPCHandler::doSMS(Json::Value jvRequest, int& cost) +{ + if (!jvRequest.isMember("text")) + return rpcError(rpcINVALID_PARAMS); + + HttpsClient::sendSMS(theApp->getIOService(), jvRequest["text"].asString()); + + return "sms dispatched"; +} Json::Value RPCHandler::doStop(Json::Value, int& cost) { theApp->stop(); @@ -3250,6 +3259,7 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole, int & { "submit", &RPCHandler::doSubmit, false, optCurrent }, { "server_info", &RPCHandler::doServerInfo, false, optNone }, { "server_state", &RPCHandler::doServerState, false, optNone }, + { "sms", &RPCHandler::doSMS, true, optNone }, { "stop", &RPCHandler::doStop, true, optNone }, { "transaction_entry", &RPCHandler::doTransactionEntry, false, optCurrent }, { "tx", &RPCHandler::doTx, false, optNetwork }, diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index b716ebc55f..5ce16c45fd 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -76,6 +76,7 @@ class RPCHandler Json::Value doServerState(Json::Value params, int& cost); // for machines Json::Value doSessionClose(Json::Value params, int& cost); Json::Value doSessionOpen(Json::Value params, int& cost); + Json::Value doSMS(Json::Value params, int& cost); Json::Value doStop(Json::Value params, int& cost); Json::Value doSign(Json::Value params, int& cost); Json::Value doSubmit(Json::Value params, int& cost); From c67366ea25a60128068a130286788b35c47340d8 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 30 Mar 2013 01:00:00 -0700 Subject: [PATCH 22/45] Url Encode SMS messages. --- src/cpp/ripple/HttpsClient.cpp | 2 +- src/cpp/ripple/utils.cpp | 32 ++++++++++++++++++++++++++++++++ src/cpp/ripple/utils.h | 6 ++++-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/HttpsClient.cpp b/src/cpp/ripple/HttpsClient.cpp index a2c5bb385b..9e4c313c7a 100644 --- a/src/cpp/ripple/HttpsClient.cpp +++ b/src/cpp/ripple/HttpsClient.cpp @@ -473,7 +473,7 @@ void HttpsClient::sendSMS(boost::asio::io_service& io_service, const std::string % theConfig.SMS_TO % theConfig.SMS_KEY % theConfig.SMS_SECRET - % strText); + % urlEncode(strText)); // cLog(lsINFO) << "SMS: Request:" << strURI; cLog(lsINFO) << "SMS: Request: '" << strText << "'"; diff --git a/src/cpp/ripple/utils.cpp b/src/cpp/ripple/utils.cpp index 4c6d5707b4..de771bc7b9 100644 --- a/src/cpp/ripple/utils.cpp +++ b/src/cpp/ripple/utils.cpp @@ -187,6 +187,38 @@ std::string strCopy(const std::vector& vucSrc) } +extern std::string urlEncode(const std::string& strSrc) +{ + std::string strDst; + int iOutput = 0; + int iSize = strSrc.length(); + + strDst.resize(iSize*3); + + for (int iInput = 0; iInput < iSize; iInput++) { + unsigned char c = strSrc[iInput]; + + if (c == ' ') + { + strDst[iOutput++] = '+'; + } + else if (isalnum(c)) + { + strDst[iOutput++] = c; + } + else + { + strDst[iOutput++] = '%'; + strDst[iOutput++] = charHex(c >> 4); + strDst[iOutput++] = charHex(c & 15); + } + } + + strDst.resize(iOutput); + + return strDst; +} + // // DH support // diff --git a/src/cpp/ripple/utils.h b/src/cpp/ripple/utils.h index 2c60a773f3..19333c3bee 100644 --- a/src/cpp/ripple/utils.h +++ b/src/cpp/ripple/utils.h @@ -103,6 +103,8 @@ int iToSeconds(boost::posix_time::ptime ptWhen); boost::posix_time::ptime ptFromSeconds(int iSeconds); uint64_t utFromSeconds(int iSeconds); +extern std::string urlEncode(const std::string& strSrc); + /* void intIPtoStr(int ip,std::string& retStr); int strIPtoInt(std::string& ipStr); @@ -214,8 +216,8 @@ DH* DH_der_load(const std::string& strDer); std::string DH_der_gen(int iKeyLength); void getRand(unsigned char *buf, int num); -inline static void getRand(char *buf, int num) { return getRand(reinterpret_cast(buf), num); } -inline static void getRand(void *buf, int num) { return getRand(reinterpret_cast(buf), num); } +inline static void getRand(char *buf, int num) { return getRand(reinterpret_cast(buf), num); } +inline static void getRand(void *buf, int num) { return getRand(reinterpret_cast(buf), num); } inline std::string strGetEnv(const std::string& strKey) { From 97d73de16898d4036c2253809f35ccc29135a758 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 30 Mar 2013 13:46:34 -0700 Subject: [PATCH 23/45] Require path nodes for offers to specify a currency or issuer change. --- src/cpp/ripple/RippleCalc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp index a8fe832076..4cd0965c7e 100644 --- a/src/cpp/ripple/RippleCalc.cpp +++ b/src/cpp/ripple/RippleCalc.cpp @@ -160,6 +160,12 @@ TER PathState::pushNode( terResult = temBAD_PATH; } + else if (!bAccount && !bCurrency && !bIssuer) + { + cLog(lsDEBUG) << "pushNode: offer must specify at least currency or issuer."; + + terResult = temBAD_PATH; + } else if (bAccount) { // Account link From 0dadf5316702244a6f9d3cc2b166d51999fc6b72 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 30 Mar 2013 15:49:58 -0700 Subject: [PATCH 24/45] Fix pathfinding mis-setting currency on order book out. --- src/cpp/ripple/Pathfinder.cpp | 12 +++-- src/cpp/ripple/RippleCalc.cpp | 92 ++++++++++++++++++++--------------- src/cpp/ripple/RippleCalc.h | 2 + 3 files changed, 65 insertions(+), 41 deletions(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index 9628bb213d..62e44c4f57 100644 --- a/src/cpp/ripple/Pathfinder.cpp +++ b/src/cpp/ripple/Pathfinder.cpp @@ -133,13 +133,15 @@ bool Pathfinder::bDefaultPath(const STPath& spPath) // Expand the current path. pspCurrent->setExpanded(lesActive, spPath, mDstAccountID, mSrcAccountID); + // XXX Need to report or act on errors returned in pspCurrent->terStatus. // Determine if expanded current path is the default. // When path is a default (implied). Don't need to add it to return set. bDefault = pspCurrent->vpnNodes == mPsDefault->vpnNodes; cLog(lsTRACE) << "bDefaultPath: expanded path: " << pspCurrent->getJson(); - cLog(lsTRACE) << "bDefaultPath: default path: indirect: " << spPath.getJson(0); + cLog(lsTRACE) << "bDefaultPath: source path: " << spPath.getJson(0); + cLog(lsTRACE) << "bDefaultPath: default path: " << mPsDefault->getJson(); return bDefault; } @@ -384,14 +386,16 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax } else if (!speEnd.mCurrencyID) { + // 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()) { // New end is an order book with the currency and issuer. - // Don't allow looping through same order books. if (!spPath.hasSeen(ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut())) { + // Not a order book already in path. STPath spNew(spPath); STPathElement speBook(ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut()); STPathElement speAccount(book->getIssuerOut(), book->getCurrencyOut(), book->getIssuerOut()); @@ -401,6 +405,8 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax cLog(lsDEBUG) << boost::str(boost::format("findPaths: XRP -> %s/%s") +// % STAmount::createHumanCurrency(book->getCurrencyOut()) +// % RippleAddress::createHumanAccountID(book->getIssuerOut()) % STAmount::createHumanCurrency(speBook.mCurrencyID) % RippleAddress::createHumanAccountID(speBook.mIssuerID)); @@ -515,7 +521,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax spNew.mPath.push_back(speBook); // Add the order book. - if (!book->getCurrencyOut()) + if (!!book->getCurrencyOut()) { // For non-XRP out, don't end on the book, add the issuing account. STPathElement speAccount(book->getIssuerOut(), book->getCurrencyOut(), book->getIssuerOut()); diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp index 4cd0965c7e..89eba9d58d 100644 --- a/src/cpp/ripple/RippleCalc.cpp +++ b/src/cpp/ripple/RippleCalc.cpp @@ -35,6 +35,55 @@ bool PaymentNode::operator==(const PaymentNode& pnOther) const { && pnOther.uIssuerID == uIssuerID; } +// This is for debugging not end users. Output names can be changed without warning. +Json::Value PaymentNode::getJson() const +{ + Json::Value jvNode(Json::objectValue); + Json::Value jvFlags(Json::arrayValue); + + jvNode["type"] = uFlags; + + if (isSetBit(uFlags, STPathElement::typeAccount) || !!uAccountID) + jvFlags.append(!!isSetBit(uFlags, STPathElement::typeAccount) == !!uAccountID ? "account" : "-account"); + + if (isSetBit(uFlags, STPathElement::typeCurrency) || !!uCurrencyID) + jvFlags.append(!!isSetBit(uFlags, STPathElement::typeCurrency) == !!uCurrencyID ? "currency" : "-currency"); + + if (isSetBit(uFlags, STPathElement::typeIssuer) || !!uIssuerID) + jvFlags.append(!!isSetBit(uFlags, STPathElement::typeIssuer) == !!uIssuerID ? "issuer" : "-issuer"); + + jvNode["flags"] = jvFlags; + + if (!!uAccountID) + jvNode["account"] = RippleAddress::createHumanAccountID(uAccountID); + + if (!!uCurrencyID) + jvNode["currency"] = STAmount::createHumanCurrency(uCurrencyID); + + if (!!uIssuerID) + jvNode["issuer"] = RippleAddress::createHumanAccountID(uIssuerID); + + if (saRevRedeem) + jvNode["rev_redeem"] = saRevRedeem.getFullText(); + + if (saRevIssue) + jvNode["rev_issue"] = saRevIssue.getFullText(); + + if (saRevDeliver) + jvNode["rev_deliver"] = saRevDeliver.getFullText(); + + if (saFwdRedeem) + jvNode["fwd_redeem"] = saFwdRedeem.getFullText(); + + if (saFwdIssue) + jvNode["fwd_issue"] = saFwdIssue.getFullText(); + + if (saFwdDeliver) + jvNode["fwd_deliver"] = saFwdDeliver.getFullText(); + + return jvNode; +} + // // PathState implementation // @@ -273,7 +322,9 @@ TER PathState::pushNode( } if (tesSUCCESS == terResult) + { vpnNodes.push_back(pnCur); + } } else { @@ -311,6 +362,7 @@ TER PathState::pushNode( vpnNodes.push_back(pnCur); } } + cLog(lsTRACE) << boost::str(boost::format("pushNode< : %s") % transToken(terResult)); return terResult; @@ -335,7 +387,7 @@ void PathState::setExpanded( const uint160 uOutIssuerID = saOutReq.getIssuer(); const uint160 uSenderIssuerID = !!uMaxCurrencyID ? uSenderID : ACCOUNT_XRP; // Sender is always issuer for non-XRP. - // cLog(lsDEBUG) << boost::str(boost::format("setExpanded>")); + cLog(lsDEBUG) << boost::str(boost::format("setExpanded> %s") % spSourcePath.getJson(0)); lesEntries = lesSource.duplicate(); @@ -732,43 +784,7 @@ Json::Value PathState::getJson() const BOOST_FOREACH(const PaymentNode& pnNode, vpnNodes) { - Json::Value jvNode(Json::objectValue); - - Json::Value jvFlags(Json::arrayValue); - - if (pnNode.uFlags & STPathElement::typeAccount) - jvFlags.append("account"); - - jvNode["flags"] = jvFlags; - - if (pnNode.uFlags & STPathElement::typeAccount) - jvNode["account"] = RippleAddress::createHumanAccountID(pnNode.uAccountID); - - if (!!pnNode.uCurrencyID) - jvNode["currency"] = STAmount::createHumanCurrency(pnNode.uCurrencyID); - - if (!!pnNode.uIssuerID) - jvNode["issuer"] = RippleAddress::createHumanAccountID(pnNode.uIssuerID); - - if (pnNode.saRevRedeem) - jvNode["rev_redeem"] = pnNode.saRevRedeem.getFullText(); - - if (pnNode.saRevIssue) - jvNode["rev_issue"] = pnNode.saRevIssue.getFullText(); - - if (pnNode.saRevDeliver) - jvNode["rev_deliver"] = pnNode.saRevDeliver.getFullText(); - - if (pnNode.saFwdRedeem) - jvNode["fwd_redeem"] = pnNode.saFwdRedeem.getFullText(); - - if (pnNode.saFwdIssue) - jvNode["fwd_issue"] = pnNode.saFwdIssue.getFullText(); - - if (pnNode.saFwdDeliver) - jvNode["fwd_deliver"] = pnNode.saFwdDeliver.getFullText(); - - jvNodes.append(jvNode); + jvNodes.append(pnNode.getJson()); } jvPathState["status"] = terStatus; diff --git a/src/cpp/ripple/RippleCalc.h b/src/cpp/ripple/RippleCalc.h index c23f2be837..d1cdc92597 100644 --- a/src/cpp/ripple/RippleCalc.h +++ b/src/cpp/ripple/RippleCalc.h @@ -56,6 +56,8 @@ protected: public: bool operator==(const PaymentNode& pnOther) const; + + Json::Value getJson() const; }; // account id, currency id, issuer id :: node From 7ac5f3f34c3b24defa0e75d204e5f3bf25798fb8 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 30 Mar 2013 16:59:12 -0700 Subject: [PATCH 25/45] Don't return canonical paths. It's broken. --- src/cpp/ripple/RPCHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 617ac3bc82..c33649a747 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1334,7 +1334,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost) jvEntry["source_amount"] = saMaxAmountAct.getJson(0); // jvEntry["paths_expanded"] = vpsExpanded.getJson(0); - jvEntry["paths_canonical"] = spsCanonical.getJson(0); + jvEntry["paths_canonical"] = Json::arrayValue; // spsCanonical.getJson(0); jvEntry["paths_computed"] = spsComputed.getJson(0); jvArray.append(jvEntry); From fbe88234391ba3d3fb18e26aa89e20c1ff3ce5ad Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 30 Mar 2013 19:53:34 -0700 Subject: [PATCH 26/45] Don't blow up if we have a ledger in the ledger DB but not its root map node in the node DB. --- src/cpp/ripple/Ledger.cpp | 41 +++++++++++++++++++++++++++++++-------- src/cpp/ripple/Ledger.h | 2 +- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 737bda4803..95dccf65ca 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -44,7 +44,8 @@ Ledger::Ledger(const RippleAddress& masterID, uint64 startAmount) : mTotCoins(st } Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, - uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, uint32 ledgerSeq) + uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, + int closeFlags, int closeResolution, uint32 ledgerSeq, bool& loaded) : mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash), mTotCoins(totCoins), mLedgerSeq(ledgerSeq), mCloseTime(closeTime), mParentCloseTime(parentCloseTime), mCloseResolution(closeResolution), mCloseFlags(closeFlags), @@ -53,10 +54,27 @@ Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint25 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); + loaded = true; + try + { + if (mTransHash.isNonZero()) + mTransactionMap->fetchRoot(mTransHash); + } + catch (...) + { + loaded = false; + cLog(lsWARNING) << "Don't have TX root for ledger"; + } + try + { + if (mAccountHash.isNonZero()) + mAccountStateMap->fetchRoot(mAccountHash); + } + catch (...) + { + loaded = false; + cLog(lsWARNING) << "Don't have AS root for ledger"; + } mTransactionMap->setImmutable(); mAccountStateMap->setImmutable(); zeroFees(); @@ -610,8 +628,11 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) } // CAUTION: code below appears in two places + bool loaded; Ledger::pointer ret = boost::make_shared(prevHash, transHash, accountHash, totCoins, - closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq); + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded); + if (!loaded) + return Ledger::pointer(); ret->setClosed(); if (theApp->getOPs().haveLedger(ledgerSeq)) ret->setAccepted(); @@ -662,8 +683,12 @@ Ledger::pointer Ledger::getSQL1(SqliteStatement *stmt) ledgerSeq = stmt->getUInt32(9); // CAUTION: code below appears in two places - return boost::make_shared(prevHash, transHash, accountHash, totCoins, - closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq); + bool loaded; + Ledger::pointer ret = boost::make_shared(prevHash, transHash, accountHash, totCoins, + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded); + if (!loaded) + return Ledger::pointer(); + return ret; } void Ledger::getSQL2(Ledger::ref ret) diff --git a/src/cpp/ripple/Ledger.h b/src/cpp/ripple/Ledger.h index 5dda848805..36922a1795 100644 --- a/src/cpp/ripple/Ledger.h +++ b/src/cpp/ripple/Ledger.h @@ -109,7 +109,7 @@ public: Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, - uint32 ledgerSeq); // used for database ledgers + uint32 ledgerSeq, bool& loaded); // used for database ledgers Ledger(const std::vector& rawLedger, bool hasPrefix); Ledger(const std::string& rawLedger, bool hasPrefix); From ac202c199d241ccefac2f805730b63e121940618 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 31 Mar 2013 10:37:08 -0700 Subject: [PATCH 27/45] If an addition or subtraction has a microscopic amount left, make it zero. --- src/cpp/ripple/Amount.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index 7546aa9cfa..115fd9a367 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -799,7 +799,9 @@ STAmount operator+(const STAmount& v1, const STAmount& v2) // this addition cannot overflow an int64, it can overflow an STAmount and the constructor will throw int64 fv = vv1 + vv2; - if (fv >= 0) + if ((fv >= -10) && (fv <= 10)) + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer); + else if (fv >= 0) return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, fv, ov1, false); else return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, -fv, ov1, true); @@ -834,7 +836,9 @@ STAmount operator-(const STAmount& v1, const STAmount& v2) // this subtraction cannot overflow an int64, it can overflow an STAmount and the constructor will throw int64 fv = vv1 - vv2; - if (fv >= 0) + if ((fv >= -10) && (fv <= 10)) + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer); + else if (fv >= 0) return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, fv, ov1, false); else return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, -fv, ov1, true); From 2f943126e69600fa6fc63ed45e4cee015fb104fd Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 31 Mar 2013 15:36:34 -0700 Subject: [PATCH 28/45] Must pass by reference. --- src/cpp/ripple/Ledger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 95dccf65ca..89820e8d72 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -630,7 +630,7 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) // CAUTION: code below appears in two places bool loaded; Ledger::pointer ret = boost::make_shared(prevHash, transHash, accountHash, totCoins, - closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded); + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, boost::ref(loaded)); if (!loaded) return Ledger::pointer(); ret->setClosed(); @@ -685,7 +685,7 @@ Ledger::pointer Ledger::getSQL1(SqliteStatement *stmt) // CAUTION: code below appears in two places bool loaded; Ledger::pointer ret = boost::make_shared(prevHash, transHash, accountHash, totCoins, - closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded); + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, boost::ref(loaded)); if (!loaded) return Ledger::pointer(); return ret; From bd4e00e391c39a45a9d73a6c0e0dec8731389f51 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 31 Mar 2013 15:39:38 -0700 Subject: [PATCH 29/45] On some platforms, make_shared can only count to 9. --- src/cpp/ripple/Ledger.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 89820e8d72..dea0b4b882 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -629,8 +629,8 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) // CAUTION: code below appears in two places bool loaded; - Ledger::pointer ret = boost::make_shared(prevHash, transHash, accountHash, totCoins, - closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, boost::ref(loaded)); + Ledger::pointer ret(new Ledger(prevHash, transHash, accountHash, totCoins, + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded)); if (!loaded) return Ledger::pointer(); ret->setClosed(); @@ -684,8 +684,8 @@ Ledger::pointer Ledger::getSQL1(SqliteStatement *stmt) // CAUTION: code below appears in two places bool loaded; - Ledger::pointer ret = boost::make_shared(prevHash, transHash, accountHash, totCoins, - closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, boost::ref(loaded)); + Ledger::pointer ret(new Ledger(prevHash, transHash, accountHash, totCoins, + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded)); if (!loaded) return Ledger::pointer(); return ret; From 96733c287476b7279289e8884a357a1c827a7bf7 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 31 Mar 2013 16:15:45 -0700 Subject: [PATCH 30/45] Add trust auto clear. Fixes #28 --- src/cpp/ripple/LedgerEntrySet.cpp | 79 ++++++++++++++-- src/cpp/ripple/LedgerEntrySet.h | 1 + src/cpp/ripple/TrustSetTransactor.cpp | 17 +--- test/path-test.js | 128 ++++++++++++++++++++++++++ test/testutils.js | 2 +- 5 files changed, 200 insertions(+), 27 deletions(-) diff --git a/src/cpp/ripple/LedgerEntrySet.cpp b/src/cpp/ripple/LedgerEntrySet.cpp index d96a820fda..5f58c25932 100644 --- a/src/cpp/ripple/LedgerEntrySet.cpp +++ b/src/cpp/ripple/LedgerEntrySet.cpp @@ -150,8 +150,9 @@ void LedgerEntrySet::entryCreate(SLE::ref sle) case taaMODIFY: throw std::runtime_error("Create after modify"); + case taaCREATE: - throw std::runtime_error("Create after create"); // We could make this work + throw std::runtime_error("Create after create"); // This could be made to work case taaCACHED: throw std::runtime_error("Create after cache"); @@ -532,7 +533,7 @@ TER LedgerEntrySet::dirCount(const uint256& uRootIndex, uint32& uCount) // <-- uNodeDir: For deletion, present to make dirDelete efficient. // --> uRootIndex: The index of the base of the directory. Nodes are based off of this. // --> uLedgerIndex: Value to add to directory. -// We only append. This allow for things that watch append only structure to just monitor from the last node on ward. +// Only append. This allow for things that watch append only structure to just monitor from the last node on ward. // Within a node with no deletions order of elements is sequential. Otherwise, order of elements is random. TER LedgerEntrySet::dirAdd( uint64& uNodeDir, @@ -850,7 +851,7 @@ bool LedgerEntrySet::dirFirst( sleNode = entryCache(ltDIR_NODE, uRootIndex); uDirEntry = 0; - assert(sleNode); // We never probe for directories. + assert(sleNode); // Never probe for directories. return LedgerEntrySet::dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex); } @@ -1243,13 +1244,38 @@ TER LedgerEntrySet::trustCreate( ownerCountAdjust(!bSetDst ? uSrcAccountID : uDstAccountID, 1, sleAccount); - sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); + sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); // ONLY: Create ripple balance. } return terResult; } -// Direct send w/o fees: redeeming IOUs and/or sending own IOUs. +TER LedgerEntrySet::trustDelete(SLE::ref sleRippleState, const uint160& uLowAccountID, const uint160& uHighAccountID) +{ + bool bLowNode = sleRippleState->isFieldPresent(sfLowNode); // Detect legacy dirs. + bool bHighNode = sleRippleState->isFieldPresent(sfHighNode); + uint64 uLowNode = sleRippleState->getFieldU64(sfLowNode); + uint64 uHighNode = sleRippleState->getFieldU64(sfHighNode); + TER terResult; + + cLog(lsTRACE) << "trustDelete: Deleting ripple line: low"; + terResult = dirDelete(false, uLowNode, Ledger::getOwnerDirIndex(uLowAccountID), sleRippleState->getIndex(), false, !bLowNode); + + if (tesSUCCESS == terResult) + { + cLog(lsTRACE) << "trustDelete: Deleting ripple line: high"; + terResult = dirDelete(false, uHighNode, Ledger::getOwnerDirIndex(uHighAccountID), sleRippleState->getIndex(), false, !bHighNode); + } + + cLog(lsINFO) << "trustDelete: Deleting ripple line: state"; + entryDelete(sleRippleState); + + return terResult; +} + +// Direct send w/o fees: +// - Redeeming IOUs and/or sending sender's own IOUs. +// - Create trust line of needed. TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer) { uint160 uIssuerID = saAmount.getIssuer(); @@ -1306,14 +1332,46 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei % saAmount.getFullText() % saBalance.getFullText()); + bool bDelete = false; + uint32 uFlags; + + // YYY Could skip this if rippling in reverse. + if (saBefore.isPositive() // Sender balance was positive. + && !saBalance.isPositive() // Sender is zero or negative. + && isSetBit((uFlags = sleRippleState->getFieldU32(sfFlags)), !bSenderHigh ? lsfLowReserve : lsfHighReserve) // Sender reserve is set. + && !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) // Sender trust limit is 0. + && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) // Sender quality in is 0. + && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) // Sender quality out is 0. + { + // Clear the reserve of the sender, possibly delete the line! + SLE::pointer sleSender = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uSenderID)); + + ownerCountAdjust(uSenderID, -1, sleSender); + + sleRippleState->setFieldU32(sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); // Clear reserve flag. + + bDelete = !saBalance // Balance is zero. + && !isSetBit(uFlags, bSenderHigh ? lsfLowReserve : lsfHighReserve); // Receiver reserve is clear. + } + if (bSenderHigh) saBalance.negate(); - sleRippleState->setFieldAmount(sfBalance, saBalance); + // Want to reflect balance to zero even if we are deleting line. + sleRippleState->setFieldAmount(sfBalance, saBalance); // ONLY: Adjust ripple balance. - entryModify(sleRippleState); + if (bDelete) + { + terResult = trustDelete(sleRippleState, + bSenderHigh ? uReceiverID : uSenderID, + !bSenderHigh ? uReceiverID : uSenderID); + } + else + { + entryModify(sleRippleState); - terResult = tesSUCCESS; + terResult = tesSUCCESS; + } } return terResult; @@ -1374,6 +1432,7 @@ TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiv } else if (saAmount.isNative()) { + // XRP send which does not check reserve and can do pure adjustment. SLE::pointer sleSender = !!uSenderID ? entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uSenderID)) : SLE::pointer(); @@ -1398,14 +1457,14 @@ TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiv } else { - sleSender->setFieldAmount(sfBalance, sleSender->getFieldAmount(sfBalance) - saAmount); + sleSender->setFieldAmount(sfBalance, sleSender->getFieldAmount(sfBalance) - saAmount); // Decrement XRP balance. entryModify(sleSender); } } if (tesSUCCESS == terResult && sleReceiver) { - sleReceiver->setFieldAmount(sfBalance, sleReceiver->getFieldAmount(sfBalance) + saAmount); + sleReceiver->setFieldAmount(sfBalance, sleReceiver->getFieldAmount(sfBalance) + saAmount); // Increment XRP balance. entryModify(sleReceiver); } diff --git a/src/cpp/ripple/LedgerEntrySet.h b/src/cpp/ripple/LedgerEntrySet.h index 09f9bb0cee..44e61b53f4 100644 --- a/src/cpp/ripple/LedgerEntrySet.h +++ b/src/cpp/ripple/LedgerEntrySet.h @@ -156,6 +156,7 @@ public: const STAmount& saSrcLimit, const uint32 uSrcQualityIn = 0, const uint32 uSrcQualityOut = 0); + TER trustDelete(SLE::ref sleRippleState, const uint160& uLowAccountID, const uint160& uHighAccountID); Json::Value getJson(int) const; void calcRawMeta(Serializer&, TER result, uint32 index); diff --git a/src/cpp/ripple/TrustSetTransactor.cpp b/src/cpp/ripple/TrustSetTransactor.cpp index 9333d51259..00925ea6ee 100644 --- a/src/cpp/ripple/TrustSetTransactor.cpp +++ b/src/cpp/ripple/TrustSetTransactor.cpp @@ -252,22 +252,7 @@ TER TrustSetTransactor::doApply() { // Can delete. - bool bLowNode = sleRippleState->isFieldPresent(sfLowNode); // Detect legacy dirs. - bool bHighNode = sleRippleState->isFieldPresent(sfHighNode); - uint64 uLowNode = sleRippleState->getFieldU64(sfLowNode); - uint64 uHighNode = sleRippleState->getFieldU64(sfHighNode); - - cLog(lsTRACE) << "doTrustSet: Deleting ripple line: low"; - terResult = mEngine->getNodes().dirDelete(false, uLowNode, Ledger::getOwnerDirIndex(uLowAccountID), sleRippleState->getIndex(), false, !bLowNode); - - if (tesSUCCESS == terResult) - { - cLog(lsTRACE) << "doTrustSet: Deleting ripple line: high"; - terResult = mEngine->getNodes().dirDelete(false, uHighNode, Ledger::getOwnerDirIndex(uHighAccountID), sleRippleState->getIndex(), false, !bHighNode); - } - - cLog(lsINFO) << "doTrustSet: Deleting ripple line: state"; - mEngine->entryDelete(sleRippleState); + terResult = mEngine->getNodes().trustDelete(sleRippleState, uLowAccountID, uHighAccountID); } else if (bReserveIncrease && mPriorBalance.getNValue() < uReserveCreate) // Reserve is not scaled by load. diff --git a/test/path-test.js b/test/path-test.js index ee75afbd67..00587a2c1f 100644 --- a/test/path-test.js +++ b/test/path-test.js @@ -1274,4 +1274,132 @@ buster.testCase("Quality paths", { }); }, }); + +buster.testCase("Trust auto clear", { + 'setUp' : testutils.build_setup(), + // 'setUp' : testutils.build_setup({ verbose: true }), + // 'setUp' : testutils.build_setup({ verbose: true, no_server: true }), + 'tearDown' : testutils.build_teardown(), + + "trust normal clear" : + function (done) { + var self = this; + + async.waterfall([ + function (callback) { + self.what = "Create accounts."; + + testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob"], callback); + }, + function (callback) { + self.what = "Set credit limits."; + + // Mutual trust. + testutils.credit_limits(self.remote, + { + "alice" : "1000/USD/bob", + "bob" : "1000/USD/alice", + }, + callback); + }, + function (callback) { + self.what = "Verify credit limits."; + + testutils.verify_limit(self.remote, "bob", "1000/USD/alice", callback); + }, + function (callback) { + self.what = "Clear credit limits."; + + // Mutual trust. + testutils.credit_limits(self.remote, + { + "alice" : "0/USD/bob", + "bob" : "0/USD/alice", + }, + callback); + }, + function (callback) { + self.what = "Verify credit limits."; + + testutils.verify_limit(self.remote, "bob", "0/USD/alice", function (m) { + var success = m && 'remoteError' === m.error && 'entryNotFound' === m.remote.error; + + callback(!success); + }); + }, + // YYY Could verify owner counts are zero. + ], function (error) { + buster.refute(error, self.what); + done(); + }); + }, + + "trust auto clear" : + function (done) { + var self = this; + + async.waterfall([ + function (callback) { + self.what = "Create accounts."; + + testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob"], callback); + }, + function (callback) { + self.what = "Set credit limits."; + + // Mutual trust. + testutils.credit_limits(self.remote, + { + "alice" : "1000/USD/bob", + }, + callback); + }, + function (callback) { + self.what = "Distribute funds."; + + testutils.payments(self.remote, + { + "bob" : [ "50/USD/alice" ], + }, + callback); + }, + function (callback) { + self.what = "Clear credit limits."; + + // Mutual trust. + testutils.credit_limits(self.remote, + { + "alice" : "0/USD/bob", + }, + callback); + }, + function (callback) { + self.what = "Verify credit limits."; + + testutils.verify_limit(self.remote, "alice", "0/USD/bob", callback); + }, + function (callback) { + self.what = "Return funds."; + + testutils.payments(self.remote, + { + "alice" : [ "50/USD/bob" ], + }, + callback); + }, + function (callback) { + self.what = "Verify credit limit gone."; + + testutils.verify_limit(self.remote, "bob", "0/USD/alice", function (m) { + var success = m && 'remoteError' === m.error && 'entryNotFound' === m.remote.error; + + callback(!success); + }); + }, + ], function (error) { + buster.refute(error, self.what); + done(); + }); + }, +}); // vim:sw=2:sts=2:ts=8:et diff --git a/test/testutils.js b/test/testutils.js index 39495f213a..0799ee97bc 100644 --- a/test/testutils.js +++ b/test/testutils.js @@ -226,7 +226,7 @@ var verify_limit = function (remote, src, amount, callback) { callback(); }) - .on('error', function (m) { + .once('error', function (m) { // console.log("error: %s", JSON.stringify(m)); callback(m); From 526b4a4a6d4e3374412e8a39eaea12ac948bf482 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 31 Mar 2013 23:55:59 -0700 Subject: [PATCH 31/45] Don't permit XRP to be specified as an object. --- src/cpp/ripple/Amount.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index 115fd9a367..df38bbee76 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -158,7 +158,13 @@ STAmount::STAmount(SField::ref n, const Json::Value& v) mIsNative = !currency.isString() || currency.asString().empty() || (currency.asString() == SYSTEM_CURRENCY_CODE); - if (!mIsNative) { + if (mIsNative) + { + if (v.isObject()) + throw std::runtime_error("XRP may not be specified as an object"); + } + else + { // non-XRP if (!currencyFromString(mCurrency, currency.asString())) throw std::runtime_error("invalid currency"); From f6b9e0ca80da438df09dd70cc56466cbea0aeeb6 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 31 Mar 2013 23:56:37 -0700 Subject: [PATCH 32/45] Should return 'false', not throw. --- src/cpp/ripple/SHAMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp index 676290ccad..a08028047e 100644 --- a/src/cpp/ripple/SHAMap.cpp +++ b/src/cpp/ripple/SHAMap.cpp @@ -582,7 +582,7 @@ bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta) stack.pop(); if (node->isLeaf() && (node->peekItem()->getTag() == tag)) - throw std::runtime_error("addGiveItem ends on leaf with same tag"); + return false; uint256 prevHash; returnNode(node, true); From 450fb5c0dbb070a595053bca699eaeecd1ca62fe Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 31 Mar 2013 23:56:48 -0700 Subject: [PATCH 33/45] Cosmetic. --- src/cpp/ripple/Transactor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/Transactor.cpp b/src/cpp/ripple/Transactor.cpp index 7534546d54..c7924b7a80 100644 --- a/src/cpp/ripple/Transactor.cpp +++ b/src/cpp/ripple/Transactor.cpp @@ -142,7 +142,8 @@ TER Transactor::checkSeq() cLog(lsWARNING) << "applyTransaction: past sequence number"; return tefPAST_SEQ; - }else + } + else { mTxnAccount->setFieldU32(sfSequence, t_seq + 1); } From d0009872c1c4cb7239bed3c036942ad5a0237b94 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 31 Mar 2013 23:57:03 -0700 Subject: [PATCH 34/45] Check. --- src/cpp/ripple/TransactionCheck.cpp | 5 +++++ src/cpp/ripple/TransactionEngine.cpp | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/TransactionCheck.cpp b/src/cpp/ripple/TransactionCheck.cpp index 50b1a7f7cf..433bd6960d 100644 --- a/src/cpp/ripple/TransactionCheck.cpp +++ b/src/cpp/ripple/TransactionCheck.cpp @@ -7,5 +7,10 @@ bool TransactionEngine::checkInvariants(TER result, const SerializedTransaction& txn, TransactionEngineParams params) { + +// 1) Make sure transaction changed account sequence number to correct value + +// 2) Make sure transaction didn't create XRP + return true; } diff --git a/src/cpp/ripple/TransactionEngine.cpp b/src/cpp/ripple/TransactionEngine.cpp index 1c0049fb20..1747f114d1 100644 --- a/src/cpp/ripple/TransactionEngine.cpp +++ b/src/cpp/ripple/TransactionEngine.cpp @@ -176,12 +176,20 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa if (isSetBit(params, tapOPEN_LEDGER)) { if (!mLedger->addTransaction(txID, s)) + { + cLog(lsFATAL) << "Tried to add transaction to open ledger that already had it"; assert(false); + throw std::runtime_error("Duplicate transaction applied"); + } } else { if (!mLedger->addTransaction(txID, s, m)) - assert(false); + { + cLog(lsFATAL) << "Tried to add transaction to ledger that already had it"; + assert(false); + throw std::runtime_error("Duplicate transaction applied to closed ledger"); + } // Charge whatever fee they specified. STAmount saPaid = txn.getTransactionFee(); From 1725b5df48083310ffc6275f1b81b1e02e50d9a8 Mon Sep 17 00:00:00 2001 From: Vahe Hovhannisyan Date: Fri, 29 Mar 2013 14:54:43 +0400 Subject: [PATCH 35/45] JS: new account_tx --- src/js/remote.js | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/js/remote.js b/src/js/remote.js index 085a279858..ed7c83022a 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -821,20 +821,33 @@ Remote.prototype.request_account_offers = function (accountID, account_index, cu .ledger_choose(current); }; -Remote.prototype.request_account_tx = function (accountID, ledger_min, ledger_max) { +Remote.prototype.request_account_tx = function (accountID, ledger_index_min, ledger_index_max, descending, limit, offset) { // XXX Does this require the server to be trusted? //utils.assert(this.trusted); var request = new Request(this, 'account_tx'); - request.message.account = accountID; + request.message.account = accountID; + request.message.count = true; - if (ledger_min === ledger_max) { - request.message.ledger = ledger_min; + if (descending) { + request.message.descending = descending; + } + + if (limit) { + request.message.limit = limit; + } + + if (offset) { + request.message.offset = offset; + } + + if (ledger_index_min === ledger_index_max) { + request.message.ledger = ledger_index_min; } else { - request.message.ledger_min = ledger_min; - request.message.ledger_max = ledger_max; + request.message.ledger_index_min = ledger_index_min; + request.message.ledger_index_max = ledger_index_max; } return request; From 5659fb44b38ee64944be0374222379779ba6de65 Mon Sep 17 00:00:00 2001 From: jatchili Date: Mon, 1 Apr 2013 16:24:48 -0700 Subject: [PATCH 36/45] Added unit test: account_tx-test.js. Also, revised request_account_tx in remote.js to use the new argument list for account_tx. --- src/js/remote.js | 29 ++++-- test/account_tx-test.js | 190 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 test/account_tx-test.js diff --git a/src/js/remote.js b/src/js/remote.js index 085a279858..b363e6068b 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -821,20 +821,37 @@ Remote.prototype.request_account_offers = function (accountID, account_index, cu .ledger_choose(current); }; -Remote.prototype.request_account_tx = function (accountID, ledger_min, ledger_max) { + +/* + account: account, + ledger_index_min: ledger_index, // optional, defaults to -1 if ledger_index_max is specified. + ledger_index_max: ledger_index, // optional, defaults to -1 if ledger_index_min is specified. + binary: boolean, // optional, defaults to false + count: boolean, // optional, defaults to false + descending: boolean, // optional, defaults to false + offset: integer, // optional, defaults to 0 + limit: integer // optional +*/ + +Remote.prototype.request_account_tx = function (obj) { // XXX Does this require the server to be trusted? //utils.assert(this.trusted); var request = new Request(this, 'account_tx'); - request.message.account = accountID; + request.message.account = obj.account; - if (ledger_min === ledger_max) { - request.message.ledger = ledger_min; + if (false && ledger_min === ledger_max) { + //request.message.ledger = ledger_min; } else { - request.message.ledger_min = ledger_min; - request.message.ledger_max = ledger_max; + if (obj.ledger_index_min) {request.message.ledger_index_min = obj.ledger_index_min;} + if (obj.ledger_index_max) {request.message.ledger_index_max = obj.ledger_index_max;} + if (obj.binary) {request.message.binary = obj.binary;} + if (obj.count) {request.message.count = obj.count;} + if (obj.descending) {request.message.descending = obj.descending;} + if (obj.offset) {request.message.offset = obj.offset;} + if (obj.limit) {request.message.limit = obj.limit;} } return request; diff --git a/test/account_tx-test.js b/test/account_tx-test.js new file mode 100644 index 0000000000..cdc8b29f54 --- /dev/null +++ b/test/account_tx-test.js @@ -0,0 +1,190 @@ +var async = require("async"); +var buster = require("buster"); + +var Amount = require("../src/js/amount").Amount; +var Remote = require("../src/js/remote").Remote; +var Transaction = require("../src/js/transaction").Transaction; +var Server = require("./server").Server; + +var testutils = require("./testutils"); + +require('../src/js/config').load(require('./config')); + +buster.testRunner.timeout = 250000; //This is a very long test! + + +// Hard-coded limits we'll be testing: +var BINARY_LIMIT = 500; +var NONBINARY_LIMIT = 200; + +var ACCOUNT = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; +var FIRST_BATCH = 199; // Within both limits +var OFFSET = 180; +var LIMIT = 170; +var SECOND_BATCH = 10; // Between NONBINARY_LIMIT and BINARY_LIMIT +var THIRD_BATCH = 295; // Exceeds both limits + +buster.testCase("Account_tx tests", { + 'setUp' : testutils.build_setup(), + 'tearDown' : testutils.build_teardown(), + + "make a lot of transactions and query using account_tx" : + function (done) { + var self = this; + var final_create; + + var transactionCounter = 0; + + var createOfferFunction = function (callback) { + self.remote.transaction() + .offer_create("root", "500", "100/USD/root") + .on('proposed', function (m) { + transactionCounter++; + console.log('Submitted transaction', transactionCounter); + callback(m.result !== 'tesSUCCESS'); + }) + .on('final', function (m) { + buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); + buster.assert(final_create); + }) + .submit(); + }; + + function lotsOfTransactions(number, whenDone) { + var bunchOfOffers = []; + for (var i=0; it2.inLedger || (t1.inLedger==t2.inLedger && t1.hash > t2.hash ), + "Transactions were not ordered correctly: "+t1.inLedger+"#"+t1.hash+" should not have come before "+t2.inLedger+"#"+t2.hash); + } + callback(false); + }) + .on('error', standardErrorHandler(callback)) + .request(); + }, + + + ], function (error) { + buster.refute(error); + finalCallback(); + } + ); + } + } +}); + + + +// TODO: +// Test the "count" feature. \ No newline at end of file From 5cc9314d03a641f6ea63d809b7ccc2d8e2fb524c Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 1 Apr 2013 19:03:28 -0700 Subject: [PATCH 37/45] Add a framework for detecting databases that need updating. Put transaction from account_tx command in transaction application sequence. CAUTION: This modifies your databases and will cause a delay on your next startup. --- newcoin.vcxproj | 1 + newcoin.vcxproj.filters | 3 + ripple2010.vcxproj | 1 + ripple2010.vcxproj.filters | 3 + src/cpp/ripple/AcceptedLedger.h | 1 + src/cpp/ripple/Application.cpp | 3 + src/cpp/ripple/Application.h | 1 + src/cpp/ripple/DBInit.cpp | 3 +- src/cpp/ripple/Ledger.cpp | 4 +- src/cpp/ripple/LedgerMaster.cpp | 2 +- src/cpp/ripple/NetworkOPs.cpp | 4 +- src/cpp/ripple/UpdateTables.cpp | 108 ++++++++++++++++++++++++++++++++ 12 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 src/cpp/ripple/UpdateTables.cpp diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 07ca73e105..cd9990a880 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -186,6 +186,7 @@ + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 483cb0c2d0..a4f8be012f 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -270,6 +270,9 @@ Source Files + + Source Files + Source Files diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj index d3e9aff5c2..e044141116 100644 --- a/ripple2010.vcxproj +++ b/ripple2010.vcxproj @@ -185,6 +185,7 @@ + diff --git a/ripple2010.vcxproj.filters b/ripple2010.vcxproj.filters index bbdf822554..14f56c6328 100644 --- a/ripple2010.vcxproj.filters +++ b/ripple2010.vcxproj.filters @@ -267,6 +267,9 @@ Source Files + + Source Files + Source Files diff --git a/src/cpp/ripple/AcceptedLedger.h b/src/cpp/ripple/AcceptedLedger.h index dcc662b959..04f917a788 100644 --- a/src/cpp/ripple/AcceptedLedger.h +++ b/src/cpp/ripple/AcceptedLedger.h @@ -31,6 +31,7 @@ public: uint256 getTransactionID() const { return mTxn->getTransactionID(); } TransactionType getTxnType() const { return mTxn->getTxnType(); } TER getResult() const { return mResult; } + uint32 getTxnSeq() const { return mMeta->getIndex(); } bool isApplied() const { return !!mMeta; } int getIndex() const { return mMeta ? mMeta->getIndex() : 0; } diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index a9cef25392..a82d08cdf9 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -143,6 +143,9 @@ void Application::setup() mLedgerDB->getDB()->setupCheckpointing(&mJobQueue); mHashNodeDB->getDB()->setupCheckpointing(&mJobQueue); + if (!theConfig.RUN_STANDALONE) + updateTables(); + if (theConfig.START_UP == Config::FRESH) { cLog(lsINFO) << "Starting new Ledger"; diff --git a/src/cpp/ripple/Application.h b/src/cpp/ripple/Application.h index a467202665..ad09c620d7 100644 --- a/src/cpp/ripple/Application.h +++ b/src/cpp/ripple/Application.h @@ -90,6 +90,7 @@ class Application volatile bool mShutdown; + void updateTables(); void startNewLedger(); void loadOldLedger(const std::string&); diff --git a/src/cpp/ripple/DBInit.cpp b/src/cpp/ripple/DBInit.cpp index 6df0611fa5..1ec5554591 100644 --- a/src/cpp/ripple/DBInit.cpp +++ b/src/cpp/ripple/DBInit.cpp @@ -26,11 +26,12 @@ const char *TxnDBInit[] = { TransID CHARACTER(64), \ Account CHARACTER(64), \ LedgerSeq BIGINT UNSIGNED \ + TxnSeq INTEGER \ );", "CREATE INDEX AcctTxIDIndex ON \ AccountTransactions(TransID);", "CREATE INDEX AcctTxIndex ON \ - AccountTransactions(Account, LedgerSeq, TransID);", + AccountTransactions(Account, LedgerSeq, TxnSeq, TransID);", "CREATE INDEX AcctLgrIndex ON \ AccountTransactions(LedgerSeq, Account, TransID);", diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index dea0b4b882..a05876a1c0 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -479,7 +479,7 @@ void Ledger::saveAcceptedLedger(Job&, bool fromConsensus) const std::vector& accts = vt.second.getAffected(); if (!accts.empty()) { - std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES "; + std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq, TxnSeq) VALUES "; bool first = true; for (std::vector::const_iterator it = accts.begin(), end = accts.end(); it != end; ++it) { @@ -495,6 +495,8 @@ void Ledger::saveAcceptedLedger(Job&, bool fromConsensus) sql += it->humanAccountID(); sql += "',"; sql += boost::lexical_cast(getLedgerSeq()); + sql += ","; + sql += boost::lexical_cast(vt.second.getTxnSeq()); sql += ")"; } sql += ";"; diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index 67381e195e..7fa42d7d35 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -106,7 +106,7 @@ Ledger::pointer LedgerMaster::closeLedger(bool recover) ++recovers; } catch (...) - { + { // CHECKME: We got a few of these cLog(lsWARNING) << "Held transaction throws"; } } diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 4ad0042228..7415a605b3 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1093,13 +1093,15 @@ std::string boost::str(boost::format("SELECT %s FROM " "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " "WHERE Account = '%s' %s %s " - "ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TransID %s LIMIT %u, %u;") + "ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TxnSeq %s, AccountTransactions.TransID %s " + "LIMIT %u, %u;") % selection % account.humanAccountID() % maxClause % minClause % (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") + % (descending ? "DESC" : "ASC") % boost::lexical_cast(offset) % boost::lexical_cast(numberOfResults) ); diff --git a/src/cpp/ripple/UpdateTables.cpp b/src/cpp/ripple/UpdateTables.cpp new file mode 100644 index 0000000000..b5302b09e1 --- /dev/null +++ b/src/cpp/ripple/UpdateTables.cpp @@ -0,0 +1,108 @@ + +#include "Application.h" +#include "Log.h" + +static std::vector getSchema(DatabaseCon* dbc, const std::string& dbName) +{ + std::vector schema; + + std::string sql = "SELECT sql FROM sqlite_master WHERE tbl_name='"; + sql += dbName; + sql += "';"; + + SQL_FOREACH(dbc->getDB(), sql) + { + dbc->getDB()->getStr("sql", sql); + schema.push_back(sql); + } + + return schema; +} + +static bool schemaHas(DatabaseCon* dbc, const std::string& dbName, int line, const std::string& content) +{ + std::vector schema = getSchema(dbc, dbName); + if (schema.size() <= line) + { + Log(lsFATAL) << "Schema for " << dbName << " has too few lines"; + throw std::runtime_error("bad schema"); + } + return schema[line].find(content) != std::string::npos; +} + +static void addTxnSeqField() +{ + if (schemaHas(theApp->getTxnDB(), "AccountTransactions", 0, "TxnSeq")) + return; + Log(lsWARNING) << "Transaction sequence field is missing"; + + Database* db = theApp->getTxnDB()->getDB(); + + std::vector< std::pair > txIDs; + txIDs.reserve(300000); + + Log(lsINFO) << "Parsing transactions"; + int i = 0; + uint256 transID; + SQL_FOREACH(db, "SELECT TransID,TxnMeta FROM Transactions;") + { + std::vector rawMeta; + int metaSize = 2048; + rawMeta.resize(metaSize); + metaSize = db->getBinary("TxnMeta", &*rawMeta.begin(), rawMeta.size()); + if (metaSize > rawMeta.size()) + { + rawMeta.resize(metaSize); + db->getBinary("TxnMeta", &*rawMeta.begin(), rawMeta.size()); + } + else rawMeta.resize(metaSize); + + std::string tid; + db->getStr("TransID", tid); + transID.SetHex(tid, true); + + if (rawMeta.size() == 0) + { + txIDs.push_back(std::make_pair(transID, -1)); + Log(lsINFO) << "No metadata for " << transID; + } + else + { + TransactionMetaSet m(transID, 0, rawMeta); + txIDs.push_back(std::make_pair(transID, m.getIndex())); + } + + if ((++i % 1000) == 0) + Log(lsINFO) << i << " transactions read"; + } + + Log(lsINFO) << "All " << i << " transactions read"; + + db->executeSQL("BEGIN TRANSACTION;"); + + Log(lsINFO) << "Dropping old index"; + db->executeSQL("DROP INDEX AcctTxIndex;"); + + Log(lsINFO) << "Altering table"; + db->executeSQL("ALTER TABLE AccountTransactions ADD COLUMN TxnSeq INTEGER;"); + + typedef std::pair u256_int_pair_t; + boost::format fmt("UPDATE AccountTransactions SET TxnSeq = %d WHERE TransID = '%s';"); + i = 0; + BOOST_FOREACH(u256_int_pair_t& t, txIDs) + { + db->executeSQL(boost::str(fmt % t.second % t.first.GetHex())); + if ((++i % 1000) == 0) + Log(lsINFO) << i << " transactions updated"; + } + + db->executeSQL("CREATE INDEX AcctTxIndex ON AccountTransactions(Account, LedgerSeq, TxnSeq, TransID);"); + db->executeSQL("END TRANSACTION;"); +} + +void Application::updateTables() +{ // perform any needed table updates + assert(schemaHas(theApp->getTxnDB(), "AccountTransactions", 0, "TransID")); + assert(!schemaHas(theApp->getTxnDB(), "AccountTransactions", 0, "foobar")); + addTxnSeqField(); +} From 78d1a93e69a03c6b6b9253dacf2253dee6716ea1 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 1 Apr 2013 20:24:01 -0700 Subject: [PATCH 38/45] Reusing prepared statements is slower. --- src/cpp/ripple/HashedObject.cpp | 6 ++---- src/cpp/ripple/Ledger.cpp | 19 ++++--------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp index 0725d5530f..45cdde10c2 100644 --- a/src/cpp/ripple/HashedObject.cpp +++ b/src/cpp/ripple/HashedObject.cpp @@ -95,7 +95,7 @@ void HashedObjectStore::bulkWrite() { Database* db = theApp->getHashNodeDB()->getDB(); ScopedLock sl(theApp->getHashNodeDB()->getDBLock()); - static SqliteStatement pSt(db->getSqliteDB(), + SqliteStatement pSt(db->getSqliteDB(), "INSERT OR IGNORE INTO CommittedObjects " "(Hash,ObjType,LedgerIndex,Object) VALUES (?, ?, ?, ?);"); @@ -185,7 +185,7 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) { ScopedLock sl(theApp->getHashNodeDB()->getDBLock()); LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtDISK, "HOS::retrieve")); - static SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(), + SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(), "SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;"); pSt.bind(1, hash.GetHex()); @@ -193,7 +193,6 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) int ret = pSt.step(); if (pSt.isDone(ret)) { - pSt.reset(); mNegativeCache.add(hash); cLog(lsTRACE) << "HOS: " << hash <<" fetch: not in db"; return obj; @@ -202,7 +201,6 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) type = pSt.peekString(0); index = pSt.getUInt32(1); pSt.getBlob(2).swap(data); - pSt.reset(); } #else diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index a05876a1c0..32aed9709e 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -538,14 +538,13 @@ Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex) Database* db = theApp->getLedgerDB()->getDB(); ScopedLock sl(theApp->getLedgerDB()->getDBLock()); - static SqliteStatement pSt(db->getSqliteDB(), "SELECT " + SqliteStatement pSt(db->getSqliteDB(), "SELECT " "LedgerHash,PrevHash,AccountSetHash,TransSetHash,TotalCoins," "ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq" " from Ledgers WHERE LedgerSeq = ?;"); pSt.bind(1, ledgerIndex); ledger = getSQL1(&pSt); - pSt.reset(); } if (ledger) Ledger::getSQL2(ledger); @@ -559,14 +558,13 @@ Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash) Database* db = theApp->getLedgerDB()->getDB(); ScopedLock sl(theApp->getLedgerDB()->getDBLock()); - static SqliteStatement pSt(db->getSqliteDB(), "SELECT " + SqliteStatement pSt(db->getSqliteDB(), "SELECT " "LedgerHash,PrevHash,AccountSetHash,TransSetHash,TotalCoins," "ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq" " from Ledgers WHERE LedgerHash = ?;"); pSt.bind(1, ledgerHash.GetHex()); ledger = getSQL1(&pSt); - pSt.reset(); } if (ledger) { @@ -730,7 +728,7 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& DatabaseCon *con = theApp->getLedgerDB(); ScopedLock sl(con->getDBLock()); - static SqliteStatement pSt(con->getDB()->getSqliteDB(), + SqliteStatement pSt(con->getDB()->getSqliteDB(), "SELECT LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger Where LedgerSeq = ?;"); pSt.bind(1, ledgerIndex); @@ -738,13 +736,11 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& int ret = pSt.step(); if (pSt.isDone(ret)) { - pSt.reset(); cLog(lsTRACE) << "Don't have ledger " << ledgerIndex; return false; } if (!pSt.isRow(ret)) { - pSt.reset(); assert(false); cLog(lsFATAL) << "Unexpected statement result " << ret; return false; @@ -752,7 +748,6 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& ledgerHash.SetHex(pSt.peekString(0), true); parentHash.SetHex(pSt.peekString(1), true); - pSt.reset(); return true; @@ -790,7 +785,7 @@ std::map< uint32, std::pair > Ledger::getHashesByIndex(uint32 DatabaseCon *con = theApp->getLedgerDB(); ScopedLock sl(con->getDBLock()); - static SqliteStatement pSt(con->getDB()->getSqliteDB(), + SqliteStatement pSt(con->getDB()->getSqliteDB(), "SELECT LedgerSeq,LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger " "WHERE LedgerSeq >= ? AND LedgerSeq <= ?;"); @@ -803,15 +798,9 @@ std::map< uint32, std::pair > Ledger::getHashesByIndex(uint32 { int r = pSt.step(); if (pSt.isDone(r)) - { - pSt.reset(); return ret; - } if (!pSt.isRow(r)) - { - pSt.reset(); return ret; - } hashes.first.SetHex(pSt.peekString(1), true); hashes.second.SetHex(pSt.peekString(2), true); ret[pSt.getUInt32(0)] = hashes; From d73995e6950982f2ed2ee047a6dda4be011fe3c2 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 1 Apr 2013 21:01:06 -0700 Subject: [PATCH 39/45] An optimization. Faster GetHex for exact strings. --- src/cpp/ripple/HashedObject.cpp | 2 +- src/cpp/ripple/Ledger.cpp | 30 ++++++++++++------------- src/cpp/ripple/uint256.h | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp index 45cdde10c2..313b955f98 100644 --- a/src/cpp/ripple/HashedObject.cpp +++ b/src/cpp/ripple/HashedObject.cpp @@ -269,7 +269,7 @@ int HashedObjectStore::import(const std::string& file) uint256 hash; std::string hashStr; importDB->getStr("Hash", hashStr); - hash.SetHex(hashStr, true); + hash.SetHexExact(hashStr); if (hash.isZero()) { cLog(lsWARNING) << "zero hash found in import table"; diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 32aed9709e..4a195cbcad 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -611,13 +611,13 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) return Ledger::pointer(); db->getStr("LedgerHash", hash); - ledgerHash.SetHex(hash, true); + ledgerHash.SetHexExact(hash); db->getStr("PrevHash", hash); - prevHash.SetHex(hash, true); + prevHash.SetHexExact(hash); db->getStr("AccountSetHash", hash); - accountHash.SetHex(hash, true); + accountHash.SetHexExact(hash); db->getStr("TransSetHash", hash); - transHash.SetHex(hash, true); + transHash.SetHexExact(hash); totCoins = db->getBigInt("TotalCoins"); closingTime = db->getBigInt("ClosingTime"); prevClosingTime = db->getBigInt("PrevClosingTime"); @@ -671,10 +671,10 @@ Ledger::pointer Ledger::getSQL1(SqliteStatement *stmt) unsigned closeFlags; std::string hash; - ledgerHash.SetHex(stmt->peekString(0), true); - prevHash.SetHex(stmt->peekString(1), true); - accountHash.SetHex(stmt->peekString(2), true); - transHash.SetHex(stmt->peekString(3), true); + ledgerHash.SetHexExact(stmt->peekString(0)); + prevHash.SetHexExact(stmt->peekString(1)); + accountHash.SetHexExact(stmt->peekString(2)); + transHash.SetHexExact(stmt->peekString(3)); totCoins = stmt->getInt64(4); closingTime = stmt->getUInt32(5); prevClosingTime = stmt->getUInt32(6); @@ -717,7 +717,7 @@ uint256 Ledger::getHashByIndex(uint32 ledgerIndex) db->endIterRows(); } - ret.SetHex(hash, true); + ret.SetHexExact(hash); return ret; } @@ -746,8 +746,8 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& return false; } - ledgerHash.SetHex(pSt.peekString(0), true); - parentHash.SetHex(pSt.peekString(1), true); + ledgerHash.SetHexExact(pSt.peekString(0)); + parentHash.SetHexExact(pSt.peekString(1)); return true; @@ -768,8 +768,8 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& db->endIterRows(); } - ledgerHash.SetHex(hash, true); - parentHash.SetHex(prevHash, true); + ledgerHash.SetHexExact(hash); + parentHash.SetHexExact(prevHash); assert(ledgerHash.isNonZero() && ((ledgerIndex == 0) || parentHash.isNonZero())); @@ -801,8 +801,8 @@ std::map< uint32, std::pair > Ledger::getHashesByIndex(uint32 return ret; if (!pSt.isRow(r)) return ret; - hashes.first.SetHex(pSt.peekString(1), true); - hashes.second.SetHex(pSt.peekString(2), true); + hashes.first.SetHexExact(pSt.peekString(1)); + hashes.second.SetHexExact(pSt.peekString(2)); ret[pSt.getUInt32(0)] = hashes; } while(1); diff --git a/src/cpp/ripple/uint256.h b/src/cpp/ripple/uint256.h index a65f91c43d..f24e7dd059 100644 --- a/src/cpp/ripple/uint256.h +++ b/src/cpp/ripple/uint256.h @@ -219,6 +219,41 @@ public: return strHex(begin(), size()); } + void SetHexExact(const char* psz) + { // must be precisely the correct number of hex digits + static signed char phexdigit[256] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, + + -1,0xa,0xb,0xc, 0xd,0xe,0xf,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,0xa,0xb,0xc, 0xd,0xe,0xf,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + }; + + char* pOut = reinterpret_cast(pn); + for (int i = 0; i < sizeof(pn); ++i) + { + *pOut = phexdigit[*psz++] << 4; + *pOut++ |= phexdigit[*psz++]; + } + + assert(*psz == 0); + assert(pOut == reinterpret_cast(end())); + } + // Allow leading whitespace. // Allow leading "0x". // To be valid must be '\0' terminated. @@ -291,6 +326,11 @@ public: return SetHex(str.c_str(), bStrict); } + void SetHexExact(const std::string& str) + { + SetHexExact(str.c_str()); + } + std::string ToString() const { return GetHex(); From 2e28179217a1544928e6c50604627eb93327b97e Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 2 Apr 2013 12:24:05 -0700 Subject: [PATCH 40/45] Suppress some warnings. --- src/cpp/ripple/Application.cpp | 4 ++-- src/cpp/ripple/SNTPClient.cpp | 4 ++-- src/cpp/ripple/TaggedCache.h | 4 ++-- src/cpp/ripple/UpdateTables.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index a82d08cdf9..f9fa85f386 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -419,12 +419,12 @@ void Application::loadOldLedger(const std::string& l) mLedgerMaster.switchLedgers(loadLedger, openLedger); mNetOps.setLastCloseTime(loadLedger->getCloseTimeNC()); } - catch (SHAMapMissingNode& mn) + catch (SHAMapMissingNode&) { cLog(lsFATAL) << "Data is missing for selected ledger"; exit(-1); } - catch (boost::bad_lexical_cast& blc) + catch (boost::bad_lexical_cast&) { cLog(lsFATAL) << "Ledger specified '" << l << "' is not valid"; exit(-1); diff --git a/src/cpp/ripple/SNTPClient.cpp b/src/cpp/ripple/SNTPClient.cpp index cf3d7b9938..c3febd27be 100644 --- a/src/cpp/ripple/SNTPClient.cpp +++ b/src/cpp/ripple/SNTPClient.cpp @@ -81,7 +81,7 @@ void SNTPClient::resolveComplete(const boost::system::error_code& error, boost:: query.mReceivedReply = false; query.mLocalTimeSent = now; getRand(reinterpret_cast(&query.mQueryNonce), sizeof(query.mQueryNonce)); - reinterpret_cast(SNTPQueryData)[NTP_OFF_XMITTS_INT] = time(NULL) + NTP_UNIX_OFFSET; + reinterpret_cast(SNTPQueryData)[NTP_OFF_XMITTS_INT] = static_cast(time(NULL)) + NTP_UNIX_OFFSET; reinterpret_cast(SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.mQueryNonce; mSocket.async_send_to(boost::asio::buffer(SNTPQueryData, 48), *sel, boost::bind(&SNTPClient::sendComplete, this, @@ -148,7 +148,7 @@ void SNTPClient::processReply() return; } - time_t now = time(NULL); + int64 now = static_cast(time(NULL)); timev -= now; timev -= NTP_UNIX_OFFSET; diff --git a/src/cpp/ripple/TaggedCache.h b/src/cpp/ripple/TaggedCache.h index cd89c4fe2d..8b12d80da6 100644 --- a/src/cpp/ripple/TaggedCache.h +++ b/src/cpp/ripple/TaggedCache.h @@ -99,7 +99,7 @@ template void TaggedCache::setTa boost::recursive_mutex::scoped_lock sl(mLock); mTargetSize = s; if (s > 0) - mCache.rehash((s + (s >> 2)) / mCache.max_load_factor() + 1); + mCache.rehash(static_cast((s + (s >> 2)) / mCache.max_load_factor() + 1)); Log(lsDEBUG, TaggedCachePartition) << mName << " target size set to " << s; } @@ -136,7 +136,7 @@ template void TaggedCache::sweep int target = mLastSweep - mTargetAge; int cacheRemovals = 0, mapRemovals = 0, cc = 0; - if ((mTargetSize != 0) && (mCache.size() > mTargetSize)) + if ((mTargetSize != 0) && (static_cast(mCache.size()) > mTargetSize)) { target = mLastSweep - (mTargetAge * mTargetSize / mCache.size()); if (target > (mLastSweep - 2)) diff --git a/src/cpp/ripple/UpdateTables.cpp b/src/cpp/ripple/UpdateTables.cpp index b5302b09e1..6d829d9495 100644 --- a/src/cpp/ripple/UpdateTables.cpp +++ b/src/cpp/ripple/UpdateTables.cpp @@ -22,7 +22,7 @@ static std::vector getSchema(DatabaseCon* dbc, const std::string& d static bool schemaHas(DatabaseCon* dbc, const std::string& dbName, int line, const std::string& content) { std::vector schema = getSchema(dbc, dbName); - if (schema.size() <= line) + if (static_cast(schema.size()) <= line) { Log(lsFATAL) << "Schema for " << dbName << " has too few lines"; throw std::runtime_error("bad schema"); @@ -50,7 +50,7 @@ static void addTxnSeqField() int metaSize = 2048; rawMeta.resize(metaSize); metaSize = db->getBinary("TxnMeta", &*rawMeta.begin(), rawMeta.size()); - if (metaSize > rawMeta.size()) + if (metaSize > static_cast(rawMeta.size())) { rawMeta.resize(metaSize); db->getBinary("TxnMeta", &*rawMeta.begin(), rawMeta.size()); From 335716c1c6eb564dbb4e0b9f59eaed16c64a73c5 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 2 Apr 2013 13:41:17 -0700 Subject: [PATCH 41/45] Extra statement constructor. --- src/cpp/database/SqliteDatabase.cpp | 8 ++++++++ src/cpp/database/SqliteDatabase.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/cpp/database/SqliteDatabase.cpp b/src/cpp/database/SqliteDatabase.cpp index 4bb0850bf7..c9a540ff83 100644 --- a/src/cpp/database/SqliteDatabase.cpp +++ b/src/cpp/database/SqliteDatabase.cpp @@ -279,6 +279,14 @@ SqliteStatement::SqliteStatement(SqliteDatabase* db, const char *sql) throw j; } +SqliteStatement::SqliteStatement(SqliteDatabase* db, const std::string& sql) +{ + assert(db); + int j = sqlite3_prepare_v2(db->peekConnection(), sql.c_str(), sql.size() + 1, &statement, NULL); + if (j != SQLITE_OK) + throw j; +} + SqliteStatement::~SqliteStatement() { sqlite3_finalize(statement); diff --git a/src/cpp/database/SqliteDatabase.h b/src/cpp/database/SqliteDatabase.h index ed2d829457..19569a69b8 100644 --- a/src/cpp/database/SqliteDatabase.h +++ b/src/cpp/database/SqliteDatabase.h @@ -73,6 +73,7 @@ protected: public: SqliteStatement(SqliteDatabase* db, const char *statement); + SqliteStatement(SqliteDatabase* db, const std::string& statement); ~SqliteStatement(); sqlite3_stmt* peekStatement(); From 24fe80e629db87b8d6237db474f3d186988c90f5 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 2 Apr 2013 13:41:34 -0700 Subject: [PATCH 42/45] Cleanup. --- src/cpp/ripple/HashedObject.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp index 313b955f98..cc8b21a686 100644 --- a/src/cpp/ripple/HashedObject.cpp +++ b/src/cpp/ripple/HashedObject.cpp @@ -183,12 +183,13 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) #ifndef NO_SQLITE3_PREPARE { + std::string sql = "SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = '"; + sql.append(hash.GetHex()); + sql.append("';"); + ScopedLock sl(theApp->getHashNodeDB()->getDBLock()); LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtDISK, "HOS::retrieve")); - SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(), - "SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;"); - - pSt.bind(1, hash.GetHex()); + SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(), sql.c_str()); int ret = pSt.step(); if (pSt.isDone(ret)) From 00e2ac829863aad39cea18e4065eeb6335b6aec9 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 2 Apr 2013 13:41:42 -0700 Subject: [PATCH 43/45] Fix a race applying connections to the open ledger. --- src/cpp/ripple/LedgerMaster.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index 7fa42d7d35..a9471493a7 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -74,10 +74,10 @@ void LedgerMaster::switchLedgers(Ledger::pointer lastClosed, Ledger::pointer cur mFinalizedLedger->setClosed(); mFinalizedLedger->setAccepted(); mCurrentLedger = current; - } - assert(!mCurrentLedger->isClosed()); - mEngine.setLedger(mCurrentLedger); + assert(!mCurrentLedger->isClosed()); + mEngine.setLedger(mCurrentLedger); + } checkAccept(lastClosed->getHash(), lastClosed->getLedgerSeq()); } @@ -122,6 +122,7 @@ Ledger::pointer LedgerMaster::closeLedger(bool recover) TER LedgerMaster::doTransaction(SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply) { + boost::recursive_mutex::scoped_lock sl(mLock); TER result = mEngine.applyTransaction(*txn, params, didApply); // if (didApply) theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result); From 116c89fad5472665c3e4a1441316177ffe05866e Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 2 Apr 2013 15:07:40 -0700 Subject: [PATCH 44/45] Add "round to zero" to subRound/addRound. --- src/cpp/ripple/AmountRound.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/AmountRound.cpp b/src/cpp/ripple/AmountRound.cpp index 12deef7c9a..68633a6f4f 100644 --- a/src/cpp/ripple/AmountRound.cpp +++ b/src/cpp/ripple/AmountRound.cpp @@ -105,7 +105,9 @@ STAmount STAmount::addRound(const STAmount& v1, const STAmount& v2, bool roundUp } int64 fv = vv1 + vv2; - if (fv >= 0) + if ((fv >= -10) && (fv <= -10)) + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer); + else if (fv >= 0) { uint64 v = static_cast(fv); canonicalizeRound(false, v, ov1, roundUp); @@ -168,7 +170,9 @@ STAmount STAmount::subRound(const STAmount& v1, const STAmount& v2, bool roundUp } int64 fv = vv1 + vv2; - if (fv >= 0) + if ((fv >= -10) && (fv <= -10)) + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer); + else if (fv >= 0) { uint64 v = static_cast(fv); canonicalizeRound(false, v, ov1, roundUp); From e7a41fab8f4d0a38ce00e6dd2c94dcbca8f7019b Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 2 Apr 2013 16:46:28 -0700 Subject: [PATCH 45/45] Restore the original behavior. We do not want to call RAND_screen because it calls RAND_poll. Let me know if this breaks anything. --- src/cpp/ripple/PlatRand.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/cpp/ripple/PlatRand.cpp b/src/cpp/ripple/PlatRand.cpp index 6db6203116..e367cbf841 100644 --- a/src/cpp/ripple/PlatRand.cpp +++ b/src/cpp/ripple/PlatRand.cpp @@ -8,12 +8,9 @@ bool AddSystemEntropy() { // Get entropy from the Windows crypto provider - RAND_screen(); // this isn't really that safe since it only works for end users not servers - -/* TODO: you need the cryptoAPI installed I think for the below to work. I suppose we should require people to install this to build the windows version char name[512], rand[128]; DWORD count = 500; - HCRYPTOPROV cryptoHandle; + HCRYPTPROV cryptoHandle; if (!CryptGetDefaultProvider(PROV_RSA_FULL, NULL, CRYPT_MACHINE_DEFAULT, name, &count)) { @@ -43,7 +40,6 @@ bool AddSystemEntropy() CryptReleaseContext(cryptoHandle, 0); RAND_seed(rand, 128); -*/ return true; }