]]]", program);
}
@@ -248,15 +328,10 @@ else {
// console.log("START");
var self = this;
- self.base = {
- hostname: ip,
- port: port,
- };
-
var remote = (new Remote({
websocket_ip: ws_ip,
websocket_port: ws_port,
-// trace: true
+ trace: false
}))
.on('state', function (m) {
console.log("STATE: %s", m);
@@ -267,6 +342,12 @@ else {
.connect()
;
+ self.base = {
+ hostname: ip,
+ port: port,
+ remote: remote,
+ };
+
// console.log("SERVE");
var server = http.createServer(function (req, res) {
var input = "";
@@ -294,14 +375,16 @@ else {
.on('success', function (m) {
// console.log("account_root: %s", JSON.stringify(m, undefined, 2));
- httpd_response(res,
- {
- statusCode: 200,
- url: _url,
- body: ""
- + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
- + "
"
- });
+ augment_object(m, self.base, function() {
+ httpd_response(res,
+ {
+ statusCode: 200,
+ url: _url,
+ body: ""
+ + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
+ + "
"
+ });
+ });
})
.request();
diff --git a/newcoin.vcxproj b/newcoin.vcxproj
index 21c419eb1..fbd79cb56 100644
--- a/newcoin.vcxproj
+++ b/newcoin.vcxproj
@@ -174,6 +174,7 @@
+
diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters
index e828b851f..961347ae6 100644
--- a/newcoin.vcxproj.filters
+++ b/newcoin.vcxproj.filters
@@ -243,6 +243,9 @@
Source Files
+
+ Source Files
+
Source Files
diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj
index e785bc9c3..843c6f9dc 100644
--- a/ripple2010.vcxproj
+++ b/ripple2010.vcxproj
@@ -174,6 +174,7 @@
+
diff --git a/ripple2010.vcxproj.filters b/ripple2010.vcxproj.filters
index bec881141..0ef380379 100644
--- a/ripple2010.vcxproj.filters
+++ b/ripple2010.vcxproj.filters
@@ -240,6 +240,9 @@
Source Files
+
+ Source Files
+
Source Files
diff --git a/src/cpp/database/SqliteDatabase.cpp b/src/cpp/database/SqliteDatabase.cpp
index aa3195daf..4bb0850bf 100644
--- a/src/cpp/database/SqliteDatabase.cpp
+++ b/src/cpp/database/SqliteDatabase.cpp
@@ -196,11 +196,18 @@ uint64 SqliteDatabase::getBigInt(int colIndex)
return(sqlite3_column_int64(mCurrentStmt, colIndex));
}
-int SqliteDatabase::getKBUsed()
+int SqliteDatabase::getKBUsedAll()
{
return static_cast(sqlite3_memory_used() / 1024);
}
+int SqliteDatabase::getKBUsedDB()
+{
+ int cur = 0, hiw = 0;
+ sqlite3_db_status(mConnection, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiw, 0);
+ return cur / 1024;
+}
+
static int SqliteWALHook(void *s, sqlite3* dbCon, const char *dbName, int walSize)
{
(reinterpret_cast(s))->doHook(dbName, walSize);
diff --git a/src/cpp/database/SqliteDatabase.h b/src/cpp/database/SqliteDatabase.h
index 2cd989613..ed2d82945 100644
--- a/src/cpp/database/SqliteDatabase.h
+++ b/src/cpp/database/SqliteDatabase.h
@@ -58,7 +58,8 @@ public:
void runWal();
void doHook(const char *db, int walSize);
- int getKBUsed();
+ int getKBUsedDB();
+ int getKBUsedAll();
};
class SqliteStatement
diff --git a/src/cpp/database/database.h b/src/cpp/database/database.h
index 579ed2929..dca77106e 100644
--- a/src/cpp/database/database.h
+++ b/src/cpp/database/database.h
@@ -89,7 +89,8 @@ public:
virtual bool setupCheckpointing(JobQueue*) { return false; }
virtual SqliteDatabase* getSqliteDB() { return NULL; }
- virtual int getKBUsed() { return -1; }
+ virtual int getKBUsedAll() { return -1; }
+ virtual int getKBUsedDB() { return -1; }
};
#endif
diff --git a/src/cpp/ripple/AmountRound.cpp b/src/cpp/ripple/AmountRound.cpp
index 10f7d7b21..12deef7c9 100644
--- a/src/cpp/ripple/AmountRound.cpp
+++ b/src/cpp/ripple/AmountRound.cpp
@@ -32,12 +32,14 @@ static void canonicalizeRound(bool isNative, uint64& value, int& offset, bool ro
{
if (offset < 0)
{
+ int loops = 0;
while (offset < -1)
{
value /= 10;
++offset;
+ ++loops;
}
- value += 10; // add before last divide
+ value += (loops >= 2) ? 9 : 10; // add before last divide
value /= 10;
++offset;
}
@@ -294,6 +296,10 @@ BOOST_AUTO_TEST_SUITE(amountRound)
BOOST_AUTO_TEST_CASE( amountRound_test )
{
+ uint64 value = 25000000000000000ull;
+ int offset = -14;
+ canonicalizeRound(false, value, offset, true);
+
STAmount one(CURRENCY_ONE, ACCOUNT_ONE, 1);
STAmount two(CURRENCY_ONE, ACCOUNT_ONE, 2);
STAmount three(CURRENCY_ONE, ACCOUNT_ONE, 3);
diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp
index e088f91f9..e43b95918 100644
--- a/src/cpp/ripple/Application.cpp
+++ b/src/cpp/ripple/Application.cpp
@@ -24,8 +24,11 @@ LogPartition TaggedCachePartition("TaggedCache");
LogPartition AutoSocketPartition("AutoSocket");
Application* theApp = NULL;
+int DatabaseCon::sCount = 0;
+
DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount)
{
+ ++sCount;
boost::filesystem::path pPath = (theConfig.RUN_STANDALONE && (theConfig.START_UP != Config::LOAD))
? "" // Use temporary files.
: (theConfig.DATA_DIR / strName); // Use regular db files.
@@ -63,6 +66,7 @@ bool Instance::running = true;
void Application::stop()
{
cLog(lsINFO) << "Received shutdown request";
+ StopSustain();
mShutdown = true;
mIOService.stop();
mHashedObjectStore.bulkWrite();
@@ -88,6 +92,12 @@ void sigIntHandler(int)
}
#endif
+static void runAux(boost::asio::io_service& svc)
+{
+ NameThread("aux");
+ svc.run();
+}
+
void Application::setup()
{
mJobQueue.setThreadCount();
@@ -115,7 +125,7 @@ void Application::setup()
LogPartition::setSeverity(lsDEBUG);
}
- boost::thread(boost::bind(&boost::asio::io_service::run, &mAuxService)).detach();
+ boost::thread(boost::bind(runAux, boost::ref(mAuxService))).detach();
if (!theConfig.RUN_STANDALONE)
mSNTPClient.init(theConfig.SNTP_SERVERS);
@@ -178,6 +188,13 @@ void Application::setup()
mLedgerMaster.tune(theConfig.getSize(siLedgerSize), theConfig.getSize(siLedgerAge));
mLedgerMaster.setMinValidations(theConfig.VALIDATION_QUORUM);
+ theApp->getHashNodeDB()->getDB()->executeSQL(boost::str(boost::format("PRAGMA cache_size=-%d;") %
+ (theConfig.getSize(siHashNodeDBCache) * 1024)));
+ theApp->getLedgerDB()->getDB()->executeSQL(boost::str(boost::format("PRAGMA cache_size=-%d;") %
+ (theConfig.getSize(siTxnDBCache) * 1024)));
+ theApp->getTxnDB()->getDB()->executeSQL(boost::str(boost::format("PRAGMA cache_size=-%d;") %
+ (theConfig.getSize(siLgrDBCache) * 1024)));
+
//
// Allow peer connections.
//
diff --git a/src/cpp/ripple/Application.h b/src/cpp/ripple/Application.h
index 8f793ba82..53836e110 100644
--- a/src/cpp/ripple/Application.h
+++ b/src/cpp/ripple/Application.h
@@ -36,12 +36,14 @@ class DatabaseCon
protected:
Database* mDatabase;
boost::recursive_mutex mLock;
+ static int sCount;
public:
DatabaseCon(const std::string& name, const char *initString[], int countInit);
~DatabaseCon();
- Database* getDB() { return mDatabase; }
- boost::recursive_mutex& getDBLock() { return mLock; }
+ Database* getDB() { return mDatabase; }
+ boost::recursive_mutex& getDBLock() { return mLock; }
+ static int getCount() { return sCount; }
};
class Application
diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp
index 1d0f00910..662170f2a 100644
--- a/src/cpp/ripple/CallRPC.cpp
+++ b/src/cpp/ripple/CallRPC.cpp
@@ -65,19 +65,11 @@ std::string EncodeBase64(const std::string& s)
// TODO New routine for parsing ledger parameters, other routines should standardize on this.
static bool jvParseLedger(Json::Value& jvRequest, const std::string& strLedger)
{
- if (strLedger == "closed")
+ if (strLedger == "current" || strLedger == "closed" || strLedger == "validated")
{
- jvRequest["ledger_index"] = -1;
+ jvRequest["ledger_index"] = strLedger;
}
- else if (strLedger == "current")
- {
- jvRequest["ledger_index"] = -2;
- }
- else if (strLedger == "validated")
- {
- jvRequest["ledger_index"] = -3;
- }
- else if (strLedger.length() > 12)
+ else if (strLedger.length() == 64)
{
// YYY Could confirm this is a uint256.
jvRequest["ledger_hash"] = strLedger;
@@ -143,7 +135,7 @@ Json::Value RPCParser::parseInternal(const Json::Value& jvParams)
}
// account_info ||
-// account_info || []
+// account_info || [[] ]
Json::Value RPCParser::parseAccountInfo(const Json::Value& jvParams)
{
Json::Value jvRequest(Json::objectValue);
@@ -159,6 +151,9 @@ Json::Value RPCParser::parseAccountInfo(const Json::Value& jvParams)
jvRequest["ident"] = strIdent;
jvRequest["account_index"] = iIndex;
+ if (jvParams.size() == 3 && !jvParseLedger(jvRequest, jvParams[2u].asString()))
+ return rpcError(rpcLGR_IDX_MALFORMED);
+
return jvRequest;
}
@@ -333,6 +328,25 @@ Json::Value RPCParser::parseGetCounts(const Json::Value& jvParams)
return jvRequest;
}
+// json
+Json::Value RPCParser::parseJson(const Json::Value& jvParams)
+{
+ Json::Reader reader;
+ Json::Value jvRequest;
+
+ cLog(lsTRACE) << "RPC method: " << jvParams[0u];
+ cLog(lsTRACE) << "RPC json: " << jvParams[1u];
+
+ if (reader.parse(jvParams[1u].asString(), jvRequest))
+ {
+ jvRequest["method"] = jvParams[0u];
+
+ return jvRequest;
+ }
+
+ return rpcError(rpcINVALID_PARAMS);
+}
+
// ledger [id|index|current|closed|validated] [full]
Json::Value RPCParser::parseLedger(const Json::Value& jvParams)
{
@@ -343,20 +357,7 @@ Json::Value RPCParser::parseLedger(const Json::Value& jvParams)
return jvRequest;
}
- std::string strLedger = jvParams[0u].asString();
-
- if (strLedger == "current" || strLedger == "closed" || strLedger == "validated")
- {
- jvRequest["ledger_index"] = strLedger;
- }
- else if (strLedger.length() > 12)
- {
- jvRequest["ledger_hash"] = strLedger;
- }
- else
- {
- jvRequest["ledger_index"] = lexical_cast_s(strLedger);
- }
+ jvParseLedger(jvRequest, jvParams[0u].asString());
if (2 == jvParams.size() && jvParams[1u].asString() == "full")
{
@@ -373,7 +374,7 @@ Json::Value RPCParser::parseLedgerId(const Json::Value& jvParams)
std::string strLedger = jvParams[0u].asString();
- if (strLedger.length() > 32)
+ if (strLedger.length() == 32)
{
jvRequest["ledger_hash"] = strLedger;
}
@@ -641,7 +642,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
// Request-response methods
// - Returns an error, or the request.
// - To modify the method, provide a new method in the request.
- { "account_info", &RPCParser::parseAccountInfo, 1, 2 },
+ { "account_info", &RPCParser::parseAccountInfo, 1, 3 },
{ "account_lines", &RPCParser::parseAccountItems, 1, 2 },
{ "account_offers", &RPCParser::parseAccountItems, 1, 2 },
{ "account_tx", &RPCParser::parseAccountTransactions, 2, 4 },
@@ -649,6 +650,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
{ "connect", &RPCParser::parseConnect, 1, 2 },
{ "consensus_info", &RPCParser::parseAsIs, 0, 0 },
{ "get_counts", &RPCParser::parseGetCounts, 0, 1 },
+ { "json", &RPCParser::parseJson, 2, 2 },
{ "ledger", &RPCParser::parseLedger, 0, 2 },
{ "ledger_accept", &RPCParser::parseAsIs, 0, 0 },
{ "ledger_closed", &RPCParser::parseAsIs, 0, 0 },
diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h
index 5d9a0377f..c87ffbe95 100644
--- a/src/cpp/ripple/CallRPC.h
+++ b/src/cpp/ripple/CallRPC.h
@@ -23,9 +23,10 @@ protected:
#endif
Json::Value parseEvented(const Json::Value& jvParams);
Json::Value parseGetCounts(const Json::Value& jvParams);
+ Json::Value parseInternal(const Json::Value& jvParams);
+ Json::Value parseJson(const Json::Value& jvParams);
Json::Value parseLedger(const Json::Value& jvParams);
Json::Value parseLedgerId(const Json::Value& jvParams);
- Json::Value parseInternal(const Json::Value& jvParams);
#if ENABLE_INSECURE
Json::Value parseLogin(const Json::Value& jvParams);
#endif
diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp
index 8d7abeb31..096683d3f 100644
--- a/src/cpp/ripple/Config.cpp
+++ b/src/cpp/ripple/Config.cpp
@@ -486,7 +486,7 @@ void Config::load()
int Config::getSize(SizedItemName item)
{
- SizedItem sizeTable[] = {
+ SizedItem sizeTable[] = { // tiny small medium large huge
{ siSweepInterval, { 10, 30, 60, 90, 90 } },
{ siLedgerFetch, { 2, 2, 3, 4, 5 } },
{ siValidationsSize, { 256, 256, 512, 1024, 1024 } },
@@ -496,8 +496,11 @@ int Config::getSize(SizedItemName item)
{ siLedgerSize, { 32, 64, 128, 1024, 0 } },
{ siLedgerAge, { 30, 60, 120, 300, 600 } },
{ siLineCacheSize, { 8192, 32768, 131072, 1048576, 0 } },
- { siLineCacheAge, { 500, 600, 1800, 3600, 7200 } }
- };
+ { siLineCacheAge, { 500, 600, 1800, 3600, 7200 } },
+ { siHashNodeDBCache, { 24, 48, 64, 128, 256 } },
+ { siTxnDBCache, { 4, 8, 32, 64, 128 } },
+ { siLgrDBCache, { 4, 8, 32, 64, 128 } }
+ };
for (int i = 0; i < (sizeof(sizeTable) / sizeof(SizedItem)); ++i)
{
diff --git a/src/cpp/ripple/Config.h b/src/cpp/ripple/Config.h
index 7d1a420c8..f6e65856f 100644
--- a/src/cpp/ripple/Config.h
+++ b/src/cpp/ripple/Config.h
@@ -64,7 +64,10 @@ enum SizedItemName
siLedgerAge,
siLedgerFetch,
siLineCacheSize,
- siLineCacheAge
+ siLineCacheAge,
+ siHashNodeDBCache,
+ siTxnDBCache,
+ siLgrDBCache
};
struct SizedItem
diff --git a/src/cpp/ripple/ConnectionPool.cpp b/src/cpp/ripple/ConnectionPool.cpp
index d17f38885..8faacaff1 100644
--- a/src/cpp/ripple/ConnectionPool.cpp
+++ b/src/cpp/ripple/ConnectionPool.cpp
@@ -319,7 +319,7 @@ Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort)
if (ppResult)
{
ppResult->connect(strIp, iPort);
- cLog(lsTRACE) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort;
+ cLog(lsDEBUG) << "Pool: Connecting: " << strIp << " " << iPort;
}
else
{
diff --git a/src/cpp/ripple/DBInit.cpp b/src/cpp/ripple/DBInit.cpp
index 16bebd573..082ccd567 100644
--- a/src/cpp/ripple/DBInit.cpp
+++ b/src/cpp/ripple/DBInit.cpp
@@ -24,6 +24,8 @@ const char *TxnDBInit[] = {
Account CHARACTER(64), \
LedgerSeq BIGINT UNSIGNED \
);",
+ "CREATE INDEX AcctTxIDIndex ON \
+ AccountTransactions(TransID);",
"CREATE INDEX AcctTxIndex ON \
AccountTransactions(Account, LedgerSeq, TransID);",
"CREATE INDEX AcctLgrIndex ON \
diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp
index 8e2d618a6..303035fe4 100644
--- a/src/cpp/ripple/JobQueue.cpp
+++ b/src/cpp/ripple/JobQueue.cpp
@@ -266,6 +266,7 @@ void JobQueue::threadEntry()
boost::mutex::scoped_lock sl(mJobLock);
while (1)
{
+ NameThread("waiting");
while (mJobSet.empty() && !mShuttingDown)
mJobCond.wait(sl);
@@ -286,6 +287,7 @@ void JobQueue::threadEntry()
++(mJobCounts[type].second);
sl.unlock();
+ NameThread(Job::toString(type));
cLog(lsTRACE) << "Doing " << Job::toString(type) << " job";
job.doJob();
} // must destroy job without holding lock
diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp
index d9fdab4ca..737bda480 100644
--- a/src/cpp/ripple/Ledger.cpp
+++ b/src/cpp/ripple/Ledger.cpp
@@ -500,10 +500,8 @@ void Ledger::saveAcceptedLedger(Job&, bool fromConsensus)
mCloseResolution % mCloseFlags % mAccountHash.GetHex() % mTransHash.GetHex()));
}
-#if 0
- if (!fromConsensus)
+ if (!fromConsensus && (theConfig.NODE_SIZE < 2)) // tiny or small
dropCache();
-#endif
if (theApp->getJobQueue().getJobCountTotal(jtPUBOLDLEDGER) < 2)
theApp->getLedgerMaster().resumeAcquiring();
@@ -844,14 +842,11 @@ Json::Value Ledger::getJson(int options)
if (mCloseTime != 0)
{
+ ledger["close_time"] = mCloseTime;
+ ledger["close_time_human"] = boost::posix_time::to_simple_string(ptFromSeconds(mCloseTime));
+ ledger["close_time_resolution"] = mCloseResolution;
if ((mCloseFlags & sLCF_NoConsensusTime) != 0)
- ledger["close_time_estimate"] = boost::posix_time::to_simple_string(ptFromSeconds(mCloseTime));
- else
- {
- ledger["close_time"] = mCloseTime;
- ledger["close_time_human"] = boost::posix_time::to_simple_string(ptFromSeconds(mCloseTime));
- ledger["close_time_resolution"] = mCloseResolution;
- }
+ ledger["close_time_estimated"] = true;
}
}
else
diff --git a/src/cpp/ripple/LedgerConsensus.cpp b/src/cpp/ripple/LedgerConsensus.cpp
index d4fc7d97f..23744cb1e 100644
--- a/src/cpp/ripple/LedgerConsensus.cpp
+++ b/src/cpp/ripple/LedgerConsensus.cpp
@@ -14,8 +14,6 @@
#include "Log.h"
#include "SHAMapSync.h"
-#define TX_ACQUIRE_TIMEOUT 250
-
#define LEDGER_TOTAL_PASSES 8
#define LEDGER_RETRY_PASSES 5
@@ -28,159 +26,6 @@ typedef std::map::value_type u256_lct_pair;
SETUP_LOG();
DECLARE_INSTANCE(LedgerConsensus);
-DECLARE_INSTANCE(TransactionAcquire);
-
-TransactionAcquire::TransactionAcquire(const uint256& hash) : PeerSet(hash, TX_ACQUIRE_TIMEOUT), mHaveRoot(false)
-{
- mMap = boost::make_shared(smtTRANSACTION, hash);
-}
-
-void TransactionAcquire::done()
-{
- if (mFailed)
- {
- cLog(lsWARNING) << "Failed to acquire TX set " << mHash;
- theApp->getOPs().mapComplete(mHash, SHAMap::pointer());
- }
- else
- {
- cLog(lsINFO) << "Acquired TX set " << mHash;
- mMap->setImmutable();
- theApp->getOPs().mapComplete(mHash, mMap);
- }
- theApp->getMasterLedgerAcquire().dropLedger(mHash);
-}
-
-void TransactionAcquire::onTimer(bool progress)
-{
- if (!getPeerCount())
- { // out of peers
- cLog(lsWARNING) << "Out of peers for TX set " << getHash();
-
- bool found = false;
- std::vector peerList = theApp->getConnectionPool().getPeerVector();
- BOOST_FOREACH(Peer::ref peer, peerList)
- {
- if (peer->hasTxSet(getHash()))
- {
- found = true;
- peerHas(peer);
- }
- }
- if (!found)
- {
- BOOST_FOREACH(Peer::ref peer, peerList)
- peerHas(peer);
- }
- }
- else if (!progress)
- trigger(Peer::pointer());
-}
-
-boost::weak_ptr TransactionAcquire::pmDowncast()
-{
- return boost::shared_polymorphic_downcast(shared_from_this());
-}
-
-void TransactionAcquire::trigger(Peer::ref peer)
-{
- if (mComplete || mFailed)
- {
- cLog(lsINFO) << "complete or failed";
- return;
- }
- if (!mHaveRoot)
- {
- cLog(lsTRACE) << "TransactionAcquire::trigger " << (peer ? "havePeer" : "noPeer") << " no root";
- ripple::TMGetLedger tmGL;
- tmGL.set_ledgerhash(mHash.begin(), mHash.size());
- tmGL.set_itype(ripple::liTS_CANDIDATE);
- if (getTimeouts() != 0)
- tmGL.set_querytype(ripple::qtINDIRECT);
- *(tmGL.add_nodeids()) = SHAMapNode().getRawString();
- sendRequest(tmGL, peer);
- }
- else
- {
- std::vector nodeIDs;
- std::vector nodeHashes;
- ConsensusTransSetSF sf;
- mMap->getMissingNodes(nodeIDs, nodeHashes, 256, &sf);
- if (nodeIDs.empty())
- {
- if (mMap->isValid())
- mComplete = true;
- else
- mFailed = true;
- done();
- return;
- }
- ripple::TMGetLedger tmGL;
- tmGL.set_ledgerhash(mHash.begin(), mHash.size());
- tmGL.set_itype(ripple::liTS_CANDIDATE);
- if (getTimeouts() != 0)
- tmGL.set_querytype(ripple::qtINDIRECT);
- BOOST_FOREACH(SHAMapNode& it, nodeIDs)
- *(tmGL.add_nodeids()) = it.getRawString();
- sendRequest(tmGL, peer);
- }
-}
-
-SMAddNode TransactionAcquire::takeNodes(const std::list& nodeIDs,
- const std::list< std::vector >& data, Peer::ref peer)
-{
- if (mComplete)
- {
- cLog(lsTRACE) << "TX set complete";
- return SMAddNode();
- }
- if (mFailed)
- {
- cLog(lsTRACE) << "TX set failed";
- return SMAddNode();
- }
- try
- {
- if (nodeIDs.empty())
- return SMAddNode::invalid();
- std::list::const_iterator nodeIDit = nodeIDs.begin();
- std::list< std::vector >::const_iterator nodeDatait = data.begin();
- ConsensusTransSetSF sf;
- while (nodeIDit != nodeIDs.end())
- {
- if (nodeIDit->isRoot())
- {
- if (mHaveRoot)
- {
- cLog(lsWARNING) << "Got root TXS node, already have it";
- return SMAddNode();
- }
- if (!mMap->addRootNode(getHash(), *nodeDatait, snfWIRE, NULL))
- {
- cLog(lsWARNING) << "TX acquire got bad root node";
- return SMAddNode::invalid();
- }
- else
- mHaveRoot = true;
- }
- else if (!mMap->addKnownNode(*nodeIDit, *nodeDatait, &sf))
- {
- cLog(lsWARNING) << "TX acquire got bad non-root node";
- return SMAddNode::invalid();
- }
- ++nodeIDit;
- ++nodeDatait;
- }
- trigger(peer);
- progress();
- return SMAddNode::useful();
- }
- catch (...)
- {
- cLog(lsERROR) << "Peer sends us junky transaction node data";
- return SMAddNode::invalid();
- }
-}
void LCTransaction::setVote(const uint160& peer, bool votesYes)
{ // Track a peer's yes/no vote on a particular disputed transaction
@@ -356,7 +201,7 @@ void LedgerConsensus::checkOurValidation()
v->setTrusted();
v->sign(signingHash, mValPrivate);
theApp->isNew(signingHash);
- theApp->getValidations().addValidation(v);
+ theApp->getValidations().addValidation(v, "localMissing");
std::vector validation = v->getSigned();
ripple::TMValidation val;
val.set_validation(&validation[0], validation.size());
@@ -1337,7 +1182,7 @@ void LedgerConsensus::accept(SHAMap::ref set, LoadEvent::pointer)
v->sign(signingHash, mValPrivate);
v->setTrusted();
theApp->isNew(signingHash); // suppress it if we receive it
- theApp->getValidations().addValidation(v);
+ theApp->getValidations().addValidation(v, "local");
theApp->getOPs().setLastValidation(v);
std::vector validation = v->getSigned();
ripple::TMValidation val;
diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp
index 50e96b347..4d4920f74 100644
--- a/src/cpp/ripple/LedgerMaster.cpp
+++ b/src/cpp/ripple/LedgerMaster.cpp
@@ -289,6 +289,9 @@ bool LedgerMaster::shouldAcquire(uint32 currentLedger, uint32 ledgerHistory, uin
void LedgerMaster::resumeAcquiring()
{
+ if (theApp->getOPs().isNeedNetworkLedger())
+ return;
+
boost::recursive_mutex::scoped_lock ml(mLock);
if (mMissingLedger && mMissingLedger->isDone())
@@ -355,6 +358,9 @@ void LedgerMaster::setFullLedger(Ledger::pointer ledger)
cLog(lsDEBUG) << "Ledger " << ledger->getLedgerSeq() << " accepted :" << ledger->getHash();
assert(ledger->peekAccountStateMap()->getHash().isNonZero());
+ if (theApp->getOPs().isNeedNetworkLedger())
+ return;
+
boost::recursive_mutex::scoped_lock ml(mLock);
mCompleteLedgers.setValue(ledger->getLedgerSeq());
diff --git a/src/cpp/ripple/LoadManager.cpp b/src/cpp/ripple/LoadManager.cpp
index 41d15ac94..9a09b3378 100644
--- a/src/cpp/ripple/LoadManager.cpp
+++ b/src/cpp/ripple/LoadManager.cpp
@@ -317,6 +317,7 @@ int LoadManager::getUptime()
void LoadManager::threadEntry()
{
+ NameThread("loadmgr");
boost::posix_time::ptime t = boost::posix_time::microsec_clock::universal_time();
while (1)
{
diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp
index db8a28c0d..7b4615440 100644
--- a/src/cpp/ripple/NetworkOPs.cpp
+++ b/src/cpp/ripple/NetworkOPs.cpp
@@ -1068,7 +1068,7 @@ std::vector< std::pair >
str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM "
"AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID "
"WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' "
- "ORDER BY AccountTransactions.LedgerSeq DESC LIMIT 200;")
+ "ORDER BY AccountTransactions.LedgerSeq,AccountTransactions.TransID DESC LIMIT 200;")
% account.humanAccountID() % maxLedger % minLedger);
{
@@ -1105,7 +1105,7 @@ std::vector NetworkOPs::getAccountTxsB(
std::string sql = str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM "
"AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID "
"WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' "
- "ORDER BY AccountTransactions.LedgerSeq DESC LIMIT 500;")
+ "ORDER BY AccountTransactions.LedgerSeq,AccountTransactions.TransID DESC LIMIT 500;")
% account.humanAccountID() % maxLedger % minLedger);
{
@@ -1164,10 +1164,10 @@ std::vector
return accounts;
}
-bool NetworkOPs::recvValidation(SerializedValidation::ref val)
+bool NetworkOPs::recvValidation(SerializedValidation::ref val, const std::string& source)
{
- cLog(lsDEBUG) << "recvValidation " << val->getLedgerHash();
- return theApp->getValidations().addValidation(val);
+ cLog(lsDEBUG) << "recvValidation " << val->getLedgerHash() << " from " << source;
+ return theApp->getValidations().addValidation(val, source);
}
Json::Value NetworkOPs::getConsensusInfo()
@@ -1665,18 +1665,20 @@ void NetworkOPs::unsubAccountChanges(InfoSub* isrListener)
// <-- bool: true=added, false=already there
bool NetworkOPs::subLedger(InfoSub::ref isrListener, Json::Value& jvResult)
{
- Ledger::pointer lpClosed = getClosedLedger();
+ Ledger::pointer lpClosed = getValidatedLedger();
+ if (lpClosed)
+ {
+ jvResult["ledger_index"] = lpClosed->getLedgerSeq();
+ jvResult["ledger_hash"] = lpClosed->getHash().ToString();
+ jvResult["ledger_time"] = Json::Value::UInt(lpClosed->getCloseTimeNC());
- jvResult["ledger_index"] = lpClosed->getLedgerSeq();
- jvResult["ledger_hash"] = lpClosed->getHash().ToString();
- jvResult["ledger_time"] = Json::Value::UInt(lpClosed->getCloseTimeNC());
+ jvResult["fee_ref"] = Json::UInt(lpClosed->getReferenceFeeUnits());
+ jvResult["fee_base"] = Json::UInt(lpClosed->getBaseFee());
+ jvResult["reserve_base"] = Json::UInt(lpClosed->getReserve(0));
+ jvResult["reserve_inc"] = Json::UInt(lpClosed->getReserveInc());
+ }
- jvResult["fee_ref"] = Json::UInt(lpClosed->getReferenceFeeUnits());
- jvResult["fee_base"] = Json::UInt(lpClosed->getBaseFee());
- jvResult["reserve_base"] = Json::UInt(lpClosed->getReserve(0));
- jvResult["reserve_inc"] = Json::UInt(lpClosed->getReserveInc());
-
- if ((mMode == omFULL) || (mMode == omTRACKING))
+ if (((mMode == omFULL) || (mMode == omTRACKING)) && !isNeedNetworkLedger())
jvResult["validated_ledgers"] = theApp->getLedgerMaster().getCompleteLedgers();
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h
index 99daf9483..00cb710c9 100644
--- a/src/cpp/ripple/NetworkOPs.h
+++ b/src/cpp/ripple/NetworkOPs.h
@@ -253,7 +253,7 @@ public:
RippleAddress nodePublic, uint256 checkLedger, bool sigGood);
SMAddNode gotTXData(const boost::shared_ptr& peer, const uint256& hash,
const std::list& nodeIDs, const std::list< std::vector >& nodeData);
- bool recvValidation(SerializedValidation::ref val);
+ bool recvValidation(SerializedValidation::ref val, const std::string& source);
void takePosition(int seq, SHAMap::ref position);
SHAMap::pointer getTXMap(const uint256& hash);
bool hasTXSet(const boost::shared_ptr& peer, const uint256& set, ripple::TxSetStatus status);
diff --git a/src/cpp/ripple/OfferCreateTransactor.cpp b/src/cpp/ripple/OfferCreateTransactor.cpp
index afff90699..0743b3638 100644
--- a/src/cpp/ripple/OfferCreateTransactor.cpp
+++ b/src/cpp/ripple/OfferCreateTransactor.cpp
@@ -7,6 +7,80 @@
SETUP_LOG();
+// Make sure an offer is still valid. If not, mark it unfunded.
+bool OfferCreateTransactor::bValidOffer(
+ SLE::ref sleOfferDir,
+ const uint256& uOfferIndex,
+ const uint160& uOfferOwnerID,
+ const STAmount& saOfferPays,
+ const STAmount& saOfferGets,
+ const uint160& uTakerAccountID,
+ boost::unordered_set& usOfferUnfundedFound,
+ boost::unordered_set& usOfferUnfundedBecame,
+ boost::unordered_set& usAccountTouched,
+ STAmount& saOfferFunds) { // <--
+ bool bValid;
+
+ if (sleOfferDir->isFieldPresent(sfExpiration) && sleOfferDir->getFieldU32(sfExpiration) <= mEngine->getLedger()->getParentCloseTimeNC())
+ {
+ // Offer is expired. Expired offers are considered unfunded. Delete it.
+ cLog(lsINFO) << "bValidOffer: encountered expired offer";
+
+ usOfferUnfundedFound.insert(uOfferIndex);
+
+ bValid = false;
+ }
+ else if (uOfferOwnerID == uTakerAccountID)
+ {
+ // Would take own offer. Consider old offer expired. Delete it.
+ cLog(lsINFO) << "bValidOffer: encountered taker's own old offer";
+
+ usOfferUnfundedFound.insert(uOfferIndex);
+
+ bValid = false;
+ }
+ else if (!saOfferGets.isPositive() || !saOfferPays.isPositive())
+ {
+ // Offer has bad amounts. Consider offer expired. Delete it.
+ cLog(lsWARNING) << boost::str(boost::format("bValidOffer: BAD OFFER: saOfferPays=%s saOfferGets=%s")
+ % saOfferPays % saOfferGets);
+
+ usOfferUnfundedFound.insert(uOfferIndex);
+ }
+ else
+ {
+ cLog(lsINFO) << "bValidOffer: saOfferPays=" << saOfferPays.getFullText();
+
+ saOfferFunds = mEngine->getNodes().accountFunds(uOfferOwnerID, saOfferPays);
+
+ if (!saOfferFunds.isPositive())
+ {
+ // Offer is unfunded, possibly due to previous balance action.
+ cLog(lsINFO) << "bValidOffer: offer unfunded: delete";
+
+ boost::unordered_set::iterator account = usAccountTouched.find(uOfferOwnerID);
+ if (account != usAccountTouched.end())
+ {
+ // Previously touched account.
+ usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success.
+ }
+ else
+ {
+ // Never touched source account.
+ usOfferUnfundedFound.insert(uOfferIndex); // Delete found unfunded offer when possible.
+ }
+
+ bValid = false;
+ }
+ else
+ {
+ bValid = true;
+ }
+ }
+
+ return bValid;
+}
+
// Take as much as possible. Adjusts account balances. Charges fees on top to taker.
// --> uBookBase: The order book to take against.
// --> saTakerPays: What the taker offers (w/ issuer)
@@ -28,7 +102,7 @@ TER OfferCreateTransactor::takeOffers(
bool& bUnfunded)
{
// The book has the most elements. Take the perspective of the book.
- // Book is ordered for taker: taker pays / taker gets, < is better
+ // Book is ordered for taker: taker pays / taker gets (smaller is better)
// The order is for the other books currencys for get and pays are opposites.
// We want the same ratio for the respective currencies.
@@ -141,162 +215,121 @@ TER OfferCreateTransactor::takeOffers(
STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets);
STAmount saOfferGets = sleOffer->getFieldAmount(sfTakerPays);
- if (sleOffer->isFieldPresent(sfExpiration) && sleOffer->getFieldU32(sfExpiration) <= mEngine->getLedger()->getParentCloseTimeNC())
- {
- // Offer is expired. Expired offers are considered unfunded. Delete it.
- cLog(lsINFO) << "takeOffers: encountered expired offer";
+ STAmount saOfferFunds; // Funds of offer owner to payout.
+ bool bValid;
- usOfferUnfundedFound.insert(uOfferIndex);
- }
- else if (uOfferOwnerID == uTakerAccountID)
- {
- // Would take own offer. Consider old offer expired. Delete it.
- cLog(lsINFO) << "takeOffers: encountered taker's own old offer";
+ bValid = bValidOffer(
+ sleOfferDir, uOfferIndex, uOfferOwnerID, saOfferPays, saOfferGets,
+ uTakerAccountID,
+ usOfferUnfundedFound, usOfferUnfundedBecame, usAccountTouched,
+ saOfferFunds);
- usOfferUnfundedBecame.insert(uOfferIndex);
- }
- else if (!saOfferGets.isPositive() || !saOfferPays.isPositive())
- {
- // Offer has bad amounts. Consider offer expired. Delete it.
- cLog(lsWARNING) << boost::str(boost::format("takeOffers: BAD OFFER: saOfferPays=%s saOfferGets=%s")
- % saOfferPays % saOfferGets);
+ if (bValid) {
+ STAmount saSubTakerPaid;
+ STAmount saSubTakerGot;
+ STAmount saTakerIssuerFee;
+ STAmount saOfferIssuerFee;
+ STAmount saOfferRate = STAmount::setRate(uTipQuality);
- usOfferUnfundedFound.insert(uOfferIndex);
- }
- else
- {
- // Get offer funds available.
+ cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPays: " << saSubTakerPays.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGets: " << saSubTakerGets.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText();
- cLog(lsINFO) << "takeOffers: saOfferPays=" << saOfferPays.getFullText();
+ bool bOfferDelete = STAmount::applyOffer(
+ lesActive.rippleTransferRate(uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID),
+ lesActive.rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID),
+ saOfferRate,
+ saOfferFunds,
+ saTakerFunds,
+ saOfferPays,
+ saOfferGets,
+ saSubTakerPays,
+ saSubTakerGets,
+ saSubTakerPaid,
+ saSubTakerGot,
+ saTakerIssuerFee,
+ saOfferIssuerFee);
- STAmount saOfferFunds = lesActive.accountFunds(uOfferOwnerID, saOfferPays);
- SLE::pointer sleOfferAccount; // Owner of offer.
+ cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText();
- if (!saOfferFunds.isPositive()) // Includes zero.
+ // Adjust offer
+
+ // Offer owner will pay less. Subtract what taker just got.
+ sleOffer->setFieldAmount(sfTakerGets, saOfferPays -= saSubTakerGot);
+
+ // Offer owner will get less. Subtract what owner just paid.
+ sleOffer->setFieldAmount(sfTakerPays, saOfferGets -= saSubTakerPaid);
+
+ mEngine->entryModify(sleOffer);
+
+ if (bOfferDelete)
{
- // Offer is unfunded, possibly due to previous balance action.
- cLog(lsINFO) << "takeOffers: offer unfunded: delete";
+ // Offer now fully claimed or now unfunded.
+ cLog(lsINFO) << "takeOffers: Offer claimed: Delete.";
- boost::unordered_set::iterator account = usAccountTouched.find(uOfferOwnerID);
- if (account != usAccountTouched.end())
+ usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success.
+
+ // Offer owner's account is no longer pristine.
+ usAccountTouched.insert(uOfferOwnerID);
+ }
+ else if (saSubTakerGot)
+ {
+ cLog(lsINFO) << "takeOffers: Offer partial claim.";
+
+ if (!saOfferPays.isPositive() || !saOfferGets.isPositive())
{
- // Previously touched account.
- usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success.
- }
- else
- {
- // Never touched source account.
- usOfferUnfundedFound.insert(uOfferIndex); // Delete found unfunded offer when possible.
+ cLog(lsWARNING) << "takeOffers: ILLEGAL OFFER RESULT.";
+ bUnfunded = true;
+ terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
}
}
else
{
- STAmount saSubTakerPaid;
- STAmount saSubTakerGot;
- STAmount saTakerIssuerFee;
- STAmount saOfferIssuerFee;
- STAmount saOfferRate = STAmount::setRate(uTipQuality);
+ // Taker got nothing, probably due to rounding. Consider taker unfunded.
+ cLog(lsINFO) << "takeOffers: No claim.";
- cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPays: " << saSubTakerPays.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGets: " << saSubTakerGets.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText();
+ bUnfunded = true;
+ terResult = tesSUCCESS; // Done.
+ }
- bool bOfferDelete = STAmount::applyOffer(
- lesActive.rippleTransferRate(uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID),
- lesActive.rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID),
- saOfferRate,
- saOfferFunds,
- saTakerFunds,
- saOfferPays,
- saOfferGets,
- saSubTakerPays,
- saSubTakerGets,
- saSubTakerPaid,
- saSubTakerGot,
- saTakerIssuerFee,
- saOfferIssuerFee);
+ assert(uTakerGetsAccountID == saSubTakerGot.getIssuer());
+ assert(uTakerPaysAccountID == saSubTakerPaid.getIssuer());
- cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText();
+ if (!bUnfunded)
+ {
+ // Distribute funds. The sends charge appropriate fees which are implied by offer.
+
+ terResult = lesActive.accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker.
+
+ if (tesSUCCESS == terResult)
+ terResult = lesActive.accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid); // Taker pays offer owner.
+
+ // Reduce amount considered paid by taker's rate (not actual cost).
+ STAmount saTakerCould = saTakerPays - saTakerPaid; // Taker could pay.
+ if (saTakerFunds < saTakerCould)
+ saTakerCould = saTakerFunds;
+
+ STAmount saTakerUsed = STAmount::multiply(saSubTakerGot, saTakerRate, saTakerPays);
+
+ cLog(lsINFO) << "takeOffers: applyOffer: saTakerCould: " << saTakerCould.getFullText();
cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saTakerRate: " << saTakerRate.getFullText();
+ cLog(lsINFO) << "takeOffers: applyOffer: saTakerUsed: " << saTakerUsed.getFullText();
- // Adjust offer
+ saTakerPaid += std::min(saTakerCould, saTakerUsed);
+ saTakerGot += saSubTakerGot;
- // Offer owner will pay less. Subtract what taker just got.
- sleOffer->setFieldAmount(sfTakerGets, saOfferPays -= saSubTakerGot);
-
- // Offer owner will get less. Subtract what owner just paid.
- sleOffer->setFieldAmount(sfTakerPays, saOfferGets -= saSubTakerPaid);
-
- mEngine->entryModify(sleOffer);
-
- if (bOfferDelete)
- {
- // Offer now fully claimed or now unfunded.
- cLog(lsINFO) << "takeOffers: Offer claimed: Delete.";
-
- usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success.
-
- // Offer owner's account is no longer pristine.
- usAccountTouched.insert(uOfferOwnerID);
- }
- else if (saSubTakerGot)
- {
- cLog(lsINFO) << "takeOffers: Offer partial claim.";
-
- if (!saOfferPays.isPositive() || !saOfferGets.isPositive())
- {
- cLog(lsWARNING) << "takeOffers: ILLEGAL OFFER RESULT.";
- bUnfunded = true;
- terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
- }
- }
- else
- {
- // Taker got nothing, probably due to rounding. Consider taker unfunded.
- cLog(lsINFO) << "takeOffers: No claim.";
-
- bUnfunded = true;
- terResult = tesSUCCESS; // Done.
- }
-
- assert(uTakerGetsAccountID == saSubTakerGot.getIssuer());
- assert(uTakerPaysAccountID == saSubTakerPaid.getIssuer());
-
- if (!bUnfunded)
- {
- // Distribute funds. The sends charge appropriate fees which are implied by offer.
-
- terResult = lesActive.accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker.
-
- if (tesSUCCESS == terResult)
- terResult = lesActive.accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid); // Taker pays offer owner.
-
- // Reduce amount considered paid by taker's rate (not actual cost).
- STAmount saTakerCould = saTakerPays - saTakerPaid; // Taker could pay.
- if (saTakerFunds < saTakerCould)
- saTakerCould = saTakerFunds;
-
- STAmount saTakerUsed = STAmount::multiply(saSubTakerGot, saTakerRate, saTakerPays);
-
- cLog(lsINFO) << "takeOffers: applyOffer: saTakerCould: " << saTakerCould.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saTakerRate: " << saTakerRate.getFullText();
- cLog(lsINFO) << "takeOffers: applyOffer: saTakerUsed: " << saTakerUsed.getFullText();
-
- saTakerPaid += std::min(saTakerCould, saTakerUsed);
- saTakerGot += saSubTakerGot;
-
- if (tesSUCCESS == terResult)
- terResult = temUNCERTAIN;
- }
+ if (tesSUCCESS == terResult)
+ terResult = temUNCERTAIN;
}
}
}
@@ -426,6 +459,17 @@ TER OfferCreateTransactor::doApply()
terResult = terNO_ACCOUNT;
}
+ else if (isSetBit(sleTakerPays->getFieldU32(sfFlags), lsfRequireAuth)) {
+ SLE::pointer sleRippleState = mEngine->entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uPaysIssuerID, uPaysCurrency));
+ bool bHigh = mTxnAccountID > uPaysIssuerID;
+
+ if (!sleRippleState
+ || !isSetBit(sleRippleState->getFieldU32(sfFlags), (bHigh ? lsfHighAuth : lsfLowAuth))) {
+ cLog(lsWARNING) << "OfferCreate: delay: can't receive IOUs from issuer without auth.";
+
+ terResult = terNO_AUTH;
+ }
+ }
}
STAmount saPaid;
diff --git a/src/cpp/ripple/OfferCreateTransactor.h b/src/cpp/ripple/OfferCreateTransactor.h
index 0947c656f..dfe8696bc 100644
--- a/src/cpp/ripple/OfferCreateTransactor.h
+++ b/src/cpp/ripple/OfferCreateTransactor.h
@@ -6,6 +6,18 @@
class OfferCreateTransactor : public Transactor
{
protected:
+ bool bValidOffer(
+ SLE::ref sleOfferDir,
+ const uint256& uOffer,
+ const uint160& uOfferOwnerID,
+ const STAmount& saOfferPays,
+ const STAmount& saOfferGets,
+ const uint160& uTakerAccountID,
+ boost::unordered_set& usOfferUnfundedFound,
+ boost::unordered_set& usOfferUnfundedBecame,
+ boost::unordered_set& usAccountTouched,
+ STAmount& saOfferFunds);
+
TER takeOffers(
const bool bOpenLedger,
const bool bPassive,
diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp
index 51d5831bf..ee29d50fe 100644
--- a/src/cpp/ripple/Pathfinder.cpp
+++ b/src/cpp/ripple/Pathfinder.cpp
@@ -714,9 +714,9 @@ boost::unordered_set usAccountSourceCurrencies(const RippleAddress& raA
STAmount saBalance = rspEntry->getBalance();
// Filter out non
- if (saBalance.isPositive() // Have IOUs to send.
- || (rspEntry->getLimitPeer() // Peer extends credit.
- && -saBalance < rspEntry->getLimitPeer())) // Credit left.
+ if (saBalance.isPositive() // Have IOUs to send.
+ || (rspEntry->getLimitPeer() // Peer extends credit.
+ && ((-saBalance) < rspEntry->getLimitPeer()))) // Credit left.
{
usCurrencies.insert(saBalance.getCurrency());
}
diff --git a/src/cpp/ripple/Peer.cpp b/src/cpp/ripple/Peer.cpp
index e4a418f0f..4a87a4dc4 100644
--- a/src/cpp/ripple/Peer.cpp
+++ b/src/cpp/ripple/Peer.cpp
@@ -95,6 +95,8 @@ void Peer::detach(const char *rsn, bool onIOStrand)
if (!mDetaching)
{
mDetaching = true; // Race is ok.
+
+ tLog(mCluster, lsWARNING) << "Cluster peer detach \"" << mNodeName << "\": " << rsn;
/*
cLog(lsDEBUG) << "Peer: Detach: "
<< ADDRESS(this) << "> "
@@ -384,7 +386,15 @@ void Peer::handleReadHeader(const boost::system::error_code& error)
}
else
{
- cLog(lsINFO) << "Peer: Header: Error: " << ADDRESS(this) << ": " << error.category().name() << ": " << error.message() << ": " << error;
+ if (mCluster)
+ {
+ cLog(lsINFO) << "Peer: Cluster connection lost to \"" << mNodeName << "\": " <<
+ error.category().name() << ": " << error.message() << ": " << error;
+ }
+ else
+ {
+ cLog(lsINFO) << "Peer: Header: Error: " << getIP() << ": " << error.category().name() << ": " << error.message() << ": " << error;
+ }
detach("hrh2", true);
}
}
@@ -397,7 +407,15 @@ void Peer::handleReadBody(const boost::system::error_code& error)
}
else if (error)
{
- cLog(lsINFO) << "Peer: Body: Error: " << ADDRESS(this) << ": " << error.category().name() << ": " << error.message() << ": " << error;
+ if (mCluster)
+ {
+ cLog(lsINFO) << "Peer: Cluster connection lost to \"" << mNodeName << "\": " <<
+ error.category().name() << ": " << error.message() << ": " << error;
+ }
+ else
+ {
+ cLog(lsINFO) << "Peer: Body: Error: " << getIP() << ": " << error.category().name() << ": " << error.message() << ": " << error;
+ }
boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock());
detach("hrb", true);
return;
@@ -734,6 +752,8 @@ void Peer::recvHello(ripple::TMHello& packet)
{
mCluster = true;
mLoad.setPrivileged();
+ cLog(lsINFO) << "Cluster connection to \"" << (mNodeName.empty() ? getIP() : mNodeName)
+ << "\" established";
}
if (isOutbound())
mLoad.setOutbound();
@@ -1036,8 +1056,16 @@ static void checkValidation(Job&, SerializedValidation::pointer val, uint256 sig
return;
}
+ std::string source;
+ Peer::pointer lp = peer.lock();
+ if (lp)
+ source = lp->getDisplayName();
+ else
+ source = "unknown";
+
std::set peers;
- if (theApp->getOPs().recvValidation(val) && theApp->getSuppression().swapSet(signingHash, peers, SF_RELAYED))
+ if (theApp->getOPs().recvValidation(val, source) &&
+ theApp->getSuppression().swapSet(signingHash, peers, SF_RELAYED))
{
PackedMessage::pointer message = boost::make_shared(*packet, ripple::mtVALIDATION);
theApp->getConnectionPool().relayMessageBut(peers, message);
@@ -1394,8 +1422,7 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet)
if (!map)
{
if (packet.has_querytype() && !packet.has_requestcookie())
- { // FIXME: Don't relay requests for older ledgers we would acquire
- // (if we don't have them, we can't get them)
+ {
cLog(lsDEBUG) << "Trying to route TX set request";
std::vector peerList = theApp->getConnectionPool().getPeerVector();
std::vector usablePeers;
@@ -1622,7 +1649,7 @@ void Peer::recvLedger(const boost::shared_ptr& packet_ptr)
if (target)
{
packet.clear_requestcookie();
- target->sendPacket(boost::make_shared(packet, ripple::mtLEDGER_DATA), true);
+ target->sendPacket(boost::make_shared(packet, ripple::mtLEDGER_DATA), false);
}
else
{
diff --git a/src/cpp/ripple/Peer.h b/src/cpp/ripple/Peer.h
index 1244a5170..36ba93714 100644
--- a/src/cpp/ripple/Peer.h
+++ b/src/cpp/ripple/Peer.h
@@ -117,6 +117,7 @@ public:
//bool operator == (const Peer& other);
std::string& getIP() { return mIpPort.first; }
+ std::string getDisplayName() { return mCluster ? mNodeName : mIpPort.first; }
int getPort() { return mIpPort.second; }
void setIpPort(const std::string& strIP, int iPort);
diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp
index d642e05e4..cc0a84f7e 100644
--- a/src/cpp/ripple/RPCHandler.cpp
+++ b/src/cpp/ripple/RPCHandler.cpp
@@ -2021,9 +2021,19 @@ Json::Value RPCHandler::doGetCounts(Json::Value jvRequest, int& cost)
BOOST_FOREACH(InstanceType::InstanceCount& it, count)
ret[it.first] = it.second;
- int dbKB = theApp->getLedgerDB()->getDB()->getKBUsed();
+ int dbKB = theApp->getLedgerDB()->getDB()->getKBUsedAll();
if (dbKB > 0)
- ret["dbKB"] = dbKB;
+ ret["dbKBTotal"] = dbKB;
+
+ dbKB = theApp->getLedgerDB()->getDB()->getKBUsedDB();
+ if (dbKB > 0)
+ ret["dbKBLedger"] = dbKB;
+ dbKB = theApp->getHashNodeDB()->getDB()->getKBUsedDB();
+ if (dbKB > 0)
+ ret["dbKBHashNode"] = dbKB;
+ dbKB = theApp->getTxnDB()->getDB()->getKBUsedDB();
+ if (dbKB > 0)
+ ret["dbKBTransaction"] = dbKB;
std::string uptime;
int s = upTime();
diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp
index 213923acf..bf94a4783 100644
--- a/src/cpp/ripple/RippleCalc.cpp
+++ b/src/cpp/ripple/RippleCalc.cpp
@@ -114,7 +114,7 @@ TER PathState::pushImply(
// Append a node and insert before it any implied nodes.
// Offers may go back to back.
-// <-- terResult: tesSUCCESS, temBAD_PATH, terNO_LINE, tecPATH_DRY
+// <-- terResult: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH, terNO_LINE, tecPATH_DRY
TER PathState::pushNode(
const int iType,
const uint160& uAccountID,
@@ -231,11 +231,30 @@ TER PathState::pushNode(
<< STAmount::createHumanCurrency(pnCur.uCurrencyID)
<< "." ;
- STAmount saOwed = lesEntries.rippleOwed(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID);
+ SLE::pointer sleBck = lesEntries.entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(pnBck.uAccountID));
+ bool bHigh = pnBck.uAccountID > pnCur.uAccountID;
- if (!saOwed.isPositive() && -saOwed >= lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID))
+ if (!sleBck)
{
- terResult = tecPATH_DRY;
+ cLog(lsWARNING) << "pushNode: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID(pnBck.uAccountID);
+
+ terResult = terNO_ACCOUNT;
+ }
+ else if (isSetBit(sleBck->getFieldU32(sfFlags), lsfRequireAuth)
+ && !isSetBit(sleRippleState->getFieldU32(sfFlags), (bHigh ? lsfHighAuth : lsfLowAuth))) {
+ cLog(lsWARNING) << "pushNode: delay: can't receive IOUs from issuer without auth.";
+
+ terResult = terNO_AUTH;
+ }
+
+ if (tesSUCCESS == terResult)
+ {
+ STAmount saOwed = lesEntries.rippleOwed(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID);
+
+ if (!saOwed.isPositive() && -saOwed >= lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID))
+ {
+ terResult = tecPATH_DRY;
+ }
}
}
}
@@ -287,7 +306,7 @@ TER PathState::pushNode(
// Set to an expanded path.
//
-// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, or temBAD_PATH_LOOP
+// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, terNO_ACCOUNT, terNO_AUTH, or temBAD_PATH_LOOP
void PathState::setExpanded(
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
@@ -689,6 +708,7 @@ void RippleCalc::setCanonical(STPathSet& spsDst, const std::vector bEntryAdvance: true, to advance to next entry. false, recalculate.
// <-- uOfferIndex : 0=end of list.
TER RippleCalc::calcNodeAdvance(
const unsigned int uNode, // 0 < uNode < uLast
@@ -1062,8 +1082,8 @@ TER RippleCalc::calcNodeAdvance(
}
// Between offer nodes, the fee charged may vary. Therefore, process one inbound offer at a time. Propagate the inbound offer's
-// requirements to the previous node. The previous node adjusts the amount output and the amount spent on fees. Continue process
-// till request is satisified while we the rate does not increase past the initial rate.
+// requirements to the previous node. The previous node adjusts the amount output and the amount spent on fees. Continue
+// processing until the request is satisified as long as the rate does not increase past the initial rate.
TER RippleCalc::calcNodeDeliverRev(
const unsigned int uNode, // 0 < uNode < uLast
PathState& psCur,
@@ -1152,6 +1172,9 @@ TER RippleCalc::calcNodeDeliverRev(
else if (saOutFeeRate < saRateMax)
{
// Reducing rate. Additional offers will only considered for this increment if they are at least this good.
+ // At this point, the overall rate is reducing, while the overall rate is not saOutFeeRate, it would be wrong to add
+ // anthing with a rate above saOutFeeRate.
+ // The rate would be reduced if the current offer was from the issuer and the previous offer wasn't.
saRateMax = saOutFeeRate;
@@ -1163,7 +1186,8 @@ TER RippleCalc::calcNodeDeliverRev(
STAmount saOutPass = std::min(std::min(saOfferFunds, saTakerGets), saOutReq-saOutAct); // Offer maximum out - assuming no out fees.
// Amount charged to the offer owner.
// The fee goes to issuer. The fee is paid by offer owner and not passed as a cost to taker.
- STAmount saOutPlusFees = STAmount::multiply(saOutPass, saOutFeeRate); // Offer out with fees.
+ // Round down: prefer liquidity rather than microscopic fees.
+ STAmount saOutPlusFees = STAmount::mulRound(saOutPass, saOutFeeRate, false); // Offer out with fees.
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: saOutReq=%s saOutAct=%s saTakerGets=%s saOutPass=%s saOutPlusFees=%s saOfferFunds=%s")
% saOutReq
@@ -1178,7 +1202,8 @@ TER RippleCalc::calcNodeDeliverRev(
// Offer owner can not cover all fees, compute saOutPass based on saOfferFunds.
saOutPlusFees = saOfferFunds;
- saOutPass = STAmount::divide(saOutPlusFees, saOutFeeRate);
+ // Round up: prefer liquidity rather than microscopic fees.
+ saOutPass = STAmount::divRound(saOutPlusFees, saOutFeeRate, true);
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: Total exceeds fees: saOutPass=%s saOutPlusFees=%s saOfferFunds=%s")
% saOutPass
@@ -1188,8 +1213,7 @@ TER RippleCalc::calcNodeDeliverRev(
// Compute portion of input needed to cover actual output.
- // XXX This needs to round up!
- STAmount saInPassReq = STAmount::multiply(saOutPass, saOfrRate, saTakerPays);
+ STAmount saInPassReq = STAmount::mulRound(saOutPass, saOfrRate, saTakerPays, true);
STAmount saInPassAct;
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: saInPassReq=%s saOfrRate=%s saOutPass=%s saOutPlusFees=%s")
@@ -1198,8 +1222,16 @@ TER RippleCalc::calcNodeDeliverRev(
% saOutPass
% saOutPlusFees);
+ if (!saInPassReq)
+ {
+ // After rounding did not want anything.
+ cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: micro offer is unfunded."));
+
+ bEntryAdvance = true;
+ continue;
+ }
// Find out input amount actually available at current rate.
- if (!!uPrvAccountID)
+ else if (!!uPrvAccountID)
{
// account --> OFFER --> ?
// Due to node expansion, previous is guaranteed to be the issuer.
@@ -1236,8 +1268,9 @@ TER RippleCalc::calcNodeDeliverRev(
if (saInPassAct != saInPassReq)
{
// Adjust output to conform to limited input.
- saOutPass = STAmount::divide(saInPassAct, saOfrRate, saTakerGets);
- saOutPlusFees = STAmount::multiply(saOutPass, saOutFeeRate);
+ // XXX Verify it is impossible for these to be larger than available funds.
+ saOutPass = STAmount::divRound(saInPassAct, saOfrRate, saTakerGets, true);
+ saOutPlusFees = STAmount::mulRound(saOutPass, saOutFeeRate, true);
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: adjusted: saOutPass=%s saOutPlusFees=%s")
% saOutPass
@@ -1281,15 +1314,16 @@ TER RippleCalc::calcNodeDeliverRev(
// Offer became unfunded.
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: offer became unfunded."));
- bEntryAdvance = true;
+ bEntryAdvance = true; // XXX When don't we want to set advance?
}
saOutAct += saOutPass;
saPrvDlvReq += saInPassAct;
}
+// XXX Perhaps need to check if partial is okay to relax this?
if (tesSUCCESS == terResult && !saOutAct)
- terResult = tecPATH_DRY;
+ terResult = tecPATH_DRY; // Unable to meet request, consider path dry.
return terResult;
}
@@ -1371,12 +1405,12 @@ TER RippleCalc::calcNodeDeliverFwd(
// First calculate assuming no output fees: saInPassAct, saInPassFees, saOutPassAct
- STAmount saOutFunded = std::min(saOfferFunds, saTakerGets); // Offer maximum out - If there are no out fees.
- STAmount saInFunded = STAmount::multiply(saOutFunded, saOfrRate, saTakerPays); // Offer maximum in - Limited by by payout.
- STAmount saInTotal = STAmount::multiply(saInFunded, saInTransRate); // Offer maximum in with fees.
- STAmount saInSum = std::min(saInTotal, saInReq-saInAct-saInFees); // In limited by remaining.
- STAmount saInPassAct = STAmount::divide(saInSum, saInFeeRate); // In without fees.
- STAmount saOutPassMax = STAmount::divide(saInPassAct, saOfrRate, saTakerGets); // Out limited by in remaining.
+ STAmount saOutFunded = std::min(saOfferFunds, saTakerGets); // Offer maximum out - If there are no out fees.
+ STAmount saInFunded = STAmount::mulRound(saOutFunded, saOfrRate, saTakerPays, true); // Offer maximum in - Limited by by payout.
+ STAmount saInTotal = STAmount::mulRound(saInFunded, saInTransRate, true); // Offer maximum in with fees.
+ STAmount saInSum = std::min(saInTotal, saInReq-saInAct-saInFees); // In limited by remaining.
+ STAmount saInPassAct = STAmount::divRound(saInSum, saInFeeRate, true); // In without fees.
+ STAmount saOutPassMax = STAmount::divRound(saInPassAct, saOfrRate, saTakerGets, true); // Out limited by in remaining.
STAmount saInPassFeesMax = saInSum-saInPassAct;
@@ -1469,8 +1503,8 @@ TER RippleCalc::calcNodeDeliverFwd(
{
// Fraction of output amount.
// Output fees are paid by offer owner and not passed to previous.
- saInPassAct = STAmount::multiply(saOutPassAct, saOfrRate, saInReq);
- saInPassFees = std::min(saInPassFeesMax, STAmount::multiply(saInPassAct, saInFeeRate));
+ saInPassAct = STAmount::mulRound(saOutPassAct, saOfrRate, saInReq, true);
+ saInPassFees = std::min(saInPassFeesMax, STAmount::mulRound(saInPassAct, saInFeeRate, true));
}
// Do outbound debiting.
@@ -1622,7 +1656,7 @@ TER RippleCalc::calcNodeOfferFwd(
}
-// Compute how much might flow for the node for the pass. Don not actually adjust balances.
+// Compute how much might flow for the node for the pass. Does not actually adjust balances.
// uQualityIn -> uQualityOut
// saPrvReq -> saCurReq
// sqPrvAct -> saCurAct
@@ -1698,7 +1732,7 @@ void RippleCalc::calcNodeRipple(
const uint160 uCurIssuerID = saCur.getIssuer();
// const uint160 uPrvIssuerID = saPrv.getIssuer();
- STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityOut, uCurrencyID, uCurIssuerID), uQualityIn, uCurrencyID, uCurIssuerID);
+ STAmount saCurIn = STAmount::divRound(STAmount::mulRound(saCur, uQualityOut, uCurrencyID, uCurIssuerID, true), uQualityIn, uCurrencyID, uCurIssuerID, true);
cLog(lsTRACE) << boost::str(boost::format("calcNodeRipple: bPrvUnlimited=%d saPrv=%s saCurIn=%s") % bPrvUnlimited % saPrv.getFullText() % saCurIn.getFullText());
if (bPrvUnlimited || saCurIn <= saPrv)
@@ -1711,7 +1745,7 @@ void RippleCalc::calcNodeRipple(
else
{
// A part of cur. All of prv. (cur as driver)
- STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityIn, uCurrencyID, uCurIssuerID), uQualityOut, uCurrencyID, uCurIssuerID);
+ STAmount saCurOut = STAmount::divRound(STAmount::mulRound(saPrv, uQualityIn, uCurrencyID, uCurIssuerID, true), uQualityOut, uCurrencyID, uCurIssuerID, true);
cLog(lsTRACE) << boost::str(boost::format("calcNodeRipple:4: saCurReq=%s") % saCurReq.getFullText());
saCurAct += saCurOut;
@@ -2209,7 +2243,7 @@ TER RippleCalc::calcNodeAccountFwd(
STAmount saIssueCrd = uQualityIn >= QUALITY_ONE
? saPrvIssueReq // No fee.
- : STAmount::multiply(saPrvIssueReq, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uQualityIn, -9)); // Amount to credit.
+ : STAmount::mulRound(saPrvIssueReq, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uQualityIn, -9), false); // Amount to credit.
// Amount to credit. Credit for less than received as a surcharge.
saCurReceive = saPrvRedeemReq+saIssueCrd;
diff --git a/src/cpp/ripple/SHAMapSync.h b/src/cpp/ripple/SHAMapSync.h
index 7c3eae345..35b6e9b79 100644
--- a/src/cpp/ripple/SHAMapSync.h
+++ b/src/cpp/ripple/SHAMapSync.h
@@ -12,17 +12,11 @@ class ConsensusTransSetSF : public SHAMapSyncFilter
{ // sync filter for transaction sets during consensus building
public:
ConsensusTransSetSF() { ; }
+
virtual void gotNode(const SHAMapNode& id, const uint256& nodeHash,
- const std::vector& nodeData, SHAMapTreeNode::TNType)
- {
- // WRITEME: If 'isLeaf' is true, this is a transaction
- theApp->getTempNodeCache().store(nodeHash, nodeData);
- }
- virtual bool haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector& nodeData)
- {
- // WRITEME: We could check our own map, we could check transaction tables
- return theApp->getTempNodeCache().retrieve(nodeHash, nodeData);
- }
+ const std::vector& nodeData, SHAMapTreeNode::TNType);
+
+ virtual bool haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector& nodeData);
};
// This class is only needed on add functions
diff --git a/src/cpp/ripple/SerializedTypes.h b/src/cpp/ripple/SerializedTypes.h
index 96306a79f..562886bfe 100644
--- a/src/cpp/ripple/SerializedTypes.h
+++ b/src/cpp/ripple/SerializedTypes.h
@@ -442,7 +442,7 @@ public:
static STAmount divRound(const STAmount& v1, const STAmount& v2, const STAmount& saUnit, bool roundUp)
{ return divRound(v1, v2, saUnit.getCurrency(), saUnit.getIssuer(), roundUp); }
static STAmount divRound(const STAmount& v1, const STAmount& v2, bool roundUp)
- { return divRound(v1, v2, v2.getCurrency(), v2.getIssuer(), roundUp); }
+ { return divRound(v1, v2, v1.getCurrency(), v1.getIssuer(), roundUp); }
// Someone is offering X for Y, what is the rate?
// Rate: smaller is better, the taker wants the most out: in/out
diff --git a/src/cpp/ripple/Transaction.h b/src/cpp/ripple/Transaction.h
index 0ec8eadba..cc4a4bc77 100644
--- a/src/cpp/ripple/Transaction.h
+++ b/src/cpp/ripple/Transaction.h
@@ -77,7 +77,7 @@ public:
bool checkSign() const;
void updateID() { mTransactionID=mTransaction->getTransactionID(); }
- SerializedTransaction::pointer getSTransaction() { return mTransaction; }
+ SerializedTransaction::ref getSTransaction() { return mTransaction; }
const uint256& getID() const { return mTransactionID; }
const RippleAddress& getFromAccount() const { return mAccountFrom; }
diff --git a/src/cpp/ripple/TransactionAcquire.cpp b/src/cpp/ripple/TransactionAcquire.cpp
new file mode 100644
index 000000000..69630d4d5
--- /dev/null
+++ b/src/cpp/ripple/TransactionAcquire.cpp
@@ -0,0 +1,219 @@
+#include "LedgerConsensus.h"
+
+#include
+#include
+#include
+#include
+
+#include "../json/writer.h"
+
+#include "Application.h"
+#include "NetworkOPs.h"
+#include "SerializedValidation.h"
+#include "Log.h"
+#include "SHAMapSync.h"
+#include "HashPrefixes.h"
+
+#define TX_ACQUIRE_TIMEOUT 250
+
+typedef std::map::value_type u160_prop_pair;
+typedef std::map::value_type u256_lct_pair;
+
+SETUP_LOG();
+DECLARE_INSTANCE(TransactionAcquire);
+
+TransactionAcquire::TransactionAcquire(const uint256& hash) : PeerSet(hash, TX_ACQUIRE_TIMEOUT), mHaveRoot(false)
+{
+ mMap = boost::make_shared(smtTRANSACTION, hash);
+}
+
+void TransactionAcquire::done()
+{
+ if (mFailed)
+ {
+ cLog(lsWARNING) << "Failed to acquire TX set " << mHash;
+ theApp->getOPs().mapComplete(mHash, SHAMap::pointer());
+ }
+ else
+ {
+ cLog(lsINFO) << "Acquired TX set " << mHash;
+ mMap->setImmutable();
+ theApp->getOPs().mapComplete(mHash, mMap);
+ }
+ theApp->getMasterLedgerAcquire().dropLedger(mHash);
+}
+
+void TransactionAcquire::onTimer(bool progress)
+{
+ if (!getPeerCount())
+ { // out of peers
+ cLog(lsWARNING) << "Out of peers for TX set " << getHash();
+
+ bool found = false;
+ std::vector peerList = theApp->getConnectionPool().getPeerVector();
+ BOOST_FOREACH(Peer::ref peer, peerList)
+ {
+ if (peer->hasTxSet(getHash()))
+ {
+ found = true;
+ peerHas(peer);
+ }
+ }
+ if (!found)
+ {
+ BOOST_FOREACH(Peer::ref peer, peerList)
+ peerHas(peer);
+ }
+ }
+ else if (!progress)
+ trigger(Peer::pointer());
+}
+
+boost::weak_ptr TransactionAcquire::pmDowncast()
+{
+ return boost::shared_polymorphic_downcast(shared_from_this());
+}
+
+void TransactionAcquire::trigger(Peer::ref peer)
+{
+ if (mComplete || mFailed)
+ {
+ cLog(lsINFO) << "complete or failed";
+ return;
+ }
+ if (!mHaveRoot)
+ {
+ cLog(lsTRACE) << "TransactionAcquire::trigger " << (peer ? "havePeer" : "noPeer") << " no root";
+ ripple::TMGetLedger tmGL;
+ tmGL.set_ledgerhash(mHash.begin(), mHash.size());
+ tmGL.set_itype(ripple::liTS_CANDIDATE);
+ if (getTimeouts() != 0)
+ tmGL.set_querytype(ripple::qtINDIRECT);
+ *(tmGL.add_nodeids()) = SHAMapNode().getRawString();
+ sendRequest(tmGL, peer);
+ }
+ else
+ {
+ std::vector nodeIDs;
+ std::vector nodeHashes;
+ ConsensusTransSetSF sf;
+ mMap->getMissingNodes(nodeIDs, nodeHashes, 256, &sf);
+ if (nodeIDs.empty())
+ {
+ if (mMap->isValid())
+ mComplete = true;
+ else
+ mFailed = true;
+ done();
+ return;
+ }
+ ripple::TMGetLedger tmGL;
+ tmGL.set_ledgerhash(mHash.begin(), mHash.size());
+ tmGL.set_itype(ripple::liTS_CANDIDATE);
+ if (getTimeouts() != 0)
+ tmGL.set_querytype(ripple::qtINDIRECT);
+ BOOST_FOREACH(SHAMapNode& it, nodeIDs)
+ *(tmGL.add_nodeids()) = it.getRawString();
+ sendRequest(tmGL, peer);
+ }
+}
+
+SMAddNode TransactionAcquire::takeNodes(const std::list& nodeIDs,
+ const std::list< std::vector >& data, Peer::ref peer)
+{
+ if (mComplete)
+ {
+ cLog(lsTRACE) << "TX set complete";
+ return SMAddNode();
+ }
+ if (mFailed)
+ {
+ cLog(lsTRACE) << "TX set failed";
+ return SMAddNode();
+ }
+ try
+ {
+ if (nodeIDs.empty())
+ return SMAddNode::invalid();
+ std::list::const_iterator nodeIDit = nodeIDs.begin();
+ std::list< std::vector >::const_iterator nodeDatait = data.begin();
+ ConsensusTransSetSF sf;
+ while (nodeIDit != nodeIDs.end())
+ {
+ if (nodeIDit->isRoot())
+ {
+ if (mHaveRoot)
+ {
+ cLog(lsWARNING) << "Got root TXS node, already have it";
+ return SMAddNode();
+ }
+ if (!mMap->addRootNode(getHash(), *nodeDatait, snfWIRE, NULL))
+ {
+ cLog(lsWARNING) << "TX acquire got bad root node";
+ return SMAddNode::invalid();
+ }
+ else
+ mHaveRoot = true;
+ }
+ else if (!mMap->addKnownNode(*nodeIDit, *nodeDatait, &sf))
+ {
+ cLog(lsWARNING) << "TX acquire got bad non-root node";
+ return SMAddNode::invalid();
+ }
+ ++nodeIDit;
+ ++nodeDatait;
+ }
+ trigger(peer);
+ progress();
+ return SMAddNode::useful();
+ }
+ catch (...)
+ {
+ cLog(lsERROR) << "Peer sends us junky transaction node data";
+ return SMAddNode::invalid();
+ }
+}
+
+void ConsensusTransSetSF::gotNode(const SHAMapNode& id, const uint256& nodeHash,
+ const std::vector& nodeData, SHAMapTreeNode::TNType type)
+{
+ theApp->getTempNodeCache().store(nodeHash, nodeData);
+ if ((type == SHAMapTreeNode::tnTRANSACTION_NM) && (nodeData.size() > 16))
+ { // this is a transaction, and we didn't have it
+ cLog(lsDEBUG) << "Node on our acquiring TX set is TXN we don't have";
+ try
+ {
+ Serializer s(nodeData.begin() + 4, nodeData.end()); // skip prefix
+ SerializerIterator sit(s);
+ SerializedTransaction::pointer stx = boost::make_shared(boost::ref(sit));
+ assert(stx->getTransactionID() == nodeHash);
+ theApp->getJobQueue().addJob(jtTRANSACTION, "TXS->TXN",
+ BIND_TYPE(&NetworkOPs::submitTransaction, &theApp->getOPs(), P_1, stx, NetworkOPs::stCallback()));
+ }
+ catch (...)
+ {
+ cLog(lsWARNING) << "Fetched invalid transaction in proposed set";
+ }
+ }
+}
+
+bool ConsensusTransSetSF::haveNode(const SHAMapNode& id, const uint256& nodeHash,
+ std::vector& nodeData)
+{
+ if (theApp->getTempNodeCache().retrieve(nodeHash, nodeData))
+ return true;
+
+ Transaction::pointer txn = Transaction::load(nodeHash);
+ if (txn)
+ { // this is a transaction, and we have it
+ cLog(lsDEBUG) << "Node in our acquiring TX set is TXN we have";
+ Serializer s;
+ s.add32(sHP_TransactionID);
+ txn->getSTransaction()->add(s, true);
+ assert(s.getSHA512Half() == nodeHash);
+ nodeData = s.peekData();
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/cpp/ripple/TransactionErr.cpp b/src/cpp/ripple/TransactionErr.cpp
index a605a2f49..160064ffe 100644
--- a/src/cpp/ripple/TransactionErr.cpp
+++ b/src/cpp/ripple/TransactionErr.cpp
@@ -82,6 +82,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
{ terFUNDS_SPENT, "terFUNDS_SPENT", "Can't set password, password set funds already spent." },
{ terINSUF_FEE_B, "terINSUF_FEE_B", "Account balance can't pay fee." },
{ terNO_ACCOUNT, "terNO_ACCOUNT", "The source account does not exist." },
+ { terNO_AUTH, "terNO_AUTH", "Not authorized to hold IOUs." },
{ terNO_LINE, "terNO_LINE", "No such line." },
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction." },
{ terOWNERS, "terOWNERS", "Non-zero owner count." },
diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h
index a1d9fb690..fe54d9f4b 100644
--- a/src/cpp/ripple/TransactionErr.h
+++ b/src/cpp/ripple/TransactionErr.h
@@ -95,6 +95,7 @@ enum TER // aka TransactionEngineResult
terFUNDS_SPENT, // This is a free transaction, therefore don't burden network.
terINSUF_FEE_B, // Can't pay fee, therefore don't burden network.
terNO_ACCOUNT, // Can't pay fee, therefore don't burden network.
+ terNO_AUTH, // Not authorized to hold IOUs.
terNO_LINE, // Internal flag.
terOWNERS, // Can't succeed with non-zero owner count.
terPRE_SEQ, // Can't pay fee, no point in forwarding, therefore don't burden network.
diff --git a/src/cpp/ripple/ValidationCollection.cpp b/src/cpp/ripple/ValidationCollection.cpp
index a3bdb951b..a9ffab3e1 100644
--- a/src/cpp/ripple/ValidationCollection.cpp
+++ b/src/cpp/ripple/ValidationCollection.cpp
@@ -34,7 +34,7 @@ VSpointer ValidationCollection::findSet(const uint256& ledgerHash)
return mValidations.fetch(ledgerHash);
}
-bool ValidationCollection::addValidation(SerializedValidation::ref val)
+bool ValidationCollection::addValidation(SerializedValidation::ref val, const std::string& source)
{
RippleAddress signer = val->getSignerPublic();
bool isCurrent = false;
@@ -52,7 +52,8 @@ bool ValidationCollection::addValidation(SerializedValidation::ref val)
}
else
{
- cLog(lsINFO) << "Node " << signer.humanNodePublic() << " not in UNL";
+ cLog(lsDEBUG) << "Node " << signer.humanNodePublic() << " not in UNL st=" << val->getSignTime() <<
+ ", hash=" << val->getLedgerHash() << ", shash=" << val->getSigningHash() << " src=" << source;
}
uint256 hash = val->getLedgerHash();
diff --git a/src/cpp/ripple/ValidationCollection.h b/src/cpp/ripple/ValidationCollection.h
index 112e9c111..d73a546c6 100644
--- a/src/cpp/ripple/ValidationCollection.h
+++ b/src/cpp/ripple/ValidationCollection.h
@@ -37,7 +37,7 @@ public:
ValidationCollection() : mValidations("Validations", 128, 600), mWriting(false)
{ mStaleValidations.reserve(512); }
- bool addValidation(SerializedValidation::ref);
+ bool addValidation(SerializedValidation::ref, const std::string& source);
ValidationSet getValidations(const uint256& ledger);
void getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int& untrusted);
void getValidationTypes(const uint256& ledger, int& full, int& partial);
diff --git a/src/cpp/ripple/Version.h b/src/cpp/ripple/Version.h
index 01ce5a041..897a19d97 100644
--- a/src/cpp/ripple/Version.h
+++ b/src/cpp/ripple/Version.h
@@ -6,7 +6,7 @@
#define SERVER_VERSION_MAJOR 0
#define SERVER_VERSION_MINOR 8
-#define SERVER_VERSION_SUB "-a"
+#define SERVER_VERSION_SUB "-b"
#define SERVER_NAME "Ripple"
#define SV_STRINGIZE(x) SV_STRINGIZE2(x)
diff --git a/src/cpp/ripple/WSDoor.cpp b/src/cpp/ripple/WSDoor.cpp
index ec4a84e89..40b1939f1 100644
--- a/src/cpp/ripple/WSDoor.cpp
+++ b/src/cpp/ripple/WSDoor.cpp
@@ -48,6 +48,7 @@ static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength)
void WSDoor::startListening()
{
+ NameThread("websocket");
// Generate a single SSL context for use by all connections.
boost::shared_ptr mCtx;
mCtx = boost::make_shared(boost::asio::ssl::context::sslv23);
diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp
index c791d183f..8a7ed48b2 100644
--- a/src/cpp/ripple/main.cpp
+++ b/src/cpp/ripple/main.cpp
@@ -84,6 +84,7 @@ void printHelp(const po::options_description& desc)
cerr << " data_store " << endl;
#endif
cerr << " get_counts" << endl;
+ cerr << " json " << endl;
cerr << " ledger [|current|closed|validated] [full]" << endl;
cerr << " ledger_accept" << endl;
cerr << " ledger_closed" << endl;
@@ -127,6 +128,7 @@ void printHelp(const po::options_description& desc)
int main(int argc, char* argv[])
{
+ NameThread("main");
int iResult = 0;
po::variables_map vm; // Map of options.
@@ -148,6 +150,7 @@ int main(int argc, char* argv[])
("ledger", po::value(), "Load the specified ledger and start from .")
("start", "Start from a fresh Ledger.")
("net", "Get the initial ledger from the network.")
+ ("fg", "Run in the foreground.")
;
// Interpret positional arguments as --parameters.
@@ -185,6 +188,14 @@ int main(int argc, char* argv[])
}
}
+ if (HaveSustain() &&
+ !vm.count("parameters") && !vm.count("fg") && !vm.count("standalone") && !vm.count("unittest"))
+ {
+ std::string logMe = DoSustain();
+ if (!logMe.empty())
+ Log(lsWARNING) << logMe;
+ }
+
if (vm.count("quiet"))
Log::setMinSeverity(lsFATAL, true);
else if (vm.count("verbose"))
@@ -248,11 +259,13 @@ int main(int argc, char* argv[])
{
// No arguments. Run server.
setupServer();
+ NameThread("io");
startServer();
}
else
{
// Have a RPC command.
+ NameThread("rpc");
std::vector vCmd = vm["parameters"].as >();
iResult = commandLineRPC(vCmd);
diff --git a/src/cpp/ripple/utils.cpp b/src/cpp/ripple/utils.cpp
index 4a67a2311..a8bbaa75a 100644
--- a/src/cpp/ripple/utils.cpp
+++ b/src/cpp/ripple/utils.cpp
@@ -1,5 +1,11 @@
-#include "utils.h"
-#include "uint256.h"
+
+#ifdef __linux__
+#include
+#include
+#include
+#endif
+
+#include
#include
#include
@@ -9,6 +15,9 @@
#include
+#include "utils.h"
+#include "uint256.h"
+
void getRand(unsigned char *buf, int num)
{
if (RAND_bytes(buf, num) != 1)
@@ -333,6 +342,112 @@ uint32_t be32toh(uint32_t value)
#endif
+#ifdef PR_SET_NAME
+#define HAVE_NAME_THREAD
+extern void NameThread(const char* n)
+{
+ static std::string pName;
+
+ if (pName.empty())
+ {
+ std::ifstream cLine("/proc/self/cmdline", std::ios::in);
+ cLine >> pName;
+ if (pName.empty())
+ pName = "rippled";
+ else
+ {
+ size_t zero = pName.find_first_of('\0');
+ if ((zero != std::string::npos) && (zero != 0))
+ pName = pName.substr(0, zero);
+ size_t slash = pName.find_last_of('/');
+ if (slash != std::string::npos)
+ pName = pName.substr(slash + 1);
+ }
+ pName += " ";
+ }
+
+ prctl(PR_SET_NAME, (pName + n).c_str(), 0, 0, 0);
+}
+#endif
+
+#ifndef HAVE_NAME_THREAD
+extern void NameThread(const char*)
+{ ; }
+#endif
+
+#ifdef __unix__
+
+static pid_t pManager = static_cast(0);
+static pid_t pChild = static_cast(0);
+
+static void pass_signal(int a)
+{
+ kill(pChild, a);
+}
+
+static void stop_manager(int)
+{
+ kill(pChild, SIGINT);
+ _exit(0);
+}
+
+bool HaveSustain()
+{
+ return true;
+}
+
+std::string StopSustain()
+{
+ if (getppid() != pManager)
+ return std::string();
+ kill(pManager, SIGHUP);
+ return "Terminating monitor";
+}
+
+std::string DoSustain()
+{
+ int childCount = 0;
+ pManager = getpid();
+ signal(SIGINT, stop_manager);
+ signal(SIGHUP, stop_manager);
+ signal(SIGUSR1, pass_signal);
+ signal(SIGUSR2, pass_signal);
+ while (1)
+ {
+ ++childCount;
+ pChild = fork();
+ if (pChild == -1)
+ _exit(0);
+ if (pChild == 0)
+ {
+ NameThread("main");
+ signal(SIGINT, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGUSR1, SIG_DFL);
+ signal(SIGUSR2, SIG_DFL);
+ return str(boost::format("Launching child %d") % childCount);;
+ }
+ NameThread(boost::str(boost::format("#%d") % childCount).c_str());
+ do
+ {
+ int i;
+ sleep(10);
+ waitpid(-1, &i, 0);
+ }
+ while (kill(pChild, 0) == 0);
+ rename("core", boost::str(boost::format("core.%d") % static_cast(pChild)).c_str());
+ rename("debug.log", boost::str(boost::format("debug.log.%d") % static_cast(pChild)).c_str());
+ }
+}
+
+#else
+
+bool HaveSustain() { return false; }
+std::string DoSustain() { return std::string; }
+std::string StopSustain() { return std::string; }
+
+#endif
+
BOOST_AUTO_TEST_SUITE( Utils)
BOOST_AUTO_TEST_CASE( ParseUrl )
diff --git a/src/cpp/ripple/utils.h b/src/cpp/ripple/utils.h
index 6dcea2520..2c60a773f 100644
--- a/src/cpp/ripple/utils.h
+++ b/src/cpp/ripple/utils.h
@@ -286,6 +286,12 @@ template T range_check_cast(const U& value, const T& min
bool parseUrl(const std::string& strUrl, std::string& strScheme, std::string& strDomain, int& iPort, std::string& strPath);
+extern void NameThread(const char *);
+
+extern bool HaveSustain();
+extern std::string StopSustain();
+extern std::string DoSustain();
+
#if (!defined(FORCE_NO_C11X) && (__cplusplus > 201100L)) || defined(FORCE_C11X)
#define C11X
diff --git a/test/offer-test.js b/test/offer-test.js
index dd7a62924..13724f6f5 100644
--- a/test/offer-test.js
+++ b/test/offer-test.js
@@ -1072,6 +1072,7 @@ buster.testCase("Offer tests", {
buster.testCase("Offer cross currency", {
'setUp' : testutils.build_setup(),
+ // 'setUp' : testutils.build_setup({ verbose: true }),
'tearDown' : testutils.build_teardown(),
"ripple cross currency payment - start with XRP" :
@@ -1112,7 +1113,7 @@ buster.testCase("Offer cross currency", {
self.what = "Create offer.";
self.remote.transaction()
- .offer_create("carol", "500", "50/USD/mtgox")
+ .offer_create("carol", "500.0", "50/USD/mtgox")
.on('proposed', function (m) {
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
callback(m.result !== 'tesSUCCESS');
@@ -1126,7 +1127,7 @@ buster.testCase("Offer cross currency", {
self.remote.transaction()
.payment("alice", "bob", "25/USD/mtgox")
- .send_max("333")
+ .send_max("333.0")
.on('proposed', function (m) {
// console.log("proposed: %s", JSON.stringify(m));
diff --git a/test/path-test.js b/test/path-test.js
index 59ffd8cc0..ee75afbd6 100644
--- a/test/path-test.js
+++ b/test/path-test.js
@@ -1189,8 +1189,8 @@ buster.testCase("Indirect paths", {
});
buster.testCase("Quality paths", {
- // 'setUp' : testutils.build_setup(),
- 'setUp' : testutils.build_setup({ verbose: true }),
+ 'setUp' : testutils.build_setup(),
+ // 'setUp' : testutils.build_setup({ verbose: true }),
// 'setUp' : testutils.build_setup({ verbose: true, no_server: true }),
'tearDown' : testutils.build_teardown(),