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

This commit is contained in:
jed
2013-01-28 11:21:52 -08:00
33 changed files with 707 additions and 105 deletions

View File

@@ -211,6 +211,11 @@
# Examples: RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE
# shfArahZT9Q9ckTf3s1psJ7C7qzVN
#
# [node_size]:
# Tunes the servers based on the expected load and available memory. Legal
# sizes are "tiny", "small", "medium", "large", and "huge".
# The default is "tiny".
#
# [cluster_nodes]:
# To extend full trust to other nodes, place their node public keys here.
# Generally, you should only do this for nodes under common administration.

View File

@@ -238,4 +238,120 @@ void SqliteDatabase::runWal()
}
}
SqliteStatement::SqliteStatement(SqliteDatabase* db, const char *sql)
{
assert(db);
int j = sqlite3_prepare_v2(db->peekConnection(), sql, strlen(sql) + 1, &statement, NULL);
if (j != SQLITE_OK)
throw j;
}
SqliteStatement::~SqliteStatement()
{
sqlite3_finalize(statement);
}
sqlite3_stmt* SqliteStatement::peekStatement()
{
return statement;
}
int SqliteStatement::bind(int position, const void *data, int length)
{
return sqlite3_bind_blob(statement, position, data, length, SQLITE_TRANSIENT);
}
int SqliteStatement::bindStatic(int position, const void *data, int length)
{
return sqlite3_bind_blob(statement, position, data, length, SQLITE_STATIC);
}
int SqliteStatement::bindStatic(int position, const std::vector<unsigned char>& value)
{
return sqlite3_bind_blob(statement, position, &value.front(), value.size(), SQLITE_STATIC);
}
int SqliteStatement::bind(int position, uint32 value)
{
return sqlite3_bind_int64(statement, position, static_cast<sqlite3_int64>(value));
}
int SqliteStatement::bind(int position, const std::string& value)
{
return sqlite3_bind_text(statement, position, value.data(), value.size(), SQLITE_TRANSIENT);
}
int SqliteStatement::bindStatic(int position, const std::string& value)
{
return sqlite3_bind_text(statement, position, value.data(), value.size(), SQLITE_STATIC);
}
int SqliteStatement::bind(int position)
{
return sqlite3_bind_null(statement, position);
}
int SqliteStatement::size(int column)
{
return sqlite3_column_bytes(statement, column);
}
const void* SqliteStatement::peekBlob(int column)
{
return sqlite3_column_blob(statement, column);
}
std::vector<unsigned char> SqliteStatement::getBlob(int column)
{
int size = sqlite3_column_bytes(statement, column);
std::vector<unsigned char> ret(size);
memcpy(&(ret.front()), sqlite3_column_blob(statement, column), size);
return ret;
}
std::string SqliteStatement::getString(int column)
{
return reinterpret_cast<const char *>(sqlite3_column_text(statement, column));
}
const char* SqliteStatement::peekString(int column)
{
return reinterpret_cast<const char *>(sqlite3_column_text(statement, column));
}
uint32 SqliteStatement::getUInt32(int column)
{
return static_cast<uint32>(sqlite3_column_int64(statement, column));
}
int64 SqliteStatement::getInt64(int column)
{
return sqlite3_column_int64(statement, column);
}
int SqliteStatement::step()
{
return sqlite3_step(statement);
}
int SqliteStatement::reset()
{
return sqlite3_reset(statement);
}
bool SqliteStatement::isOk(int j)
{
return j == SQLITE_OK;
}
bool SqliteStatement::isDone(int j)
{
return j == SQLITE_DONE;
}
bool SqliteStatement::isRow(int j)
{
return j == SQLITE_ROW;
}
// vim:ts=4

View File

@@ -52,9 +52,53 @@ public:
sqlite3* peekConnection() { return mConnection; }
virtual bool setupCheckpointing();
virtual SqliteDatabase* getSqliteDB() { return this; }
void runWal();
void doHook(const char *db, int walSize);
};
class SqliteStatement
{
private:
SqliteStatement(const SqliteStatement&); // no implementation
SqliteStatement& operator=(const SqliteStatement&); // no implementation
protected:
sqlite3_stmt* statement;
public:
SqliteStatement(SqliteDatabase* db, const char *statement);
~SqliteStatement();
sqlite3_stmt* peekStatement();
int bind(int position, const void *data, int length);
int bindStatic(int position, const void *data, int length);
int bindStatic(int position, const std::vector<unsigned char>& value);
int bind(int position, const std::string& value);
int bindStatic(int position, const std::string& value);
int bind(int position, uint32 value);
int bind(int position);
int size(int column);
const void* peekBlob(int column);
std::vector<unsigned char> getBlob(int column);
std::string getString(int column);
const char* peekString(int column);
uint32 getUInt32(int column);
int64 getInt64(int column);
int step();
int reset();
bool isOk(int);
bool isDone(int);
bool isRow(int);
};
// vim:ts=4

View File

@@ -16,6 +16,9 @@
/*
this maintains the connection to the database
*/
class SqliteDatabase;
class Database
{
protected:
@@ -83,7 +86,8 @@ public:
// float getSingleDBValueFloat(const char* sql);
// char* getSingleDBValueStr(const char* sql, std::string& retStr);
virtual bool setupCheckpointing() { return false; }
virtual bool setupCheckpointing() { return false; }
virtual SqliteDatabase* getSqliteDB() { return NULL; }
};
#endif

View File

