diff --git a/bin/browser.js b/bin/browser.js index 30e83c3c3..d02aeb2b3 100755 --- a/bin/browser.js +++ b/bin/browser.js @@ -18,6 +18,7 @@ // L=current | closed | validated | index | hash // +var async = require("async"); var extend = require("extend"); var http = require("http"); var url = require("url"); @@ -224,10 +225,9 @@ var rewrite_object = function (obj, opts) { // Its a ledger entry. if (obj.node.LedgerEntryType === 'AccountRoot') { - out.node.Account = rewrite_type('address', obj.node.Account, opts); -// out.node.hash = rewrite_type('transaction', obj.node.hash, opts); - out.node.PreviousTxnID = rewrite_type('transaction', out.node.PreviousTxnID, opts); - out.node.PreviousTxnLgrSeq = rewrite_type('ledger', out.node.PreviousTxnLgrSeq, opts); + rewrite_field('address', out.node, 'Account', opts); + rewrite_field('transaction', out.node, 'PreviousTxnID', opts); + rewrite_field('ledger', out.node, 'PreviousTxnLgrSeq', opts); } out.node.LedgerEntryType = '' + out.node.LedgerEntryType + ''; @@ -236,6 +236,86 @@ var rewrite_object = function (obj, opts) { return out; }; +var augment_object = function (obj, opts, done) { + if (obj.node.LedgerEntryType == 'AccountRoot') { + var tx_hash = obj.node.PreviousTxnID; + var tx_ledger = obj.node.PreviousTxnLgrSeq; + + obj.history = []; + + async.whilst( + function () { return tx_hash; }, + function (callback) { +// console.log("augment_object: request: %s %s", tx_hash, tx_ledger); + opts.remote.request_tx(tx_hash) + .on('success', function (m) { + tx_hash = undefined; + tx_ledger = undefined; + +//console.log("augment_object: ", JSON.stringify(m)); + m.meta.AffectedNodes.filter(function(n) { +// console.log("augment_object: ", JSON.stringify(n)); +// if (n.ModifiedNode) +// console.log("augment_object: %s %s %s %s %s %s/%s", 'ModifiedNode' in n, n.ModifiedNode && (n.ModifiedNode.LedgerEntryType === 'AccountRoot'), n.ModifiedNode && n.ModifiedNode.FinalFields && (n.ModifiedNode.FinalFields.Account === obj.node.Account), Object.keys(n)[0], n.ModifiedNode && (n.ModifiedNode.LedgerEntryType), obj.node.Account, n.ModifiedNode && n.ModifiedNode.FinalFields && n.ModifiedNode.FinalFields.Account); +// if ('ModifiedNode' in n && n.ModifiedNode.LedgerEntryType === 'AccountRoot') +// { +// console.log("***: ", JSON.stringify(m)); +// console.log("***: ", JSON.stringify(n)); +// } + return 'ModifiedNode' in n + && n.ModifiedNode.LedgerEntryType === 'AccountRoot' + && n.ModifiedNode.FinalFields + && n.ModifiedNode.FinalFields.Account === obj.node.Account; + }) + .forEach(function (n) { + tx_hash = n.ModifiedNode.PreviousTxnID; + tx_ledger = n.ModifiedNode.PreviousTxnLgrSeq; + + obj.history.push({ + tx_hash: tx_hash, + tx_ledger: tx_ledger + }); +console.log("augment_object: next: %s %s", tx_hash, tx_ledger); + }); + + callback(); + }) + .on('error', function (m) { + callback(m); + }) + .request(); + }, + function (err) { + if (err) { + done(); + } + else { + async.forEach(obj.history, function (o, callback) { + opts.remote.request_account_info(obj.node.Account) + .ledger_index(o.tx_ledger) + .on('success', function (m) { +//console.log("augment_object: ", JSON.stringify(m)); + o.Balance = m.account_data.Balance; +// o.account_data = m.account_data; + callback(); + }) + .on('error', function (m) { + o.error = m; + callback(); + }) + .request(); + }, + function (err) { + done(err); + }); + } + }); + } + else { + done(); + } +}; + if (process.argv.length < 4 || process.argv.length > 7) { console.log("Usage: %s ws_ip ws_port [ [ []]]", 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(),