diff --git a/js/amount.js b/js/amount.js index d2da47a05..a9e6e839d 100644 --- a/js/amount.js +++ b/js/amount.js @@ -436,7 +436,7 @@ Amount.prototype.parse_native = function(j) { var m; if ('string' === typeof j) - m = j.match(/^(-?)(\d+)(\.\d{1,6})?$/); + m = j.match(/^(-?)(\d+)(\.\d{0,6})?$/); if (m) { if (undefined === m[3]) { @@ -445,7 +445,7 @@ Amount.prototype.parse_native = function(j) { this.value = new BigInteger(m[2]); } else { - // Decimal notation + // Float notation var int_part = (new BigInteger(m[2])).multiply(exports.consts.bi_xns_unit); var fraction_part = (new BigInteger(m[3])).multiply(new BigInteger(String(Math.pow(10, 1+exports.consts.xns_precision-m[3].length)))); diff --git a/js/remote.js b/js/remote.js index 9fa97054a..8313088f0 100644 --- a/js/remote.js +++ b/js/remote.js @@ -216,10 +216,10 @@ Remote.flags = { // XXX This needs to be determined from the network. Remote.fees = { - 'default' : Amount.from_json("100"), - 'account_create' : Amount.from_json("1000"), + 'default' : Amount.from_json("10"), + 'account_create' : Amount.from_json("1000000000"), 'nickname_create' : Amount.from_json("1000"), - 'offer' : Amount.from_json("100"), + 'offer' : Amount.from_json("10"), }; // Set the emited state: 'online' or 'offline' diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 0723eb907..a2dd3bbb6 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -107,6 +107,7 @@ + @@ -275,8 +276,8 @@ Document - d:/code/protoc-2.4.1-win32/protoc -I=..\newcoin --cpp_out=D:\code\newcoin\obj\ ..\newcoin/src/ripple.proto - D:\code\newcoin\obj\src\ripple.pb.h + /code/protoc-2.4.1-win32/protoc -I=..\newcoin --cpp_out=\code\newcoin\obj\ ..\newcoin/src/ripple.proto + \code\newcoin\obj\src\ripple.pb.h diff --git a/src/Amount.cpp b/src/Amount.cpp index bfcd5e27a..cacfe72a1 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -780,7 +780,6 @@ STAmount operator+(const STAmount& v1, const STAmount& v2) if (v1.mIsNative) return STAmount(v1.getFName(), v1.getSNValue() + v2.getSNValue()); - int ov1 = v1.mOffset, ov2 = v2.mOffset; int64 vv1 = static_cast(v1.mValue), vv2 = static_cast(v2.mValue); if (v1.mIsNegative) vv1 = -vv1; diff --git a/src/Application.cpp b/src/Application.cpp index 975b0411f..7c459fc12 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -46,6 +46,7 @@ Application::Application() : { RAND_bytes(mNonce256.begin(), mNonce256.size()); RAND_bytes(reinterpret_cast(&mNonceST), sizeof(mNonceST)); + mJobQueue.setThreadCount(); } extern const char *RpcDBInit[], *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[], *NetNodeDBInit[]; @@ -54,6 +55,7 @@ extern int RpcDBCount, TxnDBCount, LedgerDBCount, WalletDBCount, HashNodeDBCount void Application::stop() { mIOService.stop(); + mJobQueue.shutdown(); mHashedObjectStore.bulkWrite(); mValidations.flush(); mAuxService.stop(); @@ -70,9 +72,10 @@ void Application::run() { assert(mTxnDB == NULL); if (!theConfig.DEBUG_LOGFILE.empty()) - { // Let DEBUG messages go to the file but only WARNING or higher to regular output + { // Let DEBUG messages go to the file but only WARNING or higher to regular output (unless verbose) Log::setLogFile(theConfig.DEBUG_LOGFILE); - LogPartition::setSeverity(lsDEBUG); + if (Log::getMinSeverity() > lsDEBUG) + LogPartition::setSeverity(lsDEBUG); } boost::thread auxThread(boost::bind(&boost::asio::io_service::run, &mAuxService)); diff --git a/src/Application.h b/src/Application.h index 9a37333fb..ebb078492 100644 --- a/src/Application.h +++ b/src/Application.h @@ -18,6 +18,7 @@ #include "Suppression.h" #include "SNTPClient.h" #include "../database/database.h" +#include "JobQueue.h" class RPCDoor; @@ -53,6 +54,7 @@ class Application SuppressionTable mSuppressions; HashedObjectStore mHashedObjectStore; SNTPClient mSNTPClient; + JobQueue mJobQueue; DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB; @@ -90,6 +92,7 @@ public: NodeCache& getTempNodeCache() { return mTempNodeCache; } HashedObjectStore& getHashedObjectStore() { return mHashedObjectStore; } ValidationCollection& getValidations() { return mValidations; } + JobQueue& getJobQueue() { return mJobQueue; } bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); } bool isNew(const uint160& s) { return mSuppressions.addSuppression(s); } bool running() { return mTxnDB != NULL; } diff --git a/src/Config.cpp b/src/Config.cpp index 91a3acbe8..2faed71d3 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -35,9 +35,9 @@ #define SECTION_VALIDATORS "validators" #define SECTION_VALIDATORS_SITE "validators_site" -// Fees are in XNB. -#define DEFAULT_FEE_DEFAULT 100 -#define DEFAULT_FEE_ACCOUNT_CREATE 1000 +// Fees are in XNS. +#define DEFAULT_FEE_DEFAULT 10 +#define DEFAULT_FEE_ACCOUNT_CREATE 1000*SYSTEM_CURRENCY_PARTS #define DEFAULT_FEE_NICKNAME_CREATE 1000 #define DEFAULT_FEE_OFFER DEFAULT_FEE_DEFAULT #define DEFAULT_FEE_OPERATION 1 diff --git a/src/ConnectionPool.cpp b/src/ConnectionPool.cpp index c9e93f3f7..a6e1a9924 100644 --- a/src/ConnectionPool.cpp +++ b/src/ConnectionPool.cpp @@ -330,6 +330,13 @@ Json::Value ConnectionPool::getPeersJson() return ret; } +int ConnectionPool::getPeerCount() +{ + boost::mutex::scoped_lock sl(mPeerLock); + + return mConnectedMap.size(); +} + std::vector ConnectionPool::getPeerVector() { std::vector ret; diff --git a/src/ConnectionPool.h b/src/ConnectionPool.h index 569a1e458..1b909f626 100644 --- a/src/ConnectionPool.h +++ b/src/ConnectionPool.h @@ -83,6 +83,7 @@ public: // As client failed connect and be accepted. void peerClosed(Peer::ref peer, const std::string& strIp, int iPort); + int getPeerCount(); Json::Value getPeersJson(); std::vector getPeerVector(); diff --git a/src/FieldNames.cpp b/src/FieldNames.cpp index 2f84ec7ab..67695731d 100644 --- a/src/FieldNames.cpp +++ b/src/FieldNames.cpp @@ -26,6 +26,31 @@ SField sfIndex(STI_HASH256, 258, "index"); #undef FIELD #undef TYPE +static int initFields() +{ + sfHighQualityIn.setMeta(SFM_CHANGE); sfHighQualityOut.setMeta(SFM_CHANGE); + sfLowQualityIn.setMeta(SFM_CHANGE); sfLowQualityOut.setMeta(SFM_CHANGE); + + sfLowLimit.setMeta(SFM_ALWAYS); sfHighLimit.setMeta(SFM_ALWAYS); + sfTakerPays.setMeta(SFM_ALWAYS); sfTakerGets.setMeta(SFM_ALWAYS); + sfQualityIn.setMeta(SFM_ALWAYS); sfQualityOut.setMeta(SFM_ALWAYS); + + sfBalance.setMeta(SFM_ALWAYS); + + sfPublicKey.setMeta(SFM_CHANGE); sfMessageKey.setMeta(SFM_CHANGE); + sfSigningPubKey.setMeta(SFM_CHANGE); sfAuthorizedKey.setMeta(SFM_CHANGE); + sfSigningAccounts.setMeta(SFM_CHANGE); + + sfWalletLocator.setMeta(SFM_CHANGE); + sfNickname.setMeta(SFM_CHANGE); + sfAmount.setMeta(SFM_ALWAYS); + sfDomain.setMeta(SFM_CHANGE); + sfOwner.setMeta(SFM_ALWAYS); + + return 0; +} +static const int f = initFields(); + SField::SField(SerializedTypeID tid, int fv) : fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv) { // call with the map mutex diff --git a/src/FieldNames.h b/src/FieldNames.h index 3a09f67a4..cdb11ad6b 100644 --- a/src/FieldNames.h +++ b/src/FieldNames.h @@ -33,6 +33,14 @@ enum SOE_Flags SOE_OPTIONAL = 1, // optional }; +enum SF_Meta +{ + SFM_NEVER = 0, + SFM_CHANGE = 1, + SFM_DELETE = 2, + SFM_ALWAYS = 3 +}; + class SField { public: @@ -51,16 +59,17 @@ public: const SerializedTypeID fieldType; // STI_* const int fieldValue; // Code number for protocol std::string fieldName; + SF_Meta fieldMeta; SField(int fc, SerializedTypeID tid, int fv, const char* fn) : - fieldCode(fc), fieldType(tid), fieldValue(fv), fieldName(fn) + fieldCode(fc), fieldType(tid), fieldValue(fv), fieldName(fn), fieldMeta(SFM_NEVER) { boost::mutex::scoped_lock sl(mapMutex); codeToField[fieldCode] = this; } SField(SerializedTypeID tid, int fv, const char *fn) : - fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv), fieldName(fn) + fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv), fieldName(fn), fieldMeta(SFM_NEVER) { boost::mutex::scoped_lock sl(mapMutex); codeToField[fieldCode] = this; @@ -84,6 +93,11 @@ public: bool isBinary() const { return fieldValue < 256; } bool isDiscardable() const { return fieldValue > 256; } + SF_Meta getMeta() const { return fieldMeta; } + bool shouldMetaDel() const { return (fieldMeta == SFM_DELETE) || (fieldMeta == SFM_ALWAYS); } + bool shouldMetaMod() const { return (fieldMeta == SFM_CHANGE) || (fieldMeta == SFM_ALWAYS); } + void setMeta(SF_Meta m) { fieldMeta = m; } + bool operator==(const SField& f) const { return fieldCode == f.fieldCode; } bool operator!=(const SField& f) const { return fieldCode != f.fieldCode; } diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index 34874e991..997be4c23 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -19,7 +19,7 @@ HashedObjectStore::HashedObjectStore(int cacheSize, int cacheAge) : bool HashedObjectStore::store(HashedObjectType type, uint32 index, const std::vector& data, const uint256& hash) -{ // return: false=already in cache, true = added to cache +{ // return: false = already in cache, true = added to cache assert(hash == Serializer::getSHA512Half(data)); if (!theApp->getHashNodeDB()) { @@ -59,15 +59,15 @@ void HashedObjectStore::waitWrite() void HashedObjectStore::bulkWrite() { - std::vector< boost::shared_ptr > set; while (1) { - set.clear(); + std::vector< boost::shared_ptr > set; set.reserve(128); { boost::unique_lock sl(mWriteMutex); mWriteSet.swap(set); + assert(mWriteSet.empty()); if (set.empty()) { mWritePending = false; diff --git a/src/JobQueue.cpp b/src/JobQueue.cpp new file mode 100644 index 000000000..cbb2bee83 --- /dev/null +++ b/src/JobQueue.cpp @@ -0,0 +1,181 @@ +#include "JobQueue.h" + +#include +#include +#include +#include + +#include "Log.h" + +SETUP_LOG(); + +const char* Job::toString(JobType t) +{ + switch(t) + { + case jtINVALID: return "invalid"; + case jtVALIDATION_ut: return "untrustedValidation"; + case jtTRANSACTION: return "transaction"; + case jtPROPOSAL_ut: return "untrustedProposal"; + case jtVALIDATION_t: return "trustedValidation"; + case jtPROPOSAL_t: return "trustedProposal"; + case jtADMIN: return "administration"; + case jtDEATH: return "jobOfDeath"; + default: assert(false); return "unknown"; + } +} + +bool Job::operator<(const Job& j) const +{ // These comparison operators make the jobs sort in priority order in the job set + if (mType < j.mType) + return true; + if (mType > j.mType) + return false; + return mJobIndex < j.mJobIndex; +} + +bool Job::operator<=(const Job& j) const +{ + if (mType < j.mType) + return true; + if (mType > j.mType) + return false; + return mJobIndex <= j.mJobIndex; +} + +bool Job::operator>(const Job& j) const +{ + if (mType < j.mType) + return false; + if (mType > j.mType) + return true; + return mJobIndex > j.mJobIndex; +} + +bool Job::operator>=(const Job& j) const +{ + if (mType < j.mType) + return false; + if (mType > j.mType) + return true; + return mJobIndex >= j.mJobIndex; +} + +void JobQueue::addJob(JobType type, const boost::function& jobFunc) +{ + assert(type != jtINVALID); + + boost::mutex::scoped_lock sl(mJobLock); + assert(mThreadCount != 0); // do not add jobs to a queue with no threads + + mJobSet.insert(Job(type, ++mLastJob, jobFunc)); + ++mJobCounts[type]; + mJobCond.notify_one(); +} + +int JobQueue::getJobCount(JobType t) +{ + boost::mutex::scoped_lock sl(mJobLock); + + std::map::iterator c = mJobCounts.find(t); + return (c == mJobCounts.end()) ? 0 : c->second; +} + +int JobQueue::getJobCountGE(JobType t) +{ // return the number of jobs at this priority level or greater + int ret = 0; + + boost::mutex::scoped_lock sl(mJobLock); + + typedef std::pair jt_int_pair; + BOOST_FOREACH(const jt_int_pair& it, mJobCounts) + if (it.first >= t) + ret += it.second; + return ret; +} + +std::vector< std::pair > JobQueue::getJobCounts() +{ // return all jobs at all priority levels + std::vector< std::pair > ret; + + boost::mutex::scoped_lock sl(mJobLock); + ret.reserve(mJobCounts.size()); + + typedef std::pair jt_int_pair; + BOOST_FOREACH(const jt_int_pair& it, mJobCounts) + ret.push_back(it); + + return ret; +} + +void JobQueue::shutdown() +{ // shut down the job queue without completing pending jobs + cLog(lsINFO) << "Job queue shutting down"; + boost::mutex::scoped_lock sl(mJobLock); + mShuttingDown = true; + mJobCond.notify_all(); + while (mThreadCount != 0) + mJobCond.wait(sl); +} + +void JobQueue::setThreadCount(int c) +{ // set the number of thread serving the job queue to precisely this number + if (c == 0) + { + c = boost::thread::hardware_concurrency(); + if (c < 2) + c = 2; + cLog(lsINFO) << "Auto-tuning to " << c << " validation/transaction/proposal threads"; + } + + boost::mutex::scoped_lock sl(mJobLock); + + while (mJobCounts[jtDEATH] != 0) + mJobCond.wait(sl); + + while (mThreadCount < c) + { + ++mThreadCount; + boost::thread t(boost::bind(&JobQueue::threadEntry, this)); + t.detach(); + } + while (mThreadCount > c) + { + if (mJobCounts[jtDEATH] != 0) + mJobCond.wait(sl); + else + { + mJobSet.insert(Job(jtDEATH, 0)); + ++mJobCounts[jtDEATH]; + } + } + mJobCond.notify_one(); // in case we sucked up someone else's signal +} + +void JobQueue::threadEntry() +{ // do jobs until asked to stop + boost::mutex::scoped_lock sl(mJobLock); + while (1) + { + while (mJobSet.empty() && !mShuttingDown) + mJobCond.wait(sl); + + if (mShuttingDown) + break; + + std::set::iterator it = mJobSet.begin(); + Job job(*it); + mJobSet.erase(it); + --mJobCounts[job.getType()]; + + if (job.getType() == jtDEATH) + break; + + sl.unlock(); + cLog(lsDEBUG) << "Doing " << Job::toString(job.getType()) << " job"; + job.doJob(); + sl.lock(); + } + --mThreadCount; + mJobCond.notify_all(); +} diff --git a/src/JobQueue.h b/src/JobQueue.h new file mode 100644 index 000000000..ddcfa1017 --- /dev/null +++ b/src/JobQueue.h @@ -0,0 +1,83 @@ +#ifndef JOB_QUEUE__H +#define JOB_QUEUE__H + +#include +#include +#include + +#include +#include +#include + +#include "types.h" + +// Note that this queue should only be used for CPU-bound jobs +// It is primarily intended for signature checking + +enum JobType +{ // must be in priority order, low to high + jtINVALID, + jtVALIDATION_ut, + jtTRANSACTION, + jtPROPOSAL_ut, + jtVALIDATION_t, + jtPROPOSAL_t, + jtADMIN, + jtDEATH, // job of death, used internally +}; + +class Job +{ +protected: + JobType mType; + uint64 mJobIndex; + boost::function mJob; + +public: + Job() : mType(jtINVALID), mJobIndex(0) { ; } + Job(JobType type, uint64 index) : mType(type), mJobIndex(index) { ; } + + Job(JobType type, uint64 index, const boost::function& job) + : mType(type), mJobIndex(index), mJob(job) { ; } + + JobType getType() const { return mType; } + void doJob(void) { mJob(*this); } + + bool operator<(const Job& j) const; + bool operator>(const Job& j) const; + bool operator<=(const Job& j) const; + bool operator>=(const Job& j) const; + + static const char* toString(JobType); +}; + +class JobQueue +{ +protected: + boost::mutex mJobLock; + boost::condition_variable mJobCond; + + uint64 mLastJob; + std::set mJobSet; + std::map mJobCounts; + int mThreadCount; + bool mShuttingDown; + + + void threadEntry(void); + +public: + + JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false) { ; } + + void addJob(JobType type, const boost::function& job); + + int getJobCount(JobType t); // Jobs at this priority + int getJobCountGE(JobType t); // All jobs at or greater than this priority + std::vector< std::pair > getJobCounts(); + + void shutdown(); + void setThreadCount(int c = 0); +}; + +#endif diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 4cdc152d1..1b6775232 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -339,7 +339,7 @@ uint256 Ledger::getHash() void Ledger::saveAcceptedLedger(bool fromConsensus) { // can be called in a different thread - cLog(lsTRACE) << "saveAcceptedLedger " << (fromConsensus ? "fromConsensus" : "fromAcquire") << getLedgerSeq(); + cLog(lsTRACE) << "saveAcceptedLedger " << (fromConsensus ? "fromConsensus " : "fromAcquire ") << getLedgerSeq(); static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers where LedgerSeq = %d;"); static boost::format deleteLedger("DELETE FROM Ledgers WHERE LedgerSeq = %d;"); static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransId = '%s';"); @@ -430,11 +430,16 @@ void Ledger::saveAcceptedLedger(bool fromConsensus) } if (!fromConsensus) + { + decPendingSaves(); return; + } + + theApp->getMasterLedger().setFullLedger(shared_from_this()); theApp->getOPs().pubLedger(shared_from_this()); - theApp->getMasterLedger().setFullLedger(shared_from_this()); + decPendingSaves(); } Ledger::pointer Ledger::getSQL(const std::string& sql) @@ -451,7 +456,10 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) ScopedLock sl(theApp->getLedgerDB()->getDBLock()); if (!db->executeSQL(sql) || !db->startIterRows()) - return Ledger::pointer(); + { + cLog(lsDEBUG) << "No ledger for query: " << sql; + return Ledger::pointer(); + } db->getStr("LedgerHash", hash); ledgerHash.SetHex(hash); @@ -471,8 +479,8 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) } Log(lsTRACE) << "Constructing ledger " << ledgerSeq << " from SQL"; - Ledger::pointer ret = Ledger::pointer(new Ledger(prevHash, transHash, accountHash, totCoins, - closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq)); + Ledger::pointer ret = boost::make_shared(prevHash, transHash, accountHash, totCoins, + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq); if (ret->getHash() != ledgerHash) { if (sLog(lsERROR)) @@ -877,6 +885,41 @@ uint256 Ledger::getAccountRootIndex(const uint160& uAccountID) return s.getSHA512Half(); } +uint256 Ledger::getLedgerHashIndex() +{ // get the index of the node that holds the last 256 ledgers + Serializer s(2); + s.add16(spaceHashes); + return s.getSHA512Half(); +} + +uint256 Ledger::getLedgerHashIndex(uint32 desiredLedgerIndex) +{ // get the index of the node that holds the set of 256 ledgers that includes this ledger's hash + // (or the first ledger after it if it's not a multiple of 256) + Serializer s(6); + s.add16(spaceHashes); + s.add32(desiredLedgerIndex >> 16); + return s.getSHA512Half(); +} + +int Ledger::getLedgerHashOffset(uint32 ledgerIndex) +{ // get the offset for this ledger's hash (or the first one after it) in the every-256-ledger table + return (ledgerIndex >> 8) % 256; +} + +int Ledger::getLedgerHashOffset(uint32 desiredLedgerIndex, uint32 currentLedgerIndex) +{ // get the offset for this ledger's hash in the every-ledger table, -1 if not in it + if (desiredLedgerIndex >= currentLedgerIndex) + return -1; + + if (currentLedgerIndex < 256) + return desiredLedgerIndex; + + if (desiredLedgerIndex < (currentLedgerIndex - 256)) + return -1; + + return currentLedgerIndex - desiredLedgerIndex - 1; +} + uint256 Ledger::getBookBase(const uint160& uTakerPaysCurrency, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrency, const uint160& uTakerGetsIssuerID) { @@ -1018,4 +1061,31 @@ bool Ledger::assertSane() return false; } +int Ledger::sPendingSaves = 0; +boost::recursive_mutex Ledger::sPendingSaveLock; + +int Ledger::getPendingSaves() +{ + boost::recursive_mutex::scoped_lock sl(sPendingSaveLock); + return sPendingSaves; +} + +void Ledger::pendSave(bool fromConsensus) +{ + if (!fromConsensus && !theApp->isNew(getHash())) + return; + + boost::thread thread(boost::bind(&Ledger::saveAcceptedLedger, shared_from_this(), fromConsensus)); + thread.detach(); + + boost::recursive_mutex::scoped_lock sl(sPendingSaveLock); + ++sPendingSaves; +} + +void Ledger::decPendingSaves() +{ + boost::recursive_mutex::scoped_lock sl(sPendingSaveLock); + --sPendingSaves; +} + // vim:ts=4 diff --git a/src/Ledger.h b/src/Ledger.h index 01ea5cd83..1074ec9fb 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -78,12 +78,19 @@ private: mutable boost::recursive_mutex mLock; + static int sPendingSaves; + static boost::recursive_mutex sPendingSaveLock; + Ledger(const Ledger&); // no implementation Ledger& operator=(const Ledger&); // no implementation protected: SLE::pointer getASNode(LedgerStateParms& parms, const uint256& nodeID, LedgerEntryType let); + static void incPendingSaves(); + static void decPendingSaves(); + void saveAcceptedLedger(bool fromConsensus); + public: Ledger(const RippleAddress& masterID, uint64 startAmount); // used for the starting bootstrap ledger @@ -101,6 +108,7 @@ public: static Ledger::pointer getSQL(const std::string& sqlStatement); static Ledger::pointer getLastFullLedger(); + static int getPendingSaves(); void updateHash(); void setClosed() { mClosed = true; } @@ -157,9 +165,9 @@ public: SLE::pointer getAccountRoot(const RippleAddress& naAccountID); // database functions - void saveAcceptedLedger(bool fromConsensus); static Ledger::pointer loadByIndex(uint32 ledgerIndex); static Ledger::pointer loadByHash(const uint256& ledgerHash); + void pendSave(bool fromConsensus); // next/prev function SLE::pointer getSLE(const uint256& uHash); @@ -170,6 +178,12 @@ public: uint256 getPrevLedgerIndex(const uint256& uHash); // last node begin + // Ledger hash table function + static uint256 getLedgerHashIndex(); + static uint256 getLedgerHashIndex(uint32 desiredLedgerIndex); + static int getLedgerHashOffset(uint32 desiredLedgerIndex); + static int getLedgerHashOffset(uint32 desiredLedgerIndex, uint32 currentLedgerIndex); + // index calculation functions static uint256 getAccountRootIndex(const uint160& uAccountID); diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp index f3bdaea9b..ca26c885c 100644 --- a/src/LedgerAcquire.cpp +++ b/src/LedgerAcquire.cpp @@ -12,7 +12,7 @@ SETUP_LOG(); -// #define LA_DEBUG +#define LA_DEBUG #define LEDGER_ACQUIRE_TIMEOUT 750 #define TRUST_NETWORK @@ -74,7 +74,7 @@ void PeerSet::invokeOnTimer() if (!mProgress) { ++mTimeouts; - cLog(lsWARNING) << "Timeout " << mTimeouts << " acquiring " << mHash; + cLog(lsWARNING) << "Timeout(" << mTimeouts << ") pc=" << mPeers.size() << " acquiring " << mHash; } else mProgress = false; @@ -144,7 +144,33 @@ void LedgerAcquire::onTimer() setFailed(); done(); } - else trigger(Peer::pointer(), true); + else + { + if (!getPeerCount()) + addPeers(); + trigger(Peer::pointer(), true); + } +} + +void LedgerAcquire::addPeers() +{ + std::vector peerList = theApp->getConnectionPool().getPeerVector(); + + bool found = false; + BOOST_FOREACH(Peer::ref peer, peerList) + { + if (peer->hasLedger(getHash())) + { + found = true; + peerHas(peer); + } + } + + if (!found) + { + BOOST_FOREACH(Peer::ref peer, peerList) + peerHas(peer); + } } boost::weak_ptr LedgerAcquire::pmDowncast() @@ -189,29 +215,35 @@ void LedgerAcquire::addOnComplete(boost::function void LedgerAcquire::trigger(Peer::ref peer, bool timer) { if (mAborted || mComplete || mFailed) + { + cLog(lsTRACE) << "Trigger on ledger:" << + (mAborted ? " aborted": "") << (mComplete ? " completed": "") << (mFailed ? " failed" : ""); return; -#ifdef LA_DEBUG - if (peer) cLog(lsTRACE) << "Trigger acquiring ledger " << mHash << " from " << peer->getIP(); - else cLog(lsTRACE) << "Trigger acquiring ledger " << mHash; - if (mComplete || mFailed) - cLog(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed; - else - cLog(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState; -#endif + } + + if (sLog(lsTRACE)) + { + if (peer) + cLog(lsTRACE) << "Trigger acquiring ledger " << mHash << " from " << peer->getIP(); + else + cLog(lsTRACE) << "Trigger acquiring ledger " << mHash; + if (mComplete || mFailed) + cLog(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed; + else + cLog(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState; + } + if (!mHaveBase) { ripple::TMGetLedger tmGL; tmGL.set_ledgerhash(mHash.begin(), mHash.size()); tmGL.set_itype(ripple::liBASE); - *(tmGL.add_nodeids()) = SHAMapNode().getRawString(); + cLog(lsTRACE) << "Sending base request to " << (peer ? "selected peer" : "all peers"); sendRequest(tmGL, peer); } if (mHaveBase && !mHaveTransactions) { -#ifdef LA_DEBUG - cLog(lsTRACE) << "need tx"; -#endif assert(mLedger); if (mLedger->peekTransactionMap()->getHash().isZero()) { // we need the root node @@ -220,6 +252,7 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) tmGL.set_ledgerseq(mLedger->getLedgerSeq()); tmGL.set_itype(ripple::liTX_NODE); *(tmGL.add_nodeids()) = SHAMapNode().getRawString(); + cLog(lsTRACE) << "Sending TX root request to " << (peer ? "selected peer" : "all peers"); sendRequest(tmGL, peer); } else @@ -246,6 +279,8 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) tmGL.set_itype(ripple::liTX_NODE); BOOST_FOREACH(SHAMapNode& it, nodeIDs) *(tmGL.add_nodeids()) = it.getRawString(); + cLog(lsTRACE) << "Sending TX node " << nodeIDs.size() + << "request to " << (peer ? "selected peer" : "all peers"); sendRequest(tmGL, peer); } } @@ -253,9 +288,6 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) if (mHaveBase && !mHaveState) { -#ifdef LA_DEBUG - cLog(lsTRACE) << "need as"; -#endif assert(mLedger); if (mLedger->peekAccountStateMap()->getHash().isZero()) { // we need the root node @@ -264,6 +296,7 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) tmGL.set_ledgerseq(mLedger->getLedgerSeq()); tmGL.set_itype(ripple::liAS_NODE); *(tmGL.add_nodeids()) = SHAMapNode().getRawString(); + cLog(lsTRACE) << "Sending AS root request to " << (peer ? "selected peer" : "all peers"); sendRequest(tmGL, peer); } else @@ -290,13 +323,18 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) tmGL.set_itype(ripple::liAS_NODE); BOOST_FOREACH(SHAMapNode& it, nodeIDs) *(tmGL.add_nodeids()) = it.getRawString(); + cLog(lsTRACE) << "Sending AS node " << nodeIDs.size() + << "request to " << (peer ? "selected peer" : "all peers"); sendRequest(tmGL, peer); } } } if (mComplete || mFailed) + { + cLog(lsDEBUG) << "Done:" << (mComplete ? " complete" : "") << (mFailed ? " failed" : ""); done(); + } else if (timer) resetTimer(); } @@ -326,7 +364,8 @@ void PeerSet::sendRequest(const ripple::TMGetLedger& tmGL) { // FIXME: Track last peer sent to and time sent Peer::pointer peer = it->lock(); - if (peer) peer->sendPacket(packet); + if (peer) + peer->sendPacket(packet); return; } } @@ -486,6 +525,7 @@ LedgerAcquire::pointer LedgerAcquireMaster::findCreate(const uint256& hash) return ptr; ptr = boost::make_shared(hash); assert(mLedgers[hash] == ptr); + ptr->addPeers(); ptr->resetTimer(); // Cannot call in constructor return ptr; } @@ -516,9 +556,6 @@ void LedgerAcquireMaster::dropLedger(const uint256& hash) bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref peer) { -#ifdef LA_DEBUG - cLog(lsTRACE) << "got data for acquiring ledger "; -#endif uint256 hash; if (packet.ledgerhash().size() != 32) { @@ -526,37 +563,34 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref return false; } memcpy(hash.begin(), packet.ledgerhash().data(), 32); -#ifdef LA_DEBUG - cLog(lsTRACE) << hash; -#endif + cLog(lsTRACE) << "Got data for acquiring ledger: " << hash; LedgerAcquire::pointer ledger = find(hash); - if (!ledger) return false; + if (!ledger) + { + cLog(lsINFO) << "Got data for ledger we're not acquiring"; + return false; + } if (packet.type() == ripple::liBASE) { if (packet.nodes_size() < 1) - return false; - const ripple::TMLedgerNode& node = packet.nodes(0); - if (!ledger->takeBase(node.nodedata())) - return false; - if (packet.nodes_size() == 1) { - ledger->trigger(peer, false); - return true; + cLog(lsWARNING) << "Got empty base data"; + return false; } - if (!ledger->takeAsRootNode(strCopy(packet.nodes(1).nodedata()))) + if (!ledger->takeBase(packet.nodes(0).nodedata())) + { + cLog(lsWARNING) << "Got unwanted base data"; + return false; + } + if ((packet.nodes().size() > 1) && !ledger->takeAsRootNode(strCopy(packet.nodes(1).nodedata()))) { cLog(lsWARNING) << "Included ASbase invalid"; } - if (packet.nodes().size() == 2) + if ((packet.nodes().size() > 2) && !ledger->takeTxRootNode(strCopy(packet.nodes(2).nodedata()))) { - ledger->trigger(peer, false); - return true; - } - if (!ledger->takeTxRootNode(strCopy(packet.nodes(2).nodedata()))) - { - cLog(lsWARNING) << "Invcluded TXbase invalid"; + cLog(lsWARNING) << "Included TXbase invalid"; } ledger->trigger(peer, false); return true; @@ -567,11 +601,16 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref std::list nodeIDs; std::list< std::vector > nodeData; - if (packet.nodes().size() <= 0) return false; + if (packet.nodes().size() <= 0) + { + cLog(lsINFO) << "Got request for no nodes"; + return false; + } for (int i = 0; i < packet.nodes().size(); ++i) { const ripple::TMLedgerNode& node = packet.nodes(i); - if (!node.has_nodeid() || !node.has_nodedata()) return false; + if (!node.has_nodeid() || !node.has_nodedata()) + return false; nodeIDs.push_back(SHAMapNode(node.nodeid().data(), node.nodeid().size())); nodeData.push_back(std::vector(node.nodedata().begin(), node.nodedata().end())); @@ -586,6 +625,7 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref return ret; } + cLog(lsWARNING) << "Not sure what ledger data we got"; return false; } diff --git a/src/LedgerAcquire.h b/src/LedgerAcquire.h index cdf9cda56..6044d1047 100644 --- a/src/LedgerAcquire.h +++ b/src/LedgerAcquire.h @@ -99,6 +99,7 @@ public: bool takeAsRootNode(const std::vector& data); void trigger(Peer::ref, bool timer); bool tryLocal(); + void addPeers(); }; class LedgerAcquireMaster diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index e0917da0e..beeaaa400 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -339,24 +339,6 @@ void LedgerConsensus::handleLCL(const uint256& lclHash) cLog(lsWARNING) << "Need consensus ledger " << mPrevLedgerHash; mAcquiringLedger = theApp->getMasterLedgerAcquire().findCreate(mPrevLedgerHash); - std::vector peerList = theApp->getConnectionPool().getPeerVector(); - - bool found = false; - BOOST_FOREACH(Peer::ref peer, peerList) - { - if (peer->hasLedger(mPrevLedgerHash)) - { - found = true; - mAcquiringLedger->peerHas(peer); - } - } - - if (!found) - { - BOOST_FOREACH(Peer::ref peer, peerList) - mAcquiringLedger->peerHas(peer); - } - mHaveCorrectLCL = false; return; } diff --git a/src/LedgerEntrySet.cpp b/src/LedgerEntrySet.cpp index 5cbca45df..bdf3958b0 100644 --- a/src/LedgerEntrySet.cpp +++ b/src/LedgerEntrySet.cpp @@ -319,9 +319,11 @@ bool LedgerEntrySet::threadTx(SLE::ref threadTo, Ledger::ref ledger, uint32 prevLgrID; if (!threadTo->thread(mSet.getTxID(), mSet.getLgrSeq(), prevTxID, prevLgrID)) return false; - if (TransactionMetaSet::thread(mSet.getAffectedNode(threadTo->getIndex(), sfModifiedNode), + + if (prevTxID.isZero() || TransactionMetaSet::thread(mSet.getAffectedNode(threadTo->getIndex(), sfModifiedNode), prevTxID, prevLgrID)) return true; + assert(false); return false; } @@ -336,7 +338,7 @@ bool LedgerEntrySet::threadOwners(SLE::ref node, Ledger::ref ledger, #endif return threadTx(node->getOwner(), ledger, newMods); } - else if (node->hasTwoOwners()) // thread to owner's accounts] + else if (node->hasTwoOwners()) // thread to owner's accounts { #ifdef META_DEBUG cLog(lsTRACE) << "Thread to two owners"; @@ -403,34 +405,26 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result) assert(origNode); threadOwners(origNode, mLedger, newMod); - if (origNode->isFieldPresent(sfAmount)) - { // node has an amount, covers ripple state nodes - STAmount amount = origNode->getFieldAmount(sfAmount); - if (amount.isNonZero()) - mSet.getAffectedNode(it.first).setFieldAmount(sfPreviousBalance, amount); - amount = curNode->getFieldAmount(sfAmount); - if (amount.isNonZero()) - mSet.getAffectedNode(it.first).setFieldAmount(sfFinalBalance, amount); - - if (origNode->getType() == ltRIPPLE_STATE) - { - mSet.getAffectedNode(it.first).setFieldAccount(sfLowID, - RippleAddress::createAccountID(origNode->getFieldAmount(sfLowLimit).getIssuer())); - mSet.getAffectedNode(it.first).setFieldAccount(sfHighID, - RippleAddress::createAccountID(origNode->getFieldAmount(sfHighLimit).getIssuer())); - } + STObject finals(sfFinalFields); + BOOST_FOREACH(const SerializedType& obj, *curNode) + { // search the deleted node for values saved on delete + if (obj.getFName().shouldMetaDel() && !obj.isDefault()) + finals.addObject(obj); } + if (!finals.empty()) + mSet.getAffectedNode(it.first, *type).addObject(finals); + } - if (origNode->getType() == ltOFFER) - { // check for non-zero balances - STAmount amount = origNode->getFieldAmount(sfTakerPays); - if (amount.isNonZero()) - mSet.getAffectedNode(it.first).setFieldAmount(sfFinalTakerPays, amount); - amount = origNode->getFieldAmount(sfTakerGets); - if (amount.isNonZero()) - mSet.getAffectedNode(it.first).setFieldAmount(sfFinalTakerGets, amount); + if ((type == &sfDeletedNode || type == &sfModifiedNode)) + { + STObject mods(sfPreviousFields); + BOOST_FOREACH(const SerializedType& obj, *origNode) + { // search the original node for values saved on modify + if (obj.getFName().shouldMetaMod() && !obj.isDefault() && !curNode->hasMatchingEntry(obj)) + mods.addObject(obj); } - + if (!mods.empty()) + mSet.getAffectedNode(it.first, *type).addObject(mods); } if (type == &sfCreatedNode) // if created, thread to owner(s) @@ -444,28 +438,6 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result) if (curNode->isThreadedType()) // always thread to self threadTx(curNode, mLedger, newMod); } - - if (type == &sfModifiedNode) - { - assert(origNode); - if (origNode->isFieldPresent(sfAmount)) - { // node has an amount, covers account root nodes and ripple nodes - STAmount amount = origNode->getFieldAmount(sfAmount); - if (amount != curNode->getFieldAmount(sfAmount)) - mSet.getAffectedNode(it.first).setFieldAmount(sfPreviousBalance, amount); - } - - if (origNode->getType() == ltOFFER) - { - STAmount amount = origNode->getFieldAmount(sfTakerPays); - if (amount != curNode->getFieldAmount(sfTakerPays)) - mSet.getAffectedNode(it.first).setFieldAmount(sfPreviousTakerPays, amount); - amount = origNode->getFieldAmount(sfTakerGets); - if (amount != curNode->getFieldAmount(sfTakerGets)) - mSet.getAffectedNode(it.first).setFieldAmount(sfPreviousTakerGets, amount); - } - - } } // add any new modified nodes to the modification set @@ -473,9 +445,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result) it != end; ++it) entryModify(it->second); -#ifdef META_DEBUG - cLog(lsINFO) << "Metadata:" << mSet.getJson(0); -#endif + cLog(lsTRACE) << "Metadata:" << mSet.getJson(0); mSet.addRaw(s, result); } diff --git a/src/LedgerFormats.cpp b/src/LedgerFormats.cpp index c696d72ed..4456ebbf0 100644 --- a/src/LedgerFormats.cpp +++ b/src/LedgerFormats.cpp @@ -4,10 +4,10 @@ std::map LedgerEntryFormat::byType; std::map LedgerEntryFormat::byName; -#define LEF_BASE \ - << SOElement(sfLedgerIndex, SOE_OPTIONAL) \ - << SOElement(sfLedgerEntryType, SOE_REQUIRED) \ - << SOElement(sfFlags, SOE_REQUIRED) +#define LEF_BASE \ + << SOElement(sfLedgerIndex, SOE_OPTIONAL) \ + << SOElement(sfLedgerEntryType, SOE_REQUIRED) \ + << SOElement(sfFlags, SOE_REQUIRED) #define DECLARE_LEF(name, type) lef = new LedgerEntryFormat(#name, type); (*lef) LEF_BASE @@ -16,74 +16,78 @@ static bool LEFInit() LedgerEntryFormat* lef; DECLARE_LEF(AccountRoot, ltACCOUNT_ROOT) - << SOElement(sfAccount, SOE_REQUIRED) - << SOElement(sfSequence, SOE_REQUIRED) - << SOElement(sfBalance, SOE_REQUIRED) - << SOElement(sfLastTxnID, SOE_REQUIRED) - << SOElement(sfLastTxnSeq, SOE_REQUIRED) - << SOElement(sfAuthorizedKey, SOE_OPTIONAL) - << SOElement(sfEmailHash, SOE_OPTIONAL) - << SOElement(sfWalletLocator, SOE_OPTIONAL) - << SOElement(sfMessageKey, SOE_OPTIONAL) - << SOElement(sfTransferRate, SOE_OPTIONAL) - << SOElement(sfDomain, SOE_OPTIONAL) - << SOElement(sfPublishHash, SOE_OPTIONAL) - << SOElement(sfPublishSize, SOE_OPTIONAL) + << SOElement(sfAccount, SOE_REQUIRED) + << SOElement(sfSequence, SOE_REQUIRED) + << SOElement(sfBalance, SOE_REQUIRED) + << SOElement(sfPreviousTxnID, SOE_REQUIRED) + << SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED) + << SOElement(sfAuthorizedKey, SOE_OPTIONAL) + << SOElement(sfEmailHash, SOE_OPTIONAL) + << SOElement(sfWalletLocator, SOE_OPTIONAL) + << SOElement(sfMessageKey, SOE_OPTIONAL) + << SOElement(sfTransferRate, SOE_OPTIONAL) + << SOElement(sfDomain, SOE_OPTIONAL) + << SOElement(sfPublishHash, SOE_OPTIONAL) + << SOElement(sfPublishSize, SOE_OPTIONAL) ; DECLARE_LEF(Contract, ltCONTRACT) - << SOElement(sfAccount, SOE_REQUIRED) - << SOElement(sfBalance, SOE_REQUIRED) - << SOElement(sfLastTxnID, SOE_REQUIRED) - << SOElement(sfLastTxnSeq, SOE_REQUIRED) - << SOElement(sfIssuer, SOE_REQUIRED) - << SOElement(sfOwner, SOE_REQUIRED) - << SOElement(sfExpiration, SOE_REQUIRED) - << SOElement(sfBondAmount, SOE_REQUIRED) - << SOElement(sfCreateCode, SOE_REQUIRED) - << SOElement(sfFundCode, SOE_REQUIRED) - << SOElement(sfRemoveCode, SOE_REQUIRED) - << SOElement(sfExpireCode, SOE_REQUIRED) + << SOElement(sfAccount, SOE_REQUIRED) + << SOElement(sfBalance, SOE_REQUIRED) + << SOElement(sfPreviousTxnID, SOE_REQUIRED) + << SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED) + << SOElement(sfIssuer, SOE_REQUIRED) + << SOElement(sfOwner, SOE_REQUIRED) + << SOElement(sfExpiration, SOE_REQUIRED) + << SOElement(sfBondAmount, SOE_REQUIRED) + << SOElement(sfCreateCode, SOE_REQUIRED) + << SOElement(sfFundCode, SOE_REQUIRED) + << SOElement(sfRemoveCode, SOE_REQUIRED) + << SOElement(sfExpireCode, SOE_REQUIRED) ; DECLARE_LEF(DirectoryNode, ltDIR_NODE) - << SOElement(sfIndexes, SOE_REQUIRED) - << SOElement(sfIndexNext, SOE_OPTIONAL) - << SOElement(sfIndexPrevious, SOE_OPTIONAL) + << SOElement(sfIndexes, SOE_REQUIRED) + << SOElement(sfIndexNext, SOE_OPTIONAL) + << SOElement(sfIndexPrevious, SOE_OPTIONAL) ; DECLARE_LEF(GeneratorMap, ltGENERATOR_MAP) - << SOElement(sfGenerator, SOE_REQUIRED) + << SOElement(sfGenerator, SOE_REQUIRED) ; DECLARE_LEF(Nickname, ltNICKNAME) - << SOElement(sfAccount, SOE_REQUIRED) - << SOElement(sfMinimumOffer, SOE_OPTIONAL) + << SOElement(sfAccount, SOE_REQUIRED) + << SOElement(sfMinimumOffer, SOE_OPTIONAL) ; DECLARE_LEF(Offer, ltOFFER) - << SOElement(sfAccount, SOE_REQUIRED) - << SOElement(sfSequence, SOE_REQUIRED) - << SOElement(sfTakerPays, SOE_REQUIRED) - << SOElement(sfTakerGets, SOE_REQUIRED) - << SOElement(sfBookDirectory, SOE_REQUIRED) - << SOElement(sfBookNode, SOE_REQUIRED) - << SOElement(sfOwnerNode, SOE_REQUIRED) - << SOElement(sfLastTxnID, SOE_REQUIRED) - << SOElement(sfLastTxnSeq, SOE_REQUIRED) - << SOElement(sfExpiration, SOE_OPTIONAL) + << SOElement(sfAccount, SOE_REQUIRED) + << SOElement(sfSequence, SOE_REQUIRED) + << SOElement(sfTakerPays, SOE_REQUIRED) + << SOElement(sfTakerGets, SOE_REQUIRED) + << SOElement(sfBookDirectory, SOE_REQUIRED) + << SOElement(sfBookNode, SOE_REQUIRED) + << SOElement(sfOwnerNode, SOE_REQUIRED) + << SOElement(sfPreviousTxnID, SOE_REQUIRED) + << SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED) + << SOElement(sfExpiration, SOE_OPTIONAL) ; DECLARE_LEF(RippleState, ltRIPPLE_STATE) - << SOElement(sfBalance, SOE_REQUIRED) - << SOElement(sfLowLimit, SOE_REQUIRED) - << SOElement(sfHighLimit, SOE_REQUIRED) - << SOElement(sfLastTxnID, SOE_REQUIRED) - << SOElement(sfLastTxnSeq, SOE_REQUIRED) - << SOElement(sfLowQualityIn, SOE_OPTIONAL) - << SOElement(sfLowQualityOut, SOE_OPTIONAL) - << SOElement(sfHighQualityIn, SOE_OPTIONAL) - << SOElement(sfHighQualityOut, SOE_OPTIONAL) + << SOElement(sfBalance, SOE_REQUIRED) + << SOElement(sfLowLimit, SOE_REQUIRED) + << SOElement(sfHighLimit, SOE_REQUIRED) + << SOElement(sfPreviousTxnID, SOE_REQUIRED) + << SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED) + << SOElement(sfLowQualityIn, SOE_OPTIONAL) + << SOElement(sfLowQualityOut, SOE_OPTIONAL) + << SOElement(sfHighQualityIn, SOE_OPTIONAL) + << SOElement(sfHighQualityOut, SOE_OPTIONAL) + ; + + DECLARE_LEF(LedgerHashes, ltLEDGER_HASHES) + << SOElement(sfHashes, SOE_REQUIRED) ; return true; diff --git a/src/LedgerFormats.h b/src/LedgerFormats.h index 947cb65be..cae045f23 100644 --- a/src/LedgerFormats.h +++ b/src/LedgerFormats.h @@ -14,6 +14,7 @@ enum LedgerEntryType ltNICKNAME = 'n', ltOFFER = 'o', ltCONTRACT = 'c', + ltLEDGER_HASHES = 'h', }; // Used as a prefix for computing ledger indexes (keys). @@ -28,6 +29,7 @@ enum LedgerNameSpace spaceOwnerDir = 'O', // Directory of things owned by an account. spaceBookDir = 'B', // Directory of order books. spaceContract = 'c', + spaceHashes = 'h', }; enum LedgerSpecificFlags diff --git a/src/LedgerHistory.cpp b/src/LedgerHistory.cpp index c0149431b..854fc011f 100644 --- a/src/LedgerHistory.cpp +++ b/src/LedgerHistory.cpp @@ -1,10 +1,11 @@ +#include "LedgerHistory.h" + #include #include #include -#include "LedgerHistory.h" #include "Config.h" #include "Application.h" @@ -13,13 +14,13 @@ #endif #ifndef CACHED_LEDGER_AGE -#define CACHED_LEDGER_AGE 600 +#define CACHED_LEDGER_AGE 900 #endif +// FIXME: Need to clean up ledgers by index, probably should switch to just mapping sequence to hash + LedgerHistory::LedgerHistory() : mLedgersByHash(CACHED_LEDGER_NUM, CACHED_LEDGER_AGE) -{ -; -} +{ ; } void LedgerHistory::addLedger(Ledger::pointer ledger) { @@ -37,8 +38,7 @@ void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger, bool fromConsensus assert(ledger->isImmutable()); mLedgersByIndex.insert(std::make_pair(ledger->getLedgerSeq(), ledger)); - boost::thread thread(boost::bind(&Ledger::saveAcceptedLedger, ledger, fromConsensus)); - thread.detach(); + ledger->pendSave(fromConsensus); } Ledger::pointer LedgerHistory::getLedgerBySeq(uint32 index) @@ -50,7 +50,8 @@ Ledger::pointer LedgerHistory::getLedgerBySeq(uint32 index) sl.unlock(); Ledger::pointer ret(Ledger::loadByIndex(index)); - if (!ret) return ret; + if (!ret) + return ret; assert(ret->getLedgerSeq() == index); sl.lock(); diff --git a/src/LedgerMaster.cpp b/src/LedgerMaster.cpp index 2619c893e..d83c72bf8 100644 --- a/src/LedgerMaster.cpp +++ b/src/LedgerMaster.cpp @@ -98,6 +98,14 @@ TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEng void LedgerMaster::acquireMissingLedger(const uint256& ledgerHash, uint32 ledgerSeq) { mMissingLedger = theApp->getMasterLedgerAcquire().findCreate(ledgerHash); + if (mMissingLedger->isComplete()) + { + Ledger::pointer lgr = mMissingLedger->getLedger(); + if (lgr && (lgr->getLedgerSeq() == ledgerSeq)) + missingAcquireComplete(mMissingLedger); + mMissingLedger = LedgerAcquire::pointer(); + return; + } mMissingSeq = ledgerSeq; if (mMissingLedger->setAccept()) mMissingLedger->addOnComplete(boost::bind(&LedgerMaster::missingAcquireComplete, this, _1)); @@ -107,20 +115,19 @@ void LedgerMaster::missingAcquireComplete(LedgerAcquire::pointer acq) { boost::recursive_mutex::scoped_lock ml(mLock); - if (acq->isFailed()) + if (acq->isFailed() && (mMissingSeq != 0)) { - if (mMissingSeq != 0) - { - cLog(lsWARNING) << "Acquire failed, invalidating following ledger " << mMissingSeq + 1; - mCompleteLedgers.clearValue(mMissingSeq + 1); - } + cLog(lsWARNING) << "Acquire failed for " << mMissingSeq; } mMissingLedger = LedgerAcquire::pointer(); mMissingSeq = 0; if (!acq->isFailed()) + { setFullLedger(acq->getLedger()); + acq->getLedger()->pendSave(false); + } } void LedgerMaster::setFullLedger(Ledger::ref ledger) @@ -139,9 +146,18 @@ void LedgerMaster::setFullLedger(Ledger::ref ledger) } } + if (mMissingLedger && mMissingLedger->isComplete()) + mMissingLedger = LedgerAcquire::pointer(); + if (mMissingLedger || !theConfig.FULL_HISTORY) return; + if (Ledger::getPendingSaves() > 3) + { + cLog(lsINFO) << "Too many pending ledger saves"; + return; + } + // see if there's a ledger gap we need to fill if (!mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1)) { @@ -154,11 +170,15 @@ void LedgerMaster::setFullLedger(Ledger::ref ledger) if (prevMissing != RangeSet::RangeSetAbsent) { cLog(lsINFO) << "Ledger " << prevMissing << " is missing"; - Ledger::pointer nextLedger = getLedgerBySeq(prevMissing); + assert(!mCompleteLedgers.hasValue(prevMissing)); + Ledger::pointer nextLedger = getLedgerBySeq(prevMissing + 1); if (nextLedger) acquireMissingLedger(nextLedger->getParentHash(), nextLedger->getLedgerSeq() - 1); else - cLog(lsWARNING) << "We have a ledger gap we can't quite fix"; + { + mCompleteLedgers.clearValue(prevMissing); + cLog(lsWARNING) << "We have a gap we can't fix: " << prevMissing + 1; + } } } } diff --git a/src/LedgerMaster.h b/src/LedgerMaster.h index bccf9c2ed..967026fed 100644 --- a/src/LedgerMaster.h +++ b/src/LedgerMaster.h @@ -62,6 +62,8 @@ public: void switchLedgers(Ledger::ref lastClosed, Ledger::ref newCurrent); + std::string getCompleteLedgers() { return mCompleteLedgers.toString(); } + Ledger::pointer closeLedger(); Ledger::pointer getLedgerBySeq(uint32 index) @@ -75,12 +77,11 @@ public: Ledger::pointer getLedgerByHash(const uint256& hash) { - if (!hash) + if (hash.isZero()) return mCurrentLedger; if (mCurrentLedger && (mCurrentLedger->getHash() == hash)) return mCurrentLedger; - if (mFinalizedLedger && (mFinalizedLedger->getHash() == hash)) return mFinalizedLedger; diff --git a/src/Log.cpp b/src/Log.cpp index c4cf54ea2..5f4e3425d 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -41,14 +41,18 @@ std::vector< std::pair > LogPartition::getSeverities() Log::~Log() { std::string logMsg = boost::posix_time::to_simple_string(boost::posix_time::second_clock::universal_time()); + if (!mPartitionName.empty()) + logMsg += " " + mPartitionName + ":"; + else + logMsg += " "; switch (mSeverity) { - case lsTRACE: logMsg += " TRAC "; break; - case lsDEBUG: logMsg += " DEBG "; break; - case lsINFO: logMsg += " INFO "; break; - case lsWARNING: logMsg += " WARN "; break; - case lsERROR: logMsg += " EROR "; break; - case lsFATAL: logMsg += " FATL "; break; + case lsTRACE: logMsg += "TRC "; break; + case lsDEBUG: logMsg += "DBG "; break; + case lsINFO: logMsg += "NFO "; break; + case lsWARNING: logMsg += "WRN "; break; + case lsERROR: logMsg += "ERR "; break; + case lsFATAL: logMsg += "FTL "; break; case lsINVALID: assert(false); return; } logMsg += oss.str(); diff --git a/src/Log.h b/src/Log.h index 7dbc2c833..6da9e0b5c 100644 --- a/src/Log.h +++ b/src/Log.h @@ -17,10 +17,10 @@ #define SETUP_LOG() static LogPartition logPartition(__FILE__) // Standard conditional log -#define cLog(x) if (!logPartition.doLog(x)) do {} while (0); else Log(x) +#define cLog(x) if (!logPartition.doLog(x)) do {} while (0); else Log(x, logPartition) // Log only if an additional condition 'c' is true. Condition is not computed if not needed -#define tLog(c,x) if (!logPartition.doLog(x) || !(c)) do {} while(0); else Log(x) +#define tLog(c,x) if (!logPartition.doLog(x) || !(c)) do {} while(0); else Log(x, logPartition) // Check if should log #define sLog(x) (logPartition.doLog(x)) @@ -49,10 +49,8 @@ protected: public: LogPartition(const char *name); - bool doLog(LogSeverity s) - { - return s >= mMinSeverity; - } + bool doLog(LogSeverity s) { return s >= mMinSeverity; } + const std::string& getName() const { return mName; } static bool setSeverity(const std::string& partition, LogSeverity severity); static void setSeverity(LogSeverity severity); @@ -70,8 +68,9 @@ protected: static LogSeverity sMinSeverity; static std::ofstream* outStream; - mutable std::ostringstream oss; - LogSeverity mSeverity; + mutable std::ostringstream oss; + LogSeverity mSeverity; + std::string mPartitionName; static boost::filesystem::path *pathToLog; static uint32 logRotateCounter; @@ -80,6 +79,9 @@ public: Log(LogSeverity s) : mSeverity(s) { ; } + Log(LogSeverity s, const LogPartition& p) : mSeverity(s), mPartitionName(p.getName()) + { ; } + ~Log(); template std::ostream& operator<<(const T& t) const diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index 1c64fecec..dbeb8fd1d 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -889,6 +889,9 @@ Json::Value NetworkOPs::getServerInfo() if (mNeedNetworkLedger) info["networkLedger"] = "waiting"; + info["completeLedgers"] = theApp->getMasterLedger().getCompleteLedgers(); + info["peers"] = theApp->getConnectionPool().getPeerCount(); + Json::Value lastClose = Json::objectValue; lastClose["proposers"] = theApp->getOPs().getPreviousProposers(); lastClose["convergeTime"] = theApp->getOPs().getPreviousConvergeTime(); diff --git a/src/Peer.cpp b/src/Peer.cpp index 8dd7a5c8c..609bf90e5 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -1,4 +1,3 @@ - #include #include @@ -539,8 +538,8 @@ void Peer::processReadBuffer() case ripple::mtVALIDATION: { - ripple::TMValidation msg; - if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) + boost::shared_ptr msg = boost::make_shared(); + if (msg->ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvValidation(msg); else cLog(lsWARNING) << "parse error: " << type; @@ -575,6 +574,13 @@ void Peer::processReadBuffer() } } +void Peer::punishPeer(const boost::weak_ptr& wp, PeerPunish pp) +{ + Peer::pointer p = wp.lock(); + if (p) + p->punishPeer(pp); +} + void Peer::recvHello(ripple::TMHello& packet) { bool bDetach = true; @@ -766,23 +772,50 @@ void Peer::recvHaveTxSet(ripple::TMHaveTransactionSet& packet) punishPeer(PP_UNWANTED_DATA); } -void Peer::recvValidation(ripple::TMValidation& packet) +static void checkValidation(Job&, SerializedValidation::pointer val, uint256 signingHash, + bool isTrusted, boost::shared_ptr packet, boost::weak_ptr peer) { - if (packet.validation().size() < 50) +#ifndef TRUST_NETWORK + try +#endif + { + if (!val->isValid(signingHash)) + { + cLog(lsWARNING) << "Validation is invalid"; + Peer::punishPeer(peer, PP_UNKNOWN_REQUEST); + return; + } + + if (theApp->getOPs().recvValidation(val)) + { + Peer::pointer pp = peer.lock(); + PackedMessage::pointer message = boost::make_shared(*packet, ripple::mtVALIDATION); + theApp->getConnectionPool().relayMessage(pp ? pp.get() : NULL, message); + } + } +#ifndef TRUST_NETWORK + catch (...) + { + cLog(lsWARNING) << "Exception processing validation"; + Peer::punishPeer(peer, PP_UNKNOWN_REQUEST); + } +#endif +} + +void Peer::recvValidation(const boost::shared_ptr& packet) +{ + if (packet->validation().size() < 50) { cLog(lsWARNING) << "Too small validation from peer"; punishPeer(PP_UNKNOWN_REQUEST); return; } -// OPTIMIZEME: Should just defer validation checking to another thread -// checking the signature is expensive (but should do 'isNew' check here) - #ifndef TRUST_NETWORK try #endif { - Serializer s(packet.validation()); + Serializer s(packet->validation()); SerializerIterator sit(s); SerializedValidation::pointer val = boost::make_shared(boost::ref(sit), false); @@ -793,18 +826,10 @@ void Peer::recvValidation(ripple::TMValidation& packet) return; } - if (!val->isValid(signingHash)) - { - cLog(lsWARNING) << "Validation is invalid"; - punishPeer(PP_UNKNOWN_REQUEST); - return; - } - - if (theApp->getOPs().recvValidation(val)) - { - PackedMessage::pointer message = boost::make_shared(packet, ripple::mtVALIDATION); - theApp->getConnectionPool().relayMessage(this, message); - } + bool isTrusted = theApp->getUNL().nodeInUNL(val->getSignerPublic()); + theApp->getJobQueue().addJob(isTrusted ? jtVALIDATION_t : jtVALIDATION_ut, + boost::bind(&checkValidation, _1, val, signingHash, isTrusted, packet, + boost::weak_ptr(shared_from_this()))); } #ifndef TRUST_NETWORK catch (...) @@ -993,6 +1018,9 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) ripple::TMLedgerData reply; bool fatLeaves = true, fatRoot = false; + if (packet.has_requestcookie()) + reply.set_requestcookie(packet.requestcookie()); + if (packet.itype() == ripple::liTS_CANDIDATE) { // Request is for a transaction candidate set cLog(lsINFO) << "Received request for TX candidate set data " << getIP(); @@ -1034,7 +1062,10 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) tLog(!ledger, lsINFO) << "Don't have ledger " << ledgerhash; } else if (packet.has_ledgerseq()) + { ledger = theApp->getMasterLedger().getLedgerBySeq(packet.ledgerseq()); + tLog(!ledger, lsINFO) << "Don't have ledger " << packet.ledgerseq(); + } else if (packet.has_ltype() && (packet.ltype() == ripple::ltCURRENT)) ledger = theApp->getMasterLedger().getCurrentLedger(); else if (packet.has_ltype() && (packet.ltype() == ripple::ltCLOSED) ) @@ -1053,10 +1084,13 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) if ((!ledger) || (packet.has_ledgerseq() && (packet.ledgerseq() != ledger->getLedgerSeq()))) { punishPeer(PP_UNKNOWN_REQUEST); - if (ledger) - cLog(lsWARNING) << "Ledger has wrong sequence"; - else - cLog(lsWARNING) << "Can't find the ledger they want"; + if (sLog(lsWARNING)) + { + if (ledger) + Log(lsWARNING) << "Ledger has wrong sequence"; + else + Log(lsWARNING) << "Can't find the ledger they want"; + } return; } @@ -1068,12 +1102,11 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) if(packet.itype() == ripple::liBASE) { // they want the ledger base data - cLog(lsTRACE) << "Want ledger base data"; + cLog(lsTRACE) << "They want ledger base data"; Serializer nData(128); ledger->addRaw(nData); reply.add_nodes()->set_nodedata(nData.getDataPtr(), nData.getLength()); - cLog(lsINFO) << "Ledger root w/map roots request"; SHAMap::pointer map = ledger->peekAccountStateMap(); if (map && map->getHash().isNonZero()) { // return account state root node if possible @@ -1086,7 +1119,7 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) map = ledger->peekTransactionMap(); if (map && map->getHash().isNonZero()) { - rootNode.resize(0); + rootNode.erase(); if (map->getRootNode(rootNode, snfWIRE)) reply.add_nodes()->set_nodedata(rootNode.getDataPtr(), rootNode.getLength()); } @@ -1099,9 +1132,10 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) return; } - if ((packet.itype() == ripple::liTX_NODE) || (packet.itype() == ripple::liAS_NODE)) - map = (packet.itype() == ripple::liTX_NODE) ? - ledger->peekTransactionMap() : ledger->peekAccountStateMap(); + if (packet.itype() == ripple::liTX_NODE) + map = ledger->peekTransactionMap(); + else if (packet.itype() == ripple::liAS_NODE) + map = ledger->peekAccountStateMap(); } if ((!map) || (packet.nodeids_size() == 0)) @@ -1125,7 +1159,6 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) { std::vector::iterator nodeIDIterator; std::list< std::vector >::iterator rawNodeIterator; - int count = 0; for(nodeIDIterator = nodeIDs.begin(), rawNodeIterator = rawNodes.begin(); nodeIDIterator != nodeIDs.end(); ++nodeIDIterator, ++rawNodeIterator) { @@ -1134,12 +1167,9 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) ripple::TMLedgerNode* node = reply.add_nodes(); node->set_nodeid(nID.getDataPtr(), nID.getLength()); node->set_nodedata(&rawNodeIterator->front(), rawNodeIterator->size()); - ++count; } } } - if (packet.has_requestcookie()) - reply.set_requestcookie(packet.requestcookie()); PackedMessage::pointer oPacket = boost::make_shared(reply, ripple::mtLEDGER_DATA); sendPacket(oPacket); } @@ -1148,6 +1178,7 @@ void Peer::recvLedger(ripple::TMLedgerData& packet) { if (packet.nodes().size() <= 0) { + cLog(lsWARNING) << "Ledger data with no nodes"; punishPeer(PP_INVALID_REQUEST); return; } diff --git a/src/Peer.h b/src/Peer.h index 6636a51ae..76b11d092 100644 --- a/src/Peer.h +++ b/src/Peer.h @@ -96,7 +96,7 @@ protected: void recvHello(ripple::TMHello& packet); void recvTransaction(ripple::TMTransaction& packet); - void recvValidation(ripple::TMValidation& packet); + void recvValidation(const boost::shared_ptr& packet); void recvGetValidation(ripple::TMGetValidations& packet); void recvContact(ripple::TMContact& packet); void recvGetContacts(ripple::TMGetContacts& packet); @@ -151,6 +151,7 @@ public: void sendGetPeers(); void punishPeer(PeerPunish pp); + static void punishPeer(const boost::weak_ptr&, PeerPunish); Json::Value getJson(); bool isConnected() const { return mHelloed && !mDetaching; } diff --git a/src/RPCHandler.cpp b/src/RPCHandler.cpp index c6c8e9dba..2b8fe7225 100644 --- a/src/RPCHandler.cpp +++ b/src/RPCHandler.cpp @@ -216,7 +216,7 @@ Json::Value RPCHandler::authorize(const uint256& uLedger, { naMasterAccountPublic.setAccountPublic(naMasterGenerator, iIndex); - Log(lsDEBUG) << "authorize: " << iIndex << " : " << naMasterAccountPublic.humanAccountID() << " : " << naSrcAccountID.humanAccountID(); + cLog(lsDEBUG) << "authorize: " << iIndex << " : " << naMasterAccountPublic.humanAccountID() << " : " << naSrcAccountID.humanAccountID(); bFound = naSrcAccountID.getAccountID() == naMasterAccountPublic.getAccountID(); if (!bFound) @@ -247,7 +247,7 @@ Json::Value RPCHandler::authorize(const uint256& uLedger, if (saSrcBalance < saFee) { - Log(lsINFO) << "authorize: Insufficent funds for fees: fee=" << saFee.getText() << " balance=" << saSrcBalance.getText(); + cLog(lsINFO) << "authorize: Insufficent funds for fees: fee=" << saFee.getText() << " balance=" << saSrcBalance.getText(); return rpcError(rpcINSUF_FUNDS); } @@ -2410,7 +2410,7 @@ Json::Value RPCHandler::doLogRotate(const Json::Value& params) Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& params,int role) { - Log(lsTRACE) << "RPC:" << command; + cLog(lsTRACE) << "RPC:" << command; static struct { const char* pCommand; diff --git a/src/RangeSet.cpp b/src/RangeSet.cpp index fe218df39..e9c4b38f9 100644 --- a/src/RangeSet.cpp +++ b/src/RangeSet.cpp @@ -54,15 +54,18 @@ uint32 RangeSet::getPrev(uint32 v) const uint32 RangeSet::prevMissing(uint32 v) const { // largest number not in the set that is less than the given number + cLog(lsTRACE) << "prevMissing(" << v << ") " << toString(); for (const_reverse_iterator it = rbegin(); it != rend(); ++it) { - if (lower(it) <= v) - { - if (upper(it) < v) - return upper(it) + 1; + if ((upper(it) + 1) < v) + return upper(it) + 1; + if (lower(it) == 0) + return RangeSetAbsent; + if ((lower(it) - 1) < v) return lower(it) - 1; - } } + if (v > 0) + return v - 1; return RangeSetAbsent; } diff --git a/src/RippleCalc.cpp b/src/RippleCalc.cpp index c951f3143..6cbbf46d8 100644 --- a/src/RippleCalc.cpp +++ b/src/RippleCalc.cpp @@ -1,3 +1,5 @@ +// YYY OPTIMIZE: When calculating path increment, note if increment consumes all liquidity. No need to revesit path in the future +// if all liquidity is used. #include #include @@ -685,22 +687,26 @@ TER RippleCalc::calcNodeOfferFwd( } -// Cur is the driver and will be filled exactly. +// Compute how much might flow for the node for the pass. Don not actually adjust balances. // uQualityIn -> uQualityOut // saPrvReq -> saCurReq // sqPrvAct -> saCurAct // This is a minimizing routine: moving in reverse it propagates the send limit to the sender, moving forward it propagates the // actual send toward the receiver. -// This routine works backwards as it calculates previous wants based on previous credit limits and current wants. -// This routine works forwards as it calculates current deliver based on previous delivery limits and current wants. +// This routine works backwards: +// - cur is the driver: it calculates previous wants based on previous credit limits and current wants. +// This routine works forwards: +// - prv is the driver: it calculates current deliver based on previous delivery limits and current wants. +// This routine is called one or two times for a node in a pass. If called once, it will work and set a rate. If called again, +// the new work must not worsen the previous rate. // XXX Deal with uQualityIn or uQualityOut = 0 void RippleCalc::calcNodeRipple( const uint32 uQualityIn, const uint32 uQualityOut, const STAmount& saPrvReq, // --> in limit including fees, <0 = unlimited const STAmount& saCurReq, // --> out limit (driver) - STAmount& saPrvAct, // <-> in limit including achieved - STAmount& saCurAct, // <-> out limit achieved. + STAmount& saPrvAct, // <-> in limit including achieved so far: <-- <= --> + STAmount& saCurAct, // <-> out limit including achieved : <-- <= --> uint64& uRateMax) { cLog(lsINFO) << boost::str(boost::format("calcNodeRipple> uQualityIn=%d uQualityOut=%d saPrvReq=%s saCurReq=%s saPrvAct=%s saCurAct=%s") @@ -729,13 +735,18 @@ void RippleCalc::calcNodeRipple( // No fee. cLog(lsINFO) << boost::str(boost::format("calcNodeRipple: No fees")); + // Only process if we are not worsing previously processed. if (!uRateMax || STAmount::uRateOne <= uRateMax) { + // Limit amount to transfer if need. STAmount saTransfer = bPrvUnlimited ? saCur : std::min(saPrv, saCur); + // In reverse, we want to propagate the limited cur to prv and set actual cur. + // In forward, we want to propagate the limited prv to cur and set actual prv. saPrvAct += saTransfer; saCurAct += saTransfer; + // If no rate limit, set rate limit to avoid combining with something with a worse rate. if (!uRateMax) uRateMax = STAmount::uRateOne; } @@ -788,6 +799,7 @@ void RippleCalc::calcNodeRipple( } // Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur... +// No account adjustments in reverse as we don't know how much is going to actually be pushed through yet. // <-- tesSUCCESS or tepPATH_DRY TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality) { @@ -899,23 +911,36 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref psp if (saPrvRedeemReq) // Previous has IOUs to redeem. { // Redeem at 1:1 - cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Redeem at 1:1")); saCurWantedAct = std::min(saPrvRedeemReq, saCurWantedReq); saPrvRedeemAct = saCurWantedAct; uRateMax = STAmount::uRateOne; + + cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Redeem at 1:1 saPrvRedeemReq=%s (available) saPrvRedeemAct=%s uRateMax=%s") + % saPrvRedeemReq.getFullText() + % saPrvRedeemAct.getFullText() + % STAmount::saFromRate(uRateMax).getText()); + } + else + { + saPrvRedeemAct.zero(saCurWantedAct); } // Calculate issuing. + saPrvIssueAct.zero(saCurWantedAct); + if (saCurWantedReq != saCurWantedAct // Need more. && saPrvIssueReq) // Will accept IOUs from prevous. { // Rate: quality in : 1.0 - cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : 1.0")); // If we previously redeemed and this has a poorer rate, this won't be included the current increment. calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurWantedReq, saPrvIssueAct, saCurWantedAct, uRateMax); + + cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Issuing: Rate: quality in : 1.0 saPrvIssueAct=%s saCurWantedAct=%s") + % saPrvIssueAct.getFullText() + % saCurWantedAct.getFullText()); } if (!saCurWantedAct) @@ -927,15 +952,19 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref psp else { // ^|account --> ACCOUNT --> account + saPrvRedeemAct.zero(saCurRedeemReq); + saPrvIssueAct.zero(saCurRedeemReq); // redeem (part 1) -> redeem if (saCurRedeemReq // Next wants IOUs redeemed. && saPrvRedeemReq) // Previous has IOUs to redeem. { // Rate : 1.0 : quality out - cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate : 1.0 : quality out")); - calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct, uRateMax); + + cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate : 1.0 : quality out saPrvRedeemAct=%s saCurRedeemAct=%s") + % saPrvRedeemAct.getFullText() + % saCurRedeemAct.getFullText()); } // issue (part 1) -> redeem @@ -943,9 +972,11 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref psp && saPrvRedeemAct == saPrvRedeemReq) // Previous has no IOUs to redeem remaining. { // Rate: quality in : quality out - cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : quality out")); - calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct, uRateMax); + + cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : quality out: saPrvIssueAct=%s saCurRedeemAct=%s") + % saPrvIssueAct.getFullText() + % saCurRedeemAct.getFullText()); } // redeem (part 2) -> issue. @@ -954,25 +985,30 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref psp && saPrvRedeemAct != saPrvRedeemReq) // Did not complete redeeming previous IOUs. { // Rate : 1.0 : transfer_rate - cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate : 1.0 : transfer_rate")); - calcNodeRipple(QUALITY_ONE, lesActive.rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct, uRateMax); + + cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate : 1.0 : transfer_rate: saPrvRedeemAct=%s saCurIssueAct=%s") + % saPrvRedeemAct.getFullText() + % saCurIssueAct.getFullText()); } // issue (part 2) -> issue if (saCurIssueReq != saCurIssueAct // Need wants more IOUs issued. && saCurRedeemAct == saCurRedeemReq // Can only issue if completed redeeming. - && saPrvRedeemReq == saPrvRedeemAct) // Previously redeemed all owed IOUs. + && saPrvRedeemReq == saPrvRedeemAct // Previously redeemed all owed IOUs. + && saPrvIssueReq) // Previous can issue. { // Rate: quality in : 1.0 - cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : 1.0")); - calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct, uRateMax); + + cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : 1.0: saPrvIssueAct=%s saCurIssueAct=%s") + % saPrvIssueAct.getFullText() + % saCurIssueAct.getFullText()); } if (!saCurRedeemAct && !saCurIssueAct) { - // Must want something. + // Did not make progress. terResult = tepPATH_DRY; } @@ -990,6 +1026,9 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref psp // Note: deliver is always issue as ACCOUNT is the issuer for the offer input. cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: account --> ACCOUNT --> offer")); + saPrvRedeemAct.zero(saCurRedeemReq); + saPrvIssueAct.zero(saCurRedeemReq); + // redeem -> deliver/issue. if (saPrvOwed.isPositive() // Previous has IOUs to redeem. && saCurDeliverReq) // Need some issued. @@ -1019,6 +1058,8 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref psp } else if (!bPrvAccount && bNxtAccount) { + saPrvDeliverAct.zero(saCurRedeemReq); + if (uIndex == uLast) { // offer --> ACCOUNT --> $ @@ -1079,6 +1120,8 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref psp // deliver/redeem -> deliver/issue. cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> offer")); + saPrvDeliverAct.zero(saCurRedeemReq); + // Rate : 1.0 : transfer_rate calcNodeRipple(QUALITY_ONE, lesActive.rippleTransferRate(uCurAccountID), saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct, saCurDeliverAct, uRateMax); @@ -1092,6 +1135,7 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref psp return terResult; } +// When moving forward, we know the actual amount to push through so adjust balances. // Perform balance adjustments between previous and current node. // - The previous node: specifies what to push through to current. // - All of previous output is consumed. @@ -1429,7 +1473,7 @@ TER PathState::pushImply( } // Append a node and insert before it any implied nodes. -// <-- terResult: tesSUCCESS, temBAD_PATH, terNO_LINE +// <-- terResult: tesSUCCESS, temBAD_PATH, terNO_LINE, tepPATH_DRY TER PathState::pushNode( const int iType, const uint160& uAccountID, @@ -1477,6 +1521,8 @@ TER PathState::pushNode( pnCur.uAccountID, // Current account. pnCur.uCurrencyID, // Wanted currency. !!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as issuer. + + // Note: pnPrv may no longer be the immediately previous node. } if (tesSUCCESS == terResult && !vpnNodes.empty()) @@ -1511,6 +1557,13 @@ TER PathState::pushNode( << " for " << STAmount::createHumanCurrency(pnPrv.uCurrencyID) << "." ; + + STAmount saOwed = lesEntries.rippleOwed(pnCur.uAccountID, pnBck.uAccountID, uCurrencyID); + + if (!saOwed.isPositive() && *saOwed.negate() >= lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, uCurrencyID)) + { + terResult = tepPATH_DRY; + } } } } @@ -1792,6 +1845,8 @@ void RippleCalc::pathNext(PathState::ref pspCur, const int iPaths, const LedgerE const bool bMultiQuality = iPaths == 1; const unsigned int uLast = pspCur->vpnNodes.size() - 1; + pspCur->bConsumed = false; + // YYY This clearing should only be needed for nice logging. pspCur->saInPass = STAmount(pspCur->saInReq.getCurrency(), pspCur->saInReq.getIssuer()); pspCur->saOutPass = STAmount(pspCur->saOutReq.getCurrency(), pspCur->saOutReq.getIssuer()); @@ -1897,6 +1952,10 @@ TER RippleCalc::rippleCalc( vpsPaths.push_back(pspDirect); } + else if (terNO_LINE != pspDirect->terStatus) + { + terResult = pspDirect->terStatus; + } } } @@ -1917,28 +1976,26 @@ TER RippleCalc::rippleCalc( if (pspExpanded) { - // Return if malformed. + // Return, if the path specification was malformed. if (isTemMalformed(pspExpanded->terStatus)) return pspExpanded->terStatus; - if (tesSUCCESS == pspExpanded->terStatus) + if (tesSUCCESS == pspExpanded->terStatus) { terResult = tesSUCCESS; // Had a success. - vpsPaths.push_back(pspExpanded); + vpsPaths.push_back(pspExpanded); + } + else if (terNO_LINE != pspExpanded->terStatus) + { + terResult = pspExpanded->terStatus; + } } } - if (vpsPaths.empty()) + if (tesSUCCESS != terResult) { - // No paths. Missing credit lines. - return terNO_LINE; - } - else if (tesSUCCESS != terResult) - { - // No path successes. - - return vpsPaths[0]->terStatus; - } + return terResult == temUNCERTAIN ? terNO_LINE : terResult; + } else { terResult = temUNCERTAIN; @@ -1955,6 +2012,7 @@ TER RippleCalc::rippleCalc( { PathState::pointer pspBest; const LedgerEntrySet lesCheckpoint = lesActive; + int iDry = 0; // Find the best path. BOOST_FOREACH(PathState::pointer& pspCur, vpsPaths) @@ -1969,12 +2027,12 @@ TER RippleCalc::rippleCalc( if (!pspCur->uQuality) { // Path was dry. - nothing(); + ++iDry; } else { tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG) - << boost::str(boost::format("calcOfferFirst: better: uQuality=%016lX saInPass=%s saOutPass=%s") - % pspCur->uQuality + << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s") + % STAmount::saFromRate(pspCur->uQuality) % pspCur->saInPass.getFullText() % pspCur->saOutPass.getFullText()); @@ -1984,8 +2042,8 @@ TER RippleCalc::rippleCalc( || !pspBest // Best is not yet set. || PathState::lessPriority(pspBest, pspCur)) // Current is better than set. { - cLog(lsDEBUG) << boost::str(boost::format("calcOfferFirst: better: uQuality=%016lX saInPass=%s saOutPass=%s") - % pspCur->uQuality + cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s") + % STAmount::saFromRate(pspCur->uQuality) % pspCur->saInPass.getFullText() % pspCur->saOutPass.getFullText()); @@ -2000,8 +2058,8 @@ TER RippleCalc::rippleCalc( { // Apply best path. - cLog(lsDEBUG) << boost::str(boost::format("calcOfferFirst: best: uQuality=%016lX saInPass=%s saOutPass=%s") - % pspBest->uQuality + cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: best: uQuality=%s saInPass=%s saOutPass=%s") + % STAmount::saFromRate(pspBest->uQuality) % pspBest->saInPass.getFullText() % pspBest->saOutPass.getFullText()); @@ -2014,18 +2072,25 @@ TER RippleCalc::rippleCalc( saInAct += pspBest->saInPass; saOutAct += pspBest->saOutPass; + if (pspBest->bConsumed) + { + ++iDry; + pspBest->uQuality = 0; + } + if (saOutAct == saDstAmountReq) { // Done. Delivered requested amount. terResult = tesSUCCESS; } - else if (saInAct != saMaxAmountReq) + else if (saInAct != saMaxAmountReq && iDry != vpsPaths.size()) { - // Have not met requested amount or max send. Prepare for next pass. + // Have not met requested amount or max send, try to do more. Prepare for next pass. // Merge best pass' umReverse. rc.mumSource.insert(pspBest->umReverse.begin(), pspBest->umReverse.end()); + } else if (!bPartialPayment) { diff --git a/src/RippleCalc.h b/src/RippleCalc.h index b212c162e..7bd6321d2 100644 --- a/src/RippleCalc.h +++ b/src/RippleCalc.h @@ -90,14 +90,15 @@ public: LedgerEntrySet lesEntries; - int mIndex; - uint64 uQuality; // 0 = none. + int mIndex; // Index/rank amoung siblings. + uint64 uQuality; // 0 = no quality/liquity left. const STAmount& saInReq; // --> Max amount to spend by sender. STAmount saInAct; // --> Amount spent by sender so far. STAmount saInPass; // <-- Amount spent by sender. const STAmount& saOutReq; // --> Amount to send. STAmount saOutAct; // --> Amount actually sent so far. STAmount saOutPass; // <-- Amount actually sent. + bool bConsumed; // If true, use consumes full liquidity. False, may or may not. PathState( const int iIndex, diff --git a/src/SHAMapSync.cpp b/src/SHAMapSync.cpp index b8202fb96..e0ced0d8d 100644 --- a/src/SHAMapSync.cpp +++ b/src/SHAMapSync.cpp @@ -41,6 +41,7 @@ void SHAMap::getMissingNodes(std::vector& nodeIDs, std::vector& nodeIDs, std::vectorgetChildNodeID(branch)); + have_all = false; + nodeIDs.push_back(childID); if (--max <= 0) return; } @@ -84,6 +86,8 @@ void SHAMap::getMissingNodes(std::vector& nodeIDs, std::vectorsetFullBelow(); } } diff --git a/src/SerializeProto.h b/src/SerializeProto.h index 1a7b67641..f74413ad6 100644 --- a/src/SerializeProto.h +++ b/src/SerializeProto.h @@ -35,7 +35,7 @@ FIELD(Flags, UINT32, 2) FIELD(SourceTag, UINT32, 3) FIELD(Sequence, UINT32, 4) - FIELD(LastTxnSeq, UINT32, 5) + FIELD(PreviousTxnLgrSeq, UINT32, 5) FIELD(LedgerSequence, UINT32, 6) FIELD(CloseTime, UINT32, 7) FIELD(ParentCloseTime, UINT32, 8) @@ -71,7 +71,7 @@ FIELD(ParentHash, HASH256, 2) FIELD(TransactionHash, HASH256, 3) FIELD(AccountHash, HASH256, 4) - FIELD(LastTxnID, HASH256, 5) + FIELD(PreviousTxnID, HASH256, 5) FIELD(LedgerIndex, HASH256, 6) FIELD(WalletLocator, HASH256, 7) FIELD(PublishHash, HASH256, 8) @@ -95,12 +95,6 @@ // currency amount (uncommon) FIELD(MinimumOffer, AMOUNT, 16) FIELD(RippleEscrow, AMOUNT, 17) - FIELD(PreviousBalance, AMOUNT, 18) - FIELD(FinalBalance, AMOUNT, 19) - FIELD(PreviousTakerPays, AMOUNT, 20) - FIELD(PreviousTakerGets, AMOUNT, 21) - FIELD(FinalTakerPays, AMOUNT, 22) - FIELD(FinalTakerGets, AMOUNT, 23) // variable length FIELD(PublicKey, VL, 1) @@ -123,16 +117,12 @@ FIELD(Target, ACCOUNT, 7) FIELD(AuthorizedKey, ACCOUNT, 8) - // account (uncommon) - FIELD(PreviousAccount, ACCOUNT, 16) - FIELD(LowID, ACCOUNT, 17) - FIELD(HighID, ACCOUNT, 18) - // path set FIELD(Paths, PATHSET, 1) // vector of 256-bit FIELD(Indexes, VECTOR256, 1) + FIELD(Hashes, VECTOR256, 2) // inner object // OBJECT/1 is reserved for end of object @@ -141,6 +131,8 @@ FIELD(CreatedNode, OBJECT, 3) FIELD(DeletedNode, OBJECT, 4) FIELD(ModifiedNode, OBJECT, 5) + FIELD(PreviousFields, OBJECT, 6) + FIELD(FinalFields, OBJECT, 7) // array of objects // ARRAY/1 is reserved for end of array diff --git a/src/SerializedLedger.cpp b/src/SerializedLedger.cpp index 0e22a190e..359a93fa5 100644 --- a/src/SerializedLedger.cpp +++ b/src/SerializedLedger.cpp @@ -75,37 +75,37 @@ Json::Value SerializedLedgerEntry::getJson(int options) const bool SerializedLedgerEntry::isThreadedType() { - return getFieldIndex(sfLastTxnID) != -1; + return getFieldIndex(sfPreviousTxnID) != -1; } bool SerializedLedgerEntry::isThreaded() { - return isFieldPresent(sfLastTxnID); + return isFieldPresent(sfPreviousTxnID); } uint256 SerializedLedgerEntry::getThreadedTransaction() { - return getFieldH256(sfLastTxnID); + return getFieldH256(sfPreviousTxnID); } uint32 SerializedLedgerEntry::getThreadedLedger() { - return getFieldU32(sfLastTxnSeq); + return getFieldU32(sfPreviousTxnLgrSeq); } bool SerializedLedgerEntry::thread(const uint256& txID, uint32 ledgerSeq, uint256& prevTxID, uint32& prevLedgerID) { - uint256 oldPrevTxID = getFieldH256(sfLastTxnID); + uint256 oldPrevTxID = getFieldH256(sfPreviousTxnID); Log(lsTRACE) << "Thread Tx:" << txID << " prev:" << oldPrevTxID; if (oldPrevTxID == txID) { // this transaction is already threaded - assert(getFieldU32(sfLastTxnSeq) == ledgerSeq); + assert(getFieldU32(sfPreviousTxnLgrSeq) == ledgerSeq); return false; } prevTxID = oldPrevTxID; - prevLedgerID = getFieldU32(sfLastTxnSeq); - setFieldH256(sfLastTxnID, txID); - setFieldU32(sfLastTxnSeq, ledgerSeq); + prevLedgerID = getFieldU32(sfPreviousTxnLgrSeq); + setFieldH256(sfPreviousTxnID, txID); + setFieldU32(sfPreviousTxnLgrSeq, ledgerSeq); return true; } diff --git a/src/SerializedObject.cpp b/src/SerializedObject.cpp index afd876410..1477a2393 100644 --- a/src/SerializedObject.cpp +++ b/src/SerializedObject.cpp @@ -206,6 +206,8 @@ bool STObject::isValidForType() bool STObject::isFieldAllowed(SField::ref field) { + if (isFree()) + return true; BOOST_FOREACH(SOElement::ptr elem, mType) { // are any required elemnents missing if (elem->e_field == field) @@ -242,6 +244,14 @@ std::auto_ptr STObject::deserialize(SerializerIterator& sit, SFi return object; } +bool STObject::hasMatchingEntry(const SerializedType& t) +{ + const SerializedType* o = peekAtPField(t.getFName()); + if (!o) + return false; + return t == *o; +} + std::string STObject::getFullText() const { std::string ret; diff --git a/src/SerializedObject.h b/src/SerializedObject.h index 02587d36b..00c89ed1e 100644 --- a/src/SerializedObject.h +++ b/src/SerializedObject.h @@ -58,6 +58,7 @@ public: virtual SerializedTypeID getSType() const { return STI_OBJECT; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return mData.empty(); } virtual void add(Serializer& s) const { add(s, true); } // just inner elements void add(Serializer& s, bool withSignature) const; @@ -156,6 +157,9 @@ public: iterator end() { return mData.end(); } const_iterator begin() const { return mData.begin(); } const_iterator end() const { return mData.end(); } + bool empty() const { return mData.empty(); } + + bool hasMatchingEntry(const SerializedType&); bool operator==(const STObject& o) const; bool operator!=(const STObject& o) const { return ! (*this == o); } @@ -238,6 +242,7 @@ public: virtual SerializedTypeID getSType() const { return STI_ARRAY; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.empty(); } }; inline STArray::iterator range_begin(STArray& x) { return x.begin(); } diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 80f5ca110..62f7347c8 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -72,6 +72,8 @@ public: { return (getSType() == t.getSType()) && isEquivalent(t); } bool operator!=(const SerializedType& t) const { return (getSType() != t.getSType()) || !isEquivalent(t); } + + virtual bool isDefault() const { return true; } }; inline SerializedType* new_clone(const SerializedType& s) { return s.clone().release(); } @@ -103,6 +105,7 @@ public: operator unsigned char() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value == 0; } }; class STUInt16 : public SerializedType @@ -130,6 +133,7 @@ public: operator uint16() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value == 0; } }; class STUInt32 : public SerializedType @@ -157,6 +161,7 @@ public: operator uint32() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value == 0; } }; class STUInt64 : public SerializedType @@ -184,6 +189,7 @@ public: operator uint64() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value == 0; } }; class STAmount : public SerializedType @@ -200,12 +206,12 @@ class STAmount : public SerializedType // Low 56 bits are value, legal range is 10^15 to (10^16 - 1) inclusive protected: - uint160 mCurrency; - uint160 mIssuer; // Only for access, not compared. + uint160 mCurrency; // Compared by ==. Always update mIsNative. + uint160 mIssuer; // Not compared by ==. 0 for XNS. uint64 mValue; int mOffset; - bool mIsNative; // True for native stamps, ripple stamps are not native. + bool mIsNative; // Always !mCurrency. Native is XNS. bool mIsNegative; void canonicalize(); @@ -282,12 +288,17 @@ public: bool isGEZero() const { return !mIsNegative; } operator bool() const { return !isZero(); } - void negate() { if (!isZero()) mIsNegative = !mIsNegative; } - void zero() { mOffset = mIsNative ? -100 : 0; mValue = 0; mIsNegative = false; } + STAmount* negate() { if (!isZero()) mIsNegative = !mIsNegative; return this; } + STAmount* zero() { mOffset = mIsNative ? 0 : -100; mValue = 0; mIsNegative = false; return this; } + + // Zero while copying currency and issuer. + STAmount* zero(const STAmount& saTmpl) + { mCurrency = saTmpl.mCurrency; mIssuer = saTmpl.mIssuer; mIsNative = saTmpl.mIsNative; return zero(); } + int compare(const STAmount&) const; const uint160& getIssuer() const { return mIssuer; } - void setIssuer(const uint160& uIssuer) { mIssuer = uIssuer; } + STAmount* setIssuer(const uint160& uIssuer) { mIssuer = uIssuer; return this; } const uint160& getCurrency() const { return mCurrency; } bool setValue(const std::string& sAmount); @@ -295,6 +306,7 @@ public: void setValue(const STAmount &); virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return mValue == 0 && mIssuer.isZero() && mCurrency.isZero(); } bool operator==(const STAmount&) const; bool operator!=(const STAmount&) const; @@ -398,6 +410,7 @@ public: operator uint128() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.isZero(); } }; class STHash160 : public SerializedType @@ -428,6 +441,7 @@ public: operator uint160() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.isZero(); } }; class STHash256 : public SerializedType @@ -458,6 +472,7 @@ public: operator uint256() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.isZero(); } }; class STVariableLength : public SerializedType @@ -489,6 +504,7 @@ public: operator std::vector() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.empty(); } }; class STAccount : public STVariableLength @@ -668,6 +684,7 @@ public: void addPath(const STPath& e) { value.push_back(e); } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.empty(); } void printDebug(); @@ -735,6 +752,7 @@ public: const std::vector& peekValue() const { return mValue; } std::vector& peekValue() { return mValue; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return mValue.empty(); } std::vector getValue() const { return mValue; } bool isEmpty() const { return mValue.empty(); } diff --git a/src/Serializer.h b/src/Serializer.h index 93add9d61..fc1f1df49 100644 --- a/src/Serializer.h +++ b/src/Serializer.h @@ -90,16 +90,16 @@ public: { return getPrefixHash(prefix, reinterpret_cast(strData.c_str()), strData.size()); } // totality functions + const std::vector& peekData() const { return mData; } + std::vector getData() const { return mData; } int getCapacity() const { return mData.capacity(); } int getDataLength() const { return mData.size(); } const void* getDataPtr() const { return &mData.front(); } void* getDataPtr() { return &mData.front(); } int getLength() const { return mData.size(); } - const std::vector& peekData() const { return mData; } - std::vector getData() const { return mData; } - std::string getString() const { return std::string(static_cast(getDataPtr()), size()); } - void secureErase() { memset(&(mData.front()), 0, mData.size()); erase(); } - void erase() { mData.clear(); } + std::string getString() const { return std::string(static_cast(getDataPtr()), size()); } + void secureErase() { memset(&(mData.front()), 0, mData.size()); erase(); } + void erase() { mData.clear(); } int removeLastByte(); bool chop(int num); @@ -113,10 +113,10 @@ public: void resize(size_t n) { mData.resize(n); } size_t capacity() const { return mData.capacity(); } - bool operator==(const std::vector& v) { return v == mData; } - bool operator!=(const std::vector& v) { return v != mData; } - bool operator==(const Serializer& v) { return v.mData == mData; } - bool operator!=(const Serializer& v) { return v.mData != mData; } + bool operator==(const std::vector& v) { return v == mData; } + bool operator!=(const std::vector& v) { return v != mData; } + bool operator==(const Serializer& v) { return v.mData == mData; } + bool operator!=(const Serializer& v) { return v.mData != mData; } // signature functions bool checkSignature(int pubkeyOffset, int signatureOffset) const; @@ -147,12 +147,12 @@ public: // Reference is not const because we don't want to bind to a temporary SerializerIterator(Serializer& s) : mSerializer(s), mPos(0) { ; } - void reset(void) { mPos = 0; } - void setPos(int p) { mPos = p; } const Serializer& operator*(void) { return mSerializer; } + void reset(void) { mPos = 0; } + void setPos(int p) { mPos = p; } - int getPos(void) { return mPos; } - bool empty() { return mPos == mSerializer.getLength(); } + int getPos(void) { return mPos; } + bool empty() { return mPos == mSerializer.getLength(); } int getBytesLeft(); // get functions throw on error diff --git a/src/TaggedCache.h b/src/TaggedCache.h index cf1b55bab..ece2b3663 100644 --- a/src/TaggedCache.h +++ b/src/TaggedCache.h @@ -4,6 +4,7 @@ #include #include #include +#include #include // This class implemented a cache and a map. The cache keeps objects alive diff --git a/src/TransactionMeta.cpp b/src/TransactionMeta.cpp index 2a504247c..f8fdff08d 100644 --- a/src/TransactionMeta.cpp +++ b/src/TransactionMeta.cpp @@ -101,15 +101,15 @@ void TransactionMetaSet::swap(TransactionMetaSet& s) bool TransactionMetaSet::thread(STObject& node, const uint256& prevTxID, uint32 prevLgrID) { - if (node.getFieldIndex(sfLastTxnID) == -1) + if (node.getFieldIndex(sfPreviousTxnID) == -1) { - assert(node.getFieldIndex(sfLastTxnSeq) == -1); - node.setFieldH256(sfLastTxnID, prevTxID); - node.setFieldU32(sfLastTxnSeq, prevLgrID); + assert(node.getFieldIndex(sfPreviousTxnLgrSeq) == -1); + node.setFieldH256(sfPreviousTxnID, prevTxID); + node.setFieldU32(sfPreviousTxnLgrSeq, prevLgrID); return true; } - assert(node.getFieldH256(sfLastTxnID) == prevTxID); - assert(node.getFieldU32(sfLastTxnSeq) == prevLgrID); + assert(node.getFieldH256(sfPreviousTxnID) == prevTxID); + assert(node.getFieldU32(sfPreviousTxnLgrSeq) == prevLgrID); return false; } diff --git a/src/UniqueNodeList.cpp b/src/UniqueNodeList.cpp index d8f374993..8d11acde2 100644 --- a/src/UniqueNodeList.cpp +++ b/src/UniqueNodeList.cpp @@ -58,8 +58,8 @@ void UniqueNodeList::start() { miscLoad(); - Log(lsDEBUG) << "Validator fetch updated: " << mtpFetchUpdated; - Log(lsDEBUG) << "Validator score updated: " << mtpScoreUpdated; + cLog(lsDEBUG) << "Validator fetch updated: " << mtpFetchUpdated; + cLog(lsDEBUG) << "Validator score updated: " << mtpScoreUpdated; fetchNext(); // Start fetching. scoreNext(false); // Start scoring. @@ -139,10 +139,10 @@ bool UniqueNodeList::scoreRound(std::vector& vsnNodes) if (sLog(lsTRACE)) { - Log(lsTRACE) << "midway: "; + cLog(lsTRACE) << "midway: "; BOOST_FOREACH(scoreNode& sn, vsnNodes) { - Log(lsTRACE) << str(boost::format("%s| %d, %d, %d: [%s]") + cLog(lsTRACE) << str(boost::format("%s| %d, %d, %d: [%s]") % sn.strValidator % sn.iScore % sn.iRoundScore @@ -164,10 +164,10 @@ bool UniqueNodeList::scoreRound(std::vector& vsnNodes) if (sLog(lsTRACE)) { - Log(lsTRACE) << "finish: "; + cLog(lsTRACE) << "finish: "; BOOST_FOREACH(scoreNode& sn, vsnNodes) { - Log(lsTRACE) << str(boost::format("%s| %d, %d, %d: [%s]") + cLog(lsTRACE) << str(boost::format("%s| %d, %d, %d: [%s]") % sn.strValidator % sn.iScore % sn.iRoundScore @@ -290,7 +290,7 @@ void UniqueNodeList::scoreCompute() { BOOST_FOREACH(scoreNode& sn, vsnNodes) { - Log(lsTRACE) << str(boost::format("%s| %d, %d, %d") + cLog(lsTRACE) << str(boost::format("%s| %d, %d, %d") % sn.strValidator % sn.iScore % sn.iRoundScore @@ -376,10 +376,10 @@ void UniqueNodeList::scoreCompute() if (sLog(lsTRACE)) { - Log(lsTRACE) << "Scored:"; + cLog(lsTRACE) << "Scored:"; BOOST_FOREACH(scoreNode& sn, vsnNodes) { - Log(lsTRACE) << str(boost::format("%s| %d, %d, %d: [%s]") + cLog(lsTRACE) << str(boost::format("%s| %d, %d, %d: [%s]") % sn.strValidator % sn.iScore % sn.iRoundScore diff --git a/src/rpc.cpp b/src/rpc.cpp index 281c68e10..15f53061c 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -17,6 +17,8 @@ #include "Log.h" #include "Version.h" +SETUP_LOG(); + using namespace boost; using namespace boost::asio; @@ -69,7 +71,7 @@ std::string rfc1123Time() std::string HTTPReply(int nStatus, const std::string& strMsg) { - Log(lsTRACE) << "HTTP Reply " << nStatus << " " << strMsg; + cLog(lsTRACE) << "HTTP Reply " << nStatus << " " << strMsg; if (nStatus == 401) return strprintf("HTTP/1.0 401 Authorization Required\r\n" diff --git a/test/send-test.js b/test/send-test.js index 898132209..13ca51376 100644 --- a/test/send-test.js +++ b/test/send-test.js @@ -12,7 +12,7 @@ var serverDelay = 1500; buster.testRunner.timeout = 3000; -buster.testCase("// Sending", { +buster.testCase("Sending", { 'setUp' : testutils.build_setup(), 'tearDown' : testutils.build_teardown(), @@ -234,7 +234,7 @@ buster.testCase("// Sending", { }); // XXX In the future add ledger_accept after partial retry is implemented in the server. -buster.testCase("// Sending future", { +buster.testCase("Sending future", { 'setUp' : testutils.build_setup(), 'tearDown' : testutils.build_teardown(), @@ -242,7 +242,7 @@ buster.testCase("// Sending future", { function (done) { var self = this; - self.remote.set_trace(); + // self.remote.set_trace(); async.waterfall([ function (callback) { @@ -279,12 +279,6 @@ buster.testCase("// Sending future", { self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT') .once('ripple_state', function (m) { - console.log("BALANCE: %s", JSON.stringify(m)); - console.log("account_balance: %s", m.account_balance.to_text_full()); - console.log("account_limit: %s", m.account_limit.to_text_full()); - console.log("issuer_balance: %s", m.issuer_balance.to_text_full()); - console.log("issuer_limit: %s", m.issuer_limit.to_text_full()); - buster.assert(m.account_balance.equals("-24/USD/alice")); buster.assert(m.issuer_balance.equals("24/USD/bob")); @@ -401,7 +395,7 @@ buster.testCase("// Sending future", { .payment('bob', 'alice', "1/USD/bob") .once('proposed', function (m) { // console.log("proposed: %s", JSON.stringify(m)); - callback(m.result != 'tepPATH_PARTIAL'); + callback(m.result != 'tepPATH_DRY'); }) .submit(); }, @@ -454,14 +448,14 @@ buster.testCase("// Sending future", { }); buster.testCase("Indirect ripple", { - 'setUp' : testutils.build_setup({ verbose: true, no_server: false }), - 'tearDown' : testutils.test_teardown, + 'setUp' : testutils.build_setup({ verbose: false, no_server: false }), + 'tearDown' : testutils.build_teardown(), "indirect ripple" : function (done) { var self = this; - self.remote.set_trace(); + // self.remote.set_trace(); async.waterfall([ function (callback) { @@ -499,13 +493,40 @@ buster.testCase("Indirect ripple", { testutils.verify_balance(self.remote, "bob", "50/USD/mtgox", callback); }, + function (callback) { + self.what = "Alice sends more than has to issuer: 100 out of 70"; + + self.remote.transaction() + .payment("alice", "mtgox", "100/USD/mtgox") + .on('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + + callback(m.result != 'tepPATH_PARTIAL'); + }) + .submit(); + }, + function (callback) { + self.what = "Alice sends more than has to bob: 100 out of 70"; + + self.remote.transaction() + .payment("alice", "bob", "100/USD/mtgox") + .on('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + + callback(m.result != 'tepPATH_PARTIAL'); + }) + .submit(); + }, ], function (error) { buster.refute(error, self.what); done(); }); }, + // Direct ripple without no liqudity. // Ripple without credit path. // Ripple with one-way credit path. + // Transfer Fees + // Use multiple paths. }); // vim:sw=2:sts=2:ts=8 diff --git a/test/testutils.js b/test/testutils.js index d573111c8..8ab9af69e 100644 --- a/test/testutils.js +++ b/test/testutils.js @@ -117,7 +117,7 @@ var credit_limit = function (remote, src, amount, callback) { remote.transaction() .ripple_line_set(src, amount) .on('proposed', function (m) { - console.log("proposed: %s", JSON.stringify(m)); + // console.log("proposed: %s", JSON.stringify(m)); callback(m.result != 'tesSUCCESS'); }) @@ -153,11 +153,11 @@ var verify_balance = function (remote, src, amount_json, callback) { remote.request_ripple_balance(src, amount.issuer.to_json(), amount.currency.to_json(), 'CURRENT') .once('ripple_state', function (m) { - console.log("BALANCE: %s", JSON.stringify(m)); - console.log("account_balance: %s", m.account_balance.to_text_full()); - console.log("account_limit: %s", m.account_limit.to_text_full()); - console.log("issuer_balance: %s", m.issuer_balance.to_text_full()); - console.log("issuer_limit: %s", m.issuer_limit.to_text_full()); +// console.log("BALANCE: %s", JSON.stringify(m)); +// console.log("account_balance: %s", m.account_balance.to_text_full()); +// console.log("account_limit: %s", m.account_limit.to_text_full()); +// console.log("issuer_balance: %s", m.issuer_balance.to_text_full()); +// console.log("issuer_limit: %s", m.issuer_limit.to_text_full()); callback(!m.account_balance.equals(amount)); })