@@ -1026,6 +1026,7 @@ STAmount STAmount::setRate(uint64 rate)
// --> saTakerFunds: Limit for saOfferGets : How much taker really wants. : Driver
// --> saOfferPays: Request : this should be reduced as the offer is fullfilled.
// --> saOfferGets: Request : this should be reduced as the offer is fullfilled.
// --> saTakerPays: Limit for taker to Pays.
// --> saTakerGets: Limit for taker to get.
// <-- saTakerPaid: Actual
// <-- saTakerGot: Actual
@@ -1037,7 +1038,7 @@ bool STAmount::applyOffer(
const STAmount& saOfferRate,
const STAmount& saOfferFunds, const STAmount& saTakerFunds,
const STAmount& saOfferPays, const STAmount& saOfferGets,
const STAmount& saTakerGets,
const STAmount& saTakerPays, const STAmount& saTakerGets,
STAmount& saTakerPaid, STAmount& saTakerGot,
STAmount& saTakerIssuerFee, STAmount& saOfferIssuerFee)
{
@@ -1075,21 +1076,27 @@ bool STAmount::applyOffer(
else
{
// Offer has limited funding, limit offer gets and pays by funds available.
saOfferPaysAvailable = saOfferFundsAvailable;
saOfferGetsAvailable = multiply(saOfferFundsAvailable, saOfferRate, saOfferGets);
saOfferGetsAvailable = std::min(saOfferGets, multiply(saOfferPaysAvailable, saOfferRate, saOfferGets));
}
cLog(lsINFO) << "applyOffer: saOfferPaysAvailable=" << saOfferFundsAvailable.getFullText();
cLog(lsINFO) << "applyOffer: saOfferGetsAvailable=" << saOfferGetsAvailable.getFullText();
STAmount saTakerGetsAvailable = saTakerFunds >= saOfferGetsAvailable
? saTakerGets
: multiply(saTakerFunds, saOfferRate, saTakerGets); // Amount can afford.
STAmount saTakerPaysAvailable = std::min(saTakerPays, saTakerFundsAvailable);
STAmount saTakerPaysMax = std::min(saTakerPaysAvailable, saOfferGetsAvailable);
STAmount saTakerGetsMax = saTakerPaysMax >= saOfferGetsAvailable
? saOfferPaysAvailable // Potentially take entire offer. Avoid math shenanigans.
: std::min(saOfferPaysAvailable, multiply(saTakerPaysMax, saOfferRate, saTakerGets)); // Taker a portion of offer.
saTakerGot = std::min(saTakerGets, saOfferPaysAvailable); // Limit by wanted and available.
saTakerGot = std::min(saTakerGets, saTakerGetsMax); // Limit by wanted.
saTakerPaid = saTakerGot == saOfferPaysAvailable
? saOfferGetsAvailable
: multiply(saTakerGot, saOfferRate, saTakerFunds);
: std::min(saOfferGetsAvailable, multiply(saTakerGot, saOfferRate, saTakerFunds));
cLog(lsINFO) << "applyOffer: saTakerGot=" << saTakerGot.getFullText();
cLog(lsINFO) << "applyOffer: saTakerPaid=" << saTakerPaid.getFullText();
if (uTakerPaysRate == QUALITY_ONE)
{
@@ -1098,9 +1105,19 @@ bool STAmount::applyOffer(
else
{
// Compute fees in a rounding safe way.
STAmount saTotal = STAmount::multiply(saTakerPaid, STAmount(CURRENCY_ONE, uTakerPaysRate, -9));
saTakerIssuerFee = (saTotal > saTakerFunds) ? saTakerFunds-saTakerPaid : saTotal-saTakerPaid;
// TakerCost includes transfer fees.
STAmount saTakerCost = STAmount::multiply(saTakerPaid, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9));
STAmount saTransferRate = STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9);
cLog(lsINFO) << "applyOffer: saTransferRate=" << saTransferRate.getFullText();
cLog(lsINFO) << "applyOffer: saTakerCost=" << saTakerCost.getFullText();
cLog(lsINFO) << "applyOffer: saTakerFunds=" << saTakerFunds.getFullText();
saTakerIssuerFee = saTakerCost > saTakerFunds
? saTakerFunds-saTakerPaid // Not enough funds to cover fee, stiff issuer the rounding error.
: saTakerCost-saTakerPaid;
cLog(lsINFO) << "applyOffer: saTakerIssuerFee=" << saTakerIssuerFee.getFullText();
assert(!saTakerIssuerFee.isNegative());
}
if (uOfferPaysRate == QUALITY_ONE)
@@ -1110,14 +1127,16 @@ bool STAmount::applyOffer(
else
{
// Compute fees in a rounding safe way.
STAmount saTotal = STAmount::multiply(saTakerGot, STAmount(CURRENCY_ONE, uOfferPaysRate, -9));
STAmount saOfferCost = STAmount::multiply(saTakerGot, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferPaysRate, -9));
saOfferIssuerFee = (saTotal > saOfferFunds) ? saOfferFunds-saTakerGot : saTotal-saTakerGot;
saOfferIssuerFee = saOfferCost > saOfferFunds
? saOfferFunds-saTakerGot // Not enough funds to cover fee, stiff issuer the rounding error.
: saOfferCost-saTakerGot;
}
cLog(lsINFO) << "applyOffer: saTakerGot=" << saTakerGot.getFullText();
return saTakerGot >= saOfferPays;
return saTakerGot >= saOfferPaysAvailable;
}
STAmount STAmount::getPay(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed)

View File

@@ -50,7 +50,7 @@ Application::Application() :
getRand(mNonce256.begin(), mNonce256.size());
getRand(reinterpret_cast<unsigned char *>(&mNonceST), sizeof(mNonceST));
mJobQueue.setThreadCount();
mSweepTimer.expires_from_now(boost::posix_time::seconds(60));
mSweepTimer.expires_from_now(boost::posix_time::seconds(20));
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
}
@@ -159,6 +159,9 @@ void Application::setup()
if (!theConfig.RUN_STANDALONE)
getUNL().nodeBootstrap();
mValidations.tune(theConfig.getSize(siValidationsSize), theConfig.getSize(siValidationsAge));
mHashedObjectStore.tune(theConfig.getSize(siNodeCacheSize), theConfig.getSize(siNodeCacheAge));
mLedgerMaster.tune(theConfig.getSize(siLedgerSize), theConfig.getSize(siLedgerAge));
//
// Allow peer connections.
@@ -294,7 +297,7 @@ void Application::sweep()
mTempNodeCache.sweep();
mValidations.sweep();
getMasterLedgerAcquire().sweep();
mSweepTimer.expires_from_now(boost::posix_time::seconds(60));
mSweepTimer.expires_from_now(boost::posix_time::seconds(theConfig.getSize(siSweepInterval)));
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
}

View File

