Merge branch 'master' of github.com:jedmccaleb/NewCoin into new_pathfinding

This commit is contained in:
JoelKatz
2013-03-22 14:48:15 -07:00
46 changed files with 909 additions and 442 deletions

View File

@@ -18,6 +18,7 @@
// L=current | closed | validated | index | hash // L=current | closed | validated | index | hash
// //
var async = require("async");
var extend = require("extend"); var extend = require("extend");
var http = require("http"); var http = require("http");
var url = require("url"); var url = require("url");
@@ -224,10 +225,9 @@ var rewrite_object = function (obj, opts) {
// Its a ledger entry. // Its a ledger entry.
if (obj.node.LedgerEntryType === 'AccountRoot') { if (obj.node.LedgerEntryType === 'AccountRoot') {
out.node.Account = rewrite_type('address', obj.node.Account, opts); rewrite_field('address', out.node, 'Account', opts);
// out.node.hash = rewrite_type('transaction', obj.node.hash, opts); rewrite_field('transaction', out.node, 'PreviousTxnID', opts);
out.node.PreviousTxnID = rewrite_type('transaction', out.node.PreviousTxnID, opts); rewrite_field('ledger', out.node, 'PreviousTxnLgrSeq', opts);
out.node.PreviousTxnLgrSeq = rewrite_type('ledger', out.node.PreviousTxnLgrSeq, opts);
} }
out.node.LedgerEntryType = '<B>' + out.node.LedgerEntryType + '</B>'; out.node.LedgerEntryType = '<B>' + out.node.LedgerEntryType + '</B>';
@@ -236,6 +236,86 @@ var rewrite_object = function (obj, opts) {
return out; 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) { if (process.argv.length < 4 || process.argv.length > 7) {
console.log("Usage: %s ws_ip ws_port [<ip> [<port> [<start>]]]", program); console.log("Usage: %s ws_ip ws_port [<ip> [<port> [<start>]]]", program);
} }
@@ -248,15 +328,10 @@ else {
// console.log("START"); // console.log("START");
var self = this; var self = this;
self.base = {
hostname: ip,
port: port,
};
var remote = (new Remote({ var remote = (new Remote({
websocket_ip: ws_ip, websocket_ip: ws_ip,
websocket_port: ws_port, websocket_port: ws_port,
// trace: true trace: false
})) }))
.on('state', function (m) { .on('state', function (m) {
console.log("STATE: %s", m); console.log("STATE: %s", m);
@@ -267,6 +342,12 @@ else {
.connect() .connect()
; ;
self.base = {
hostname: ip,
port: port,
remote: remote,
};
// console.log("SERVE"); // console.log("SERVE");
var server = http.createServer(function (req, res) { var server = http.createServer(function (req, res) {
var input = ""; var input = "";
@@ -294,14 +375,16 @@ else {
.on('success', function (m) { .on('success', function (m) {
// console.log("account_root: %s", JSON.stringify(m, undefined, 2)); // console.log("account_root: %s", JSON.stringify(m, undefined, 2));
httpd_response(res, augment_object(m, self.base, function() {
{ httpd_response(res,
statusCode: 200, {
url: _url, statusCode: 200,
body: "<PRE>" url: _url,
+ JSON.stringify(rewrite_object(m, self.base), undefined, 2) body: "<PRE>"
+ "</PRE>" + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
}); + "</PRE>"
});
});
}) })
.request(); .request();

View File

@@ -174,6 +174,7 @@
<ClCompile Include="src\cpp\ripple\SNTPClient.cpp" /> <ClCompile Include="src\cpp\ripple\SNTPClient.cpp" />
<ClCompile Include="src\cpp\ripple\Suppression.cpp" /> <ClCompile Include="src\cpp\ripple\Suppression.cpp" />
<ClCompile Include="src\cpp\ripple\Transaction.cpp" /> <ClCompile Include="src\cpp\ripple\Transaction.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionAcquire.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionEngine.cpp" /> <ClCompile Include="src\cpp\ripple\TransactionEngine.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionErr.cpp" /> <ClCompile Include="src\cpp\ripple\TransactionErr.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionFormats.cpp" /> <ClCompile Include="src\cpp\ripple\TransactionFormats.cpp" />

View File

@@ -243,6 +243,9 @@
<ClCompile Include="src\cpp\ripple\Transaction.cpp"> <ClCompile Include="src\cpp\ripple\Transaction.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\cpp\ripple\TransactionAcquire.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\TransactionEngine.cpp"> <ClCompile Include="src\cpp\ripple\TransactionEngine.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>

View File

@@ -174,6 +174,7 @@
<ClCompile Include="src\cpp\ripple\SNTPClient.cpp" /> <ClCompile Include="src\cpp\ripple\SNTPClient.cpp" />
<ClCompile Include="src\cpp\ripple\Suppression.cpp" /> <ClCompile Include="src\cpp\ripple\Suppression.cpp" />
<ClCompile Include="src\cpp\ripple\Transaction.cpp" /> <ClCompile Include="src\cpp\ripple\Transaction.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionAcquire.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionEngine.cpp" /> <ClCompile Include="src\cpp\ripple\TransactionEngine.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionErr.cpp" /> <ClCompile Include="src\cpp\ripple\TransactionErr.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionFormats.cpp" /> <ClCompile Include="src\cpp\ripple\TransactionFormats.cpp" />

View File

@@ -240,6 +240,9 @@
<ClCompile Include="src\cpp\ripple\Transaction.cpp"> <ClCompile Include="src\cpp\ripple\Transaction.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\cpp\ripple\TransactionAcquire.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\TransactionEngine.cpp"> <ClCompile Include="src\cpp\ripple\TransactionEngine.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>

View File

@@ -196,11 +196,18 @@ uint64 SqliteDatabase::getBigInt(int colIndex)
return(sqlite3_column_int64(mCurrentStmt, colIndex)); return(sqlite3_column_int64(mCurrentStmt, colIndex));
} }
int SqliteDatabase::getKBUsed() int SqliteDatabase::getKBUsedAll()
{ {
return static_cast<int>(sqlite3_memory_used() / 1024); return static_cast<int>(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) static int SqliteWALHook(void *s, sqlite3* dbCon, const char *dbName, int walSize)
{ {
(reinterpret_cast<SqliteDatabase*>(s))->doHook(dbName, walSize); (reinterpret_cast<SqliteDatabase*>(s))->doHook(dbName, walSize);

View File

@@ -58,7 +58,8 @@ public:
void runWal(); void runWal();
void doHook(const char *db, int walSize); void doHook(const char *db, int walSize);
int getKBUsed(); int getKBUsedDB();
int getKBUsedAll();
}; };
class SqliteStatement class SqliteStatement

View File

@@ -89,7 +89,8 @@ public:
virtual bool setupCheckpointing(JobQueue*) { return false; } virtual bool setupCheckpointing(JobQueue*) { return false; }
virtual SqliteDatabase* getSqliteDB() { return NULL; } virtual SqliteDatabase* getSqliteDB() { return NULL; }
virtual int getKBUsed() { return -1; } virtual int getKBUsedAll() { return -1; }
virtual int getKBUsedDB() { return -1; }
}; };
#endif #endif

View File

@@ -32,12 +32,14 @@ static void canonicalizeRound(bool isNative, uint64& value, int& offset, bool ro
{ {
if (offset < 0) if (offset < 0)
{ {
int loops = 0;
while (offset < -1) while (offset < -1)
{ {
value /= 10; value /= 10;
++offset; ++offset;
++loops;
} }
value += 10; // add before last divide value += (loops >= 2) ? 9 : 10; // add before last divide
value /= 10; value /= 10;
++offset; ++offset;
} }
@@ -294,6 +296,10 @@ BOOST_AUTO_TEST_SUITE(amountRound)
BOOST_AUTO_TEST_CASE( amountRound_test ) 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 one(CURRENCY_ONE, ACCOUNT_ONE, 1);
STAmount two(CURRENCY_ONE, ACCOUNT_ONE, 2); STAmount two(CURRENCY_ONE, ACCOUNT_ONE, 2);
STAmount three(CURRENCY_ONE, ACCOUNT_ONE, 3); STAmount three(CURRENCY_ONE, ACCOUNT_ONE, 3);

View File

@@ -24,8 +24,11 @@ LogPartition TaggedCachePartition("TaggedCache");
LogPartition AutoSocketPartition("AutoSocket"); LogPartition AutoSocketPartition("AutoSocket");
Application* theApp = NULL; Application* theApp = NULL;
int DatabaseCon::sCount = 0;
DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount) DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount)
{ {
++sCount;
boost::filesystem::path pPath = (theConfig.RUN_STANDALONE && (theConfig.START_UP != Config::LOAD)) boost::filesystem::path pPath = (theConfig.RUN_STANDALONE && (theConfig.START_UP != Config::LOAD))
? "" // Use temporary files. ? "" // Use temporary files.
: (theConfig.DATA_DIR / strName); // Use regular db files. : (theConfig.DATA_DIR / strName); // Use regular db files.
@@ -63,6 +66,7 @@ bool Instance::running = true;
void Application::stop() void Application::stop()
{ {
cLog(lsINFO) << "Received shutdown request"; cLog(lsINFO) << "Received shutdown request";
StopSustain();
mShutdown = true; mShutdown = true;
mIOService.stop(); mIOService.stop();
mHashedObjectStore.bulkWrite(); mHashedObjectStore.bulkWrite();
@@ -88,6 +92,12 @@ void sigIntHandler(int)
} }
#endif #endif
static void runAux(boost::asio::io_service& svc)
{
NameThread("aux");
svc.run();
}
void Application::setup() void Application::setup()
{ {
mJobQueue.setThreadCount(); mJobQueue.setThreadCount();
@@ -115,7 +125,7 @@ void Application::setup()
LogPartition::setSeverity(lsDEBUG); 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) if (!theConfig.RUN_STANDALONE)
mSNTPClient.init(theConfig.SNTP_SERVERS); mSNTPClient.init(theConfig.SNTP_SERVERS);
@@ -178,6 +188,13 @@ void Application::setup()
mLedgerMaster.tune(theConfig.getSize(siLedgerSize), theConfig.getSize(siLedgerAge)); mLedgerMaster.tune(theConfig.getSize(siLedgerSize), theConfig.getSize(siLedgerAge));
mLedgerMaster.setMinValidations(theConfig.VALIDATION_QUORUM); 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. // Allow peer connections.
// //

View File

@@ -36,12 +36,14 @@ class DatabaseCon
protected: protected:
Database* mDatabase; Database* mDatabase;
boost::recursive_mutex mLock; boost::recursive_mutex mLock;
static int sCount;
public: public:
DatabaseCon(const std::string& name, const char *initString[], int countInit); DatabaseCon(const std::string& name, const char *initString[], int countInit);
~DatabaseCon(); ~DatabaseCon();
Database* getDB() { return mDatabase; } Database* getDB() { return mDatabase; }
boost::recursive_mutex& getDBLock() { return mLock; } boost::recursive_mutex& getDBLock() { return mLock; }
static int getCount() { return sCount; }
}; };
class Application class Application

View File

@@ -65,19 +65,11 @@ std::string EncodeBase64(const std::string& s)
// TODO New routine for parsing ledger parameters, other routines should standardize on this. // TODO New routine for parsing ledger parameters, other routines should standardize on this.
static bool jvParseLedger(Json::Value& jvRequest, const std::string& strLedger) 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") else if (strLedger.length() == 64)
{
jvRequest["ledger_index"] = -2;
}
else if (strLedger == "validated")
{
jvRequest["ledger_index"] = -3;
}
else if (strLedger.length() > 12)
{ {
// YYY Could confirm this is a uint256. // YYY Could confirm this is a uint256.
jvRequest["ledger_hash"] = strLedger; jvRequest["ledger_hash"] = strLedger;
@@ -143,7 +135,7 @@ Json::Value RPCParser::parseInternal(const Json::Value& jvParams)
} }
// account_info <account>|<nickname>|<account_public_key> // account_info <account>|<nickname>|<account_public_key>
// account_info <seed>|<pass_phrase>|<key> [<index>] // account_info <seed>|<pass_phrase>|<key> [[<index>] <ledger>]
Json::Value RPCParser::parseAccountInfo(const Json::Value& jvParams) Json::Value RPCParser::parseAccountInfo(const Json::Value& jvParams)
{ {
Json::Value jvRequest(Json::objectValue); Json::Value jvRequest(Json::objectValue);
@@ -159,6 +151,9 @@ Json::Value RPCParser::parseAccountInfo(const Json::Value& jvParams)
jvRequest["ident"] = strIdent; jvRequest["ident"] = strIdent;
jvRequest["account_index"] = iIndex; jvRequest["account_index"] = iIndex;
if (jvParams.size() == 3 && !jvParseLedger(jvRequest, jvParams[2u].asString()))
return rpcError(rpcLGR_IDX_MALFORMED);
return jvRequest; return jvRequest;
} }
@@ -333,6 +328,25 @@ Json::Value RPCParser::parseGetCounts(const Json::Value& jvParams)
return jvRequest; return jvRequest;
} }
// json <command> <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] // ledger [id|index|current|closed|validated] [full]
Json::Value RPCParser::parseLedger(const Json::Value& jvParams) Json::Value RPCParser::parseLedger(const Json::Value& jvParams)
{ {
@@ -343,20 +357,7 @@ Json::Value RPCParser::parseLedger(const Json::Value& jvParams)
return jvRequest; return jvRequest;
} }
std::string strLedger = jvParams[0u].asString(); jvParseLedger(jvRequest, 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<uint32>(strLedger);
}
if (2 == jvParams.size() && jvParams[1u].asString() == "full") 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(); std::string strLedger = jvParams[0u].asString();
if (strLedger.length() > 32) if (strLedger.length() == 32)
{ {
jvRequest["ledger_hash"] = strLedger; jvRequest["ledger_hash"] = strLedger;
} }
@@ -641,7 +642,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
// Request-response methods // Request-response methods
// - Returns an error, or the request. // - Returns an error, or the request.
// - To modify the method, provide a new method in 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_lines", &RPCParser::parseAccountItems, 1, 2 },
{ "account_offers", &RPCParser::parseAccountItems, 1, 2 }, { "account_offers", &RPCParser::parseAccountItems, 1, 2 },
{ "account_tx", &RPCParser::parseAccountTransactions, 2, 4 }, { "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 }, { "connect", &RPCParser::parseConnect, 1, 2 },
{ "consensus_info", &RPCParser::parseAsIs, 0, 0 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 },
{ "get_counts", &RPCParser::parseGetCounts, 0, 1 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 },
{ "json", &RPCParser::parseJson, 2, 2 },
{ "ledger", &RPCParser::parseLedger, 0, 2 }, { "ledger", &RPCParser::parseLedger, 0, 2 },
{ "ledger_accept", &RPCParser::parseAsIs, 0, 0 }, { "ledger_accept", &RPCParser::parseAsIs, 0, 0 },
{ "ledger_closed", &RPCParser::parseAsIs, 0, 0 }, { "ledger_closed", &RPCParser::parseAsIs, 0, 0 },

View File

@@ -23,9 +23,10 @@ protected:
#endif #endif
Json::Value parseEvented(const Json::Value& jvParams); Json::Value parseEvented(const Json::Value& jvParams);
Json::Value parseGetCounts(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 parseLedger(const Json::Value& jvParams);
Json::Value parseLedgerId(const Json::Value& jvParams); Json::Value parseLedgerId(const Json::Value& jvParams);
Json::Value parseInternal(const Json::Value& jvParams);
#if ENABLE_INSECURE #if ENABLE_INSECURE
Json::Value parseLogin(const Json::Value& jvParams); Json::Value parseLogin(const Json::Value& jvParams);
#endif #endif

View File

@@ -486,7 +486,7 @@ void Config::load()
int Config::getSize(SizedItemName item) int Config::getSize(SizedItemName item)
{ {
SizedItem sizeTable[] = { SizedItem sizeTable[] = { // tiny small medium large huge
{ siSweepInterval, { 10, 30, 60, 90, 90 } }, { siSweepInterval, { 10, 30, 60, 90, 90 } },
{ siLedgerFetch, { 2, 2, 3, 4, 5 } }, { siLedgerFetch, { 2, 2, 3, 4, 5 } },
{ siValidationsSize, { 256, 256, 512, 1024, 1024 } }, { siValidationsSize, { 256, 256, 512, 1024, 1024 } },
@@ -496,8 +496,11 @@ int Config::getSize(SizedItemName item)
{ siLedgerSize, { 32, 64, 128, 1024, 0 } }, { siLedgerSize, { 32, 64, 128, 1024, 0 } },
{ siLedgerAge, { 30, 60, 120, 300, 600 } }, { siLedgerAge, { 30, 60, 120, 300, 600 } },
{ siLineCacheSize, { 8192, 32768, 131072, 1048576, 0 } }, { 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) for (int i = 0; i < (sizeof(sizeTable) / sizeof(SizedItem)); ++i)
{ {

View File

@@ -64,7 +64,10 @@ enum SizedItemName
siLedgerAge, siLedgerAge,
siLedgerFetch, siLedgerFetch,
siLineCacheSize, siLineCacheSize,
siLineCacheAge siLineCacheAge,
siHashNodeDBCache,
siTxnDBCache,
siLgrDBCache
}; };
struct SizedItem struct SizedItem

View File

@@ -319,7 +319,7 @@ Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort)
if (ppResult) if (ppResult)
{ {
ppResult->connect(strIp, iPort); ppResult->connect(strIp, iPort);
cLog(lsTRACE) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort; cLog(lsDEBUG) << "Pool: Connecting: " << strIp << " " << iPort;
} }
else else
{ {

View File

@@ -24,6 +24,8 @@ const char *TxnDBInit[] = {
Account CHARACTER(64), \ Account CHARACTER(64), \
LedgerSeq BIGINT UNSIGNED \ LedgerSeq BIGINT UNSIGNED \
);", );",
"CREATE INDEX AcctTxIDIndex ON \
AccountTransactions(TransID);",
"CREATE INDEX AcctTxIndex ON \ "CREATE INDEX AcctTxIndex ON \
AccountTransactions(Account, LedgerSeq, TransID);", AccountTransactions(Account, LedgerSeq, TransID);",
"CREATE INDEX AcctLgrIndex ON \ "CREATE INDEX AcctLgrIndex ON \

View File

@@ -266,6 +266,7 @@ void JobQueue::threadEntry()
boost::mutex::scoped_lock sl(mJobLock); boost::mutex::scoped_lock sl(mJobLock);
while (1) while (1)
{ {
NameThread("waiting");
while (mJobSet.empty() && !mShuttingDown) while (mJobSet.empty() && !mShuttingDown)
mJobCond.wait(sl); mJobCond.wait(sl);
@@ -286,6 +287,7 @@ void JobQueue::threadEntry()
++(mJobCounts[type].second); ++(mJobCounts[type].second);
sl.unlock(); sl.unlock();
NameThread(Job::toString(type));
cLog(lsTRACE) << "Doing " << Job::toString(type) << " job"; cLog(lsTRACE) << "Doing " << Job::toString(type) << " job";
job.doJob(); job.doJob();
} // must destroy job without holding lock } // must destroy job without holding lock

View File

@@ -500,10 +500,8 @@ void Ledger::saveAcceptedLedger(Job&, bool fromConsensus)
mCloseResolution % mCloseFlags % mAccountHash.GetHex() % mTransHash.GetHex())); mCloseResolution % mCloseFlags % mAccountHash.GetHex() % mTransHash.GetHex()));
} }
#if 0 if (!fromConsensus && (theConfig.NODE_SIZE < 2)) // tiny or small
if (!fromConsensus)
dropCache(); dropCache();
#endif
if (theApp->getJobQueue().getJobCountTotal(jtPUBOLDLEDGER) < 2) if (theApp->getJobQueue().getJobCountTotal(jtPUBOLDLEDGER) < 2)
theApp->getLedgerMaster().resumeAcquiring(); theApp->getLedgerMaster().resumeAcquiring();
@@ -844,14 +842,11 @@ Json::Value Ledger::getJson(int options)
if (mCloseTime != 0) 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) if ((mCloseFlags & sLCF_NoConsensusTime) != 0)
ledger["close_time_estimate"] = boost::posix_time::to_simple_string(ptFromSeconds(mCloseTime)); ledger["close_time_estimated"] = true;
else
{
ledger["close_time"] = mCloseTime;
ledger["close_time_human"] = boost::posix_time::to_simple_string(ptFromSeconds(mCloseTime));
ledger["close_time_resolution"] = mCloseResolution;
}
} }
} }
else else

View File

@@ -14,8 +14,6 @@
#include "Log.h" #include "Log.h"
#include "SHAMapSync.h" #include "SHAMapSync.h"
#define TX_ACQUIRE_TIMEOUT 250
#define LEDGER_TOTAL_PASSES 8 #define LEDGER_TOTAL_PASSES 8
#define LEDGER_RETRY_PASSES 5 #define LEDGER_RETRY_PASSES 5
@@ -28,159 +26,6 @@ typedef std::map<uint256, LCTransaction::pointer>::value_type u256_lct_pair;
SETUP_LOG(); SETUP_LOG();
DECLARE_INSTANCE(LedgerConsensus); DECLARE_INSTANCE(LedgerConsensus);
DECLARE_INSTANCE(TransactionAcquire);
TransactionAcquire::TransactionAcquire(const uint256& hash) : PeerSet(hash, TX_ACQUIRE_TIMEOUT), mHaveRoot(false)
{
mMap = boost::make_shared<SHAMap>(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<Peer::pointer> 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<PeerSet> TransactionAcquire::pmDowncast()
{
return boost::shared_polymorphic_downcast<PeerSet>(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<SHAMapNode> nodeIDs;
std::vector<uint256> 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<SHAMapNode>& nodeIDs,
const std::list< std::vector<unsigned char> >& 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<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin();
std::list< std::vector<unsigned char> >::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) void LCTransaction::setVote(const uint160& peer, bool votesYes)
{ // Track a peer's yes/no vote on a particular disputed transaction { // Track a peer's yes/no vote on a particular disputed transaction
@@ -356,7 +201,7 @@ void LedgerConsensus::checkOurValidation()
v->setTrusted(); v->setTrusted();
v->sign(signingHash, mValPrivate); v->sign(signingHash, mValPrivate);
theApp->isNew(signingHash); theApp->isNew(signingHash);
theApp->getValidations().addValidation(v); theApp->getValidations().addValidation(v, "localMissing");
std::vector<unsigned char> validation = v->getSigned(); std::vector<unsigned char> validation = v->getSigned();
ripple::TMValidation val; ripple::TMValidation val;
val.set_validation(&validation[0], validation.size()); val.set_validation(&validation[0], validation.size());
@@ -1337,7 +1182,7 @@ void LedgerConsensus::accept(SHAMap::ref set, LoadEvent::pointer)
v->sign(signingHash, mValPrivate); v->sign(signingHash, mValPrivate);
v->setTrusted(); v->setTrusted();
theApp->isNew(signingHash); // suppress it if we receive it theApp->isNew(signingHash); // suppress it if we receive it
theApp->getValidations().addValidation(v); theApp->getValidations().addValidation(v, "local");
theApp->getOPs().setLastValidation(v); theApp->getOPs().setLastValidation(v);
std::vector<unsigned char> validation = v->getSigned(); std::vector<unsigned char> validation = v->getSigned();
ripple::TMValidation val; ripple::TMValidation val;

View File

@@ -289,6 +289,9 @@ bool LedgerMaster::shouldAcquire(uint32 currentLedger, uint32 ledgerHistory, uin
void LedgerMaster::resumeAcquiring() void LedgerMaster::resumeAcquiring()
{ {
if (theApp->getOPs().isNeedNetworkLedger())
return;
boost::recursive_mutex::scoped_lock ml(mLock); boost::recursive_mutex::scoped_lock ml(mLock);
if (mMissingLedger && mMissingLedger->isDone()) if (mMissingLedger && mMissingLedger->isDone())
@@ -355,6 +358,9 @@ void LedgerMaster::setFullLedger(Ledger::pointer ledger)
cLog(lsDEBUG) << "Ledger " << ledger->getLedgerSeq() << " accepted :" << ledger->getHash(); cLog(lsDEBUG) << "Ledger " << ledger->getLedgerSeq() << " accepted :" << ledger->getHash();
assert(ledger->peekAccountStateMap()->getHash().isNonZero()); assert(ledger->peekAccountStateMap()->getHash().isNonZero());
if (theApp->getOPs().isNeedNetworkLedger())
return;
boost::recursive_mutex::scoped_lock ml(mLock); boost::recursive_mutex::scoped_lock ml(mLock);
mCompleteLedgers.setValue(ledger->getLedgerSeq()); mCompleteLedgers.setValue(ledger->getLedgerSeq());

View File

@@ -317,6 +317,7 @@ int LoadManager::getUptime()
void LoadManager::threadEntry() void LoadManager::threadEntry()
{ {
NameThread("loadmgr");
boost::posix_time::ptime t = boost::posix_time::microsec_clock::universal_time(); boost::posix_time::ptime t = boost::posix_time::microsec_clock::universal_time();
while (1) while (1)
{ {

View File

@@ -1068,7 +1068,7 @@ std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >
str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM " str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM "
"AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID "
"WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' " "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); % account.humanAccountID() % maxLedger % minLedger);
{ {
@@ -1105,7 +1105,7 @@ std::vector<NetworkOPs::txnMetaLedgerType> NetworkOPs::getAccountTxsB(
std::string sql = str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM " std::string sql = str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM "
"AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID "
"WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' " "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); % account.humanAccountID() % maxLedger % minLedger);
{ {
@@ -1164,10 +1164,10 @@ std::vector<RippleAddress>
return accounts; return accounts;
} }
bool NetworkOPs::recvValidation(SerializedValidation::ref val) bool NetworkOPs::recvValidation(SerializedValidation::ref val, const std::string& source)
{ {
cLog(lsDEBUG) << "recvValidation " << val->getLedgerHash(); cLog(lsDEBUG) << "recvValidation " << val->getLedgerHash() << " from " << source;
return theApp->getValidations().addValidation(val); return theApp->getValidations().addValidation(val, source);
} }
Json::Value NetworkOPs::getConsensusInfo() Json::Value NetworkOPs::getConsensusInfo()
@@ -1665,18 +1665,20 @@ void NetworkOPs::unsubAccountChanges(InfoSub* isrListener)
// <-- bool: true=added, false=already there // <-- bool: true=added, false=already there
bool NetworkOPs::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) 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["fee_ref"] = Json::UInt(lpClosed->getReferenceFeeUnits());
jvResult["ledger_hash"] = lpClosed->getHash().ToString(); jvResult["fee_base"] = Json::UInt(lpClosed->getBaseFee());
jvResult["ledger_time"] = Json::Value::UInt(lpClosed->getCloseTimeNC()); jvResult["reserve_base"] = Json::UInt(lpClosed->getReserve(0));
jvResult["reserve_inc"] = Json::UInt(lpClosed->getReserveInc());
}
jvResult["fee_ref"] = Json::UInt(lpClosed->getReferenceFeeUnits()); if (((mMode == omFULL) || (mMode == omTRACKING)) && !isNeedNetworkLedger())
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))
jvResult["validated_ledgers"] = theApp->getLedgerMaster().getCompleteLedgers(); jvResult["validated_ledgers"] = theApp->getLedgerMaster().getCompleteLedgers();
boost::recursive_mutex::scoped_lock sl(mMonitorLock); boost::recursive_mutex::scoped_lock sl(mMonitorLock);

View File

@@ -253,7 +253,7 @@ public:
RippleAddress nodePublic, uint256 checkLedger, bool sigGood); RippleAddress nodePublic, uint256 checkLedger, bool sigGood);
SMAddNode gotTXData(const boost::shared_ptr<Peer>& peer, const uint256& hash, SMAddNode gotTXData(const boost::shared_ptr<Peer>& peer, const uint256& hash,
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData); const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData);
bool recvValidation(SerializedValidation::ref val); bool recvValidation(SerializedValidation::ref val, const std::string& source);
void takePosition(int seq, SHAMap::ref position); void takePosition(int seq, SHAMap::ref position);
SHAMap::pointer getTXMap(const uint256& hash); SHAMap::pointer getTXMap(const uint256& hash);
bool hasTXSet(const boost::shared_ptr<Peer>& peer, const uint256& set, ripple::TxSetStatus status); bool hasTXSet(const boost::shared_ptr<Peer>& peer, const uint256& set, ripple::TxSetStatus status);

View File

@@ -7,6 +7,80 @@
SETUP_LOG(); 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<uint256>& usOfferUnfundedFound,
boost::unordered_set<uint256>& usOfferUnfundedBecame,
boost::unordered_set<uint160>& 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<uint160>::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. // Take as much as possible. Adjusts account balances. Charges fees on top to taker.
// --> uBookBase: The order book to take against. // --> uBookBase: The order book to take against.
// --> saTakerPays: What the taker offers (w/ issuer) // --> saTakerPays: What the taker offers (w/ issuer)
@@ -28,7 +102,7 @@ TER OfferCreateTransactor::takeOffers(
bool& bUnfunded) bool& bUnfunded)
{ {
// The book has the most elements. Take the perspective of the book. // 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. // The order is for the other books currencys for get and pays are opposites.
// We want the same ratio for the respective currencies. // We want the same ratio for the respective currencies.
@@ -141,162 +215,121 @@ TER OfferCreateTransactor::takeOffers(
STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets); STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets);
STAmount saOfferGets = sleOffer->getFieldAmount(sfTakerPays); STAmount saOfferGets = sleOffer->getFieldAmount(sfTakerPays);
if (sleOffer->isFieldPresent(sfExpiration) && sleOffer->getFieldU32(sfExpiration) <= mEngine->getLedger()->getParentCloseTimeNC()) STAmount saOfferFunds; // Funds of offer owner to payout.
{ bool bValid;
// Offer is expired. Expired offers are considered unfunded. Delete it.
cLog(lsINFO) << "takeOffers: encountered expired offer";
usOfferUnfundedFound.insert(uOfferIndex); bValid = bValidOffer(
} sleOfferDir, uOfferIndex, uOfferOwnerID, saOfferPays, saOfferGets,
else if (uOfferOwnerID == uTakerAccountID) uTakerAccountID,
{ usOfferUnfundedFound, usOfferUnfundedBecame, usAccountTouched,
// Would take own offer. Consider old offer expired. Delete it. saOfferFunds);
cLog(lsINFO) << "takeOffers: encountered taker's own old offer";
usOfferUnfundedBecame.insert(uOfferIndex); if (bValid) {
} STAmount saSubTakerPaid;
else if (!saOfferGets.isPositive() || !saOfferPays.isPositive()) STAmount saSubTakerGot;
{ STAmount saTakerIssuerFee;
// Offer has bad amounts. Consider offer expired. Delete it. STAmount saOfferIssuerFee;
cLog(lsWARNING) << boost::str(boost::format("takeOffers: BAD OFFER: saOfferPays=%s saOfferGets=%s") STAmount saOfferRate = STAmount::setRate(uTipQuality);
% saOfferPays % saOfferGets);
usOfferUnfundedFound.insert(uOfferIndex); cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText();
} cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText();
else cLog(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText();
{ cLog(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText();
// Get offer funds available. 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); cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText();
SLE::pointer sleOfferAccount; // Owner of offer. 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. // Offer now fully claimed or now unfunded.
cLog(lsINFO) << "takeOffers: offer unfunded: delete"; cLog(lsINFO) << "takeOffers: Offer claimed: Delete.";
boost::unordered_set<uint160>::iterator account = usAccountTouched.find(uOfferOwnerID); usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success.
if (account != usAccountTouched.end())
// 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. cLog(lsWARNING) << "takeOffers: ILLEGAL OFFER RESULT.";
usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. bUnfunded = true;
} terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
else
{
// Never touched source account.
usOfferUnfundedFound.insert(uOfferIndex); // Delete found unfunded offer when possible.
} }
} }
else else
{ {
STAmount saSubTakerPaid; // Taker got nothing, probably due to rounding. Consider taker unfunded.
STAmount saSubTakerGot; cLog(lsINFO) << "takeOffers: No claim.";
STAmount saTakerIssuerFee;
STAmount saOfferIssuerFee;
STAmount saOfferRate = STAmount::setRate(uTipQuality);
cLog(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); bUnfunded = true;
cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText(); terResult = tesSUCCESS; // Done.
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();
bool bOfferDelete = STAmount::applyOffer( assert(uTakerGetsAccountID == saSubTakerGot.getIssuer());
lesActive.rippleTransferRate(uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID), assert(uTakerPaysAccountID == saSubTakerPaid.getIssuer());
lesActive.rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID),
saOfferRate,
saOfferFunds,
saTakerFunds,
saOfferPays,
saOfferGets,
saSubTakerPays,
saSubTakerGets,
saSubTakerPaid,
saSubTakerGot,
saTakerIssuerFee,
saOfferIssuerFee);
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: 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. if (tesSUCCESS == terResult)
sleOffer->setFieldAmount(sfTakerGets, saOfferPays -= saSubTakerGot); terResult = temUNCERTAIN;
// 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;
}
} }
} }
} }
@@ -426,6 +459,17 @@ TER OfferCreateTransactor::doApply()
terResult = terNO_ACCOUNT; 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; STAmount saPaid;

View File

@@ -6,6 +6,18 @@
class OfferCreateTransactor : public Transactor class OfferCreateTransactor : public Transactor
{ {
protected: protected:
bool bValidOffer(
SLE::ref sleOfferDir,
const uint256& uOffer,
const uint160& uOfferOwnerID,
const STAmount& saOfferPays,
const STAmount& saOfferGets,
const uint160& uTakerAccountID,
boost::unordered_set<uint256>& usOfferUnfundedFound,
boost::unordered_set<uint256>& usOfferUnfundedBecame,
boost::unordered_set<uint160>& usAccountTouched,
STAmount& saOfferFunds);
TER takeOffers( TER takeOffers(
const bool bOpenLedger, const bool bOpenLedger,
const bool bPassive, const bool bPassive,

View File

@@ -714,9 +714,9 @@ boost::unordered_set<uint160> usAccountSourceCurrencies(const RippleAddress& raA
STAmount saBalance = rspEntry->getBalance(); STAmount saBalance = rspEntry->getBalance();
// Filter out non // Filter out non
if (saBalance.isPositive() // Have IOUs to send. if (saBalance.isPositive() // Have IOUs to send.
|| (rspEntry->getLimitPeer() // Peer extends credit. || (rspEntry->getLimitPeer() // Peer extends credit.
&& -saBalance < rspEntry->getLimitPeer())) // Credit left. && ((-saBalance) < rspEntry->getLimitPeer()))) // Credit left.
{ {
usCurrencies.insert(saBalance.getCurrency()); usCurrencies.insert(saBalance.getCurrency());
} }

View File

@@ -95,6 +95,8 @@ void Peer::detach(const char *rsn, bool onIOStrand)
if (!mDetaching) if (!mDetaching)
{ {
mDetaching = true; // Race is ok. mDetaching = true; // Race is ok.
tLog(mCluster, lsWARNING) << "Cluster peer detach \"" << mNodeName << "\": " << rsn;
/* /*
cLog(lsDEBUG) << "Peer: Detach: " cLog(lsDEBUG) << "Peer: Detach: "
<< ADDRESS(this) << "> " << ADDRESS(this) << "> "
@@ -384,7 +386,15 @@ void Peer::handleReadHeader(const boost::system::error_code& error)
} }
else 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); detach("hrh2", true);
} }
} }
@@ -397,7 +407,15 @@ void Peer::handleReadBody(const boost::system::error_code& error)
} }
else if (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()); boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock());
detach("hrb", true); detach("hrb", true);
return; return;
@@ -734,6 +752,8 @@ void Peer::recvHello(ripple::TMHello& packet)
{ {
mCluster = true; mCluster = true;
mLoad.setPrivileged(); mLoad.setPrivileged();
cLog(lsINFO) << "Cluster connection to \"" << (mNodeName.empty() ? getIP() : mNodeName)
<< "\" established";
} }
if (isOutbound()) if (isOutbound())
mLoad.setOutbound(); mLoad.setOutbound();
@@ -1036,8 +1056,16 @@ static void checkValidation(Job&, SerializedValidation::pointer val, uint256 sig
return; return;
} }
std::string source;
Peer::pointer lp = peer.lock();
if (lp)
source = lp->getDisplayName();
else
source = "unknown";
std::set<uint64> peers; std::set<uint64> 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<PackedMessage>(*packet, ripple::mtVALIDATION); PackedMessage::pointer message = boost::make_shared<PackedMessage>(*packet, ripple::mtVALIDATION);
theApp->getConnectionPool().relayMessageBut(peers, message); theApp->getConnectionPool().relayMessageBut(peers, message);
@@ -1394,8 +1422,7 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet)
if (!map) if (!map)
{ {
if (packet.has_querytype() && !packet.has_requestcookie()) 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"; cLog(lsDEBUG) << "Trying to route TX set request";
std::vector<Peer::pointer> peerList = theApp->getConnectionPool().getPeerVector(); std::vector<Peer::pointer> peerList = theApp->getConnectionPool().getPeerVector();
std::vector<Peer::pointer> usablePeers; std::vector<Peer::pointer> usablePeers;
@@ -1622,7 +1649,7 @@ void Peer::recvLedger(const boost::shared_ptr<ripple::TMLedgerData>& packet_ptr)
if (target) if (target)
{ {
packet.clear_requestcookie(); packet.clear_requestcookie();
target->sendPacket(boost::make_shared<PackedMessage>(packet, ripple::mtLEDGER_DATA), true); target->sendPacket(boost::make_shared<PackedMessage>(packet, ripple::mtLEDGER_DATA), false);
} }
else else
{ {

View File

@@ -117,6 +117,7 @@ public:
//bool operator == (const Peer& other); //bool operator == (const Peer& other);
std::string& getIP() { return mIpPort.first; } std::string& getIP() { return mIpPort.first; }
std::string getDisplayName() { return mCluster ? mNodeName : mIpPort.first; }
int getPort() { return mIpPort.second; } int getPort() { return mIpPort.second; }
void setIpPort(const std::string& strIP, int iPort); void setIpPort(const std::string& strIP, int iPort);

View File

@@ -2021,9 +2021,19 @@ Json::Value RPCHandler::doGetCounts(Json::Value jvRequest, int& cost)
BOOST_FOREACH(InstanceType::InstanceCount& it, count) BOOST_FOREACH(InstanceType::InstanceCount& it, count)
ret[it.first] = it.second; ret[it.first] = it.second;
int dbKB = theApp->getLedgerDB()->getDB()->getKBUsed(); int dbKB = theApp->getLedgerDB()->getDB()->getKBUsedAll();
if (dbKB > 0) 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; std::string uptime;
int s = upTime(); int s = upTime();

View File

@@ -114,7 +114,7 @@ TER PathState::pushImply(
// Append a node and insert before it any implied nodes. // Append a node and insert before it any implied nodes.
// Offers may go back to back. // 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( TER PathState::pushNode(
const int iType, const int iType,
const uint160& uAccountID, const uint160& uAccountID,
@@ -231,11 +231,30 @@ TER PathState::pushNode(
<< STAmount::createHumanCurrency(pnCur.uCurrencyID) << 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. // 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( void PathState::setExpanded(
const LedgerEntrySet& lesSource, const LedgerEntrySet& lesSource,
const STPath& spSourcePath, const STPath& spSourcePath,
@@ -689,6 +708,7 @@ void RippleCalc::setCanonical(STPathSet& spsDst, const std::vector<PathState::po
// cLog(lsDEBUG) << boost::str(boost::format("SET: setCanonical< %d") % spsDst.size()); // cLog(lsDEBUG) << boost::str(boost::format("SET: setCanonical< %d") % spsDst.size());
} }
// This is for debugging not end users. Output names can be changed without warning.
Json::Value PathState::getJson() const Json::Value PathState::getJson() const
{ {
Json::Value jvPathState(Json::objectValue); Json::Value jvPathState(Json::objectValue);
@@ -769,7 +789,7 @@ Json::Value PathState::getJson() const
// If needed, advance to next funded offer. // If needed, advance to next funded offer.
// - Automatically advances to first offer. // - Automatically advances to first offer.
// - Set bEntryAdvance to advance to next entry. // --> bEntryAdvance: true, to advance to next entry. false, recalculate.
// <-- uOfferIndex : 0=end of list. // <-- uOfferIndex : 0=end of list.
TER RippleCalc::calcNodeAdvance( TER RippleCalc::calcNodeAdvance(
const unsigned int uNode, // 0 < uNode < uLast 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 // 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 // requirements to the previous node. The previous node adjusts the amount output and the amount spent on fees. Continue
// till request is satisified while we the rate does not increase past the initial rate. // processing until the request is satisified as long as the rate does not increase past the initial rate.
TER RippleCalc::calcNodeDeliverRev( TER RippleCalc::calcNodeDeliverRev(
const unsigned int uNode, // 0 < uNode < uLast const unsigned int uNode, // 0 < uNode < uLast
PathState& psCur, PathState& psCur,
@@ -1152,6 +1172,9 @@ TER RippleCalc::calcNodeDeliverRev(
else if (saOutFeeRate < saRateMax) else if (saOutFeeRate < saRateMax)
{ {
// Reducing rate. Additional offers will only considered for this increment if they are at least this good. // 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; 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. STAmount saOutPass = std::min(std::min(saOfferFunds, saTakerGets), saOutReq-saOutAct); // Offer maximum out - assuming no out fees.
// Amount charged to the offer owner. // 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. // 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") cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: saOutReq=%s saOutAct=%s saTakerGets=%s saOutPass=%s saOutPlusFees=%s saOfferFunds=%s")
% saOutReq % saOutReq
@@ -1178,7 +1202,8 @@ TER RippleCalc::calcNodeDeliverRev(
// Offer owner can not cover all fees, compute saOutPass based on saOfferFunds. // Offer owner can not cover all fees, compute saOutPass based on saOfferFunds.
saOutPlusFees = 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") cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: Total exceeds fees: saOutPass=%s saOutPlusFees=%s saOfferFunds=%s")
% saOutPass % saOutPass
@@ -1188,8 +1213,7 @@ TER RippleCalc::calcNodeDeliverRev(
// Compute portion of input needed to cover actual output. // Compute portion of input needed to cover actual output.
// XXX This needs to round up! STAmount saInPassReq = STAmount::mulRound(saOutPass, saOfrRate, saTakerPays, true);
STAmount saInPassReq = STAmount::multiply(saOutPass, saOfrRate, saTakerPays);
STAmount saInPassAct; STAmount saInPassAct;
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: saInPassReq=%s saOfrRate=%s saOutPass=%s saOutPlusFees=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: saInPassReq=%s saOfrRate=%s saOutPass=%s saOutPlusFees=%s")
@@ -1198,8 +1222,16 @@ TER RippleCalc::calcNodeDeliverRev(
% saOutPass % saOutPass
% saOutPlusFees); % 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. // Find out input amount actually available at current rate.
if (!!uPrvAccountID) else if (!!uPrvAccountID)
{ {
// account --> OFFER --> ? // account --> OFFER --> ?
// Due to node expansion, previous is guaranteed to be the issuer. // Due to node expansion, previous is guaranteed to be the issuer.
@@ -1236,8 +1268,9 @@ TER RippleCalc::calcNodeDeliverRev(
if (saInPassAct != saInPassReq) if (saInPassAct != saInPassReq)
{ {
// Adjust output to conform to limited input. // Adjust output to conform to limited input.
saOutPass = STAmount::divide(saInPassAct, saOfrRate, saTakerGets); // XXX Verify it is impossible for these to be larger than available funds.
saOutPlusFees = STAmount::multiply(saOutPass, saOutFeeRate); 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") cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: adjusted: saOutPass=%s saOutPlusFees=%s")
% saOutPass % saOutPass
@@ -1281,15 +1314,16 @@ TER RippleCalc::calcNodeDeliverRev(
// Offer became unfunded. // Offer became unfunded.
cLog(lsINFO) << boost::str(boost::format("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; saOutAct += saOutPass;
saPrvDlvReq += saInPassAct; saPrvDlvReq += saInPassAct;
} }
// XXX Perhaps need to check if partial is okay to relax this?
if (tesSUCCESS == terResult && !saOutAct) if (tesSUCCESS == terResult && !saOutAct)
terResult = tecPATH_DRY; terResult = tecPATH_DRY; // Unable to meet request, consider path dry.
return terResult; return terResult;
} }
@@ -1371,12 +1405,12 @@ TER RippleCalc::calcNodeDeliverFwd(
// First calculate assuming no output fees: saInPassAct, saInPassFees, saOutPassAct // 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 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 saInFunded = STAmount::mulRound(saOutFunded, saOfrRate, saTakerPays, true); // Offer maximum in - Limited by by payout.
STAmount saInTotal = STAmount::multiply(saInFunded, saInTransRate); // Offer maximum in with fees. 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 saInSum = std::min(saInTotal, saInReq-saInAct-saInFees); // In limited by remaining.
STAmount saInPassAct = STAmount::divide(saInSum, saInFeeRate); // In without fees. STAmount saInPassAct = STAmount::divRound(saInSum, saInFeeRate, true); // In without fees.
STAmount saOutPassMax = STAmount::divide(saInPassAct, saOfrRate, saTakerGets); // Out limited by in remaining. STAmount saOutPassMax = STAmount::divRound(saInPassAct, saOfrRate, saTakerGets, true); // Out limited by in remaining.
STAmount saInPassFeesMax = saInSum-saInPassAct; STAmount saInPassFeesMax = saInSum-saInPassAct;
@@ -1469,8 +1503,8 @@ TER RippleCalc::calcNodeDeliverFwd(
{ {
// Fraction of output amount. // Fraction of output amount.
// Output fees are paid by offer owner and not passed to previous. // Output fees are paid by offer owner and not passed to previous.
saInPassAct = STAmount::multiply(saOutPassAct, saOfrRate, saInReq); saInPassAct = STAmount::mulRound(saOutPassAct, saOfrRate, saInReq, true);
saInPassFees = std::min(saInPassFeesMax, STAmount::multiply(saInPassAct, saInFeeRate)); saInPassFees = std::min(saInPassFeesMax, STAmount::mulRound(saInPassAct, saInFeeRate, true));
} }
// Do outbound debiting. // 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 // uQualityIn -> uQualityOut
// saPrvReq -> saCurReq // saPrvReq -> saCurReq
// sqPrvAct -> saCurAct // sqPrvAct -> saCurAct
@@ -1698,7 +1732,7 @@ void RippleCalc::calcNodeRipple(
const uint160 uCurIssuerID = saCur.getIssuer(); const uint160 uCurIssuerID = saCur.getIssuer();
// const uint160 uPrvIssuerID = saPrv.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()); cLog(lsTRACE) << boost::str(boost::format("calcNodeRipple: bPrvUnlimited=%d saPrv=%s saCurIn=%s") % bPrvUnlimited % saPrv.getFullText() % saCurIn.getFullText());
if (bPrvUnlimited || saCurIn <= saPrv) if (bPrvUnlimited || saCurIn <= saPrv)
@@ -1711,7 +1745,7 @@ void RippleCalc::calcNodeRipple(
else else
{ {
// A part of cur. All of prv. (cur as driver) // 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()); cLog(lsTRACE) << boost::str(boost::format("calcNodeRipple:4: saCurReq=%s") % saCurReq.getFullText());
saCurAct += saCurOut; saCurAct += saCurOut;
@@ -2209,7 +2243,7 @@ TER RippleCalc::calcNodeAccountFwd(
STAmount saIssueCrd = uQualityIn >= QUALITY_ONE STAmount saIssueCrd = uQualityIn >= QUALITY_ONE
? saPrvIssueReq // No fee. ? 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. // Amount to credit. Credit for less than received as a surcharge.
saCurReceive = saPrvRedeemReq+saIssueCrd; saCurReceive = saPrvRedeemReq+saIssueCrd;

View File

@@ -12,17 +12,11 @@ class ConsensusTransSetSF : public SHAMapSyncFilter
{ // sync filter for transaction sets during consensus building { // sync filter for transaction sets during consensus building
public: public:
ConsensusTransSetSF() { ; } ConsensusTransSetSF() { ; }
virtual void gotNode(const SHAMapNode& id, const uint256& nodeHash, virtual void gotNode(const SHAMapNode& id, const uint256& nodeHash,
const std::vector<unsigned char>& nodeData, SHAMapTreeNode::TNType) const std::vector<unsigned char>& nodeData, SHAMapTreeNode::TNType);
{
// WRITEME: If 'isLeaf' is true, this is a transaction virtual bool haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector<unsigned char>& nodeData);
theApp->getTempNodeCache().store(nodeHash, nodeData);
}
virtual bool haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector<unsigned char>& nodeData)
{
// WRITEME: We could check our own map, we could check transaction tables
return theApp->getTempNodeCache().retrieve(nodeHash, nodeData);
}
}; };
// This class is only needed on add functions // This class is only needed on add functions

View File

@@ -442,7 +442,7 @@ public:
static STAmount divRound(const STAmount& v1, const STAmount& v2, const STAmount& saUnit, bool roundUp) static STAmount divRound(const STAmount& v1, const STAmount& v2, const STAmount& saUnit, bool roundUp)
{ return divRound(v1, v2, saUnit.getCurrency(), saUnit.getIssuer(), roundUp); } { return divRound(v1, v2, saUnit.getCurrency(), saUnit.getIssuer(), roundUp); }
static STAmount divRound(const STAmount& v1, const STAmount& v2, bool 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? // Someone is offering X for Y, what is the rate?
// Rate: smaller is better, the taker wants the most out: in/out // Rate: smaller is better, the taker wants the most out: in/out

View File

@@ -77,7 +77,7 @@ public:
bool checkSign() const; bool checkSign() const;
void updateID() { mTransactionID=mTransaction->getTransactionID(); } void updateID() { mTransactionID=mTransaction->getTransactionID(); }
SerializedTransaction::pointer getSTransaction() { return mTransaction; } SerializedTransaction::ref getSTransaction() { return mTransaction; }
const uint256& getID() const { return mTransactionID; } const uint256& getID() const { return mTransactionID; }
const RippleAddress& getFromAccount() const { return mAccountFrom; } const RippleAddress& getFromAccount() const { return mAccountFrom; }

View File

@@ -0,0 +1,219 @@
#include "LedgerConsensus.h"
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/unordered_set.hpp>
#include <boost/foreach.hpp>
#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<uint160, LedgerProposal::pointer>::value_type u160_prop_pair;
typedef std::map<uint256, LCTransaction::pointer>::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<SHAMap>(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<Peer::pointer> 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<PeerSet> TransactionAcquire::pmDowncast()
{
return boost::shared_polymorphic_downcast<PeerSet>(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<SHAMapNode> nodeIDs;
std::vector<uint256> 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<SHAMapNode>& nodeIDs,
const std::list< std::vector<unsigned char> >& 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<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin();
std::list< std::vector<unsigned char> >::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<unsigned char>& 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<SerializedTransaction>(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<unsigned char>& 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;
}

View File

@@ -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." }, { 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." }, { terINSUF_FEE_B, "terINSUF_FEE_B", "Account balance can't pay fee." },
{ terNO_ACCOUNT, "terNO_ACCOUNT", "The source account does not exist." }, { 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." }, { terNO_LINE, "terNO_LINE", "No such line." },
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction." }, { terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction." },
{ terOWNERS, "terOWNERS", "Non-zero owner count." }, { terOWNERS, "terOWNERS", "Non-zero owner count." },

View File

@@ -95,6 +95,7 @@ enum TER // aka TransactionEngineResult
terFUNDS_SPENT, // This is a free transaction, therefore don't burden network. terFUNDS_SPENT, // This is a free transaction, therefore don't burden network.
terINSUF_FEE_B, // Can't pay fee, 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_ACCOUNT, // Can't pay fee, therefore don't burden network.
terNO_AUTH, // Not authorized to hold IOUs.
terNO_LINE, // Internal flag. terNO_LINE, // Internal flag.
terOWNERS, // Can't succeed with non-zero owner count. terOWNERS, // Can't succeed with non-zero owner count.
terPRE_SEQ, // Can't pay fee, no point in forwarding, therefore don't burden network. terPRE_SEQ, // Can't pay fee, no point in forwarding, therefore don't burden network.

View File

@@ -34,7 +34,7 @@ VSpointer ValidationCollection::findSet(const uint256& ledgerHash)
return mValidations.fetch(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(); RippleAddress signer = val->getSignerPublic();
bool isCurrent = false; bool isCurrent = false;
@@ -52,7 +52,8 @@ bool ValidationCollection::addValidation(SerializedValidation::ref val)
} }
else 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(); uint256 hash = val->getLedgerHash();

View File

@@ -37,7 +37,7 @@ public:
ValidationCollection() : mValidations("Validations", 128, 600), mWriting(false) ValidationCollection() : mValidations("Validations", 128, 600), mWriting(false)
{ mStaleValidations.reserve(512); } { mStaleValidations.reserve(512); }
bool addValidation(SerializedValidation::ref); bool addValidation(SerializedValidation::ref, const std::string& source);
ValidationSet getValidations(const uint256& ledger); ValidationSet getValidations(const uint256& ledger);
void getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int& untrusted); void getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int& untrusted);
void getValidationTypes(const uint256& ledger, int& full, int& partial); void getValidationTypes(const uint256& ledger, int& full, int& partial);

View File

@@ -6,7 +6,7 @@
#define SERVER_VERSION_MAJOR 0 #define SERVER_VERSION_MAJOR 0
#define SERVER_VERSION_MINOR 8 #define SERVER_VERSION_MINOR 8
#define SERVER_VERSION_SUB "-a" #define SERVER_VERSION_SUB "-b"
#define SERVER_NAME "Ripple" #define SERVER_NAME "Ripple"
#define SV_STRINGIZE(x) SV_STRINGIZE2(x) #define SV_STRINGIZE(x) SV_STRINGIZE2(x)

View File

@@ -48,6 +48,7 @@ static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength)
void WSDoor::startListening() void WSDoor::startListening()
{ {
NameThread("websocket");
// Generate a single SSL context for use by all connections. // Generate a single SSL context for use by all connections.
boost::shared_ptr<boost::asio::ssl::context> mCtx; boost::shared_ptr<boost::asio::ssl::context> mCtx;
mCtx = boost::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23); mCtx = boost::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);

View File

@@ -84,6 +84,7 @@ void printHelp(const po::options_description& desc)
cerr << " data_store <key> <value>" << endl; cerr << " data_store <key> <value>" << endl;
#endif #endif
cerr << " get_counts" << endl; cerr << " get_counts" << endl;
cerr << " json <method> <json>" << endl;
cerr << " ledger [<id>|current|closed|validated] [full]" << endl; cerr << " ledger [<id>|current|closed|validated] [full]" << endl;
cerr << " ledger_accept" << endl; cerr << " ledger_accept" << endl;
cerr << " ledger_closed" << endl; cerr << " ledger_closed" << endl;
@@ -127,6 +128,7 @@ void printHelp(const po::options_description& desc)
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
NameThread("main");
int iResult = 0; int iResult = 0;
po::variables_map vm; // Map of options. po::variables_map vm; // Map of options.
@@ -148,6 +150,7 @@ int main(int argc, char* argv[])
("ledger", po::value<std::string>(), "Load the specified ledger and start from .") ("ledger", po::value<std::string>(), "Load the specified ledger and start from .")
("start", "Start from a fresh Ledger.") ("start", "Start from a fresh Ledger.")
("net", "Get the initial ledger from the network.") ("net", "Get the initial ledger from the network.")
("fg", "Run in the foreground.")
; ;
// Interpret positional arguments as --parameters. // 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")) if (vm.count("quiet"))
Log::setMinSeverity(lsFATAL, true); Log::setMinSeverity(lsFATAL, true);
else if (vm.count("verbose")) else if (vm.count("verbose"))
@@ -248,11 +259,13 @@ int main(int argc, char* argv[])
{ {
// No arguments. Run server. // No arguments. Run server.
setupServer(); setupServer();
NameThread("io");
startServer(); startServer();
} }
else else
{ {
// Have a RPC command. // Have a RPC command.
NameThread("rpc");
std::vector<std::string> vCmd = vm["parameters"].as<std::vector<std::string> >(); std::vector<std::string> vCmd = vm["parameters"].as<std::vector<std::string> >();
iResult = commandLineRPC(vCmd); iResult = commandLineRPC(vCmd);

View File

@@ -1,5 +1,11 @@
#include "utils.h"
#include "uint256.h" #ifdef __linux__
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#endif
#include <fstream>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@@ -9,6 +15,9 @@
#include <openssl/rand.h> #include <openssl/rand.h>
#include "utils.h"
#include "uint256.h"
void getRand(unsigned char *buf, int num) void getRand(unsigned char *buf, int num)
{ {
if (RAND_bytes(buf, num) != 1) if (RAND_bytes(buf, num) != 1)
@@ -333,6 +342,112 @@ uint32_t be32toh(uint32_t value)
#endif #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<pid_t>(0);
static pid_t pChild = static_cast<pid_t>(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<int>(pChild)).c_str());
rename("debug.log", boost::str(boost::format("debug.log.%d") % static_cast<int>(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_SUITE( Utils)
BOOST_AUTO_TEST_CASE( ParseUrl ) BOOST_AUTO_TEST_CASE( ParseUrl )

View File

@@ -286,6 +286,12 @@ template<typename T, typename U> 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); 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) #if (!defined(FORCE_NO_C11X) && (__cplusplus > 201100L)) || defined(FORCE_C11X)
#define C11X #define C11X

View File

@@ -1072,6 +1072,7 @@ buster.testCase("Offer tests", {
buster.testCase("Offer cross currency", { buster.testCase("Offer cross currency", {
'setUp' : testutils.build_setup(), 'setUp' : testutils.build_setup(),
// 'setUp' : testutils.build_setup({ verbose: true }),
'tearDown' : testutils.build_teardown(), 'tearDown' : testutils.build_teardown(),
"ripple cross currency payment - start with XRP" : "ripple cross currency payment - start with XRP" :
@@ -1112,7 +1113,7 @@ buster.testCase("Offer cross currency", {
self.what = "Create offer."; self.what = "Create offer.";
self.remote.transaction() self.remote.transaction()
.offer_create("carol", "500", "50/USD/mtgox") .offer_create("carol", "500.0", "50/USD/mtgox")
.on('proposed', function (m) { .on('proposed', function (m) {
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); // console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
callback(m.result !== 'tesSUCCESS'); callback(m.result !== 'tesSUCCESS');
@@ -1126,7 +1127,7 @@ buster.testCase("Offer cross currency", {
self.remote.transaction() self.remote.transaction()
.payment("alice", "bob", "25/USD/mtgox") .payment("alice", "bob", "25/USD/mtgox")
.send_max("333") .send_max("333.0")
.on('proposed', function (m) { .on('proposed', function (m) {
// console.log("proposed: %s", JSON.stringify(m)); // console.log("proposed: %s", JSON.stringify(m));

View File

@@ -1189,8 +1189,8 @@ buster.testCase("Indirect paths", {
}); });
buster.testCase("Quality paths", { buster.testCase("Quality paths", {
// 'setUp' : testutils.build_setup(), 'setUp' : testutils.build_setup(),
'setUp' : testutils.build_setup({ verbose: true }), // 'setUp' : testutils.build_setup({ verbose: true }),
// 'setUp' : testutils.build_setup({ verbose: true, no_server: true }), // 'setUp' : testutils.build_setup({ verbose: true, no_server: true }),
'tearDown' : testutils.build_teardown(), 'tearDown' : testutils.build_teardown(),