mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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&);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(); }
|
||||
};
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) <<
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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); }
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(); }
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user