@@ -27,6 +27,7 @@
#define SECTION_IPS "ips"
#define SECTION_NETWORK_QUORUM "network_quorum"
#define SECTION_NODE_SEED "node_seed"
#define SECTION_NODE_SIZE "node_size"
#define SECTION_PEER_CONNECT_LOW_WATER "peer_connect_low_water"
#define SECTION_PEER_IP "peer_ip"
#define SECTION_PEER_PORT "peer_port"
@@ -81,8 +82,9 @@ void Config::setup(const std::string& strConf, bool bTestNet, bool bQuiet)
// that with "db" as the data directory.
//
TESTNET = bTestNet;
QUIET = bQuiet;
TESTNET = bTestNet;
QUIET = bQuiet;
NODE_SIZE = 0;
// TESTNET forces a "testnet-" prefix on the conf file and db directory.
strDbPath = TESTNET ? "testnet-db" : "db";
@@ -329,6 +331,28 @@ void Config::load()
if (sectionSingleB(secConfig, SECTION_RPC_ALLOW_REMOTE, strTemp))
RPC_ALLOW_REMOTE = boost::lexical_cast<bool>(strTemp);
if (sectionSingleB(secConfig, SECTION_NODE_SIZE, strTemp))
{
if (strTemp == "tiny")
NODE_SIZE = 0;
else if (strTemp == "small")
NODE_SIZE = 1;
else if (strTemp == "medium")
NODE_SIZE = 2;
else if (strTemp == "large")
NODE_SIZE = 3;
else if (strTemp == "huge")
NODE_SIZE = 4;
else
{
NODE_SIZE = boost::lexical_cast<int>(strTemp);
if (NODE_SIZE < 0)
NODE_SIZE = 0;
else if (NODE_SIZE > 4)
NODE_SIZE = 4;
}
}
(void) sectionSingleB(secConfig, SECTION_WEBSOCKET_IP, WEBSOCKET_IP);
if (sectionSingleB(secConfig, SECTION_WEBSOCKET_PORT, strTemp))
@@ -427,4 +451,27 @@ void Config::load()
}
}
int Config::getSize(SizedItemName item)
{
SizedItem sizeTable[] = {
{ siSweepInterval, { 10, 30, 60, 90, 90 } },
{ siLedgerFetch, { 2, 4, 5, 6, 6 } },
{ siValidationsSize, { 256, 256, 512, 1024, 1024 } },
{ siValidationsAge, { 500, 500, 500, 500, 500 } },
{ siNodeCacheSize, { 8192, 32768, 131072, 1048576, 0 } },
{ siNodeCacheAge, { 30, 60, 90, 300, 600 } },
{ siLedgerSize, { 32, 64, 128, 1024, 0 } },
{ siLedgerAge, { 30, 60, 120, 300, 600 } },
};
for (int i = 0; i < (sizeof(sizeTable) / sizeof(SizedItem)); ++i)
{
if (sizeTable[i].item == item)
return sizeTable[i].sizes[NODE_SIZE];
}
assert(false);
return -1;
}
// vim:ts=4

View File

@@ -48,6 +48,24 @@ const int SYSTEM_WEBSOCKET_PUBLIC_PORT = 6563; // XXX Going away.
// Might connect with fewer for testing.
#define DEFAULT_PEER_CONNECT_LOW_WATER 4
enum SizedItemName
{
siSweepInterval,
siValidationsSize,
siValidationsAge,
siNodeCacheSize,
siNodeCacheAge,
siLedgerSize,
siLedgerAge,
siLedgerFetch,
};
struct SizedItem
{
SizedItemName item;
int sizes[5];
};
class Config
{
public:
@@ -139,6 +157,7 @@ public:
// Node storage configuration
uint32 LEDGER_HISTORY;
int NODE_SIZE;
// Client behavior
int ACCOUNT_PROBE_MAX; // How far to scan for accounts.
@@ -150,6 +169,7 @@ public:
Config();
int getSize(SizedItemName);
void setup(const std::string& strConf, bool bTestNet, bool bQuiet);
void load();
};

View File

