mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-25 21:45:52 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin into new_pathfinding
This commit is contained in:
@@ -186,6 +186,7 @@
|
||||
<ClCompile Include="src\cpp\ripple\Transactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TrustSetTransactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\UniqueNodeList.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\UpdateTables.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\utils.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\ValidationCollection.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\Wallet.cpp" />
|
||||
|
||||
@@ -270,6 +270,9 @@
|
||||
<ClCompile Include="src\cpp\ripple\UniqueNodeList.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\UpdateTables.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -185,6 +185,7 @@
|
||||
<ClCompile Include="src\cpp\ripple\Transactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\TrustSetTransactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\UniqueNodeList.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\UpdateTables.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\utils.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\ValidationCollection.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\Wallet.cpp" />
|
||||
|
||||
@@ -267,6 +267,9 @@
|
||||
<ClCompile Include="src\cpp\ripple\UniqueNodeList.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\UpdateTables.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -279,6 +279,14 @@ SqliteStatement::SqliteStatement(SqliteDatabase* db, const char *sql)
|
||||
throw j;
|
||||
}
|
||||
|
||||
SqliteStatement::SqliteStatement(SqliteDatabase* db, const std::string& sql)
|
||||
{
|
||||
assert(db);
|
||||
int j = sqlite3_prepare_v2(db->peekConnection(), sql.c_str(), sql.size() + 1, &statement, NULL);
|
||||
if (j != SQLITE_OK)
|
||||
throw j;
|
||||
}
|
||||
|
||||
SqliteStatement::~SqliteStatement()
|
||||
{
|
||||
sqlite3_finalize(statement);
|
||||
|
||||
@@ -73,6 +73,7 @@ protected:
|
||||
|
||||
public:
|
||||
SqliteStatement(SqliteDatabase* db, const char *statement);
|
||||
SqliteStatement(SqliteDatabase* db, const std::string& statement);
|
||||
~SqliteStatement();
|
||||
|
||||
sqlite3_stmt* peekStatement();
|
||||
|
||||
@@ -14,17 +14,21 @@ ALTransaction::ALTransaction(uint32 seq, SerializerIterator& sit)
|
||||
mMeta = boost::make_shared<TransactionMetaSet>(mTxn->getTransactionID(), seq, mRawMeta);
|
||||
mAffected = mMeta->getAffectedAccounts();
|
||||
mResult = mMeta->getResultTER();
|
||||
buildJson();
|
||||
}
|
||||
|
||||
ALTransaction::ALTransaction(SerializedTransaction::ref txn, TransactionMetaSet::ref met) :
|
||||
mTxn(txn), mMeta(met), mAffected(met->getAffectedAccounts())
|
||||
{
|
||||
mResult = mMeta->getResultTER();
|
||||
buildJson();
|
||||
}
|
||||
|
||||
ALTransaction::ALTransaction(SerializedTransaction::ref txn, TER result) :
|
||||
mTxn(txn), mResult(result), mAffected(txn->getMentionedAccounts())
|
||||
{ ; }
|
||||
{
|
||||
buildJson();
|
||||
}
|
||||
|
||||
std::string ALTransaction::getEscMeta() const
|
||||
{
|
||||
@@ -32,16 +36,16 @@ std::string ALTransaction::getEscMeta() const
|
||||
return sqlEscape(mRawMeta);
|
||||
}
|
||||
|
||||
Json::Value ALTransaction::getJson(int j) const
|
||||
void ALTransaction::buildJson()
|
||||
{
|
||||
Json::Value ret(Json::objectValue);
|
||||
ret["transaction"] = mTxn->getJson(j);
|
||||
mJson = Json::objectValue;
|
||||
mJson["transaction"] = mTxn->getJson(0);
|
||||
if (mMeta)
|
||||
{
|
||||
ret["meta"] = mMeta->getJson(j);
|
||||
ret["raw_meta"] = strHex(mRawMeta);
|
||||
mJson["meta"] = mMeta->getJson(0);
|
||||
mJson["raw_meta"] = strHex(mRawMeta);
|
||||
}
|
||||
ret["result"] = transHuman(mResult);
|
||||
mJson["result"] = transHuman(mResult);
|
||||
|
||||
if (!mAffected.empty())
|
||||
{
|
||||
@@ -50,10 +54,8 @@ Json::Value ALTransaction::getJson(int j) const
|
||||
{
|
||||
affected.append(ra.humanAccountID());
|
||||
}
|
||||
ret["affected"] = affected;
|
||||
mJson["affected"] = affected;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AcceptedLedger::AcceptedLedger(Ledger::ref ledger) : mLedger(ledger)
|
||||
|
||||
@@ -14,6 +14,9 @@ protected:
|
||||
TER mResult;
|
||||
std::vector<RippleAddress> mAffected;
|
||||
std::vector<unsigned char> mRawMeta;
|
||||
Json::Value mJson;
|
||||
|
||||
void buildJson();
|
||||
|
||||
public:
|
||||
|
||||
@@ -28,11 +31,12 @@ public:
|
||||
uint256 getTransactionID() const { return mTxn->getTransactionID(); }
|
||||
TransactionType getTxnType() const { return mTxn->getTxnType(); }
|
||||
TER getResult() const { return mResult; }
|
||||
uint32 getTxnSeq() const { return mMeta->getIndex(); }
|
||||
|
||||
bool isApplied() const { return !!mMeta; }
|
||||
int getIndex() const { return mMeta ? mMeta->getIndex() : 0; }
|
||||
std::string getEscMeta() const;
|
||||
Json::Value getJson(int) const;
|
||||
Json::Value getJson() const { return mJson; }
|
||||
};
|
||||
|
||||
class AcceptedLedger
|
||||
|
||||
@@ -158,7 +158,13 @@ STAmount::STAmount(SField::ref n, const Json::Value& v)
|
||||
|
||||
mIsNative = !currency.isString() || currency.asString().empty() || (currency.asString() == SYSTEM_CURRENCY_CODE);
|
||||
|
||||
if (!mIsNative) {
|
||||
if (mIsNative)
|
||||
{
|
||||
if (v.isObject())
|
||||
throw std::runtime_error("XRP may not be specified as an object");
|
||||
}
|
||||
else
|
||||
{ // non-XRP
|
||||
if (!currencyFromString(mCurrency, currency.asString()))
|
||||
throw std::runtime_error("invalid currency");
|
||||
|
||||
@@ -799,7 +805,9 @@ STAmount operator+(const STAmount& v1, const STAmount& v2)
|
||||
// this addition cannot overflow an int64, it can overflow an STAmount and the constructor will throw
|
||||
|
||||
int64 fv = vv1 + vv2;
|
||||
if (fv >= 0)
|
||||
if ((fv >= -10) && (fv <= 10))
|
||||
return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer);
|
||||
else if (fv >= 0)
|
||||
return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, fv, ov1, false);
|
||||
else
|
||||
return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, -fv, ov1, true);
|
||||
@@ -834,7 +842,9 @@ STAmount operator-(const STAmount& v1, const STAmount& v2)
|
||||
// this subtraction cannot overflow an int64, it can overflow an STAmount and the constructor will throw
|
||||
|
||||
int64 fv = vv1 - vv2;
|
||||
if (fv >= 0)
|
||||
if ((fv >= -10) && (fv <= 10))
|
||||
return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer);
|
||||
else if (fv >= 0)
|
||||
return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, fv, ov1, false);
|
||||
else
|
||||
return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, -fv, ov1, true);
|
||||
|
||||
@@ -105,7 +105,9 @@ STAmount STAmount::addRound(const STAmount& v1, const STAmount& v2, bool roundUp
|
||||
}
|
||||
|
||||
int64 fv = vv1 + vv2;
|
||||
if (fv >= 0)
|
||||
if ((fv >= -10) && (fv <= -10))
|
||||
return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer);
|
||||
else if (fv >= 0)
|
||||
{
|
||||
uint64 v = static_cast<uint64>(fv);
|
||||
canonicalizeRound(false, v, ov1, roundUp);
|
||||
@@ -168,7 +170,9 @@ STAmount STAmount::subRound(const STAmount& v1, const STAmount& v2, bool roundUp
|
||||
}
|
||||
|
||||
int64 fv = vv1 + vv2;
|
||||
if (fv >= 0)
|
||||
if ((fv >= -10) && (fv <= -10))
|
||||
return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer);
|
||||
else if (fv >= 0)
|
||||
{
|
||||
uint64 v = static_cast<uint64>(fv);
|
||||
canonicalizeRound(false, v, ov1, roundUp);
|
||||
|
||||
@@ -46,6 +46,7 @@ DatabaseCon::~DatabaseCon()
|
||||
}
|
||||
|
||||
Application::Application() :
|
||||
// mIOService(2),
|
||||
mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mLedgerMaster),
|
||||
mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), mSLECache("LedgerEntryCache", 4096, 120),
|
||||
mSNTPClient(mAuxService), mJobQueue(mIOService), mFeeTrack(),
|
||||
@@ -149,6 +150,9 @@ void Application::setup()
|
||||
mLedgerDB->getDB()->setupCheckpointing(&mJobQueue);
|
||||
mHashNodeDB->getDB()->setupCheckpointing(&mJobQueue);
|
||||
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
updateTables();
|
||||
|
||||
if (theConfig.START_UP == Config::FRESH)
|
||||
{
|
||||
cLog(lsINFO) << "Starting new Ledger";
|
||||
@@ -423,12 +427,12 @@ void Application::loadOldLedger(const std::string& l)
|
||||
mLedgerMaster.switchLedgers(loadLedger, openLedger);
|
||||
mNetOps.setLastCloseTime(loadLedger->getCloseTimeNC());
|
||||
}
|
||||
catch (SHAMapMissingNode& mn)
|
||||
catch (SHAMapMissingNode&)
|
||||
{
|
||||
cLog(lsFATAL) << "Data is missing for selected ledger";
|
||||
exit(-1);
|
||||
}
|
||||
catch (boost::bad_lexical_cast& blc)
|
||||
catch (boost::bad_lexical_cast&)
|
||||
{
|
||||
cLog(lsFATAL) << "Ledger specified '" << l << "' is not valid";
|
||||
exit(-1);
|
||||
|
||||
@@ -90,6 +90,7 @@ class Application
|
||||
|
||||
volatile bool mShutdown;
|
||||
|
||||
void updateTables();
|
||||
void startNewLedger();
|
||||
void loadOldLedger(const std::string&);
|
||||
|
||||
|
||||
@@ -505,6 +505,16 @@ Json::Value RPCParser::parseSignSubmit(const Json::Value& jvParams)
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
// sms <text>
|
||||
Json::Value RPCParser::parseSMS(const Json::Value& jvParams)
|
||||
{
|
||||
Json::Value jvRequest;
|
||||
|
||||
jvRequest["text"] = jvParams[0u].asString();
|
||||
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
// tx <transaction_id>
|
||||
Json::Value RPCParser::parseTx(const Json::Value& jvParams)
|
||||
{
|
||||
@@ -669,6 +679,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
|
||||
{ "random", &RPCParser::parseAsIs, 0, 0 },
|
||||
{ "ripple_path_find", &RPCParser::parseRipplePathFind, 1, 2 },
|
||||
{ "sign", &RPCParser::parseSignSubmit, 2, 2 },
|
||||
{ "sms", &RPCParser::parseSMS, 1, 1 },
|
||||
{ "submit", &RPCParser::parseSignSubmit, 1, 2 },
|
||||
{ "server_info", &RPCParser::parseAsIs, 0, 0 },
|
||||
{ "server_state", &RPCParser::parseAsIs, 0, 0 },
|
||||
|
||||
@@ -33,6 +33,7 @@ protected:
|
||||
Json::Value parseOwnerInfo(const Json::Value& jvParams);
|
||||
Json::Value parseRandom(const Json::Value& jvParams);
|
||||
Json::Value parseRipplePathFind(const Json::Value& jvParams);
|
||||
Json::Value parseSMS(const Json::Value& jvParams);
|
||||
Json::Value parseSignSubmit(const Json::Value& jvParams);
|
||||
Json::Value parseTx(const Json::Value& jvParams);
|
||||
Json::Value parseTxHistory(const Json::Value& jvParams);
|
||||
|
||||
@@ -45,6 +45,11 @@
|
||||
#define SECTION_RPC_USER "rpc_user"
|
||||
#define SECTION_RPC_PASSWORD "rpc_password"
|
||||
#define SECTION_RPC_STARTUP "rpc_startup"
|
||||
#define SECTION_SMS_FROM "sms_from"
|
||||
#define SECTION_SMS_KEY "sms_key"
|
||||
#define SECTION_SMS_SECRET "sms_secret"
|
||||
#define SECTION_SMS_TO "sms_to"
|
||||
#define SECTION_SMS_URL "sms_url"
|
||||
#define SECTION_SNTP "sntp_servers"
|
||||
#define SECTION_SSL_VERIFY "ssl_verify"
|
||||
#define SECTION_SSL_VERIFY_FILE "ssl_verify_file"
|
||||
@@ -473,6 +478,12 @@ void Config::load()
|
||||
if (sectionSingleB(secConfig, SECTION_ACCOUNT_PROBE_MAX, strTemp))
|
||||
ACCOUNT_PROBE_MAX = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_SMS_FROM, SMS_FROM);
|
||||
(void) sectionSingleB(secConfig, SECTION_SMS_KEY, SMS_KEY);
|
||||
(void) sectionSingleB(secConfig, SECTION_SMS_SECRET, SMS_SECRET);
|
||||
(void) sectionSingleB(secConfig, SECTION_SMS_TO, SMS_TO);
|
||||
(void) sectionSingleB(secConfig, SECTION_SMS_URL, SMS_URL);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_VALIDATORS_FILE, strTemp))
|
||||
VALIDATORS_FILE = strTemp;
|
||||
|
||||
|
||||
@@ -185,6 +185,12 @@ public:
|
||||
std::string SSL_VERIFY_FILE;
|
||||
std::string SSL_VERIFY_DIR;
|
||||
|
||||
std::string SMS_FROM;
|
||||
std::string SMS_KEY;
|
||||
std::string SMS_SECRET;
|
||||
std::string SMS_TO;
|
||||
std::string SMS_URL;
|
||||
|
||||
Config();
|
||||
|
||||
int getSize(SizedItemName);
|
||||
|
||||
@@ -26,11 +26,12 @@ const char *TxnDBInit[] = {
|
||||
TransID CHARACTER(64), \
|
||||
Account CHARACTER(64), \
|
||||
LedgerSeq BIGINT UNSIGNED \
|
||||
TxnSeq INTEGER \
|
||||
);",
|
||||
"CREATE INDEX AcctTxIDIndex ON \
|
||||
AccountTransactions(TransID);",
|
||||
"CREATE INDEX AcctTxIndex ON \
|
||||
AccountTransactions(Account, LedgerSeq, TransID);",
|
||||
AccountTransactions(Account, LedgerSeq, TxnSeq, TransID);",
|
||||
"CREATE INDEX AcctLgrIndex ON \
|
||||
AccountTransactions(LedgerSeq, Account, TransID);",
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ void HashedObjectStore::bulkWrite()
|
||||
{
|
||||
Database* db = theApp->getHashNodeDB()->getDB();
|
||||
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
|
||||
static SqliteStatement pSt(db->getSqliteDB(),
|
||||
SqliteStatement pSt(db->getSqliteDB(),
|
||||
"INSERT OR IGNORE INTO CommittedObjects "
|
||||
"(Hash,ObjType,LedgerIndex,Object) VALUES (?, ?, ?, ?);");
|
||||
|
||||
@@ -183,17 +183,17 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
|
||||
#ifndef NO_SQLITE3_PREPARE
|
||||
{
|
||||
std::string sql = "SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = '";
|
||||
sql.append(hash.GetHex());
|
||||
sql.append("';");
|
||||
|
||||
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
|
||||
LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtDISK, "HOS::retrieve"));
|
||||
static SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(),
|
||||
"SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;");
|
||||
|
||||
pSt.bind(1, hash.GetHex());
|
||||
SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(), sql.c_str());
|
||||
|
||||
int ret = pSt.step();
|
||||
if (pSt.isDone(ret))
|
||||
{
|
||||
pSt.reset();
|
||||
mNegativeCache.add(hash);
|
||||
cLog(lsTRACE) << "HOS: " << hash <<" fetch: not in db";
|
||||
return obj;
|
||||
@@ -202,7 +202,6 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
type = pSt.peekString(0);
|
||||
index = pSt.getUInt32(1);
|
||||
pSt.getBlob(2).swap(data);
|
||||
pSt.reset();
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -271,7 +270,7 @@ int HashedObjectStore::import(const std::string& file)
|
||||
uint256 hash;
|
||||
std::string hashStr;
|
||||
importDB->getStr("Hash", hashStr);
|
||||
hash.SetHex(hashStr, true);
|
||||
hash.SetHexExact(hashStr);
|
||||
if (hash.isZero())
|
||||
{
|
||||
cLog(lsWARNING) << "zero hash found in import table";
|
||||
|
||||
@@ -443,4 +443,48 @@ void HttpsClient::httpsRequest(
|
||||
client->httpsRequest(bSSL, deqSites, setRequest, timeout, complete);
|
||||
}
|
||||
|
||||
#define SMS_TIMEOUT 30
|
||||
|
||||
bool responseSMS(const boost::system::error_code& ecResult, int iStatus, const std::string& strData) {
|
||||
cLog(lsINFO) << "SMS: Response:" << iStatus << " :" << strData;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpsClient::sendSMS(boost::asio::io_service& io_service, const std::string& strText) {
|
||||
std::string strScheme;
|
||||
std::string strDomain;
|
||||
int iPort;
|
||||
std::string strPath;
|
||||
|
||||
if (theConfig.SMS_URL == "" || !parseUrl(theConfig.SMS_URL, strScheme, strDomain, iPort, strPath))
|
||||
{
|
||||
cLog(lsWARNING) << "SMSRequest: Bad URL:" << theConfig.SMS_URL;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool bSSL = strScheme == "https";
|
||||
|
||||
std::deque<std::string> deqSites(1, strDomain);
|
||||
std::string strURI =
|
||||
boost::str(boost::format("%s?from=%s&to=%s&api_key=%s&api_secret=%s&text=%s")
|
||||
% (strPath.empty() ? "/" : strPath)
|
||||
% theConfig.SMS_FROM
|
||||
% theConfig.SMS_TO
|
||||
% theConfig.SMS_KEY
|
||||
% theConfig.SMS_SECRET
|
||||
% urlEncode(strText));
|
||||
|
||||
// cLog(lsINFO) << "SMS: Request:" << strURI;
|
||||
cLog(lsINFO) << "SMS: Request: '" << strText << "'";
|
||||
|
||||
if (iPort < 0)
|
||||
iPort = bSSL ? 443 : 80;
|
||||
|
||||
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, iPort, CLIENT_MAX_HEADER));
|
||||
|
||||
client->httpsGet(bSSL, deqSites, strURI, boost::posix_time::seconds(SMS_TIMEOUT),
|
||||
BIND_TYPE(&responseSMS, P_1, P_2, P_3));
|
||||
}
|
||||
}
|
||||
// vim:ts=4
|
||||
|
||||
@@ -116,6 +116,8 @@ public:
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
FUNCTION_TYPE<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete);
|
||||
|
||||
static void sendSMS(boost::asio::io_service& io_service, const std::string& strText);
|
||||
};
|
||||
#endif
|
||||
// vim:ts=4
|
||||
|
||||
@@ -272,9 +272,7 @@ void JobQueue::IOThread(boost::mutex::scoped_lock& sl)
|
||||
NameThread("IO+");
|
||||
try
|
||||
{
|
||||
do
|
||||
NameThread("IO+");
|
||||
while ((mIOService.poll_one() == 1) && !theApp->isShutdown());
|
||||
mIOService.poll();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -291,12 +289,19 @@ void JobQueue::threadEntry()
|
||||
while (1)
|
||||
{
|
||||
NameThread("waiting");
|
||||
// bool didIO = false;
|
||||
while (mJobSet.empty() && !mShuttingDown)
|
||||
{
|
||||
if ((mIOThreadCount < mMaxIOThreadCount) && !theApp->isShutdown())
|
||||
IOThread(sl);
|
||||
else
|
||||
// if ((mIOThreadCount < mMaxIOThreadCount) && !didIO && !theApp->isShutdown())
|
||||
// {
|
||||
// IOThread(sl);
|
||||
// didIO = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
mJobCond.wait(sl);
|
||||
// didIO = false;
|
||||
// }
|
||||
}
|
||||
|
||||
if (mShuttingDown)
|
||||
|
||||
@@ -44,7 +44,8 @@ Ledger::Ledger(const RippleAddress& masterID, uint64 startAmount) : mTotCoins(st
|
||||
}
|
||||
|
||||
Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
|
||||
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, uint32 ledgerSeq)
|
||||
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime,
|
||||
int closeFlags, int closeResolution, uint32 ledgerSeq, bool& loaded)
|
||||
: mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash), mTotCoins(totCoins),
|
||||
mLedgerSeq(ledgerSeq), mCloseTime(closeTime), mParentCloseTime(parentCloseTime),
|
||||
mCloseResolution(closeResolution), mCloseFlags(closeFlags),
|
||||
@@ -53,10 +54,27 @@ Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint25
|
||||
mAccountStateMap(boost::make_shared<SHAMap>(smtSTATE, accountHash))
|
||||
{ // This will throw if the root nodes are not available locally
|
||||
updateHash();
|
||||
if (mTransHash.isNonZero())
|
||||
mTransactionMap->fetchRoot(mTransHash);
|
||||
if (mAccountHash.isNonZero())
|
||||
mAccountStateMap->fetchRoot(mAccountHash);
|
||||
loaded = true;
|
||||
try
|
||||
{
|
||||
if (mTransHash.isNonZero())
|
||||
mTransactionMap->fetchRoot(mTransHash);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
loaded = false;
|
||||
cLog(lsWARNING) << "Don't have TX root for ledger";
|
||||
}
|
||||
try
|
||||
{
|
||||
if (mAccountHash.isNonZero())
|
||||
mAccountStateMap->fetchRoot(mAccountHash);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
loaded = false;
|
||||
cLog(lsWARNING) << "Don't have AS root for ledger";
|
||||
}
|
||||
mTransactionMap->setImmutable();
|
||||
mAccountStateMap->setImmutable();
|
||||
zeroFees();
|
||||
@@ -461,7 +479,7 @@ void Ledger::saveAcceptedLedger(Job&, bool fromConsensus)
|
||||
const std::vector<RippleAddress>& accts = vt.second.getAffected();
|
||||
if (!accts.empty())
|
||||
{
|
||||
std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES ";
|
||||
std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq, TxnSeq) VALUES ";
|
||||
bool first = true;
|
||||
for (std::vector<RippleAddress>::const_iterator it = accts.begin(), end = accts.end(); it != end; ++it)
|
||||
{
|
||||
@@ -477,6 +495,8 @@ void Ledger::saveAcceptedLedger(Job&, bool fromConsensus)
|
||||
sql += it->humanAccountID();
|
||||
sql += "',";
|
||||
sql += boost::lexical_cast<std::string>(getLedgerSeq());
|
||||
sql += ",";
|
||||
sql += boost::lexical_cast<std::string>(vt.second.getTxnSeq());
|
||||
sql += ")";
|
||||
}
|
||||
sql += ";";
|
||||
@@ -518,14 +538,13 @@ Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex)
|
||||
Database* db = theApp->getLedgerDB()->getDB();
|
||||
ScopedLock sl(theApp->getLedgerDB()->getDBLock());
|
||||
|
||||
static SqliteStatement pSt(db->getSqliteDB(), "SELECT "
|
||||
SqliteStatement pSt(db->getSqliteDB(), "SELECT "
|
||||
"LedgerHash,PrevHash,AccountSetHash,TransSetHash,TotalCoins,"
|
||||
"ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq"
|
||||
" from Ledgers WHERE LedgerSeq = ?;");
|
||||
|
||||
pSt.bind(1, ledgerIndex);
|
||||
ledger = getSQL1(&pSt);
|
||||
pSt.reset();
|
||||
}
|
||||
if (ledger)
|
||||
Ledger::getSQL2(ledger);
|
||||
@@ -539,14 +558,13 @@ Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash)
|
||||
Database* db = theApp->getLedgerDB()->getDB();
|
||||
ScopedLock sl(theApp->getLedgerDB()->getDBLock());
|
||||
|
||||
static SqliteStatement pSt(db->getSqliteDB(), "SELECT "
|
||||
SqliteStatement pSt(db->getSqliteDB(), "SELECT "
|
||||
"LedgerHash,PrevHash,AccountSetHash,TransSetHash,TotalCoins,"
|
||||
"ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq"
|
||||
" from Ledgers WHERE LedgerHash = ?;");
|
||||
|
||||
pSt.bind(1, ledgerHash.GetHex());
|
||||
ledger = getSQL1(&pSt);
|
||||
pSt.reset();
|
||||
}
|
||||
if (ledger)
|
||||
{
|
||||
@@ -593,13 +611,13 @@ Ledger::pointer Ledger::getSQL(const std::string& sql)
|
||||
return Ledger::pointer();
|
||||
|
||||
db->getStr("LedgerHash", hash);
|
||||
ledgerHash.SetHex(hash, true);
|
||||
ledgerHash.SetHexExact(hash);
|
||||
db->getStr("PrevHash", hash);
|
||||
prevHash.SetHex(hash, true);
|
||||
prevHash.SetHexExact(hash);
|
||||
db->getStr("AccountSetHash", hash);
|
||||
accountHash.SetHex(hash, true);
|
||||
accountHash.SetHexExact(hash);
|
||||
db->getStr("TransSetHash", hash);
|
||||
transHash.SetHex(hash, true);
|
||||
transHash.SetHexExact(hash);
|
||||
totCoins = db->getBigInt("TotalCoins");
|
||||
closingTime = db->getBigInt("ClosingTime");
|
||||
prevClosingTime = db->getBigInt("PrevClosingTime");
|
||||
@@ -610,8 +628,11 @@ Ledger::pointer Ledger::getSQL(const std::string& sql)
|
||||
}
|
||||
|
||||
// CAUTION: code below appears in two places
|
||||
Ledger::pointer ret = boost::make_shared<Ledger>(prevHash, transHash, accountHash, totCoins,
|
||||
closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq);
|
||||
bool loaded;
|
||||
Ledger::pointer ret(new Ledger(prevHash, transHash, accountHash, totCoins,
|
||||
closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded));
|
||||
if (!loaded)
|
||||
return Ledger::pointer();
|
||||
ret->setClosed();
|
||||
if (theApp->getOPs().haveLedger(ledgerSeq))
|
||||
ret->setAccepted();
|
||||
@@ -650,10 +671,10 @@ Ledger::pointer Ledger::getSQL1(SqliteStatement *stmt)
|
||||
unsigned closeFlags;
|
||||
std::string hash;
|
||||
|
||||
ledgerHash.SetHex(stmt->peekString(0), true);
|
||||
prevHash.SetHex(stmt->peekString(1), true);
|
||||
accountHash.SetHex(stmt->peekString(2), true);
|
||||
transHash.SetHex(stmt->peekString(3), true);
|
||||
ledgerHash.SetHexExact(stmt->peekString(0));
|
||||
prevHash.SetHexExact(stmt->peekString(1));
|
||||
accountHash.SetHexExact(stmt->peekString(2));
|
||||
transHash.SetHexExact(stmt->peekString(3));
|
||||
totCoins = stmt->getInt64(4);
|
||||
closingTime = stmt->getUInt32(5);
|
||||
prevClosingTime = stmt->getUInt32(6);
|
||||
@@ -662,8 +683,12 @@ Ledger::pointer Ledger::getSQL1(SqliteStatement *stmt)
|
||||
ledgerSeq = stmt->getUInt32(9);
|
||||
|
||||
// CAUTION: code below appears in two places
|
||||
return boost::make_shared<Ledger>(prevHash, transHash, accountHash, totCoins,
|
||||
closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq);
|
||||
bool loaded;
|
||||
Ledger::pointer ret(new Ledger(prevHash, transHash, accountHash, totCoins,
|
||||
closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded));
|
||||
if (!loaded)
|
||||
return Ledger::pointer();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Ledger::getSQL2(Ledger::ref ret)
|
||||
@@ -692,7 +717,7 @@ uint256 Ledger::getHashByIndex(uint32 ledgerIndex)
|
||||
db->endIterRows();
|
||||
}
|
||||
|
||||
ret.SetHex(hash, true);
|
||||
ret.SetHexExact(hash);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -703,7 +728,7 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256&
|
||||
DatabaseCon *con = theApp->getLedgerDB();
|
||||
ScopedLock sl(con->getDBLock());
|
||||
|
||||
static SqliteStatement pSt(con->getDB()->getSqliteDB(),
|
||||
SqliteStatement pSt(con->getDB()->getSqliteDB(),
|
||||
"SELECT LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger Where LedgerSeq = ?;");
|
||||
|
||||
pSt.bind(1, ledgerIndex);
|
||||
@@ -711,21 +736,18 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256&
|
||||
int ret = pSt.step();
|
||||
if (pSt.isDone(ret))
|
||||
{
|
||||
pSt.reset();
|
||||
cLog(lsTRACE) << "Don't have ledger " << ledgerIndex;
|
||||
return false;
|
||||
}
|
||||
if (!pSt.isRow(ret))
|
||||
{
|
||||
pSt.reset();
|
||||
assert(false);
|
||||
cLog(lsFATAL) << "Unexpected statement result " << ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
ledgerHash.SetHex(pSt.peekString(0), true);
|
||||
parentHash.SetHex(pSt.peekString(1), true);
|
||||
pSt.reset();
|
||||
ledgerHash.SetHexExact(pSt.peekString(0));
|
||||
parentHash.SetHexExact(pSt.peekString(1));
|
||||
|
||||
return true;
|
||||
|
||||
@@ -746,8 +768,8 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256&
|
||||
db->endIterRows();
|
||||
}
|
||||
|
||||
ledgerHash.SetHex(hash, true);
|
||||
parentHash.SetHex(prevHash, true);
|
||||
ledgerHash.SetHexExact(hash);
|
||||
parentHash.SetHexExact(prevHash);
|
||||
|
||||
assert(ledgerHash.isNonZero() && ((ledgerIndex == 0) || parentHash.isNonZero()));
|
||||
|
||||
@@ -763,7 +785,7 @@ std::map< uint32, std::pair<uint256, uint256> > Ledger::getHashesByIndex(uint32
|
||||
DatabaseCon *con = theApp->getLedgerDB();
|
||||
ScopedLock sl(con->getDBLock());
|
||||
|
||||
static SqliteStatement pSt(con->getDB()->getSqliteDB(),
|
||||
SqliteStatement pSt(con->getDB()->getSqliteDB(),
|
||||
"SELECT LedgerSeq,LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger "
|
||||
"WHERE LedgerSeq >= ? AND LedgerSeq <= ?;");
|
||||
|
||||
@@ -776,17 +798,11 @@ std::map< uint32, std::pair<uint256, uint256> > Ledger::getHashesByIndex(uint32
|
||||
{
|
||||
int r = pSt.step();
|
||||
if (pSt.isDone(r))
|
||||
{
|
||||
pSt.reset();
|
||||
return ret;
|
||||
}
|
||||
if (!pSt.isRow(r))
|
||||
{
|
||||
pSt.reset();
|
||||
return ret;
|
||||
}
|
||||
hashes.first.SetHex(pSt.peekString(1), true);
|
||||
hashes.second.SetHex(pSt.peekString(2), true);
|
||||
hashes.first.SetHexExact(pSt.peekString(1));
|
||||
hashes.second.SetHexExact(pSt.peekString(2));
|
||||
ret[pSt.getUInt32(0)] = hashes;
|
||||
} while(1);
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
|
||||
Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
|
||||
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution,
|
||||
uint32 ledgerSeq); // used for database ledgers
|
||||
uint32 ledgerSeq, bool& loaded); // used for database ledgers
|
||||
|
||||
Ledger(const std::vector<unsigned char>& rawLedger, bool hasPrefix);
|
||||
Ledger(const std::string& rawLedger, bool hasPrefix);
|
||||
|
||||
@@ -196,7 +196,7 @@ void LedgerAcquire::onTimer(bool progress)
|
||||
|
||||
if (getTimeouts() > LEDGER_TIMEOUT_COUNT)
|
||||
{
|
||||
cLog(lsWARNING) << "Too many timeouts for ledger " << mHash;
|
||||
cLog(lsWARNING) << "Too many timeouts( " << getTimeouts() << ") for ledger " << mHash;
|
||||
setFailed();
|
||||
done();
|
||||
return;
|
||||
|
||||
@@ -150,8 +150,9 @@ void LedgerEntrySet::entryCreate(SLE::ref sle)
|
||||
|
||||
case taaMODIFY:
|
||||
throw std::runtime_error("Create after modify");
|
||||
|
||||
case taaCREATE:
|
||||
throw std::runtime_error("Create after create"); // We could make this work
|
||||
throw std::runtime_error("Create after create"); // This could be made to work
|
||||
|
||||
case taaCACHED:
|
||||
throw std::runtime_error("Create after cache");
|
||||
@@ -532,7 +533,7 @@ TER LedgerEntrySet::dirCount(const uint256& uRootIndex, uint32& uCount)
|
||||
// <-- uNodeDir: For deletion, present to make dirDelete efficient.
|
||||
// --> uRootIndex: The index of the base of the directory. Nodes are based off of this.
|
||||
// --> uLedgerIndex: Value to add to directory.
|
||||
// We only append. This allow for things that watch append only structure to just monitor from the last node on ward.
|
||||
// Only append. This allow for things that watch append only structure to just monitor from the last node on ward.
|
||||
// Within a node with no deletions order of elements is sequential. Otherwise, order of elements is random.
|
||||
TER LedgerEntrySet::dirAdd(
|
||||
uint64& uNodeDir,
|
||||
@@ -850,7 +851,7 @@ bool LedgerEntrySet::dirFirst(
|
||||
sleNode = entryCache(ltDIR_NODE, uRootIndex);
|
||||
uDirEntry = 0;
|
||||
|
||||
assert(sleNode); // We never probe for directories.
|
||||
assert(sleNode); // Never probe for directories.
|
||||
|
||||
return LedgerEntrySet::dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
|
||||
}
|
||||
@@ -1243,13 +1244,38 @@ TER LedgerEntrySet::trustCreate(
|
||||
|
||||
ownerCountAdjust(!bSetDst ? uSrcAccountID : uDstAccountID, 1, sleAccount);
|
||||
|
||||
sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance);
|
||||
sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); // ONLY: Create ripple balance.
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// Direct send w/o fees: redeeming IOUs and/or sending own IOUs.
|
||||
TER LedgerEntrySet::trustDelete(SLE::ref sleRippleState, const uint160& uLowAccountID, const uint160& uHighAccountID)
|
||||
{
|
||||
bool bLowNode = sleRippleState->isFieldPresent(sfLowNode); // Detect legacy dirs.
|
||||
bool bHighNode = sleRippleState->isFieldPresent(sfHighNode);
|
||||
uint64 uLowNode = sleRippleState->getFieldU64(sfLowNode);
|
||||
uint64 uHighNode = sleRippleState->getFieldU64(sfHighNode);
|
||||
TER terResult;
|
||||
|
||||
cLog(lsTRACE) << "trustDelete: Deleting ripple line: low";
|
||||
terResult = dirDelete(false, uLowNode, Ledger::getOwnerDirIndex(uLowAccountID), sleRippleState->getIndex(), false, !bLowNode);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
cLog(lsTRACE) << "trustDelete: Deleting ripple line: high";
|
||||
terResult = dirDelete(false, uHighNode, Ledger::getOwnerDirIndex(uHighAccountID), sleRippleState->getIndex(), false, !bHighNode);
|
||||
}
|
||||
|
||||
cLog(lsINFO) << "trustDelete: Deleting ripple line: state";
|
||||
entryDelete(sleRippleState);
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// Direct send w/o fees:
|
||||
// - Redeeming IOUs and/or sending sender's own IOUs.
|
||||
// - Create trust line of needed.
|
||||
TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer)
|
||||
{
|
||||
uint160 uIssuerID = saAmount.getIssuer();
|
||||
@@ -1306,14 +1332,46 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei
|
||||
% saAmount.getFullText()
|
||||
% saBalance.getFullText());
|
||||
|
||||
bool bDelete = false;
|
||||
uint32 uFlags;
|
||||
|
||||
// YYY Could skip this if rippling in reverse.
|
||||
if (saBefore.isPositive() // Sender balance was positive.
|
||||
&& !saBalance.isPositive() // Sender is zero or negative.
|
||||
&& isSetBit((uFlags = sleRippleState->getFieldU32(sfFlags)), !bSenderHigh ? lsfLowReserve : lsfHighReserve) // Sender reserve is set.
|
||||
&& !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) // Sender trust limit is 0.
|
||||
&& !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) // Sender quality in is 0.
|
||||
&& !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) // Sender quality out is 0.
|
||||
{
|
||||
// Clear the reserve of the sender, possibly delete the line!
|
||||
SLE::pointer sleSender = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uSenderID));
|
||||
|
||||
ownerCountAdjust(uSenderID, -1, sleSender);
|
||||
|
||||
sleRippleState->setFieldU32(sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); // Clear reserve flag.
|
||||
|
||||
bDelete = !saBalance // Balance is zero.
|
||||
&& !isSetBit(uFlags, bSenderHigh ? lsfLowReserve : lsfHighReserve); // Receiver reserve is clear.
|
||||
}
|
||||
|
||||
if (bSenderHigh)
|
||||
saBalance.negate();
|
||||
|
||||
sleRippleState->setFieldAmount(sfBalance, saBalance);
|
||||
// Want to reflect balance to zero even if we are deleting line.
|
||||
sleRippleState->setFieldAmount(sfBalance, saBalance); // ONLY: Adjust ripple balance.
|
||||
|
||||
entryModify(sleRippleState);
|
||||
if (bDelete)
|
||||
{
|
||||
terResult = trustDelete(sleRippleState,
|
||||
bSenderHigh ? uReceiverID : uSenderID,
|
||||
!bSenderHigh ? uReceiverID : uSenderID);
|
||||
}
|
||||
else
|
||||
{
|
||||
entryModify(sleRippleState);
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return terResult;
|
||||
@@ -1374,6 +1432,7 @@ TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiv
|
||||
}
|
||||
else if (saAmount.isNative())
|
||||
{
|
||||
// XRP send which does not check reserve and can do pure adjustment.
|
||||
SLE::pointer sleSender = !!uSenderID
|
||||
? entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uSenderID))
|
||||
: SLE::pointer();
|
||||
@@ -1398,14 +1457,14 @@ TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiv
|
||||
}
|
||||
else
|
||||
{
|
||||
sleSender->setFieldAmount(sfBalance, sleSender->getFieldAmount(sfBalance) - saAmount);
|
||||
sleSender->setFieldAmount(sfBalance, sleSender->getFieldAmount(sfBalance) - saAmount); // Decrement XRP balance.
|
||||
entryModify(sleSender);
|
||||
}
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult && sleReceiver)
|
||||
{
|
||||
sleReceiver->setFieldAmount(sfBalance, sleReceiver->getFieldAmount(sfBalance) + saAmount);
|
||||
sleReceiver->setFieldAmount(sfBalance, sleReceiver->getFieldAmount(sfBalance) + saAmount); // Increment XRP balance.
|
||||
entryModify(sleReceiver);
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ public:
|
||||
const STAmount& saSrcLimit,
|
||||
const uint32 uSrcQualityIn = 0,
|
||||
const uint32 uSrcQualityOut = 0);
|
||||
TER trustDelete(SLE::ref sleRippleState, const uint160& uLowAccountID, const uint160& uHighAccountID);
|
||||
|
||||
Json::Value getJson(int) const;
|
||||
void calcRawMeta(Serializer&, TER result, uint32 index);
|
||||
|
||||
@@ -74,10 +74,10 @@ void LedgerMaster::switchLedgers(Ledger::pointer lastClosed, Ledger::pointer cur
|
||||
mFinalizedLedger->setClosed();
|
||||
mFinalizedLedger->setAccepted();
|
||||
mCurrentLedger = current;
|
||||
}
|
||||
|
||||
assert(!mCurrentLedger->isClosed());
|
||||
mEngine.setLedger(mCurrentLedger);
|
||||
assert(!mCurrentLedger->isClosed());
|
||||
mEngine.setLedger(mCurrentLedger);
|
||||
}
|
||||
checkAccept(lastClosed->getHash(), lastClosed->getLedgerSeq());
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ Ledger::pointer LedgerMaster::closeLedger(bool recover)
|
||||
++recovers;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
{ // CHECKME: We got a few of these
|
||||
cLog(lsWARNING) << "Held transaction throws";
|
||||
}
|
||||
}
|
||||
@@ -122,6 +122,7 @@ Ledger::pointer LedgerMaster::closeLedger(bool recover)
|
||||
|
||||
TER LedgerMaster::doTransaction(SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
TER result = mEngine.applyTransaction(*txn, params, didApply);
|
||||
// if (didApply)
|
||||
theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result);
|
||||
|
||||
@@ -1093,13 +1093,15 @@ std::string
|
||||
boost::str(boost::format("SELECT %s FROM "
|
||||
"AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID "
|
||||
"WHERE Account = '%s' %s %s "
|
||||
"ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TransID %s LIMIT %u, %u;")
|
||||
"ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TxnSeq %s, AccountTransactions.TransID %s "
|
||||
"LIMIT %u, %u;")
|
||||
% selection
|
||||
% account.humanAccountID()
|
||||
% maxClause
|
||||
% minClause
|
||||
% (descending ? "DESC" : "ASC")
|
||||
% (descending ? "DESC" : "ASC")
|
||||
% (descending ? "DESC" : "ASC")
|
||||
% boost::lexical_cast<std::string>(offset)
|
||||
% boost::lexical_cast<std::string>(numberOfResults)
|
||||
);
|
||||
@@ -1375,7 +1377,7 @@ void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, SerializedTransac
|
||||
}
|
||||
}
|
||||
ALTransaction alt(stTxn, terResult);
|
||||
cLog(lsTRACE) << "pubProposed: " << alt.getJson(0);
|
||||
cLog(lsTRACE) << "pubProposed: " << alt.getJson();
|
||||
pubAccountTransaction(lpCurrent, ALTransaction(stTxn, terResult), false);
|
||||
}
|
||||
|
||||
@@ -1429,7 +1431,7 @@ void NetworkOPs::pubLedger(Ledger::ref accepted)
|
||||
{
|
||||
BOOST_FOREACH(const AcceptedLedger::value_type& vt, alpAccepted->getMap())
|
||||
{
|
||||
cLog(lsTRACE) << "pubAccepted: " << vt.second.getJson(0);
|
||||
cLog(lsTRACE) << "pubAccepted: " << vt.second.getJson();
|
||||
pubValidatedTransaction(lpAccepted, vt.second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,18 +78,26 @@ PathOption::PathOption(PathOption::pointer other)
|
||||
}
|
||||
#endif
|
||||
|
||||
// quality, length, liquidity, index
|
||||
typedef boost::tuple<uint64, int, STAmount, unsigned int> path_LQ_t;
|
||||
|
||||
// Lower numbers have better quality. Sort higher quality first.
|
||||
static bool bQualityCmp(
|
||||
std::pair< std::pair<uint64, int>, unsigned int> a,
|
||||
std::pair< std::pair<uint64, int>, unsigned int> b)
|
||||
static bool bQualityCmp(const path_LQ_t& a, const path_LQ_t&b)
|
||||
{
|
||||
if (a.first.first != b.first.first)
|
||||
return a.first.first < b.first.first;
|
||||
// 1) Higher quality (lower cost) is better
|
||||
if (a.get<0>() != b.get<0>())
|
||||
return a.get<0>() < b.get<0>();
|
||||
|
||||
if (a.first.second != b.first.second)
|
||||
return a.first.second < b.first.second;
|
||||
// 2) More liquidity (higher volume) is better
|
||||
if (a.get<2>() != b.get<2>())
|
||||
return a.get<2>() > b.get<2>();
|
||||
|
||||
return a.second < b.second; // FIXME: this biases accounts
|
||||
// 3) Shorter paths are better
|
||||
if (a.get<1>() != b.get<1>())
|
||||
return a.get<1>() < b.get<1>();
|
||||
|
||||
// 4) Tie breaker
|
||||
return a.get<3>() > b.get<3>();
|
||||
}
|
||||
|
||||
// Return true, if path is a default path with an element.
|
||||
@@ -125,13 +133,15 @@ bool Pathfinder::bDefaultPath(const STPath& spPath)
|
||||
|
||||
// Expand the current path.
|
||||
pspCurrent->setExpanded(lesActive, spPath, mDstAccountID, mSrcAccountID);
|
||||
// XXX Need to report or act on errors returned in pspCurrent->terStatus.
|
||||
|
||||
// Determine if expanded current path is the default.
|
||||
// When path is a default (implied). Don't need to add it to return set.
|
||||
bDefault = pspCurrent->vpnNodes == mPsDefault->vpnNodes;
|
||||
|
||||
cLog(lsTRACE) << "bDefaultPath: expanded path: " << pspCurrent->getJson();
|
||||
cLog(lsTRACE) << "bDefaultPath: default path: indirect: " << spPath.getJson(0);
|
||||
cLog(lsTRACE) << "bDefaultPath: source path: " << spPath.getJson(0);
|
||||
cLog(lsTRACE) << "bDefaultPath: default path: " << mPsDefault->getJson();
|
||||
|
||||
return bDefault;
|
||||
}
|
||||
@@ -376,6 +386,8 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
|
||||
}
|
||||
else if (!speEnd.mCurrencyID)
|
||||
{
|
||||
// XXX Might restrict the number of times bridging through XRP.
|
||||
|
||||
// Cursor is for XRP, continue with qualifying books: XRP -> non-XRP
|
||||
std::vector<OrderBook::pointer> xrpBooks;
|
||||
theApp->getOrderBookDB().getBooksByTakerPays(ACCOUNT_XRP, CURRENCY_XRP, xrpBooks);
|
||||
@@ -383,9 +395,9 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
|
||||
{
|
||||
// New end is an order book with the currency and issuer.
|
||||
|
||||
// Don't allow looping through same order books.
|
||||
if (!spPath.hasSeen(ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut()))
|
||||
{
|
||||
// Not a order book already in path.
|
||||
STPath spNew(spPath);
|
||||
STPathElement speBook(ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut());
|
||||
STPathElement speAccount(book->getIssuerOut(), book->getCurrencyOut(), book->getIssuerOut());
|
||||
@@ -395,6 +407,8 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
|
||||
|
||||
cLog(lsDEBUG)
|
||||
<< boost::str(boost::format("findPaths: XRP -> %s/%s")
|
||||
// % STAmount::createHumanCurrency(book->getCurrencyOut())
|
||||
// % RippleAddress::createHumanAccountID(book->getIssuerOut())
|
||||
% STAmount::createHumanCurrency(speBook.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID(speBook.mIssuerID));
|
||||
|
||||
@@ -508,7 +522,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
|
||||
|
||||
spNew.mPath.push_back(speBook); // Add the order book.
|
||||
|
||||
if (!book->getCurrencyOut())
|
||||
if (!!book->getCurrencyOut())
|
||||
{
|
||||
// For non-XRP out, don't end on the book, add the issuing account.
|
||||
STPathElement speAccount(book->getIssuerOut(), book->getCurrencyOut(), book->getIssuerOut());
|
||||
@@ -538,7 +552,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
|
||||
// Only filter, sort, and limit if have non-default paths.
|
||||
if (iLimit)
|
||||
{
|
||||
std::vector< std::pair< std::pair<uint64, int>, unsigned int> > vMap;
|
||||
std::vector<path_LQ_t> vMap;
|
||||
|
||||
// Build map of quality to entry.
|
||||
for (int i = vspResults.size(); i--;)
|
||||
@@ -587,7 +601,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
|
||||
% uQuality
|
||||
% spCurrent.getJson(0));
|
||||
|
||||
vMap.push_back(std::make_pair(std::make_pair(uQuality, spCurrent.mPath.size()), i));
|
||||
vMap.push_back(path_LQ_t(uQuality, spCurrent.mPath.size(), saDstAmountAct, i));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -600,18 +614,69 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
|
||||
|
||||
if (vMap.size())
|
||||
{
|
||||
iLimit = std::min(iMaxPaths, (unsigned int) vMap.size());
|
||||
|
||||
bFound = true;
|
||||
|
||||
std::sort(vMap.begin(), vMap.end(), bQualityCmp); // Lower is better and should be first.
|
||||
|
||||
// Output best quality entries.
|
||||
for (int i = 0; i != iLimit; ++i)
|
||||
{
|
||||
spsDst.addPath(vspResults[vMap[i].second]);
|
||||
STAmount remaining = mDstAmount;
|
||||
if (bFound)
|
||||
{ // must subtract liquidity in default path from remaining amount
|
||||
try
|
||||
{
|
||||
STAmount saMaxAmountAct, saDstAmountAct;
|
||||
std::vector<PathState::pointer> vpsExpanded;
|
||||
LedgerEntrySet lesSandbox(lesActive.duplicate());
|
||||
|
||||
TER result = RippleCalc::rippleCalc(
|
||||
lesSandbox,
|
||||
saMaxAmountAct,
|
||||
saDstAmountAct,
|
||||
vpsExpanded,
|
||||
mSrcAmount,
|
||||
mDstAmount,
|
||||
mDstAccountID,
|
||||
mSrcAccountID,
|
||||
STPathSet(),
|
||||
true, // allow partial payment
|
||||
false,
|
||||
false, // don't suppress default paths, that's the point
|
||||
true);
|
||||
|
||||
if (tesSUCCESS == result)
|
||||
{
|
||||
cLog(lsDEBUG) << "Default path contributes: " << saDstAmountAct;
|
||||
remaining -= saDstAmountAct;
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsDEBUG) << "Default path fails: " << transToken(result);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cLog(lsDEBUG) << "Default path causes exception";
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, iPathsLeft = iMaxPaths; (iPathsLeft > 0) && (i < vMap.size()); ++i)
|
||||
{
|
||||
path_LQ_t& lqt = vMap[i];
|
||||
if ((iPathsLeft != 1) || (lqt.get<2>() >= remaining))
|
||||
{ // last path must fill
|
||||
--iPathsLeft;
|
||||
remaining -= lqt.get<2>();
|
||||
spsDst.addPath(vspResults[lqt.get<3>()]);
|
||||
}
|
||||
else
|
||||
cLog(lsDEBUG) << "Skipping a non-filling path: " << vspResults[lqt.get<3>()].getJson(0);
|
||||
}
|
||||
|
||||
if (remaining.isPositive())
|
||||
{
|
||||
bFound = false;
|
||||
cLog(lsINFO) << "Paths could not send " << remaining << " of " << mDstAmount;
|
||||
}
|
||||
else
|
||||
bFound = true;
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("findPaths: RESULTS: %s") % spsDst.getJson(0));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "RippleCalc.h"
|
||||
#include "Application.h"
|
||||
|
||||
#define RIPPLE_PATHS_MAX 3
|
||||
#define RIPPLE_PATHS_MAX 6
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
|
||||
@@ -1441,7 +1441,7 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet)
|
||||
selectedPeer->sendPacket(boost::make_shared<PackedMessage>(packet, ripple::mtGET_LEDGER), false);
|
||||
return;
|
||||
}
|
||||
cLog(lsERROR) << "We do not have the map our peer wants";
|
||||
cLog(lsERROR) << "We do not have the map our peer wants " << getIP();
|
||||
punishPeer(LT_InvalidRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,12 +8,9 @@
|
||||
|
||||
bool AddSystemEntropy()
|
||||
{ // Get entropy from the Windows crypto provider
|
||||
RAND_screen(); // this isn't really that safe since it only works for end users not servers
|
||||
|
||||
/* TODO: you need the cryptoAPI installed I think for the below to work. I suppose we should require people to install this to build the windows version
|
||||
char name[512], rand[128];
|
||||
DWORD count = 500;
|
||||
HCRYPTOPROV cryptoHandle;
|
||||
HCRYPTPROV cryptoHandle;
|
||||
|
||||
if (!CryptGetDefaultProvider(PROV_RSA_FULL, NULL, CRYPT_MACHINE_DEFAULT, name, &count))
|
||||
{
|
||||
@@ -43,7 +40,6 @@ bool AddSystemEntropy()
|
||||
CryptReleaseContext(cryptoHandle, 0);
|
||||
RAND_seed(rand, 128);
|
||||
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1334,7 +1334,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost)
|
||||
|
||||
jvEntry["source_amount"] = saMaxAmountAct.getJson(0);
|
||||
// jvEntry["paths_expanded"] = vpsExpanded.getJson(0);
|
||||
jvEntry["paths_canonical"] = spsCanonical.getJson(0);
|
||||
jvEntry["paths_canonical"] = Json::arrayValue; // spsCanonical.getJson(0);
|
||||
jvEntry["paths_computed"] = spsComputed.getJson(0);
|
||||
|
||||
jvArray.append(jvEntry);
|
||||
@@ -2235,6 +2235,15 @@ Json::Value RPCHandler::doUnlScore(Json::Value, int& cost)
|
||||
return "scoring requested";
|
||||
}
|
||||
|
||||
Json::Value RPCHandler::doSMS(Json::Value jvRequest, int& cost)
|
||||
{
|
||||
if (!jvRequest.isMember("text"))
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
|
||||
HttpsClient::sendSMS(theApp->getIOService(), jvRequest["text"].asString());
|
||||
|
||||
return "sms dispatched";
|
||||
}
|
||||
Json::Value RPCHandler::doStop(Json::Value, int& cost)
|
||||
{
|
||||
theApp->stop();
|
||||
@@ -3250,6 +3259,7 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole, int &
|
||||
{ "submit", &RPCHandler::doSubmit, false, optCurrent },
|
||||
{ "server_info", &RPCHandler::doServerInfo, false, optNone },
|
||||
{ "server_state", &RPCHandler::doServerState, false, optNone },
|
||||
{ "sms", &RPCHandler::doSMS, true, optNone },
|
||||
{ "stop", &RPCHandler::doStop, true, optNone },
|
||||
{ "transaction_entry", &RPCHandler::doTransactionEntry, false, optCurrent },
|
||||
{ "tx", &RPCHandler::doTx, false, optNetwork },
|
||||
|
||||
@@ -76,6 +76,7 @@ class RPCHandler
|
||||
Json::Value doServerState(Json::Value params, int& cost); // for machines
|
||||
Json::Value doSessionClose(Json::Value params, int& cost);
|
||||
Json::Value doSessionOpen(Json::Value params, int& cost);
|
||||
Json::Value doSMS(Json::Value params, int& cost);
|
||||
Json::Value doStop(Json::Value params, int& cost);
|
||||
Json::Value doSign(Json::Value params, int& cost);
|
||||
Json::Value doSubmit(Json::Value params, int& cost);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <boost/asio.hpp>
|
||||
#include "RippleAddress.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef __NEWCOIN_ADDRESS__
|
||||
#define __NEWCOIN_ADDRESS__
|
||||
#ifndef __RIPPLE_ADDRESS__
|
||||
#define __RIPPLE_ADDRESS__
|
||||
|
||||
#include "base58.h"
|
||||
#include "uint256.h"
|
||||
|
||||
@@ -35,6 +35,55 @@ bool PaymentNode::operator==(const PaymentNode& pnOther) const {
|
||||
&& pnOther.uIssuerID == uIssuerID;
|
||||
}
|
||||
|
||||
// This is for debugging not end users. Output names can be changed without warning.
|
||||
Json::Value PaymentNode::getJson() const
|
||||
{
|
||||
Json::Value jvNode(Json::objectValue);
|
||||
Json::Value jvFlags(Json::arrayValue);
|
||||
|
||||
jvNode["type"] = uFlags;
|
||||
|
||||
if (isSetBit(uFlags, STPathElement::typeAccount) || !!uAccountID)
|
||||
jvFlags.append(!!isSetBit(uFlags, STPathElement::typeAccount) == !!uAccountID ? "account" : "-account");
|
||||
|
||||
if (isSetBit(uFlags, STPathElement::typeCurrency) || !!uCurrencyID)
|
||||
jvFlags.append(!!isSetBit(uFlags, STPathElement::typeCurrency) == !!uCurrencyID ? "currency" : "-currency");
|
||||
|
||||
if (isSetBit(uFlags, STPathElement::typeIssuer) || !!uIssuerID)
|
||||
jvFlags.append(!!isSetBit(uFlags, STPathElement::typeIssuer) == !!uIssuerID ? "issuer" : "-issuer");
|
||||
|
||||
jvNode["flags"] = jvFlags;
|
||||
|
||||
if (!!uAccountID)
|
||||
jvNode["account"] = RippleAddress::createHumanAccountID(uAccountID);
|
||||
|
||||
if (!!uCurrencyID)
|
||||
jvNode["currency"] = STAmount::createHumanCurrency(uCurrencyID);
|
||||
|
||||
if (!!uIssuerID)
|
||||
jvNode["issuer"] = RippleAddress::createHumanAccountID(uIssuerID);
|
||||
|
||||
if (saRevRedeem)
|
||||
jvNode["rev_redeem"] = saRevRedeem.getFullText();
|
||||
|
||||
if (saRevIssue)
|
||||
jvNode["rev_issue"] = saRevIssue.getFullText();
|
||||
|
||||
if (saRevDeliver)
|
||||
jvNode["rev_deliver"] = saRevDeliver.getFullText();
|
||||
|
||||
if (saFwdRedeem)
|
||||
jvNode["fwd_redeem"] = saFwdRedeem.getFullText();
|
||||
|
||||
if (saFwdIssue)
|
||||
jvNode["fwd_issue"] = saFwdIssue.getFullText();
|
||||
|
||||
if (saFwdDeliver)
|
||||
jvNode["fwd_deliver"] = saFwdDeliver.getFullText();
|
||||
|
||||
return jvNode;
|
||||
}
|
||||
|
||||
//
|
||||
// PathState implementation
|
||||
//
|
||||
@@ -160,6 +209,12 @@ TER PathState::pushNode(
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
}
|
||||
else if (!bAccount && !bCurrency && !bIssuer)
|
||||
{
|
||||
cLog(lsDEBUG) << "pushNode: offer must specify at least currency or issuer.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
}
|
||||
else if (bAccount)
|
||||
{
|
||||
// Account link
|
||||
@@ -267,7 +322,9 @@ TER PathState::pushNode(
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
vpnNodes.push_back(pnCur);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -305,6 +362,7 @@ TER PathState::pushNode(
|
||||
vpnNodes.push_back(pnCur);
|
||||
}
|
||||
}
|
||||
|
||||
cLog(lsTRACE) << boost::str(boost::format("pushNode< : %s") % transToken(terResult));
|
||||
|
||||
return terResult;
|
||||
@@ -329,7 +387,7 @@ void PathState::setExpanded(
|
||||
const uint160 uOutIssuerID = saOutReq.getIssuer();
|
||||
const uint160 uSenderIssuerID = !!uMaxCurrencyID ? uSenderID : ACCOUNT_XRP; // Sender is always issuer for non-XRP.
|
||||
|
||||
// cLog(lsDEBUG) << boost::str(boost::format("setExpanded>"));
|
||||
cLog(lsDEBUG) << boost::str(boost::format("setExpanded> %s") % spSourcePath.getJson(0));
|
||||
|
||||
lesEntries = lesSource.duplicate();
|
||||
|
||||
@@ -726,43 +784,7 @@ Json::Value PathState::getJson() const
|
||||
|
||||
BOOST_FOREACH(const PaymentNode& pnNode, vpnNodes)
|
||||
{
|
||||
Json::Value jvNode(Json::objectValue);
|
||||
|
||||
Json::Value jvFlags(Json::arrayValue);
|
||||
|
||||
if (pnNode.uFlags & STPathElement::typeAccount)
|
||||
jvFlags.append("account");
|
||||
|
||||
jvNode["flags"] = jvFlags;
|
||||
|
||||
if (pnNode.uFlags & STPathElement::typeAccount)
|
||||
jvNode["account"] = RippleAddress::createHumanAccountID(pnNode.uAccountID);
|
||||
|
||||
if (!!pnNode.uCurrencyID)
|
||||
jvNode["currency"] = STAmount::createHumanCurrency(pnNode.uCurrencyID);
|
||||
|
||||
if (!!pnNode.uIssuerID)
|
||||
jvNode["issuer"] = RippleAddress::createHumanAccountID(pnNode.uIssuerID);
|
||||
|
||||
if (pnNode.saRevRedeem)
|
||||
jvNode["rev_redeem"] = pnNode.saRevRedeem.getFullText();
|
||||
|
||||
if (pnNode.saRevIssue)
|
||||
jvNode["rev_issue"] = pnNode.saRevIssue.getFullText();
|
||||
|
||||
if (pnNode.saRevDeliver)
|
||||
jvNode["rev_deliver"] = pnNode.saRevDeliver.getFullText();
|
||||
|
||||
if (pnNode.saFwdRedeem)
|
||||
jvNode["fwd_redeem"] = pnNode.saFwdRedeem.getFullText();
|
||||
|
||||
if (pnNode.saFwdIssue)
|
||||
jvNode["fwd_issue"] = pnNode.saFwdIssue.getFullText();
|
||||
|
||||
if (pnNode.saFwdDeliver)
|
||||
jvNode["fwd_deliver"] = pnNode.saFwdDeliver.getFullText();
|
||||
|
||||
jvNodes.append(jvNode);
|
||||
jvNodes.append(pnNode.getJson());
|
||||
}
|
||||
|
||||
jvPathState["status"] = terStatus;
|
||||
|
||||
@@ -56,6 +56,8 @@ protected:
|
||||
|
||||
public:
|
||||
bool operator==(const PaymentNode& pnOther) const;
|
||||
|
||||
Json::Value getJson() const;
|
||||
};
|
||||
|
||||
// account id, currency id, issuer id :: node
|
||||
|
||||
@@ -582,7 +582,7 @@ bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta)
|
||||
stack.pop();
|
||||
|
||||
if (node->isLeaf() && (node->peekItem()->getTag() == tag))
|
||||
throw std::runtime_error("addGiveItem ends on leaf with same tag");
|
||||
return false;
|
||||
|
||||
uint256 prevHash;
|
||||
returnNode(node, true);
|
||||
|
||||
@@ -81,7 +81,7 @@ void SNTPClient::resolveComplete(const boost::system::error_code& error, boost::
|
||||
query.mReceivedReply = false;
|
||||
query.mLocalTimeSent = now;
|
||||
getRand(reinterpret_cast<unsigned char *>(&query.mQueryNonce), sizeof(query.mQueryNonce));
|
||||
reinterpret_cast<uint32*>(SNTPQueryData)[NTP_OFF_XMITTS_INT] = time(NULL) + NTP_UNIX_OFFSET;
|
||||
reinterpret_cast<uint32*>(SNTPQueryData)[NTP_OFF_XMITTS_INT] = static_cast<uint32>(time(NULL)) + NTP_UNIX_OFFSET;
|
||||
reinterpret_cast<uint32*>(SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.mQueryNonce;
|
||||
mSocket.async_send_to(boost::asio::buffer(SNTPQueryData, 48), *sel,
|
||||
boost::bind(&SNTPClient::sendComplete, this,
|
||||
@@ -148,7 +148,7 @@ void SNTPClient::processReply()
|
||||
return;
|
||||
}
|
||||
|
||||
time_t now = time(NULL);
|
||||
int64 now = static_cast<int>(time(NULL));
|
||||
timev -= now;
|
||||
timev -= NTP_UNIX_OFFSET;
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::setTa
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
mTargetSize = s;
|
||||
if (s > 0)
|
||||
mCache.rehash((s + (s >> 2)) / mCache.max_load_factor() + 1);
|
||||
mCache.rehash(static_cast<std::size_t>((s + (s >> 2)) / mCache.max_load_factor() + 1));
|
||||
Log(lsDEBUG, TaggedCachePartition) << mName << " target size set to " << s;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::sweep
|
||||
int target = mLastSweep - mTargetAge;
|
||||
int cacheRemovals = 0, mapRemovals = 0, cc = 0;
|
||||
|
||||
if ((mTargetSize != 0) && (mCache.size() > mTargetSize))
|
||||
if ((mTargetSize != 0) && (static_cast<int>(mCache.size()) > mTargetSize))
|
||||
{
|
||||
target = mLastSweep - (mTargetAge * mTargetSize / mCache.size());
|
||||
if (target > (mLastSweep - 2))
|
||||
|
||||
@@ -45,6 +45,13 @@ void TransactionAcquire::done()
|
||||
|
||||
void TransactionAcquire::onTimer(bool progress)
|
||||
{
|
||||
if (getTimeouts() > 10)
|
||||
{
|
||||
cLog(lsWARNING) << "Giving up on TX set " << getHash();
|
||||
mFailed = true;
|
||||
done();
|
||||
return;
|
||||
}
|
||||
if (!getPeerCount())
|
||||
{ // out of peers
|
||||
cLog(lsWARNING) << "Out of peers for TX set " << getHash();
|
||||
|
||||
@@ -7,5 +7,10 @@
|
||||
|
||||
bool TransactionEngine::checkInvariants(TER result, const SerializedTransaction& txn, TransactionEngineParams params)
|
||||
{
|
||||
|
||||
// 1) Make sure transaction changed account sequence number to correct value
|
||||
|
||||
// 2) Make sure transaction didn't create XRP
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -176,12 +176,20 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa
|
||||
if (isSetBit(params, tapOPEN_LEDGER))
|
||||
{
|
||||
if (!mLedger->addTransaction(txID, s))
|
||||
{
|
||||
cLog(lsFATAL) << "Tried to add transaction to open ledger that already had it";
|
||||
assert(false);
|
||||
throw std::runtime_error("Duplicate transaction applied");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!mLedger->addTransaction(txID, s, m))
|
||||
assert(false);
|
||||
{
|
||||
cLog(lsFATAL) << "Tried to add transaction to ledger that already had it";
|
||||
assert(false);
|
||||
throw std::runtime_error("Duplicate transaction applied to closed ledger");
|
||||
}
|
||||
|
||||
// Charge whatever fee they specified.
|
||||
STAmount saPaid = txn.getTransactionFee();
|
||||
|
||||
@@ -142,7 +142,8 @@ TER Transactor::checkSeq()
|
||||
cLog(lsWARNING) << "applyTransaction: past sequence number";
|
||||
|
||||
return tefPAST_SEQ;
|
||||
}else
|
||||
}
|
||||
else
|
||||
{
|
||||
mTxnAccount->setFieldU32(sfSequence, t_seq + 1);
|
||||
}
|
||||
|
||||
@@ -252,22 +252,7 @@ TER TrustSetTransactor::doApply()
|
||||
{
|
||||
// Can delete.
|
||||
|
||||
bool bLowNode = sleRippleState->isFieldPresent(sfLowNode); // Detect legacy dirs.
|
||||
bool bHighNode = sleRippleState->isFieldPresent(sfHighNode);
|
||||
uint64 uLowNode = sleRippleState->getFieldU64(sfLowNode);
|
||||
uint64 uHighNode = sleRippleState->getFieldU64(sfHighNode);
|
||||
|
||||
cLog(lsTRACE) << "doTrustSet: Deleting ripple line: low";
|
||||
terResult = mEngine->getNodes().dirDelete(false, uLowNode, Ledger::getOwnerDirIndex(uLowAccountID), sleRippleState->getIndex(), false, !bLowNode);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
cLog(lsTRACE) << "doTrustSet: Deleting ripple line: high";
|
||||
terResult = mEngine->getNodes().dirDelete(false, uHighNode, Ledger::getOwnerDirIndex(uHighAccountID), sleRippleState->getIndex(), false, !bHighNode);
|
||||
}
|
||||
|
||||
cLog(lsINFO) << "doTrustSet: Deleting ripple line: state";
|
||||
mEngine->entryDelete(sleRippleState);
|
||||
terResult = mEngine->getNodes().trustDelete(sleRippleState, uLowAccountID, uHighAccountID);
|
||||
}
|
||||
else if (bReserveIncrease
|
||||
&& mPriorBalance.getNValue() < uReserveCreate) // Reserve is not scaled by load.
|
||||
|
||||
108
src/cpp/ripple/UpdateTables.cpp
Normal file
108
src/cpp/ripple/UpdateTables.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
#include "Application.h"
|
||||
#include "Log.h"
|
||||
|
||||
static std::vector<std::string> getSchema(DatabaseCon* dbc, const std::string& dbName)
|
||||
{
|
||||
std::vector<std::string> schema;
|
||||
|
||||
std::string sql = "SELECT sql FROM sqlite_master WHERE tbl_name='";
|
||||
sql += dbName;
|
||||
sql += "';";
|
||||
|
||||
SQL_FOREACH(dbc->getDB(), sql)
|
||||
{
|
||||
dbc->getDB()->getStr("sql", sql);
|
||||
schema.push_back(sql);
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
static bool schemaHas(DatabaseCon* dbc, const std::string& dbName, int line, const std::string& content)
|
||||
{
|
||||
std::vector<std::string> schema = getSchema(dbc, dbName);
|
||||
if (static_cast<int>(schema.size()) <= line)
|
||||
{
|
||||
Log(lsFATAL) << "Schema for " << dbName << " has too few lines";
|
||||
throw std::runtime_error("bad schema");
|
||||
}
|
||||
return schema[line].find(content) != std::string::npos;
|
||||
}
|
||||
|
||||
static void addTxnSeqField()
|
||||
{
|
||||
if (schemaHas(theApp->getTxnDB(), "AccountTransactions", 0, "TxnSeq"))
|
||||
return;
|
||||
Log(lsWARNING) << "Transaction sequence field is missing";
|
||||
|
||||
Database* db = theApp->getTxnDB()->getDB();
|
||||
|
||||
std::vector< std::pair<uint256, int> > txIDs;
|
||||
txIDs.reserve(300000);
|
||||
|
||||
Log(lsINFO) << "Parsing transactions";
|
||||
int i = 0;
|
||||
uint256 transID;
|
||||
SQL_FOREACH(db, "SELECT TransID,TxnMeta FROM Transactions;")
|
||||
{
|
||||
std::vector<unsigned char> rawMeta;
|
||||
int metaSize = 2048;
|
||||
rawMeta.resize(metaSize);
|
||||
metaSize = db->getBinary("TxnMeta", &*rawMeta.begin(), rawMeta.size());
|
||||
if (metaSize > static_cast<int>(rawMeta.size()))
|
||||
{
|
||||
rawMeta.resize(metaSize);
|
||||
db->getBinary("TxnMeta", &*rawMeta.begin(), rawMeta.size());
|
||||
}
|
||||
else rawMeta.resize(metaSize);
|
||||
|
||||
std::string tid;
|
||||
db->getStr("TransID", tid);
|
||||
transID.SetHex(tid, true);
|
||||
|
||||
if (rawMeta.size() == 0)
|
||||
{
|
||||
txIDs.push_back(std::make_pair(transID, -1));
|
||||
Log(lsINFO) << "No metadata for " << transID;
|
||||
}
|
||||
else
|
||||
{
|
||||
TransactionMetaSet m(transID, 0, rawMeta);
|
||||
txIDs.push_back(std::make_pair(transID, m.getIndex()));
|
||||
}
|
||||
|
||||
if ((++i % 1000) == 0)
|
||||
Log(lsINFO) << i << " transactions read";
|
||||
}
|
||||
|
||||
Log(lsINFO) << "All " << i << " transactions read";
|
||||
|
||||
db->executeSQL("BEGIN TRANSACTION;");
|
||||
|
||||
Log(lsINFO) << "Dropping old index";
|
||||
db->executeSQL("DROP INDEX AcctTxIndex;");
|
||||
|
||||
Log(lsINFO) << "Altering table";
|
||||
db->executeSQL("ALTER TABLE AccountTransactions ADD COLUMN TxnSeq INTEGER;");
|
||||
|
||||
typedef std::pair<uint256, int> u256_int_pair_t;
|
||||
boost::format fmt("UPDATE AccountTransactions SET TxnSeq = %d WHERE TransID = '%s';");
|
||||
i = 0;
|
||||
BOOST_FOREACH(u256_int_pair_t& t, txIDs)
|
||||
{
|
||||
db->executeSQL(boost::str(fmt % t.second % t.first.GetHex()));
|
||||
if ((++i % 1000) == 0)
|
||||
Log(lsINFO) << i << " transactions updated";
|
||||
}
|
||||
|
||||
db->executeSQL("CREATE INDEX AcctTxIndex ON AccountTransactions(Account, LedgerSeq, TxnSeq, TransID);");
|
||||
db->executeSQL("END TRANSACTION;");
|
||||
}
|
||||
|
||||
void Application::updateTables()
|
||||
{ // perform any needed table updates
|
||||
assert(schemaHas(theApp->getTxnDB(), "AccountTransactions", 0, "TransID"));
|
||||
assert(!schemaHas(theApp->getTxnDB(), "AccountTransactions", 0, "foobar"));
|
||||
addTxnSeqField();
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
// Copyright (c) 2011 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||
#ifndef NEWCOIN_UINT256_H
|
||||
#define NEWCOIN_UINT256_H
|
||||
#ifndef RIPPLE_UINT256_H
|
||||
#define RIPPLE_UINT256_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
@@ -219,6 +219,41 @@ public:
|
||||
return strHex(begin(), size());
|
||||
}
|
||||
|
||||
void SetHexExact(const char* psz)
|
||||
{ // must be precisely the correct number of hex digits
|
||||
static signed char phexdigit[256] = {
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
|
||||
|
||||
-1,0xa,0xb,0xc, 0xd,0xe,0xf,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,0xa,0xb,0xc, 0xd,0xe,0xf,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
};
|
||||
|
||||
char* pOut = reinterpret_cast<char*>(pn);
|
||||
for (int i = 0; i < sizeof(pn); ++i)
|
||||
{
|
||||
*pOut = phexdigit[*psz++] << 4;
|
||||
*pOut++ |= phexdigit[*psz++];
|
||||
}
|
||||
|
||||
assert(*psz == 0);
|
||||
assert(pOut == reinterpret_cast<char*>(end()));
|
||||
}
|
||||
|
||||
// Allow leading whitespace.
|
||||
// Allow leading "0x".
|
||||
// To be valid must be '\0' terminated.
|
||||
@@ -291,6 +326,11 @@ public:
|
||||
return SetHex(str.c_str(), bStrict);
|
||||
}
|
||||
|
||||
void SetHexExact(const std::string& str)
|
||||
{
|
||||
SetHexExact(str.c_str());
|
||||
}
|
||||
|
||||
std::string ToString() const
|
||||
{
|
||||
return GetHex();
|
||||
|
||||
@@ -4,11 +4,17 @@
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define _WINSOCK_
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -181,6 +187,38 @@ std::string strCopy(const std::vector<unsigned char>& vucSrc)
|
||||
|
||||
}
|
||||
|
||||
extern std::string urlEncode(const std::string& strSrc)
|
||||
{
|
||||
std::string strDst;
|
||||
int iOutput = 0;
|
||||
int iSize = strSrc.length();
|
||||
|
||||
strDst.resize(iSize*3);
|
||||
|
||||
for (int iInput = 0; iInput < iSize; iInput++) {
|
||||
unsigned char c = strSrc[iInput];
|
||||
|
||||
if (c == ' ')
|
||||
{
|
||||
strDst[iOutput++] = '+';
|
||||
}
|
||||
else if (isalnum(c))
|
||||
{
|
||||
strDst[iOutput++] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
strDst[iOutput++] = '%';
|
||||
strDst[iOutput++] = charHex(c >> 4);
|
||||
strDst[iOutput++] = charHex(c & 15);
|
||||
}
|
||||
}
|
||||
|
||||
strDst.resize(iOutput);
|
||||
|
||||
return strDst;
|
||||
}
|
||||
|
||||
//
|
||||
// DH support
|
||||
//
|
||||
@@ -307,8 +345,6 @@ int strIPtoInt(std::string& ipStr)
|
||||
}
|
||||
*/
|
||||
#ifdef WIN32
|
||||
#define _WINSOCK_
|
||||
#include <winsock2.h>
|
||||
|
||||
//#include "Winsock2.h"
|
||||
//#include <windows.h>
|
||||
|
||||
@@ -103,6 +103,8 @@ int iToSeconds(boost::posix_time::ptime ptWhen);
|
||||
boost::posix_time::ptime ptFromSeconds(int iSeconds);
|
||||
uint64_t utFromSeconds(int iSeconds);
|
||||
|
||||
extern std::string urlEncode(const std::string& strSrc);
|
||||
|
||||
/*
|
||||
void intIPtoStr(int ip,std::string& retStr);
|
||||
int strIPtoInt(std::string& ipStr);
|
||||
@@ -214,8 +216,8 @@ DH* DH_der_load(const std::string& strDer);
|
||||
std::string DH_der_gen(int iKeyLength);
|
||||
|
||||
void getRand(unsigned char *buf, int num);
|
||||
inline static void getRand(char *buf, int num) { return getRand(reinterpret_cast<unsigned char *>(buf), num); }
|
||||
inline static void getRand(void *buf, int num) { return getRand(reinterpret_cast<unsigned char *>(buf), num); }
|
||||
inline static void getRand(char *buf, int num) { return getRand(reinterpret_cast<unsigned char *>(buf), num); }
|
||||
inline static void getRand(void *buf, int num) { return getRand(reinterpret_cast<unsigned char *>(buf), num); }
|
||||
|
||||
inline std::string strGetEnv(const std::string& strKey)
|
||||
{
|
||||
|
||||
@@ -821,20 +821,37 @@ Remote.prototype.request_account_offers = function (accountID, account_index, cu
|
||||
.ledger_choose(current);
|
||||
};
|
||||
|
||||
Remote.prototype.request_account_tx = function (accountID, ledger_min, ledger_max) {
|
||||
|
||||
/*
|
||||
account: account,
|
||||
ledger_index_min: ledger_index, // optional, defaults to -1 if ledger_index_max is specified.
|
||||
ledger_index_max: ledger_index, // optional, defaults to -1 if ledger_index_min is specified.
|
||||
binary: boolean, // optional, defaults to false
|
||||
count: boolean, // optional, defaults to false
|
||||
descending: boolean, // optional, defaults to false
|
||||
offset: integer, // optional, defaults to 0
|
||||
limit: integer // optional
|
||||
*/
|
||||
|
||||
Remote.prototype.request_account_tx = function (obj) {
|
||||
// XXX Does this require the server to be trusted?
|
||||
//utils.assert(this.trusted);
|
||||
|
||||
var request = new Request(this, 'account_tx');
|
||||
|
||||
request.message.account = accountID;
|
||||
request.message.account = obj.account;
|
||||
|
||||
if (ledger_min === ledger_max) {
|
||||
request.message.ledger = ledger_min;
|
||||
if (false && ledger_min === ledger_max) {
|
||||
//request.message.ledger = ledger_min;
|
||||
}
|
||||
else {
|
||||
request.message.ledger_min = ledger_min;
|
||||
request.message.ledger_max = ledger_max;
|
||||
if (obj.ledger_index_min) {request.message.ledger_index_min = obj.ledger_index_min;}
|
||||
if (obj.ledger_index_max) {request.message.ledger_index_max = obj.ledger_index_max;}
|
||||
if (obj.binary) {request.message.binary = obj.binary;}
|
||||
if (obj.count) {request.message.count = obj.count;}
|
||||
if (obj.descending) {request.message.descending = obj.descending;}
|
||||
if (obj.offset) {request.message.offset = obj.offset;}
|
||||
if (obj.limit) {request.message.limit = obj.limit;}
|
||||
}
|
||||
|
||||
return request;
|
||||
|
||||
190
test/account_tx-test.js
Normal file
190
test/account_tx-test.js
Normal file
@@ -0,0 +1,190 @@
|
||||
var async = require("async");
|
||||
var buster = require("buster");
|
||||
|
||||
var Amount = require("../src/js/amount").Amount;
|
||||
var Remote = require("../src/js/remote").Remote;
|
||||
var Transaction = require("../src/js/transaction").Transaction;
|
||||
var Server = require("./server").Server;
|
||||
|
||||
var testutils = require("./testutils");
|
||||
|
||||
require('../src/js/config').load(require('./config'));
|
||||
|
||||
buster.testRunner.timeout = 250000; //This is a very long test!
|
||||
|
||||
|
||||
// Hard-coded limits we'll be testing:
|
||||
var BINARY_LIMIT = 500;
|
||||
var NONBINARY_LIMIT = 200;
|
||||
|
||||
var ACCOUNT = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
||||
var FIRST_BATCH = 199; // Within both limits
|
||||
var OFFSET = 180;
|
||||
var LIMIT = 170;
|
||||
var SECOND_BATCH = 10; // Between NONBINARY_LIMIT and BINARY_LIMIT
|
||||
var THIRD_BATCH = 295; // Exceeds both limits
|
||||
|
||||
buster.testCase("Account_tx tests", {
|
||||
'setUp' : testutils.build_setup(),
|
||||
'tearDown' : testutils.build_teardown(),
|
||||
|
||||
"make a lot of transactions and query using account_tx" :
|
||||
function (done) {
|
||||
var self = this;
|
||||
var final_create;
|
||||
|
||||
var transactionCounter = 0;
|
||||
|
||||
var createOfferFunction = function (callback) {
|
||||
self.remote.transaction()
|
||||
.offer_create("root", "500", "100/USD/root")
|
||||
.on('proposed', function (m) {
|
||||
transactionCounter++;
|
||||
console.log('Submitted transaction', transactionCounter);
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.on('final', function (m) {
|
||||
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||
buster.assert(final_create);
|
||||
})
|
||||
.submit();
|
||||
};
|
||||
|
||||
function lotsOfTransactions(number, whenDone) {
|
||||
var bunchOfOffers = [];
|
||||
for (var i=0; i<number; i++) {
|
||||
bunchOfOffers.push(createOfferFunction);
|
||||
}
|
||||
async.parallel(bunchOfOffers, function (error) {
|
||||
buster.refute(error);
|
||||
self.remote
|
||||
.once('ledger_closed', function (message) {
|
||||
final_create = message;
|
||||
})
|
||||
.ledger_accept();
|
||||
whenDone();
|
||||
});
|
||||
}
|
||||
|
||||
function firstBatch() {
|
||||
lotsOfTransactions(FIRST_BATCH,
|
||||
function(){runTests(self, FIRST_BATCH, 0, 0,
|
||||
function(){runTests(self, FIRST_BATCH, OFFSET, 0,
|
||||
function(){runTests(self, FIRST_BATCH, 0, LIMIT, secondBatch)})}
|
||||
)});
|
||||
}
|
||||
|
||||
function secondBatch() {
|
||||
lotsOfTransactions(SECOND_BATCH,
|
||||
function(){runTests(self, FIRST_BATCH+SECOND_BATCH, 0, 0,
|
||||
function(){runTests(self, FIRST_BATCH+SECOND_BATCH, OFFSET, 0, thirdBatch)}
|
||||
)});
|
||||
}
|
||||
|
||||
function thirdBatch() {
|
||||
lotsOfTransactions(THIRD_BATCH,
|
||||
function(){runTests(self, FIRST_BATCH+SECOND_BATCH+THIRD_BATCH, 0, 0,
|
||||
function(){runTests(self, FIRST_BATCH+SECOND_BATCH+THIRD_BATCH, OFFSET, 0, done)}
|
||||
)});
|
||||
}
|
||||
|
||||
firstBatch();
|
||||
|
||||
|
||||
function standardErrorHandler(callback) {
|
||||
return function(r) {
|
||||
console.log("ERROR!");
|
||||
console.log(r);
|
||||
callback(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function runTests(self, actualNumberOfTransactions, offset, limit, finalCallback) {
|
||||
console.log("Testing batch with offset and limit:", offset, limit);
|
||||
async.series([
|
||||
function(callback) {
|
||||
console.log('nonbinary');
|
||||
self.remote.request_account_tx({
|
||||
account:ACCOUNT,
|
||||
ledger_index_min:0,
|
||||
ledger_index_max:5,
|
||||
offset:offset,
|
||||
limit:limit
|
||||
}).on('success', function (r) {
|
||||
buster.assert(r.transactions, "No transactions returned.");
|
||||
var targetLength = Math.min(NONBINARY_LIMIT, limit ? Math.min(limit,actualNumberOfTransactions-offset) : actualNumberOfTransactions-offset);
|
||||
buster.assert(r.transactions.length == targetLength, "Got "+r.transactions.length+" transactions; expected "+targetLength );
|
||||
|
||||
//Check for proper ordering.
|
||||
for (var i=0; i<r.transactions.length-1; i++) {
|
||||
var t1 = r.transactions[i].tx;
|
||||
var t2 = r.transactions[i+1].tx;
|
||||
buster.assert(t1.inLedger<t2.inLedger || (t1.inLedger==t2.inLedger && t1.hash < t2.hash ),
|
||||
"Transactions were not ordered correctly: "+t1.inLedger+"#"+t1.hash+" should not have come before "+t2.inLedger+"#"+t2.hash);
|
||||
}
|
||||
callback(false);
|
||||
})
|
||||
.on('error', standardErrorHandler(callback))
|
||||
.request();
|
||||
},
|
||||
|
||||
function(callback) {
|
||||
console.log('binary');
|
||||
self.remote.request_account_tx({
|
||||
account:ACCOUNT,
|
||||
ledger_index_min:0,
|
||||
ledger_index_max:5,
|
||||
binary:true,
|
||||
offset:offset,
|
||||
limit:limit
|
||||
}).on('success', function (r) {
|
||||
buster.assert(r.transactions, "No transactions returned.");
|
||||
var targetLength = Math.min(BINARY_LIMIT, limit ? Math.min(limit,actualNumberOfTransactions-offset) : actualNumberOfTransactions-offset);
|
||||
buster.assert(r.transactions.length == targetLength, "Got "+r.transactions.length+" transactions; expected "+targetLength );
|
||||
callback(false);
|
||||
})
|
||||
.on('error', standardErrorHandler(callback))
|
||||
.request();
|
||||
},
|
||||
|
||||
function(callback) {
|
||||
console.log('nonbinary+offset');
|
||||
self.remote.request_account_tx({
|
||||
account:ACCOUNT,
|
||||
ledger_index_min:0,
|
||||
ledger_index_max:5,
|
||||
descending:true,
|
||||
offset:offset,
|
||||
limit:limit
|
||||
}).on('success', function (r) {
|
||||
buster.assert(r.transactions, "No transactions returned.");
|
||||
var targetLength = Math.min(NONBINARY_LIMIT, limit ? Math.min(limit,actualNumberOfTransactions-offset) : actualNumberOfTransactions-offset );
|
||||
buster.assert(r.transactions.length == targetLength, "Got "+r.transactions.length+" transactions; expected "+targetLength );
|
||||
//Check for proper ordering.
|
||||
for (var i=0; i<r.transactions.length-1; i++) {
|
||||
var t1 = r.transactions[i].tx;
|
||||
var t2 = r.transactions[i+1].tx;
|
||||
buster.assert(t1.inLedger>t2.inLedger || (t1.inLedger==t2.inLedger && t1.hash > t2.hash ),
|
||||
"Transactions were not ordered correctly: "+t1.inLedger+"#"+t1.hash+" should not have come before "+t2.inLedger+"#"+t2.hash);
|
||||
}
|
||||
callback(false);
|
||||
})
|
||||
.on('error', standardErrorHandler(callback))
|
||||
.request();
|
||||
},
|
||||
|
||||
|
||||
], function (error) {
|
||||
buster.refute(error);
|
||||
finalCallback();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// TODO:
|
||||
// Test the "count" feature.
|
||||
@@ -1274,4 +1274,132 @@ buster.testCase("Quality paths", {
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
buster.testCase("Trust auto clear", {
|
||||
'setUp' : testutils.build_setup(),
|
||||
// 'setUp' : testutils.build_setup({ verbose: true }),
|
||||
// 'setUp' : testutils.build_setup({ verbose: true, no_server: true }),
|
||||
'tearDown' : testutils.build_teardown(),
|
||||
|
||||
"trust normal clear" :
|
||||
function (done) {
|
||||
var self = this;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set credit limits.";
|
||||
|
||||
// Mutual trust.
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "1000/USD/bob",
|
||||
"bob" : "1000/USD/alice",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify credit limits.";
|
||||
|
||||
testutils.verify_limit(self.remote, "bob", "1000/USD/alice", callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Clear credit limits.";
|
||||
|
||||
// Mutual trust.
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "0/USD/bob",
|
||||
"bob" : "0/USD/alice",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify credit limits.";
|
||||
|
||||
testutils.verify_limit(self.remote, "bob", "0/USD/alice", function (m) {
|
||||
var success = m && 'remoteError' === m.error && 'entryNotFound' === m.remote.error;
|
||||
|
||||
callback(!success);
|
||||
});
|
||||
},
|
||||
// YYY Could verify owner counts are zero.
|
||||
], function (error) {
|
||||
buster.refute(error, self.what);
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
"trust auto clear" :
|
||||
function (done) {
|
||||
var self = this;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set credit limits.";
|
||||
|
||||
// Mutual trust.
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "1000/USD/bob",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Distribute funds.";
|
||||
|
||||
testutils.payments(self.remote,
|
||||
{
|
||||
"bob" : [ "50/USD/alice" ],
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Clear credit limits.";
|
||||
|
||||
// Mutual trust.
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "0/USD/bob",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify credit limits.";
|
||||
|
||||
testutils.verify_limit(self.remote, "alice", "0/USD/bob", callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Return funds.";
|
||||
|
||||
testutils.payments(self.remote,
|
||||
{
|
||||
"alice" : [ "50/USD/bob" ],
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify credit limit gone.";
|
||||
|
||||
testutils.verify_limit(self.remote, "bob", "0/USD/alice", function (m) {
|
||||
var success = m && 'remoteError' === m.error && 'entryNotFound' === m.remote.error;
|
||||
|
||||
callback(!success);
|
||||
});
|
||||
},
|
||||
], function (error) {
|
||||
buster.refute(error, self.what);
|
||||
done();
|
||||
});
|
||||
},
|
||||
});
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -226,7 +226,7 @@ var verify_limit = function (remote, src, amount, callback) {
|
||||
|
||||
callback();
|
||||
})
|
||||
.on('error', function (m) {
|
||||
.once('error', function (m) {
|
||||
// console.log("error: %s", JSON.stringify(m));
|
||||
|
||||
callback(m);
|
||||
|
||||
Reference in New Issue
Block a user