diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj
index 374a65bd62..7867ca8f90 100644
--- a/ripple2010.vcxproj
+++ b/ripple2010.vcxproj
@@ -122,6 +122,7 @@
+
diff --git a/ripple2010.vcxproj.filters b/ripple2010.vcxproj.filters
index 9e03588ae8..5650c27636 100644
--- a/ripple2010.vcxproj.filters
+++ b/ripple2010.vcxproj.filters
@@ -345,6 +345,9 @@
Source Files
+
+ Source Files
+
diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp
index aa5dc56618..7505a4c24e 100644
--- a/src/cpp/ripple/HashedObject.cpp
+++ b/src/cpp/ripple/HashedObject.cpp
@@ -60,6 +60,7 @@ void HashedObjectStore::waitWrite()
void HashedObjectStore::bulkWrite()
{
+ LoadEvent::pointer event = theApp->getJobQueue().getLoadEvent(jtDISK);
while (1)
{
std::vector< boost::shared_ptr > set;
diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp
index cbb2bee83f..a72e8ddae0 100644
--- a/src/cpp/ripple/JobQueue.cpp
+++ b/src/cpp/ripple/JobQueue.cpp
@@ -1,6 +1,5 @@
#include "JobQueue.h"
-#include
#include
#include
#include
@@ -21,6 +20,9 @@ const char* Job::toString(JobType t)
case jtPROPOSAL_t: return "trustedProposal";
case jtADMIN: return "administration";
case jtDEATH: return "jobOfDeath";
+ case jtCLIENT: return "clientCommand";
+ case jtPEER: return "peerCommand";
+ case jtDISK: return "diskAccess";
default: assert(false); return "unknown";
}
}
@@ -68,7 +70,7 @@ void JobQueue::addJob(JobType type, const boost::function& jobFunc)
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));
+ mJobSet.insert(Job(type, ++mLastJob, mJobLoads[type], jobFunc));
++mJobCounts[type];
mJobCond.notify_one();
}
@@ -108,6 +110,43 @@ std::vector< std::pair > JobQueue::getJobCounts()
return ret;
}
+Json::Value JobQueue::getJson(int)
+{
+ Json::Value ret(Json::objectValue);
+ boost::mutex::scoped_lock sl(mJobLock);
+
+ ret["threads"] = mThreadCount;
+
+ Json::Value priorities = Json::arrayValue;
+ for (int i = 0; i < NUM_JOB_TYPES; ++i)
+ {
+ uint64 count, latencyAvg, latencyPeak, jobCount;
+ mJobLoads[i].getCountAndLatency(count, latencyAvg, latencyPeak);
+ std::map::iterator it = mJobCounts.find(static_cast(i));
+ if (it == mJobCounts.end())
+ jobCount = 0;
+ else
+ jobCount = it->second;
+ if ((count != 0) || (jobCount != 0) || (latencyPeak != 0))
+ {
+ Json::Value pri(Json::objectValue);
+ pri["job_type"] = Job::toString(static_cast(i));
+ if (jobCount != 0)
+ pri["waiting"] = static_cast(jobCount);
+ if (count != 0)
+ pri["per_second"] = static_cast(count);
+ if (latencyPeak != 0)
+ pri["peak_latency"] = static_cast(latencyPeak);
+ if (latencyAvg != 0)
+ pri["avg_latency"] = static_cast(latencyAvg);
+ priorities.append(pri);
+ }
+ }
+ ret["job_types"] = priorities;
+
+ return ret;
+}
+
void JobQueue::shutdown()
{ // shut down the job queue without completing pending jobs
cLog(lsINFO) << "Job queue shutting down";
diff --git a/src/cpp/ripple/JobQueue.h b/src/cpp/ripple/JobQueue.h
index bdd23bc690..339804d206 100644
--- a/src/cpp/ripple/JobQueue.h
+++ b/src/cpp/ripple/JobQueue.h
@@ -8,26 +8,36 @@
#include
#include
#include
+#include
+
+#include "../json/value.h"
#include "types.h"
+#include "LoadMonitor.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, // A validation from an untrusted source
- jtCLIENTOP_ut, // A client operation from a non-local/untrusted source
- jtTRANSACTION, // A transaction received from the network
- jtPROPOSAL_ut, // A proposal from an untrusted source
- jtCLIENTOP_t, // A client operation from a trusted source
- jtVALIDATION_t, // A validation from a trusted source
- jtTRANSACTION_l, // A local transaction
- jtPROPOSAL_t, // A proposal from a trusted source
- jtADMIN, // An administrative operation
- jtDEATH, // job of death, used internally
+ jtINVALID = -1,
+ jtVALIDATION_ut = 0, // A validation from an untrusted source
+ jtCLIENTOP_ut = 1, // A client operation from a non-local/untrusted source
+ jtTRANSACTION = 2, // A transaction received from the network
+ jtPROPOSAL_ut = 3, // A proposal from an untrusted source
+ jtCLIENTOP_t = 4, // A client operation from a trusted source
+ jtVALIDATION_t = 5, // A validation from a trusted source
+ jtTRANSACTION_l = 6, // A local transaction
+ jtPROPOSAL_t = 7, // A proposal from a trusted source
+ jtADMIN = 8, // An administrative operation
+ jtDEATH = 9, // job of death, used internally
+
+// special types not dispatched by the job pool
+ jtCLIENT = 10,
+ jtPEER = 11,
+ jtDISK = 12,
};
+#define NUM_JOB_TYPES 16
class Job
{
@@ -35,13 +45,18 @@ protected:
JobType mType;
uint64 mJobIndex;
boost::function mJob;
+ LoadEvent::pointer mLoadMonitor;
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) { ; }
+ Job() : mType(jtINVALID), mJobIndex(0) { ; }
+
+ Job(JobType type, uint64 index) : mType(type), mJobIndex(index)
+ { ; }
+
+ Job(JobType type, uint64 index, LoadMonitor& lm, const boost::function& job)
+ : mType(type), mJobIndex(index), mJob(job)
+ { mLoadMonitor = boost::make_shared(boost::ref(lm), true, 1); }
JobType getType() const { return mType; }
void doJob(void) { mJob(*this); }
@@ -57,14 +72,15 @@ public:
class JobQueue
{
protected:
- boost::mutex mJobLock;
- boost::condition_variable mJobCond;
+ boost::mutex mJobLock;
+ boost::condition_variable mJobCond;
- uint64 mLastJob;
- std::set mJobSet;
- std::map mJobCounts;
- int mThreadCount;
- bool mShuttingDown;
+ uint64 mLastJob;
+ std::set mJobSet;
+ std::map mJobCounts;
+ LoadMonitor mJobLoads[NUM_JOB_TYPES];
+ int mThreadCount;
+ bool mShuttingDown;
void threadEntry(void);
@@ -81,6 +97,11 @@ public:
void shutdown();
void setThreadCount(int c = 0);
+
+ LoadEvent::pointer getLoadEvent(JobType t)
+ { return boost::make_shared(boost::ref(mJobLoads[t]), true, 1); }
+
+ Json::Value getJson(int c = 0);
};
#endif
diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp
index 03e0a48d1b..8f6d87e798 100644
--- a/src/cpp/ripple/Ledger.cpp
+++ b/src/cpp/ripple/Ledger.cpp
@@ -341,6 +341,7 @@ uint256 Ledger::getHash()
void Ledger::saveAcceptedLedger(bool fromConsensus)
{ // can be called in a different thread
+ LoadEvent::pointer event = theApp->getJobQueue().getLoadEvent(jtDISK);
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;");
diff --git a/src/cpp/ripple/LoadMonitor.cpp b/src/cpp/ripple/LoadMonitor.cpp
new file mode 100644
index 0000000000..2011035aec
--- /dev/null
+++ b/src/cpp/ripple/LoadMonitor.cpp
@@ -0,0 +1,90 @@
+#include "LoadMonitor.h"
+
+void LoadMonitor::update()
+{ // call with the mutex
+ time_t now = time(NULL);
+
+ if (now == mLastUpdate) // current
+ return;
+
+ if ((now < mLastUpdate) || (now > (mLastUpdate + 8)))
+ { // way out of date
+ mCounts = 0;
+ mLatencyEvents = 0;
+ mLatencyMSAvg = 0;
+ mLatencyMSPeak = 0;
+ mLastUpdate = now;
+ return;
+ }
+
+ do
+ { // do exponential decay
+ ++mLastUpdate;
+ mCounts -= ((mCounts + 3) / 4);
+ mLatencyEvents -= ((mLatencyEvents + 3) / 4);
+ mLatencyMSAvg -= (mLatencyMSAvg / 4);
+ mLatencyMSPeak -= (mLatencyMSPeak / 4);
+ } while (mLastUpdate < now);
+}
+
+void LoadMonitor::addCount(int counts)
+{
+ boost::mutex::scoped_lock sl(mLock);
+
+ update();
+ mCounts += counts;
+}
+
+void LoadMonitor::addLatency(int latency)
+{
+ if (latency == 1)
+ latency = 0;
+ boost::mutex::scoped_lock sl(mLock);
+
+ update();
+
+ ++mLatencyEvents;
+ mLatencyMSAvg += latency;
+ mLatencyMSPeak += latency;
+
+ int lp = mLatencyEvents * latency * 4;
+ if (mLatencyMSPeak < lp)
+ mLatencyMSPeak = lp;
+}
+
+void LoadMonitor::addCountAndLatency(int counts, int latency)
+{
+ if (latency == 1)
+ latency = 0;
+ boost::mutex::scoped_lock sl(mLock);
+
+ update();
+ mCounts += counts;
+ ++mLatencyEvents;
+ mLatencyMSAvg += latency;
+ mLatencyMSPeak += latency;
+
+ int lp = mLatencyEvents * latency * 4;
+ if (mLatencyMSPeak < lp)
+ mLatencyMSPeak = lp;
+}
+
+void LoadMonitor::getCountAndLatency(uint64& count, uint64& latencyAvg, uint64& latencyPeak)
+{
+ boost::mutex::scoped_lock sl(mLock);
+
+ update();
+
+ count = mCounts / 4;
+
+ if (mLatencyEvents == 0)
+ {
+ latencyAvg = 0;
+ latencyPeak = 0;
+ }
+ else
+ {
+ latencyAvg = mLatencyMSAvg / (mLatencyEvents * 4);
+ latencyPeak = mLatencyMSPeak / (mLatencyEvents * 4);
+ }
+}
diff --git a/src/cpp/ripple/LoadMonitor.h b/src/cpp/ripple/LoadMonitor.h
new file mode 100644
index 0000000000..08c2afbf21
--- /dev/null
+++ b/src/cpp/ripple/LoadMonitor.h
@@ -0,0 +1,76 @@
+#ifndef LOADMONITOR__H_
+#define LOADMONITOR__H_
+
+#include
+
+#include
+#include
+
+#include "types.h"
+
+// Monitors load levels and response times
+
+class LoadMonitor
+{
+protected:
+ uint64 mCounts;
+ uint64 mLatencyEvents;
+ uint64 mLatencyMSAvg;
+ uint64 mLatencyMSPeak;
+ time_t mLastUpdate;
+ boost::mutex mLock;
+
+ void update();
+
+public:
+ LoadMonitor() : mCounts(0), mLatencyEvents(0), mLatencyMSAvg(0), mLatencyMSPeak(0)
+ { mLastUpdate = time(NULL); }
+
+ void addCount(int counts);
+ void addLatency(int latency);
+ void addCountAndLatency(int counts, int latency);
+
+ void getCountAndLatency(uint64& count, uint64& latencyAvg, uint64& latencyPeak);
+};
+
+class LoadEvent
+{
+public:
+ typedef boost::shared_ptr pointer;
+
+protected:
+ LoadMonitor& mMonitor;
+ bool mRunning;
+ int mCount;
+ boost::posix_time::ptime mStartTime;
+
+public:
+ LoadEvent(LoadMonitor& monitor, bool shouldStart, int count) : mMonitor(monitor), mRunning(false), mCount(count)
+ {
+ mStartTime = boost::posix_time::microsec_clock::universal_time();
+ if (shouldStart)
+ start();
+ }
+
+ ~LoadEvent()
+ {
+ if (mRunning)
+ stop();
+ }
+
+ void start()
+ { // okay to call if already started
+ mRunning = true;
+ mStartTime = boost::posix_time::microsec_clock::universal_time();
+ }
+
+ void stop()
+ {
+ assert(mRunning);
+ mRunning = false;
+ mMonitor.addCountAndLatency(mCount,
+ (boost::posix_time::microsec_clock::universal_time() - mStartTime).total_milliseconds());
+ }
+};
+
+#endif
diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp
index 89a5d77e76..ed4847f9bd 100644
--- a/src/cpp/ripple/NetworkOPs.cpp
+++ b/src/cpp/ripple/NetworkOPs.cpp
@@ -957,22 +957,7 @@ Json::Value NetworkOPs::getServerInfo()
if (mConsensus)
info["consensus"] = mConsensus->getJson();
- typedef std::pair jt_int_pair;
- bool anyJobs = false;
- Json::Value jobs = Json::arrayValue;
- std::vector< std::pair > jobCounts = theApp->getJobQueue().getJobCounts();
- BOOST_FOREACH(jt_int_pair& it, jobCounts)
- {
- if (it.second != 0)
- {
- Json::Value o = Json::objectValue;
- o[Job::toString(it.first)] = it.second;
- jobs.append(o);
- anyJobs = true;
- }
- }
- if (anyJobs)
- info["jobs"] = jobs;
+ info["load"] = theApp->getJobQueue().getJson();
return info;
}
diff --git a/src/cpp/ripple/PaymentTransactor.cpp b/src/cpp/ripple/PaymentTransactor.cpp
index 7577341cb6..e2e6bceed4 100644
--- a/src/cpp/ripple/PaymentTransactor.cpp
+++ b/src/cpp/ripple/PaymentTransactor.cpp
@@ -4,12 +4,15 @@
#define RIPPLE_PATHS_MAX 3
-// TODO: only have the higher fee if the account doesn't in fact exist
+// only have the higher fee if the account doesn't in fact exist
void PaymentTransactor::calculateFee()
{
if (mTxn.getFlags() & tfCreateAccount)
{
- mFeeDue = theConfig.FEE_ACCOUNT_CREATE;
+ const uint160 uDstAccountID = mTxn.getFieldAccount160(sfDestination);
+ SLE::pointer sleDst = mEngine->entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID));
+ if(!sleDst) mFeeDue = theConfig.FEE_ACCOUNT_CREATE;
+ else Transactor::calculateFee();
}else Transactor::calculateFee();
}
@@ -146,7 +149,13 @@ TER PaymentTransactor::doApply()
else
{
mTxnAccount->setFieldAmount(sfBalance, saSrcXRPBalance - saDstAmount);
- sleDst->setFieldAmount(sfBalance, sleDst->getFieldAmount(sfBalance) + saDstAmount);
+ // re-arm the password change fee if we can and need to
+ if ( (sleDst->getFlags() & lsfPasswordSpent) &&
+ (saDstAmount > theConfig.FEE_DEFAULT) )
+ {
+ sleDst->setFieldAmount(sfBalance, sleDst->getFieldAmount(sfBalance) + saDstAmount-theConfig.FEE_DEFAULT);
+ sleDst->clearFlag(lsfPasswordSpent);
+ }else sleDst->setFieldAmount(sfBalance, sleDst->getFieldAmount(sfBalance) + saDstAmount);
terResult = tesSUCCESS;
}
diff --git a/src/cpp/ripple/Peer.cpp b/src/cpp/ripple/Peer.cpp
index f364df838b..76e396b3e3 100644
--- a/src/cpp/ripple/Peer.cpp
+++ b/src/cpp/ripple/Peer.cpp
@@ -373,8 +373,11 @@ void Peer::processReadBuffer()
// std::cerr << "Peer::processReadBuffer: " << mIpPort.first << " " << mIpPort.second << std::endl;
- // If connected and get a mtHELLO or if not connected and get a non-mtHELLO, wrong message was sent.
+ LoadEvent::pointer event = theApp->getJobQueue().getLoadEvent(jtPEER);
+
boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock());
+
+ // If connected and get a mtHELLO or if not connected and get a non-mtHELLO, wrong message was sent.
if (mHelloed == (type == ripple::mtHELLO))
{
cLog(lsWARNING) << "Wrong message type: " << type;
diff --git a/src/cpp/ripple/ProofOfWork.cpp b/src/cpp/ripple/ProofOfWork.cpp
index 044794a888..7243620597 100644
--- a/src/cpp/ripple/ProofOfWork.cpp
+++ b/src/cpp/ripple/ProofOfWork.cpp
@@ -214,46 +214,51 @@ struct PowEntry
int iterations;
};
-PowEntry PowEntries[32] =
-{
- // FIXME: These targets are too low and iteration counts too low
- // These get too difficulty before they become sufficently RAM intensive
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 256 }, // Hashes:5242880 KB=8
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 512 }, // Hashes:5242880 KB=16
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 512 }, // Hashes:10485760 KB=16
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1024 }, // Hashes:10485760 KB=32
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1024 }, // Hashes:20971520 KB=32
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2048 }, // Hashes:20971520 KB=64
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2048 }, // Hashes:41943040 KB=64
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 4096 }, // Hashes:41943040 KB=128
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 4096 }, // Hashes:83886080 KB=128
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 8192 }, // Hashes:83886080 KB=256
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 8192 }, // Hashes:167772160 KB=256
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16384 }, // Hashes:167772160 KB=512
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 32768 }, // Hashes:335544320 MB=1
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 32768 }, // Hashes:671088640 MB=1
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 65536 }, // Hashes:671088640 MB=2
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 65536 }, // Hashes:1342177280 MB=2
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 131072 }, // Hashes:1342177280 MB=4
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 131072 }, // Hashes:2684354560 MB=4
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144 }, // Hashes:2684354560 MB=8
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 524288 }, // Hashes:5368709120 MB=16
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 524288 }, // Hashes:10737418240 MB=16
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1048576 }, // Hashes:10737418240 MB=32
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1048576 }, // Hashes:21474836480 MB=32
- { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2097152 }, // Hashes:21474836480 MB=64
- { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2097152 }, // Hashes:42949672960 MB=64
- { "00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 524288 }, // Hashes:85899345920 MB=16
- { "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 524288 }, // Hashes:171798691840 MB=16
- { "00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1048576 }, // Hashes:171798691840 MB=32
- { "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1048576 }, // Hashes:343597383680 MB=32
- { "00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2097152 }, // Hashes:343597383680 MB=64
- { "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2097152 }, // Hashes:687194767360 MB=64
+PowEntry PowEntries[31] =
+{ // target iterations hashes memory
+ { "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 65536 }, // 1451874, 2 MB
+ { "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 98304 }, // 2177811, 3 MB
+ { "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 98304 }, // 3538944, 3 MB
+ { "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 4355623, 6 MB
+
+ { "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 131072}, // 4718592, 4 MB
+ { "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 5807497, 8 MB
+ { "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 7077888, 6 MB
+ { "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 9437184, 8 MB
+
+ { "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 14155776, 12MB
+ { "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 28311552, 12MB
+ { "00CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 92919965, 8 MB
+ { "00CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 139379948, 12MB
+
+ { "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 150994944, 8 MB
+ { "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 226492416, 12MB
+ { "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 49152 }, // 278759896, 1.5MB
+ { "003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 301989888, 8 MB
+
+ { "003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 452984832, 12MB
+ { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 98304 }, // 905969664, 3 MB
+ { "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 1115039586, 6 MB
+ { "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 1486719448 8 MB
+
+ { "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 2230079172 12MB
+ { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 2415919104, 8 MB
+ { "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 3623878656, 12MB
+ { "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 7247757312, 12MB
+
+ { "0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 23787511177, 8 MB
+ { "0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 35681266766, 12MB
+ { "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 131072}, // 38654705664, 4 MB
+ { "00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 38654705664, 8 MB
+
+ { "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 57982058496, 6 MB
+ { "00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 57982058496, 12MB
+ { "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 77309411328, 8 MB
};
void ProofOfWorkGenerator::setDifficulty(int i)
{
- assert((i >= 0) && (i <= 31));
+ assert((i >= 0) && (i <= 30));
time_t now = time(NULL);
boost::mutex::scoped_lock sl(mLock);
diff --git a/src/cpp/ripple/RegularKeySetTransactor.cpp b/src/cpp/ripple/RegularKeySetTransactor.cpp
index eb89bed283..f74b8f5a94 100644
--- a/src/cpp/ripple/RegularKeySetTransactor.cpp
+++ b/src/cpp/ripple/RegularKeySetTransactor.cpp
@@ -4,43 +4,30 @@
SETUP_LOG();
-// TODO:
-TER RegularKeySetTransactor::checkSig()
-{
- // Transaction's signing public key must be for the source account.
- // To prove the master private key made this transaction.
- if (mSigningPubKey.getAccountID() != mTxnAccountID)
- {
- // Signing Pub Key must be for Source Account ID.
- cLog(lsWARNING) << "sourceAccountID: " << mSigningPubKey.humanAccountID();
- cLog(lsWARNING) << "txn accountID: " << mTxn.getSourceAccount().humanAccountID();
- return temBAD_SET_ID;
- }
- return tesSUCCESS;
-}
-
-// TODO: this should be default fee if flag isn't set
void RegularKeySetTransactor::calculateFee()
{
- mFeeDue = 0;
+ Transactor::calculateFee();
+
+ if ( !(mTxnAccount->getFlags() & lsfPasswordSpent) &&
+ (mSigningPubKey.getAccountID() == mTxnAccountID))
+ { // flag is armed and they signed with the right account
+
+ mSourceBalance = mTxnAccount->getFieldAmount(sfBalance);
+ if(mSourceBalance < mFeeDue) mFeeDue = 0;
+ }
}
-// TODO: change to take a fee if there is one there
TER RegularKeySetTransactor::doApply()
{
std::cerr << "doRegularKeySet>" << std::endl;
- if (mTxnAccount->getFlags() & lsfPasswordSpent)
+ if(mFeeDue.isZero())
{
- std::cerr << "doRegularKeySet: Delay transaction: Funds already spent." << std::endl;
-
- return terFUNDS_SPENT;
+ mTxnAccount->setFlag(lsfPasswordSpent);
}
- mTxnAccount->setFlag(lsfPasswordSpent);
-
uint160 uAuthKeyID=mTxn.getFieldAccount160(sfRegularKey);
mTxnAccount->setFieldAccount(sfRegularKey, uAuthKeyID);
diff --git a/src/cpp/ripple/RegularKeySetTransactor.h b/src/cpp/ripple/RegularKeySetTransactor.h
index a6df0b3569..35d744c8f0 100644
--- a/src/cpp/ripple/RegularKeySetTransactor.h
+++ b/src/cpp/ripple/RegularKeySetTransactor.h
@@ -6,6 +6,5 @@ class RegularKeySetTransactor : public Transactor
public:
RegularKeySetTransactor(const SerializedTransaction& txn,TransactionEngineParams params, TransactionEngine* engine) : Transactor(txn,params,engine) {}
TER checkFee();
- TER checkSig();
TER doApply();
};
diff --git a/src/cpp/ripple/SerializedTypes.cpp b/src/cpp/ripple/SerializedTypes.cpp
index 61f526eeb0..b9daae2fe5 100644
--- a/src/cpp/ripple/SerializedTypes.cpp
+++ b/src/cpp/ripple/SerializedTypes.cpp
@@ -395,7 +395,7 @@ STPathSet* STPathSet::construct(SerializerIterator& s, SField::ref name)
if (bIssuer)
uIssuerID = s.get160();
- path.push_back(STPathElement(uAccountID, uCurrency, uIssuerID));
+ path.push_back(STPathElement(uAccountID, uCurrency, uIssuerID, bCurrency));
}
} while(1);
}
diff --git a/src/cpp/ripple/ValidationCollection.cpp b/src/cpp/ripple/ValidationCollection.cpp
index acb6c2c4b3..cab64a9508 100644
--- a/src/cpp/ripple/ValidationCollection.cpp
+++ b/src/cpp/ripple/ValidationCollection.cpp
@@ -289,6 +289,7 @@ void ValidationCollection::condWrite()
void ValidationCollection::doWrite()
{
+ LoadEvent::pointer event = theApp->getJobQueue().getLoadEvent(jtDISK);
static boost::format insVal("INSERT INTO LedgerValidations "
"(LedgerHash,NodePubKey,Flags,SignTime,Signature) VALUES ('%s','%s','%u','%u',%s);");
diff --git a/src/cpp/ripple/WSHandler.h b/src/cpp/ripple/WSHandler.h
index 43c9a9ce66..306647c5c5 100644
--- a/src/cpp/ripple/WSHandler.h
+++ b/src/cpp/ripple/WSHandler.h
@@ -1,6 +1,8 @@
#ifndef __WSHANDLER__
#define __WSHANDLER__
+#include "Application.h"
+
class WSConnection;
// A single instance of this object is made.
@@ -87,10 +89,11 @@ public:
void on_message(connection_ptr cpClient, message_ptr mpMessage)
{
+ LoadEvent::pointer event = theApp->getJobQueue().getLoadEvent(jtCLIENT);
Json::Value jvRequest;
Json::Reader jrReader;
- cLog(lsDEBUG) << "Ws:: Receiving '" << mpMessage->get_payload() << "'";
+ cLog(lsDEBUG) << "Ws:: Receiving '" << mpMessage->get_payload() << "'";
if (mpMessage->get_opcode() != websocketpp::frame::opcode::TEXT)
{
diff --git a/src/js/amount.js b/src/js/amount.js
index e75f248378..5f5f58e37c 100644
--- a/src/js/amount.js
+++ b/src/js/amount.js
@@ -353,6 +353,12 @@ Amount.prototype.issuer = function() {
return this._issuer;
};
+Amount.prototype.to_number = function(allow_nan) {
+ var s = this.to_text(allow_nan);
+
+ return ('string' === typeof s) ? Number(s) : s;
+}
+
// Convert only value to JSON wire format.
Amount.prototype.to_text = function(allow_nan) {
if (isNaN(this._value)) {
diff --git a/src/js/remote.js b/src/js/remote.js
index 0f990fe950..c7040e2396 100644
--- a/src/js/remote.js
+++ b/src/js/remote.js
@@ -806,13 +806,15 @@ Remote.prototype.ledger_accept = function () {
// Return a request to refresh the account balance.
Remote.prototype.request_account_balance = function (account, current) {
- return (this.request_ledger_entry('account_root'))
+ var request = this.request_ledger_entry('account_root');
+
+ return request
.account_root(account)
.ledger_choose(current)
.on('success', function (message) {
- // If the caller also waits for 'success', they might run before this.
- request.emit('account_balance', message.node.Balance);
- });
+ // If the caller also waits for 'success', they might run before this.
+ request.emit('account_balance', Amount.from_json(message.node.Balance));
+ });
};
// Return the next account sequence if possible.
diff --git a/test/offer-test.js b/test/offer-test.js
index fc42aac2e1..ef83dd5901 100644
--- a/test/offer-test.js
+++ b/test/offer-test.js
@@ -400,7 +400,7 @@ buster.testCase("Offer tests", {
testutils.verify_balances(self.remote,
{
- "alice" : [ "0/USD/mtgox", "500" ],
+ "alice" : [ "0/USD/mtgox", String(10000+500-2*(Remote.fees['default'].to_number())) ],
"bob" : "100/USD/mtgox",
},
callback);
@@ -482,7 +482,7 @@ buster.testCase("Offer tests", {
testutils.verify_balances(self.remote,
{
- "alice" : [ "160/USD/mtgox", "200" ], // XXX Verfiying XRP balance needs to account for fees.
+ "alice" : [ "160/USD/mtgox", String(10000+200-2*(Remote.fees['default'].to_number())) ],
"bob" : "40/USD/mtgox",
},
callback);
@@ -524,7 +524,7 @@ buster.testCase("Offer tests", {
testutils.verify_balances(self.remote,
{
- "alice" : [ "100/USD/mtgox", "500" ], // XXX Verfiying XRP balance needs to account for fees.
+ "alice" : [ "100/USD/mtgox", String(10000+200+300-4*(Remote.fees['default'].to_number())) ],
"bob" : "100/USD/mtgox",
},
callback);
@@ -534,8 +534,13 @@ buster.testCase("Offer tests", {
done();
});
},
+});
- "ripple cross currency payment" :
+buster.testCase("Offer cross currency", {
+ 'setUp' : testutils.build_setup(),
+ 'tearDown' : testutils.build_teardown(),
+
+ "ripple cross currency payment - start with XRP" :
// alice --> [XRP --> carol --> USD/mtgox] --> bob
function (done) {
@@ -616,5 +621,192 @@ buster.testCase("Offer tests", {
done();
});
},
+
+ "ripple cross currency payment - end with XRP" :
+ // alice --> [USD/mtgox --> carol --> XRP] --> bob
+
+ function (done) {
+ var self = this;
+ var seq;
+
+ // self.remote.set_trace();
+
+ async.waterfall([
+ function (callback) {
+ self.what = "Create accounts.";
+
+ testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "carol", "mtgox"], callback);
+ },
+ function (callback) {
+ self.what = "Set limits.";
+
+ testutils.credit_limits(self.remote,
+ {
+ "alice" : "1000/USD/mtgox",
+ "carol" : "2000/USD/mtgox"
+ },
+ callback);
+ },
+ function (callback) {
+ self.what = "Distribute funds.";
+
+ testutils.payments(self.remote,
+ {
+ "mtgox" : "500/USD/alice"
+ },
+ callback);
+ },
+ function (callback) {
+ self.what = "Create offer.";
+
+ self.remote.transaction()
+ .offer_create("carol", "50/USD/mtgox", "500")
+ .on('proposed', function (m) {
+ // console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
+ callback(m.result !== 'tesSUCCESS');
+
+ seq = m.tx_json.Sequence;
+ })
+ .submit();
+ },
+ function (callback) {
+ self.what = "Alice send XRP to bob converting from USD/mtgox.";
+
+ self.remote.transaction()
+ .payment("alice", "bob", "250")
+ .send_max("333/USD/mtgox")
+ .on('proposed', function (m) {
+ // console.log("proposed: %s", JSON.stringify(m));
+
+ callback(m.result !== 'tesSUCCESS');
+ })
+ .submit();
+ },
+ function (callback) {
+ self.what = "Verify balances.";
+
+ testutils.verify_balances(self.remote,
+ {
+ "alice" : "475/USD/mtgox",
+ "bob" : "10250",
+ "carol" : "25/USD/mtgox",
+ },
+ callback);
+ },
+ function (callback) {
+ self.what = "Verify offer partially consumed.";
+
+ testutils.verify_offer(self.remote, "carol", seq, "250", "25/USD/mtgox", callback);
+ },
+ ], function (error) {
+ buster.refute(error, self.what);
+ done();
+ });
+ },
+
+ "// ripple cross currency bridged payment" :
+ // alice --> [USD/mtgox --> carol --> XRP] --> [XRP --> dan --> EUR/bitstamp] --> bob
+
+ function (done) {
+ var self = this;
+ var seq_carol;
+ var seq_dan;
+
+ // self.remote.set_trace();
+
+ async.waterfall([
+ function (callback) {
+ self.what = "Create accounts.";
+
+ testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "carol", "dan", "bitstamp", "mtgox"], callback);
+ },
+ function (callback) {
+ self.what = "Set limits.";
+
+ testutils.credit_limits(self.remote,
+ {
+ "alice" : "1000/USD/mtgox",
+ "bob" : "1000/EUR/bitstamp",
+ "carol" : "1000/USD/mtgox",
+ "dan" : "1000/EUR/bitstamp"
+ },
+ callback);
+ },
+ function (callback) {
+ self.what = "Distribute funds.";
+
+ testutils.payments(self.remote,
+ {
+ "bitstamp" : "400/EUR/dan",
+ "mtgox" : "500/USD/alice",
+ },
+ callback);
+ },
+ function (callback) {
+ self.what = "Create offer carol.";
+
+ self.remote.transaction()
+ .offer_create("carol", "50/USD/mtgox", "500")
+ .on('proposed', function (m) {
+ // console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
+ callback(m.result !== 'tesSUCCESS');
+
+ seq_carol = m.tx_json.Sequence;
+ })
+ .submit();
+ },
+ function (callback) {
+ self.what = "Create offer dan.";
+
+ self.remote.transaction()
+ .offer_create("dan", "500", "50/EUR/bitstamp")
+ .on('proposed', function (m) {
+ // console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
+ callback(m.result !== 'tesSUCCESS');
+
+ seq_dan = m.tx_json.Sequence;
+ })
+ .submit();
+ },
+ function (callback) {
+ self.what = "Alice send EUR/bitstamp to bob converting from USD/mtgox.";
+
+ self.remote.transaction()
+ .payment("alice", "bob", "30/EUR/bitstamp")
+ .send_max("333/USD/mtgox")
+ .on('proposed', function (m) {
+ // console.log("proposed: %s", JSON.stringify(m));
+
+ callback(m.result !== 'tesSUCCESS');
+ })
+ .submit();
+ },
+// function (callback) {
+// self.what = "Verify balances.";
+//
+// testutils.verify_balances(self.remote,
+// {
+// "alice" : "470/USD/mtgox",
+// "bob" : "30/EUR/bitstamp",
+// "carol" : "30/USD/mtgox",
+// "dan" : "370/EUR/bitstamp",
+// },
+// callback);
+// },
+// function (callback) {
+// self.what = "Verify carol offer partially consumed.";
+//
+// testutils.verify_offer(self.remote, "carol", seq_carol, "250", "25/USD/mtgox", callback);
+// },
+// function (callback) {
+// self.what = "Verify dan offer partially consumed.";
+//
+// testutils.verify_offer(self.remote, "dan", seq_dan, "250", "25/USD/mtgox", callback);
+// },
+ ], function (error) {
+ buster.refute(error, self.what);
+ done();
+ });
+ },
});
// vim:sw=2:sts=2:ts=8
diff --git a/test/send-test.js b/test/send-test.js
index 043855714c..21d261c7a7 100644
--- a/test/send-test.js
+++ b/test/send-test.js
@@ -15,13 +15,14 @@ var serverDelay = 1500;
buster.testRunner.timeout = 5000;
+
/*
-buster.testCase("Simple", {
+buster.testCase("Fee Changes", {
'setUp' : testutils.build_setup({no_server: true}), //
'tearDown' : testutils.build_teardown(),
- "simple." :
- function (done) { buster.assert(1);
+ "varying the fee for Payment" :
+ function (done) {
this.remote.transaction()
.payment('root', 'alice', "10000")
@@ -36,8 +37,8 @@ buster.testCase("Simple", {
}).submit();
}
- }); */
-
+ });
+ */
buster.testCase("Sending", {
'setUp' : testutils.build_setup(),
'tearDown' : testutils.build_teardown(),
diff --git a/test/testutils.js b/test/testutils.js
index 8a840bd5f0..bea65eb8a1 100644
--- a/test/testutils.js
+++ b/test/testutils.js
@@ -265,14 +265,22 @@ var transfer_rate = function (remote, src, billionths, callback) {
var verify_balance = function (remote, src, amount_json, callback) {
assert(4 === arguments.length);
- var amount = Amount.from_json(amount_json);
+ var amount_req = Amount.from_json(amount_json);
- if (amount.is_native()) {
- // XXX Not implemented.
- callback();
+ if (amount_req.is_native()) {
+ remote.request_account_balance(src, 'CURRENT')
+ .once('account_balance', function (amount_act) {
+ if (!amount_act.equals(amount_req))
+ console.log("verify_balance: failed: %s / %s",
+ amount_act.to_text_full(),
+ amount_req.to_text_full());
+
+ callback(!amount_act.equals(amount_req));
+ })
+ .request();
}
else {
- remote.request_ripple_balance(src, amount.issuer().to_json(), amount.currency().to_json(), 'CURRENT')
+ remote.request_ripple_balance(src, amount_req.issuer().to_json(), amount_req.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());
@@ -282,11 +290,11 @@ var verify_balance = function (remote, src, amount_json, callback) {
var account_balance = Amount.from_json(m.account_balance);
- if (!account_balance.equals(amount)) {
- console.log("verify_balance: failed: %s vs %s is %s: %s", src, account_balance.to_text_full(), amount.to_text_full(), account_balance.not_equals_why(amount));
+ if (!account_balance.equals(amount_req)) {
+ console.log("verify_balance: failed: %s vs %s is %s: %s", src, account_balance.to_text_full(), amount_req.to_text_full(), account_balance.not_equals_why(amount_req));
}
- callback(!account_balance.equals(amount));
+ callback(!account_balance.equals(amount_req));
})
.request();
}