@@ -20,6 +20,12 @@ HashedObjectStore::HashedObjectStore(int cacheSize, int cacheAge) :
mWriteSet.reserve(128);
}
void HashedObjectStore::tune(int size, int age)
{
mCache.setTargetSize(size);
mCache.setTargetAge(age);
}
bool HashedObjectStore::store(HashedObjectType type, uint32 index,
const std::vector<unsigned char>& data, const uint256& hash)
@@ -45,7 +51,7 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index,
if (!mWritePending)
{
mWritePending = true;
boost::thread(boost::bind(&HashedObjectStore::bulkWrite, this)).detach();
theApp->getJobQueue().addJob(jtWRITE, boost::bind(&HashedObjectStore::bulkWrite, this));
}
}
// else
@@ -64,7 +70,6 @@ void HashedObjectStore::waitWrite()
void HashedObjectStore::bulkWrite()
{
LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtDISK));
while (1)
{
std::vector< boost::shared_ptr<HashedObject> > set;
@@ -84,7 +89,48 @@ void HashedObjectStore::bulkWrite()
}
// cLog(lsTRACE) << "HOS: writing " << set.size();
static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';");
#ifndef NO_SQLITE3_PREPARE
{
Database* db = theApp->getHashNodeDB()->getDB();
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
static SqliteStatement pSt(db->getSqliteDB(),
"INSERT OR IGNORE INTO CommittedObjects "
"(Hash,ObjType,LedgerIndex,Object) VALUES (?, ?, ?, ?);");
db->executeSQL("BEGIN TRANSACTION;");
BOOST_FOREACH(const boost::shared_ptr<HashedObject>& it, set)
{
const char* type;
switch (it->getType())
{
case hotLEDGER: type = "L"; break;
case hotTRANSACTION: type = "T"; break;
case hotACCOUNT_NODE: type = "A"; break;
case hotTRANSACTION_NODE: type = "N"; break;
default: type = "U";
}
pSt.reset();
pSt.bind(1, it->getHash().GetHex());
pSt.bind(2, type);
pSt.bind(3, it->getIndex());
pSt.bindStatic(4, it->getData());
int ret = pSt.step();
if (!pSt.isDone(ret))
{
cLog(lsFATAL) << "Error saving hashed object " << ret;
assert(false);
}
}
db->executeSQL("END TRANSACTION;");
}
#else
static boost::format
fAdd("INSERT OR IGNORE INTO CommittedObjects "
"(Hash,ObjType,LedgerIndex,Object) VALUES ('%s','%c','%u',%s);");
@@ -112,6 +158,8 @@ void HashedObjectStore::bulkWrite()
db->executeSQL("END TRANSACTION;");
}
#endif
}
}
@@ -129,28 +177,53 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
}
if (mNegativeCache.isPresent(hash))
return HashedObject::pointer();
return obj;
if (!theApp || !theApp->getHashNodeDB())
return HashedObject::pointer();
std::string sql = "SELECT * FROM CommittedObjects WHERE Hash='";
sql.append(hash.GetHex());
sql.append("';");
return obj;
std::vector<unsigned char> data;
std::string type;
uint32 index;
#ifndef NO_SQLITE3_PREPARE
{
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
static SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(),
"SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;");
pSt.reset();
pSt.bind(1, hash.GetHex());
int ret = pSt.step();
if (pSt.isDone(ret))
{
mNegativeCache.add(hash);
cLog(lsTRACE) << "HOS: " << hash <<" fetch: not in db";
return obj;
}
type = pSt.peekString(0);
index = pSt.getUInt32(1);
pSt.getBlob(2).swap(data);
}
#else
std::string sql = "SELECT * FROM CommittedObjects WHERE Hash='";
sql.append(hash.GetHex());
sql.append("';");
{
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
Database* db = theApp->getHashNodeDB()->getDB();
if (!db->executeSQL(sql) || !db->startIterRows())
{
// cLog(lsTRACE) << "HOS: " << hash << " fetch: not in db";
sl.unlock();
mNegativeCache.add(hash);
return HashedObject::pointer();
return obj;
}
db->getStr("ObjType", type);
@@ -161,6 +234,7 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
db->getBinary("Object", &(data.front()), size);
db->endIterRows();
}
#endif
assert(Serializer::getSHA512Half(data) == hash);
@@ -175,7 +249,7 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
assert(false);
cLog(lsERROR) << "Invalid hashed object";
mNegativeCache.add(hash);
return HashedObject::pointer();
return obj;
}
obj = boost::make_shared<HashedObject>(htype, index, data, hash);
@@ -198,7 +272,7 @@ int HashedObjectStore::import(const std::string& file)
uint256 hash;
std::string hashStr;
importDB->getStr("Hash", hashStr);
hash.SetHex(hashStr);
hash.SetHex(hashStr, true);
if (hash.isZero())
{
cLog(lsWARNING) << "zero hash found in import table";

View File

@@ -67,6 +67,7 @@ public:
void bulkWrite();
void waitWrite();
void tune(int size, int age);
void sweep() { mCache.sweep(); mNegativeCache.sweep(); }
int import(const std::string&);

View File

@@ -17,6 +17,7 @@ JobQueue::JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false)
mJobLoads[jtPROPOSAL_ut].setTargetLatency(500, 1250);
mJobLoads[jtPUBLEDGER].setTargetLatency(1000, 2500);
mJobLoads[jtVALIDATION_t].setTargetLatency(500, 1500);
mJobLoads[jtWRITE].setTargetLatency(750, 1500);
mJobLoads[jtTRANSACTION_l].setTargetLatency(100, 500);
mJobLoads[jtPROPOSAL_t].setTargetLatency(100, 500);
@@ -40,6 +41,7 @@ const char* Job::toString(JobType t)
case jtTRANSACTION: return "transaction";
case jtPUBLEDGER: return "publishLedger";
case jtVALIDATION_t: return "trustedValidation";
case jtWRITE: return "writeObjects";
case jtTRANSACTION_l: return "localTransaction";
case jtPROPOSAL_t: return "trustedProposal";
case jtADMIN: return "administration";
@@ -196,7 +198,7 @@ void JobQueue::setThreadCount(int c)
{
c = boost::thread::hardware_concurrency();
if (c < 0)
c = 0;
c = 2;
c += 2;
cLog(lsINFO) << "Auto-tuning to " << c << " validation/transaction/proposal threads";
}
@@ -244,7 +246,7 @@ void JobQueue::threadEntry()
break;
sl.unlock();
cLog(lsDEBUG) << "Doing " << Job::toString(job.getType()) << " job";
cLog(lsTRACE) << "Doing " << Job::toString(job.getType()) << " job";
job.doJob();
sl.lock();
}

View File

@@ -28,10 +28,11 @@ enum JobType
jtTRANSACTION = 5, // A transaction received from the network
jtPUBLEDGER = 6, // Publish a fully-accepted ledger
jtVALIDATION_t = 7, // A validation from a trusted source
jtTRANSACTION_l = 8, // A local transaction
jtPROPOSAL_t = 9, // A proposal from a trusted source
jtADMIN = 10, // An administrative operation
jtDEATH = 11, // job of death, used internally
jtWRITE = 8, // Write out hashed objects
jtTRANSACTION_l = 9, // A local transaction
jtPROPOSAL_t = 10, // A proposal from a trusted source
jtADMIN = 11, // An administrative operation
jtDEATH = 12, // job of death, used internally
// special types not dispatched by the job pool
jtPEER = 17,

View File

@@ -7,6 +7,8 @@
#include "../json/writer.h"
#include "../database/SqliteDatabase.h"
#include "Application.h"
#include "Ledger.h"
#include "utils.h"
@@ -507,13 +509,13 @@ Ledger::pointer Ledger::getSQL(const std::string& sql)
}
db->getStr("LedgerHash", hash);
ledgerHash.SetHex(hash);
ledgerHash.SetHex(hash, true);
db->getStr("PrevHash", hash);
prevHash.SetHex(hash);
prevHash.SetHex(hash, true);
db->getStr("AccountSetHash", hash);
accountHash.SetHex(hash);
accountHash.SetHex(hash, true);
db->getStr("TransSetHash", hash);
transHash.SetHex(hash);
transHash.SetHex(hash, true);
totCoins = db->getBigInt("TotalCoins");
closingTime = db->getBigInt("ClosingTime");
prevClosingTime = db->getBigInt("PrevClosingTime");
@@ -560,12 +562,43 @@ uint256 Ledger::getHashByIndex(uint32 ledgerIndex)
db->endIterRows();
}
ret.SetHex(hash);
ret.SetHex(hash, true);
return ret;
}
bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& parentHash)
{
#ifndef NO_SQLITE3_PREPARE
DatabaseCon *con = theApp->getLedgerDB();
ScopedLock sl(con->getDBLock());
static SqliteStatement pSt(con->getDB()->getSqliteDB(),
"SELECT LedgerHash,PrevHash FROM Ledgers Where LedgerSeq = ?;");
pSt.reset();
pSt.bind(1, ledgerIndex);
int ret = pSt.step();
if (pSt.isDone(ret))
{
cLog(lsTRACE) << "Don't have ledger " << ledgerIndex;
return false;
}
if (!pSt.isRow(ret))
{
assert(false);
cLog(lsFATAL) << "Unexpected statement result " << ret;
return false;
}
ledgerHash.SetHex(pSt.peekString(0), true);
parentHash.SetHex(pSt.peekString(1), true);
return true;
#else
std::string sql="SELECT LedgerHash,PrevHash FROM Ledgers WHERE LedgerSeq='";
sql.append(boost::lexical_cast<std::string>(ledgerIndex));
sql.append("';");
@@ -581,12 +614,14 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256&
db->endIterRows();
}
ledgerHash.SetHex(hash);
parentHash.SetHex(prevHash);
ledgerHash.SetHex(hash, true);
parentHash.SetHex(prevHash, true);
assert(ledgerHash.isNonZero() && ((ledgerIndex == 0) || parentHash.isNonZero()));
return true;
#endif
}
Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex)

View File

@@ -153,6 +153,13 @@ public:
// low level functions
SHAMap::ref peekTransactionMap() { return mTransactionMap; }
SHAMap::ref peekAccountStateMap() { return mAccountStateMap; }
void dropCache()
{
if (mTransactionMap)
mTransactionMap->dropCache();
if (mAccountStateMap)
mAccountStateMap->dropCache();
}
// ledger sync functions
void setAcquiring(void);

View File

@@ -14,7 +14,9 @@ SETUP_LOG();
DECLARE_INSTANCE(LedgerAcquire);
#define LA_DEBUG
#define LEDGER_ACQUIRE_TIMEOUT 750
#define LEDGER_ACQUIRE_TIMEOUT 750 // millisecond for each ledger timeout
#define LEDGER_TIMEOUT_COUNT 10 // how many timeouts before we giveup
#define LEDGER_TIMEOUT_AGGRESSIVE 4 // how many timeouts before we get aggressive
#define TRUST_NETWORK
PeerSet::PeerSet(const uint256& hash, int interval) : mHash(hash), mTimerInterval(interval), mTimeouts(0),
@@ -165,7 +167,7 @@ bool LedgerAcquire::tryLocal()
void LedgerAcquire::onTimer(bool progress)
{
if (getTimeouts() > 6)
if (getTimeouts() > LEDGER_TIMEOUT_COUNT)
{
cLog(lsWARNING) << "Six timeouts for ledger " << mHash;
setFailed();
@@ -301,7 +303,7 @@ void LedgerAcquire::trigger(Peer::ref peer)
{
tmGL.set_querytype(ripple::qtINDIRECT);
if (!isProgress() && !mFailed && mByHash && (getTimeouts() > 2))
if (!isProgress() && !mFailed && mByHash && (getTimeouts() > LEDGER_TIMEOUT_AGGRESSIVE))
{
std::vector<neededHash_t> need = getNeededHashes();
if (!need.empty())

View File

@@ -1261,14 +1261,17 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei
if (!bSenderHigh)
saBalance.negate(); // Put balance in low terms.
cLog(lsDEBUG) << boost::str(boost::format("rippleCredit> %s (%s) -> %s : %s")
STAmount saBefore = saBalance;
saBalance += saAmount;
cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: %s -- (%s > %s) -> %s : %s")
% RippleAddress::createHumanAccountID(uSenderID)
% saBefore.getFullText()
% saBalance.getFullText()
% RippleAddress::createHumanAccountID(uReceiverID)
% saAmount.getFullText());
saBalance += saAmount;
if (!bSenderHigh)
saBalance.negate();
@@ -1311,6 +1314,13 @@ TER LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uReceive
saActual.setIssuer(uIssuerID); // XXX Make sure this done in + above.
cLog(lsINFO) << boost::str(boost::format("rippleSend> %s -- %s--> %s (%s) : %s")
% RippleAddress::createHumanAccountID(uSenderID)
% saTransitFee.getFullText()
% RippleAddress::createHumanAccountID(uReceiverID)
% saActual.getFullText()
% saAmount.getFullText());
terResult = rippleCredit(uIssuerID, uReceiverID, saAmount);
if (tesSUCCESS == terResult)
@@ -1322,7 +1332,6 @@ TER LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uReceive
TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount)
{
assert(!saAmount.isNegative());
TER terResult = tesSUCCESS;
if (!saAmount)
@@ -1345,6 +1354,8 @@ TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiv
% (sleReceiver ? (sleReceiver->getFieldAmount(sfBalance)).getFullText() : "-")
% saAmount.getFullText());
assert(!saAmount.isNegative());
if (sleSender)
{
sleSender->setFieldAmount(sfBalance, sleSender->getFieldAmount(sfBalance) - saAmount);
@@ -1368,6 +1379,13 @@ TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiv
{
STAmount saActual;
cLog(lsINFO) << boost::str(boost::format("accountSend: %s -> %s : %s")
% RippleAddress::createHumanAccountID(uSenderID)
% RippleAddress::createHumanAccountID(uReceiverID)
% saAmount.getFullText());
assert(!saAmount.isNegative());
terResult = rippleSend(uSenderID, uReceiverID, saAmount, saActual);
}

View File

@@ -115,4 +115,10 @@ Ledger::pointer LedgerHistory::canonicalizeLedger(Ledger::pointer ledger, bool s
return ledger;
}
void LedgerHistory::tune(int size, int age)
{
mLedgersByHash.setTargetSize(size);
mLedgersByHash.setTargetAge(age);
}
// vim:ts=4

View File

@@ -19,6 +19,7 @@ public:
Ledger::pointer getLedgerBySeq(uint32 index);
Ledger::pointer getLedgerByHash(const uint256& hash);
Ledger::pointer canonicalizeLedger(Ledger::pointer, bool cache);
void tune(int size, int age);
void sweep() { mLedgersByHash.sweep(); }
};

View File

@@ -195,7 +195,8 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l
theApp->getIOService().post(boost::bind(&LedgerMaster::missingAcquireComplete, this, mMissingLedger));
}
if (theApp->getMasterLedgerAcquire().getFetchCount() < 4)
int fetch = theConfig.getSize(siLedgerFetch);
if (theApp->getMasterLedgerAcquire().getFetchCount() < fetch)
{
int count = 0;
typedef std::pair<uint32, uint256> u_pair;
@@ -203,7 +204,7 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l
std::vector<u_pair> vec = origLedger->getLedgerHashes();
BOOST_REVERSE_FOREACH(const u_pair& it, vec)
{
if ((count < 3) && (it.first < ledgerSeq) &&
if ((count < fetch) && (it.first < ledgerSeq) &&
!mCompleteLedgers.hasValue(it.first) && !theApp->getMasterLedgerAcquire().find(it.second))
{
++count;
@@ -499,11 +500,11 @@ void LedgerMaster::tryPublish()
}
}
mTooFast = false;
if (!mPubLedgers.empty() && !mPubThread)
{
theApp->getOPs().clearNeedNetworkLedger();
mPubThread = true;
mTooFast = false;
theApp->getJobQueue().addJob(jtPUBLEDGER, boost::bind(&LedgerMaster::pubThread, this));
}
}

View File

@@ -129,6 +129,7 @@ public:
void resumeAcquiring();
void tune(int size, int age) { mLedgerHistory.tune(size, age); }
void sweep(void) { mLedgerHistory.sweep(); }
void addValidateCallback(callback& c) { mOnValidate.push_back(c); }

View File

@@ -8,12 +8,13 @@
SETUP_LOG();
// 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)
// --> saTakerGets: What the taker wanted (w/ issuer)
// <-- saTakerPaid: What taker paid not including fees. To reduce an offer.
// <-- saTakerGot: What taker got not including fees. To reduce an offer.
// <-- terResult: tesSUCCESS or terNO_ACCOUNT
// --> uBookBase: The order book to take against.
// --> saTakerPays: What the taker offers (w/ issuer)
// --> saTakerGets: What the taker wanted (w/ issuer)
// <-- saTakerPaid: What taker paid including saved not including fees. To reduce an offer.
// <-- saTakerGot: What taker got not including fees. To reduce an offer.
// <-- terResult: tesSUCCESS or terNO_ACCOUNT
// <-- bUnfunded: if tesSUCCESS, consider offer unfunded after taking.
TER OfferCreateTransactor::takeOffers(
bool bPassive,
const uint256& uBookBase,
@@ -22,7 +23,8 @@ TER OfferCreateTransactor::takeOffers(
const STAmount& saTakerPays,
const STAmount& saTakerGets,
STAmount& saTakerPaid,
STAmount& saTakerGot)
STAmount& saTakerGot,
bool& bUnfunded)
{
assert(saTakerPays && saTakerGets);
@@ -31,6 +33,7 @@ TER OfferCreateTransactor::takeOffers(
uint256 uTipIndex = uBookBase;
const uint256 uBookEnd = Ledger::getQualityNext(uBookBase);
const uint64 uTakeQuality = STAmount::getRate(saTakerGets, saTakerPays);
STAmount saTakerRate = STAmount::setRate(uTakeQuality);
const uint160 uTakerPaysAccountID = saTakerPays.getIssuer();
const uint160 uTakerGetsAccountID = saTakerGets.getIssuer();
TER terResult = temUNCERTAIN;
@@ -39,8 +42,9 @@ TER OfferCreateTransactor::takeOffers(
boost::unordered_set<uint256> usOfferUnfundedBecame; // Offers that became unfunded.
boost::unordered_set<uint160> usAccountTouched; // Accounts touched.
saTakerPaid = STAmount(saTakerPays.getCurrency(), saTakerPays.getIssuer());
saTakerGot = STAmount(saTakerGets.getCurrency(), saTakerGets.getIssuer());
saTakerPaid = STAmount(saTakerPays.getCurrency(), saTakerPays.getIssuer());
saTakerGot = STAmount(saTakerGets.getCurrency(), saTakerGets.getIssuer());
bUnfunded = false;
while (temUNCERTAIN == terResult)
{
@@ -48,10 +52,9 @@ TER OfferCreateTransactor::takeOffers(
uint64 uTipQuality;
// Figure out next offer to take, if needed.
if (saTakerGets != saTakerGot && saTakerPays != saTakerPaid)
if (saTakerGot < saTakerGets // Have less than wanted.
&& saTakerPaid < saTakerPays) // Didn't spend all funds.
{
// Taker, still, needs to get and pay.
sleOfferDir = mEngine->entryCache(ltDIR_NODE, mEngine->getLedger()->getNextLedgerIndex(uTipIndex, uBookEnd));
if (sleOfferDir)
{
@@ -168,6 +171,7 @@ TER OfferCreateTransactor::takeOffers(
saPay,
saOfferPays,
saOfferGets,
saTakerPays,
saTakerGets,
saSubTakerPaid,
saSubTakerGot,
@@ -190,42 +194,49 @@ TER OfferCreateTransactor::takeOffers(
if (bOfferDelete)
{
// Offer now fully claimed or now unfunded.
cLog(lsINFO) << "takeOffers: offer claimed: delete";
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.";
}
else
{
cLog(lsINFO) << "takeOffers: offer partial claim.";
// 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());
// Offer owner pays taker.
// saSubTakerGot.setIssuer(uTakerGetsAccountID); // XXX Move this earlier?
if (!bUnfunded)
{
terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker.
terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot);
if (tesSUCCESS == terResult)
terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerGetsAccountID, saOfferIssuerFee); // Offer owner pays issuer transfer fee.
if (tesSUCCESS == terResult)
terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerGetsAccountID, saOfferIssuerFee);
// Taker pays offer owner.
// saSubTakerPaid.setIssuer(uTakerPaysAccountID);
if (tesSUCCESS == terResult)
terResult = mEngine->getNodes().accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid); // Taker pays offer owner.
if (tesSUCCESS == terResult)
terResult = mEngine->getNodes().accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid);
if (tesSUCCESS == terResult)
terResult = mEngine->getNodes().accountSend(uTakerAccountID, uTakerPaysAccountID, saTakerIssuerFee); // Taker pays issuer transfer fee.
if (tesSUCCESS == terResult)
terResult = mEngine->getNodes().accountSend(uTakerAccountID, uTakerPaysAccountID, saTakerIssuerFee);
// Reduce amount considered paid by taker's rate (not actual cost).
saTakerPaid += std::min(saPay, STAmount::multiply(saSubTakerGot, saTakerRate, saPay));
saTakerGot += saSubTakerGot;
saTakerGot += saSubTakerGot;
saTakerPaid += saSubTakerPaid;
if (tesSUCCESS == terResult)
terResult = temUNCERTAIN;
if (tesSUCCESS == terResult)
terResult = temUNCERTAIN;
}
}
}
}
@@ -359,6 +370,7 @@ TER OfferCreateTransactor::doApply()
STAmount saOfferPaid;
STAmount saOfferGot;
bool bUnfunded = false;
if (tesSUCCESS == terResult)
{
@@ -378,9 +390,9 @@ TER OfferCreateTransactor::doApply()
mTxnAccount,
saTakerGets,
saTakerPays,
saOfferPaid, // How much was spent.
saOfferGot // How much was got.
);
saOfferPaid, // How much would have spent at full price.
saOfferGot, // How much was got.
bUnfunded);
cLog(lsWARNING) << "OfferCreate: takeOffers=" << terResult;
cLog(lsWARNING) << "OfferCreate: takeOffers: saOfferPaid=" << saOfferPaid.getFullText();
@@ -388,7 +400,7 @@ TER OfferCreateTransactor::doApply()
cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText();
cLog(lsWARNING) << "OfferCreate: takeOffers: AFTER saTakerGets=" << saTakerGets.getFullText();
if (tesSUCCESS == terResult)
if (tesSUCCESS == terResult && !bUnfunded)
{
saTakerPays -= saOfferGot; // Reduce payin from takers by what offer just got.
saTakerGets -= saOfferPaid; // Reduce payout to takers by what srcAccount just paid.
@@ -406,7 +418,8 @@ TER OfferCreateTransactor::doApply()
if (tesSUCCESS != terResult
|| !saTakerPays // Wants nothing more.
|| !saTakerGets // Offering nothing more.
|| !mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive()) // Not funded.
|| !mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive() // Not funded.
|| bUnfunded) // Consider unfunded.
{
// Complete as is.
nothing();

View File

@@ -1,5 +1,7 @@
#include "Transactor.h"
#ifndef __OFFERCREATETRANSACTOR__
#define __OFFERCREATETRANSACTOR__
#include "Transactor.h"
class OfferCreateTransactor : public Transactor
{
@@ -11,12 +13,13 @@ class OfferCreateTransactor : public Transactor
const STAmount& saTakerPays,
const STAmount& saTakerGets,
STAmount& saTakerPaid,
STAmount& saTakerGot);
STAmount& saTakerGot,
bool& bUnfunded);
public:
OfferCreateTransactor(const SerializedTransaction& txn,TransactionEngineParams params, TransactionEngine* engine) : Transactor(txn,params,engine) {}
TER doApply();
};
#endif
// vim:ts=4

View File

@@ -244,8 +244,8 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
}
else
{
// Remove implied first and last nodes.
spPath.mPath.erase(spPath.mPath.begin());
spPath.mPath.erase(spPath.mPath.begin() + spPath.mPath.size()-1);
@@ -301,7 +301,9 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
// Last element is for non-XRP, continue by adding ripple lines and order books.
// Create new paths for each outbound account not already in the path.
AccountItems rippleLines(speEnd.mAccountID, mLedger, AccountItem::pointer(new RippleState()));
AccountItems rippleLines(speEnd.mAccountID, mLedger, AccountItem::pointer(new RippleState()));
SLE::pointer sleSrc = lesActive.entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(speEnd.mAccountID));
bool bRequireAuth = isSetBit(sleSrc->getFieldU32(sfFlags), lsfRequireAuth);
BOOST_FOREACH(AccountItem::ref item, rippleLines.getItems())
{
@@ -317,9 +319,10 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
% RippleAddress::createHumanAccountID(rspEntry->getAccountIDPeer().getAccountID())
% STAmount::createHumanCurrency(speEnd.mCurrencyID));
}
else if (!rspEntry->getBalance().isPositive() // No IOUs to send.
&& (!rspEntry->getLimitPeer() // Peer does not extend credit.
|| *rspEntry->getBalance().negate() >= rspEntry->getLimitPeer())) // No credit left.
else if (!rspEntry->getBalance().isPositive() // No IOUs to send.
&& (!rspEntry->getLimitPeer() // Peer does not extend credit.
|| *rspEntry->getBalance().negate() >= rspEntry->getLimitPeer() // No credit left.
|| (bRequireAuth && !rspEntry->getAuth()))) // Not authorized to hold credit.
{
// Path has no credit left. Ignore it.
cLog(lsDEBUG) <<

View File

@@ -1176,7 +1176,10 @@ void Peer::recvGetObjectByHash(ripple::TMGetObjectByHash& packet)
punishPeer(LT_BadData);
}
else
{
cLog(lsDEBUG) << "Got wanted hash " << hash;
theApp->getHashedObjectStore().store(type, seq, data, hash);
}
}
else
cLog(lsWARNING) << "Received unwanted hash " << getIP() << " " << hash;

View File

@@ -14,6 +14,8 @@ RippleState::RippleState(SerializedLedgerEntry::ref ledgerEntry) : AccountItem(l
mValid(false),
mViewLowest(true)
{
mFlags = mLedgerEntry->getFieldU32(sfFlags);
mLowLimit = mLedgerEntry->getFieldAmount(sfLowLimit);
mHighLimit = mLedgerEntry->getFieldAmount(sfHighLimit);

View File

@@ -17,6 +17,8 @@ public:
typedef boost::shared_ptr<RippleState> pointer;
private:
uint32 mFlags;
RippleAddress mLowID;
RippleAddress mHighID;
@@ -42,16 +44,19 @@ public:
void setViewAccount(const uint160& accountID);
const RippleAddress getAccountID() const { return mViewLowest ? mLowID : mHighID; }
const RippleAddress getAccountIDPeer() const { return mViewLowest ? mHighID : mLowID; }
const RippleAddress getAccountID() const { return mViewLowest ? mLowID : mHighID; }
const RippleAddress getAccountIDPeer() const { return !mViewLowest ? mLowID : mHighID; }
STAmount getBalance() const { return mBalance; }
bool getAuth() const { return isSetBit(mFlags, mViewLowest ? lsfLowAuth : lsfHighAuth); }
bool getAuthPeer() const { return isSetBit(mFlags, !mViewLowest ? lsfLowAuth : lsfHighAuth); }
STAmount getLimit() const { return mViewLowest ? mLowLimit : mHighLimit; }
STAmount getLimitPeer() const { return mViewLowest ? mHighLimit : mLowLimit; }
STAmount getBalance() const { return mBalance; }
uint32 getQualityIn() const { return((uint32) (mViewLowest ? mLowQualityIn : mHighQualityIn)); }
uint32 getQualityOut() const { return((uint32) (mViewLowest ? mLowQualityOut : mHighQualityOut)); }
STAmount getLimit() const { return mViewLowest ? mLowLimit : mHighLimit; }
STAmount getLimitPeer() const { return !mViewLowest ? mLowLimit : mHighLimit; }
uint32 getQualityIn() const { return((uint32) (mViewLowest ? mLowQualityIn : mHighQualityIn)); }
uint32 getQualityOut() const { return((uint32) (mViewLowest ? mLowQualityOut : mHighQualityOut)); }
SerializedLedgerEntry::pointer getSLE() { return mLedgerEntry; }
const SerializedLedgerEntry& peekSLE() const { return *mLedgerEntry; }

View File

@@ -851,6 +851,15 @@ bool SHAMap::getPath(const uint256& index, std::vector< std::vector<unsigned cha
return true;
}
void SHAMap::dropCache()
{ // CAUTION: Changes can be lost
boost::recursive_mutex::scoped_lock sl(mLock);
mTNByID.clear();
if (root)
mTNByID[*root] = root;
}
void SHAMap::dump(bool hash)
{
#if 0

View File

@@ -382,6 +382,9 @@ public:
// Returns a new map that's a snapshot of this one. Force CoW
SHAMap::pointer snapShot(bool isMutable);
// Remove nodes from memory
void dropCache();
// hold the map stable across operations
ScopedLock Lock() const { return ScopedLock(mLock); }

View File

@@ -376,7 +376,7 @@ public:
const STAmount& saOfferRate,
const STAmount& saOfferFunds, const STAmount& saTakerFunds,
const STAmount& saOfferPays, const STAmount& saOfferGets,
const STAmount& saTakerGets,
const STAmount& saTakerPays, const STAmount& saTakerGets,
STAmount& saTakerPaid, STAmount& saTakerGot,
STAmount& saTakerIssuerFee, STAmount& saOfferIssuerFee);

View File

@@ -85,12 +85,26 @@ template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getTar
return mTargetSize;
}
template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::setTargetSize(int s)
{
boost::recursive_mutex::scoped_lock sl(mLock);
mTargetSize = s;
Log(lsDEBUG, TaggedCachePartition) << mName << " target size set to " << s;
}
template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getTargetAge() const
{
boost::recursive_mutex::scoped_lock sl(mLock);
return mTargetAge;
}
template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::setTargetAge(int s)
{
boost::recursive_mutex::scoped_lock sl(mLock);
mTargetAge = s;
Log(lsDEBUG, TaggedCachePartition) << mName << " target age set to " << s;
}
template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getCacheSize()
{
boost::recursive_mutex::scoped_lock sl(mLock);

View File

@@ -12,6 +12,12 @@ SETUP_LOG();
typedef std::map<uint160, SerializedValidation::pointer>::value_type u160_val_pair;
typedef boost::shared_ptr<ValidationSet> VSpointer;
void ValidationCollection::tune(int size, int age)
{
mValidations.setTargetSize(size);
mValidations.setTargetAge(age);
}
VSpointer ValidationCollection::findCreateSet(const uint256& ledgerHash)
{
VSpointer j = mValidations.fetch(ledgerHash);

View File

@@ -48,6 +48,7 @@ public:
boost::unordered_map<uint256, currentValidationCount> getCurrentValidations(uint256 currentLedger);
std::list<SerializedValidation::pointer> getCurrentTrustedValidations();
void tune(int size, int age);
void flush();
void sweep() { mValidations.sweep(); }
};

View File

@@ -16,7 +16,8 @@ buster.testRunner.timeout = 5000;
buster.testCase("Offer tests", {
'setUp' : testutils.build_setup(),
// 'setUp' : testutils.build_setup({ verbose: true, standalone: false }),
// 'setUp' : testutils.build_setup({ verbose: true }),
// 'setUp' : testutils.build_setup({ verbose: true, standalone: true }),
'tearDown' : testutils.build_teardown(),
"offer create then cancel in one ledger" :
@@ -73,7 +74,7 @@ buster.testCase("Offer tests", {
});
},
"offer create then crossing offer, no trust lines with self" :
"Offer create then self crossing offer, no trust lines with self" :
function (done) {
var self = this;
@@ -109,6 +110,138 @@ buster.testCase("Offer tests", {
});
},
"Offer create then crossing offer with XRP. Negative balance." :
function (done) {
var self = this;
async.waterfall([
function (callback) {
self.what = "Create mtgox account.";
testutils.payment(self.remote, "root", "mtgox", "1149999730", callback);
},
function (callback) {
self.what = "Create alice account.";
testutils.payment(self.remote, "root", "alice", "499946999680", callback);
},
function (callback) {
self.what = "Create bob account.";
testutils.payment(self.remote, "root", "bob", "10199999920", callback);
},
function (callback) {
self.what = "Set transfer rate.";
self.remote.transaction()
.account_set("mtgox")
.transfer_rate(1005000000)
.once('proposed', function (m) {
// console.log("proposed: %s", JSON.stringify(m));
callback(m.result !== 'tesSUCCESS');
})
.submit();
},
function (callback) {
self.what = "Set limits.";
testutils.credit_limits(self.remote,
{
"alice" : "500/USD/mtgox",
"bob" : "50/USD/mtgox",
"mtgox" : "100/USD/alice",
},
callback);
},
function (callback) {
self.what = "Distribute funds.";
testutils.payments(self.remote,
{
"mtgox" : [ "50/USD/alice", "2710505431213761e-33/USD/bob" ]
},
callback);
},
function (callback) {
self.what = "Create first offer.";
self.remote.transaction()
.offer_create("alice", "50/USD/mtgox", "150000.0") // get 50/USD pay 150000/XRP
.on('proposed', function (m) {
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
callback(m.result !== 'tesSUCCESS');
})
.submit();
},
function (callback) {
self.what = "Unfund offer.";
testutils.payments(self.remote,
{
"alice" : "100/USD/mtgox"
},
callback);
},
function (callback) {
self.what = "Set limits 2.";
testutils.credit_limits(self.remote,
{
"mtgox" : "0/USD/alice",
},
callback);
},
function (callback) {
self.what = "Verify balances.";
testutils.verify_balances(self.remote,
{
"alice" : [ "-50/USD/mtgox" ],
"bob" : [ "2710505431213761e-33/USD/mtgox" ],
},
callback);
},
function (callback) {
self.what = "Create crossing offer.";
self.remote.transaction()
.offer_create("bob", "2000.0", "1/USD/mtgox") // get 2,000/XRP pay 1/USD (has insufficient USD)
.on('proposed', function (m) {
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
callback(m.result !== 'tesSUCCESS');
})
.submit();
},
function (callback) {
self.what = "Verify balances.";
testutils.verify_balances(self.remote,
{
"alice" : [ "-50/USD/mtgox", String(499946999680-3*(Transaction.fees['default'].to_number())) ],
"bob" : [ "2710505431213761e-33/USD/mtgox", String(10199999920-2*(Transaction.fees['default'].to_number())) ],
},
callback);
},
// function (callback) {
// self.what = "Display ledger";
//
// self.remote.request_ledger('current', true)
// .on('success', function (m) {
// console.log("Ledger: %s", JSON.stringify(m, undefined, 2));
//
// callback();
// })
// .request();
// },
], function (error) {
// console.log("result: error=%s", error);
buster.refute(error, self.what);
done();
});
},
"Offer create then crossing offer with XRP. Reverse order." :
function (done) {
var self = this;