diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index c228fba653..9b9530eb7c 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -21,6 +21,7 @@ static const uint64 tenTo17 = tenTo14 * 1000; #if (ULONG_MAX > UINT_MAX) #define BN_add_word64(bn, word) BN_add_word(bn, word) +#define BN_sub_word64(bn, word) BN_sub_word(bn, word) #define BN_mul_word64(bn, word) BN_mul_word(bn, word) #define BN_div_word64(bn, word) BN_div_word(bn, word) #else @@ -1248,25 +1249,6 @@ void STAmount::roundSelf() } } -#if 0 -std::string STAmount::getExtendedText() const -{ - if (mIsNative) - { - return str(boost::format("%s " SYSTEM_CURRENCY_CODE) % getText()); - } - else - { - return str(boost::format("%s/%s/%s %dE%d" ) - % getText() - % getHumanCurrency() - % RippleAddress::createHumanAccountID(mIssuer) - % getMantissa() - % getExponent()); - } -} -#endif - Json::Value STAmount::getJson(int) const { Json::Value elem(Json::objectValue); diff --git a/src/cpp/ripple/AmountRound.cpp b/src/cpp/ripple/AmountRound.cpp new file mode 100644 index 0000000000..10f7d7b211 --- /dev/null +++ b/src/cpp/ripple/AmountRound.cpp @@ -0,0 +1,339 @@ + +#include + +#include "SerializedTypes.h" +#include "Log.h" + +SETUP_LOG(); + +#if (ULONG_MAX > UINT_MAX) +#define BN_add_word64(bn, word) BN_add_word(bn, word) +#define BN_sub_word64(bn, word) BN_sub_word(bn, word) +#define BN_mul_word64(bn, word) BN_mul_word(bn, word) +#define BN_div_word64(bn, word) BN_div_word(bn, word) +#else +#include "BigNum64.h" +#endif + +static const uint64 tenTo14 = 100000000000000ull; +static const uint64 tenTo14m1 = tenTo14 - 1; +static const uint64 tenTo17 = tenTo14 * 1000; +static const uint64 tenTo17m1 = tenTo17 - 1; + +// CAUTION: This is early code and is *NOT* ready for real use yet. + +static void canonicalizeRound(bool isNative, uint64& value, int& offset, bool roundUp) +{ + if (!roundUp) // canonicalize already rounds down + return; + + cLog(lsDEBUG) << "canonicalize< " << value << ":" << offset << (roundUp ? " up" : " down"); + if (isNative) + { + if (offset < 0) + { + while (offset < -1) + { + value /= 10; + ++offset; + } + value += 10; // add before last divide + value /= 10; + ++offset; + } + } + else if (value > STAmount::cMaxValue) + { + while (value > (10 * STAmount::cMaxValue)) + { + value /= 10; + ++offset; + } + value += 9; // add before last divide + value /= 10; + ++offset; + } + cLog(lsDEBUG) << "canonicalize> " << value << ":" << offset << (roundUp ? " up" : " down"); +} + +STAmount STAmount::addRound(const STAmount& v1, const STAmount& v2, bool roundUp) +{ + v1.throwComparable(v2); + + if (v2.mValue == 0) + return v1; + + if (v1.mValue == 0) + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, v2.mValue, v2.mOffset, v2.mIsNegative); + + 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; + if (v2.mIsNegative) + vv2 = -vv2; + + if (ov1 < ov2) + { + while (ov1 < (ov2 - 1)) + { + vv1 /= 10; + ++ov1; + } + if (roundUp) + vv1 += 9; + vv1 /= 10; + ++ov1; + } + + if (ov2 < ov1) + { + while (ov2 < (ov1 - 1)) + { + vv2 /= 10; + ++ov2; + } + if (roundUp) + vv2 += 9; + vv2 /= 10; + ++ov2; + } + + int64 fv = vv1 + vv2; + if (fv >= 0) + { + uint64 v = static_cast(fv); + canonicalizeRound(false, v, ov1, roundUp); + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, v, ov1, false); + } + else + { + uint64 v = static_cast(-fv); + canonicalizeRound(false, v, ov1, !roundUp); + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, v, ov1, true); + } +} + +STAmount STAmount::subRound(const STAmount& v1, const STAmount& v2, bool roundUp) +{ + v1.throwComparable(v2); + + if (v2.mValue == 0) + return v1; + + if (v1.mValue == 0) + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, v2.mValue, v2.mOffset, !v2.mIsNegative); + + 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; + + if (!v2.mIsNegative) + vv2 = -vv2; + + if (ov1 < ov2) + { + while (ov1 < (ov2 - 1)) + { + vv1 /= 10; + ++ov1; + } + if (roundUp) + vv1 += 9; + vv1 /= 10; + ++ov1; + } + + if (ov2 < ov1) + { + while (ov2 < (ov1 - 1)) + { + vv2 /= 10; + ++ov2; + } + if (roundUp) + vv2 += 9; + vv2 /= 10; + ++ov2; + } + + int64 fv = vv1 + vv2; + if (fv >= 0) + { + uint64 v = static_cast(fv); + canonicalizeRound(false, v, ov1, roundUp); + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, v, ov1, false); + } + else + { + uint64 v = static_cast(-fv); + canonicalizeRound(false, v, ov1, !roundUp); + return STAmount(v1.getFName(), v1.mCurrency, v1.mIssuer, v, ov1, true); + } +} + +STAmount STAmount::mulRound(const STAmount& v1, const STAmount& v2, + const uint160& uCurrencyID, const uint160& uIssuerID, bool roundUp) +{ + if (v1.isZero() || v2.isZero()) + return STAmount(uCurrencyID, uIssuerID); + + if (v1.mIsNative && v2.mIsNative && uCurrencyID.isZero()) + { + uint64 minV = (v1.getSNValue() < v2.getSNValue()) ? v1.getSNValue() : v2.getSNValue(); + uint64 maxV = (v1.getSNValue() < v2.getSNValue()) ? v2.getSNValue() : v1.getSNValue(); + if (minV > 3000000000ull) // sqrt(cMaxNative) + throw std::runtime_error("Native value overflow"); + if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32 + throw std::runtime_error("Native value overflow"); + return STAmount(v1.getFName(), minV * maxV); + } + + uint64 value1 = v1.mValue, value2 = v2.mValue; + int offset1 = v1.mOffset, offset2 = v2.mOffset; + + if (v1.mIsNative) + { + while (value1 < STAmount::cMinValue) + { + value1 *= 10; + --offset1; + } + } + + if (v2.mIsNative) + { + while (value2 < STAmount::cMinValue) + { + value2 *= 10; + --offset2; + } + } + + bool resultNegative = v1.mIsNegative != v2.mIsNegative; + // Compute (numerator * denominator) / 10^14 with rounding + // 10^16 <= result <= 10^18 + CBigNum v; + if ((BN_add_word64(&v, value1) != 1) || (BN_mul_word64(&v, value2) != 1)) + throw std::runtime_error("internal bn error"); + + if (resultNegative != roundUp) + BN_add_word64(&v, tenTo14m1); + else + BN_sub_word64(&v, tenTo14m1); + + if (BN_div_word64(&v, tenTo14) == ((uint64) -1)) + throw std::runtime_error("internal bn error"); + + // 10^16 <= product <= 10^18 + assert(BN_num_bytes(&v) <= 64); + + uint64 amount = v.getuint64(); + int offset = offset1 + offset2 + 14; + canonicalizeRound(uCurrencyID.isZero(), amount, offset, resultNegative != roundUp); + return STAmount(uCurrencyID, uIssuerID, amount, offset, resultNegative); +} + +STAmount STAmount::divRound(const STAmount& num, const STAmount& den, + const uint160& uCurrencyID, const uint160& uIssuerID, bool roundUp) +{ + if (den.isZero()) + throw std::runtime_error("division by zero"); + if (num.isZero()) + return STAmount(uCurrencyID, uIssuerID); + + uint64 numVal = num.mValue, denVal = den.mValue; + int numOffset = num.mOffset, denOffset = den.mOffset; + + if (num.mIsNative) + while (numVal < STAmount::cMinValue) + { // Need to bring into range + numVal *= 10; + --numOffset; + } + + if (den.mIsNative) + while (denVal < STAmount::cMinValue) + { + denVal *= 10; + --denOffset; + } + + bool resultNegative = num.mIsNegative != num.mIsNegative; + // Compute (numerator * 10^17) / denominator + CBigNum v; + if ((BN_add_word64(&v, numVal) != 1) || (BN_mul_word64(&v, tenTo17) != 1)) + throw std::runtime_error("internal bn error"); + + if (resultNegative != roundUp) + BN_add_word64(&v, denVal - 1); + else + BN_sub_word64(&v, denVal - 1); + + if (BN_div_word64(&v, denVal) == ((uint64) -1)) + throw std::runtime_error("internal bn error"); + + // 10^16 <= quotient <= 10^18 + assert(BN_num_bytes(&v) <= 64); + + uint64 amount = v.getuint64(); + int offset = numOffset - denOffset - 17; + canonicalizeRound(uCurrencyID.isZero(), amount, offset, resultNegative != roundUp); + return STAmount(uCurrencyID, uIssuerID, amount, offset, resultNegative); +} + +BOOST_AUTO_TEST_SUITE(amountRound) + +BOOST_AUTO_TEST_CASE( amountRound_test ) +{ + STAmount one(CURRENCY_ONE, ACCOUNT_ONE, 1); + STAmount two(CURRENCY_ONE, ACCOUNT_ONE, 2); + STAmount three(CURRENCY_ONE, ACCOUNT_ONE, 3); + + STAmount oneThird1 = STAmount::divRound(one, three, CURRENCY_ONE, ACCOUNT_ONE, false); + STAmount oneThird2 = STAmount::divide(one, three, CURRENCY_ONE, ACCOUNT_ONE); + STAmount oneThird3 = STAmount::divRound(one, three, CURRENCY_ONE, ACCOUNT_ONE, true); + cLog(lsINFO) << oneThird1; + cLog(lsINFO) << oneThird2; + cLog(lsINFO) << oneThird3; + + STAmount twoThird1 = STAmount::divRound(two, three, CURRENCY_ONE, ACCOUNT_ONE, false); + STAmount twoThird2 = STAmount::divide(two, three, CURRENCY_ONE, ACCOUNT_ONE); + STAmount twoThird3 = STAmount::divRound(two, three, CURRENCY_ONE, ACCOUNT_ONE, true); + cLog(lsINFO) << twoThird1; + cLog(lsINFO) << twoThird2; + cLog(lsINFO) << twoThird3; + + STAmount oneA = STAmount::mulRound(oneThird1, three, CURRENCY_ONE, ACCOUNT_ONE, false); + STAmount oneB = STAmount::multiply(oneThird2, three, CURRENCY_ONE, ACCOUNT_ONE); + STAmount oneC = STAmount::mulRound(oneThird3, three, CURRENCY_ONE, ACCOUNT_ONE, true); + cLog(lsINFO) << oneA; + cLog(lsINFO) << oneB; + cLog(lsINFO) << oneC; + + STAmount fourThirdsA = STAmount::addRound(twoThird2, twoThird2, false); + STAmount fourThirdsB = twoThird2 + twoThird2; + STAmount fourThirdsC = STAmount::addRound(twoThird2, twoThird2, true); + cLog(lsINFO) << fourThirdsA; + cLog(lsINFO) << fourThirdsB; + cLog(lsINFO) << fourThirdsC; + + STAmount dripTest1 = STAmount::mulRound(twoThird2, two, uint160(), uint160(), false); + STAmount dripTest2 = STAmount::multiply(twoThird2, two, uint160(), uint160()); + STAmount dripTest3 = STAmount::mulRound(twoThird2, two, uint160(), uint160(), true); + cLog(lsINFO) << dripTest1; + cLog(lsINFO) << dripTest2; + cLog(lsINFO) << dripTest3; +} + +BOOST_AUTO_TEST_SUITE_END() + +// vim:ts=4 diff --git a/src/cpp/ripple/BigNum64.h b/src/cpp/ripple/BigNum64.h index 9150a25cad..0a052dda6a 100644 --- a/src/cpp/ripple/BigNum64.h +++ b/src/cpp/ripple/BigNum64.h @@ -7,6 +7,12 @@ static int BN_add_word64(BIGNUM *a, uint64 w) return BN_add(a, &bn, a); } +static int BN_sub_word64(BIGNUM *a, uint64 w) +{ + CBigNum bn(w); + return BN_sub(a, &bn, a); +} + static int BN_mul_word64(BIGNUM *a, uint64 w) { CBigNum bn(w); diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 5ea9e318d4..1403ea3ae1 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -403,12 +403,12 @@ uint256 Ledger::getHash() void Ledger::saveAcceptedLedger(Job&, bool fromConsensus) { cLog(lsTRACE) << "saveAcceptedLedger " << (fromConsensus ? "fromConsensus " : "fromAcquire ") << getLedgerSeq(); - static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers INDEXED BY SeqLedger where LedgerSeq = %d;"); - static boost::format deleteLedger("DELETE FROM Ledgers WHERE LedgerSeq = %d;"); + static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers INDEXED BY SeqLedger where LedgerSeq = %u;"); + static boost::format deleteLedger("DELETE FROM Ledgers WHERE LedgerSeq = %u;"); static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransID = '%s';"); static boost::format transExists("SELECT Status FROM Transactions WHERE TransID = '%s';"); static boost::format - updateTx("UPDATE Transactions SET LedgerSeq = %d, Status = '%c', TxnMeta = %s WHERE TransID = '%s';"); + updateTx("UPDATE Transactions SET LedgerSeq = %u, Status = '%c', TxnMeta = %s WHERE TransID = '%s';"); static boost::format addLedger("INSERT OR REPLACE INTO Ledgers " "(LedgerHash,LedgerSeq,PrevHash,TotalCoins,ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags," "AccountSetHash,TransSetHash) VALUES ('%s','%u','%s','%s','%u','%u','%d','%u','%s','%s');"); diff --git a/src/cpp/ripple/LoadManager.cpp b/src/cpp/ripple/LoadManager.cpp index e604e052ec..41d15ac94c 100644 --- a/src/cpp/ripple/LoadManager.cpp +++ b/src/cpp/ripple/LoadManager.cpp @@ -24,18 +24,18 @@ LoadManager::LoadManager(int creditRate, int creditLimit, int debitWarn, int deb mCreditRate(creditRate), mCreditLimit(creditLimit), mDebitWarn(debitWarn), mDebitLimit(debitLimit), mShutdown(false), mUptime(0), mCosts(LT_MAX) { - addLoadCost(LoadCost(LT_InvalidRequest, 10, LC_CPU | LC_Network)); - addLoadCost(LoadCost(LT_RequestNoReply, 1, LC_CPU | LC_Disk)); - addLoadCost(LoadCost(LT_InvalidSignature, 100, LC_CPU)); - addLoadCost(LoadCost(LT_UnwantedData, 5, LC_CPU | LC_Network)); - addLoadCost(LoadCost(LT_BadData, 20, LC_CPU)); + addLoadCost(LoadCost(LT_InvalidRequest, -10, LC_CPU | LC_Network)); + addLoadCost(LoadCost(LT_RequestNoReply, -1, LC_CPU | LC_Disk)); + addLoadCost(LoadCost(LT_InvalidSignature, -100, LC_CPU)); + addLoadCost(LoadCost(LT_UnwantedData, -5, LC_CPU | LC_Network)); + addLoadCost(LoadCost(LT_BadData, -20, LC_CPU)); - addLoadCost(LoadCost(LT_NewTrusted, 10, 0)); - addLoadCost(LoadCost(LT_NewTransaction, 2, 0)); - addLoadCost(LoadCost(LT_NeededData, 10, 0)); + addLoadCost(LoadCost(LT_NewTrusted, -10, 0)); + addLoadCost(LoadCost(LT_NewTransaction, -2, 0)); + addLoadCost(LoadCost(LT_NeededData, -10, 0)); - addLoadCost(LoadCost(LT_RequestData, 5, LC_Disk | LC_Network)); - addLoadCost(LoadCost(LT_CheapQuery, 1, LC_CPU)); + addLoadCost(LoadCost(LT_RequestData, -5, LC_Disk | LC_Network)); + addLoadCost(LoadCost(LT_CheapQuery, -1, LC_CPU)); } @@ -124,7 +124,10 @@ void LoadManager::canonicalize(LoadSource& source, int now) const { source.mBalance += mCreditRate * (now - source.mLastUpdate); if (source.mBalance > mCreditLimit) + { source.mBalance = mCreditLimit; + source.mLogged = false; + } } source.mLastUpdate = now; } @@ -152,8 +155,11 @@ bool LoadManager::shouldCutoff(LoadSource& source) const boost::mutex::scoped_lock sl(mLock); int now = upTime(); canonicalize(source, now); - if (!source.isPrivileged() || (source.mBalance > mDebitLimit)) + if (source.isPrivileged() || (source.mBalance > mDebitLimit)) return false; + if (source.mLogged) + return true; + source.mLogged = true; } logDisconnect(source.getName()); return true; diff --git a/src/cpp/ripple/LoadManager.h b/src/cpp/ripple/LoadManager.h index 070384d702..25b8aa5855 100644 --- a/src/cpp/ripple/LoadManager.h +++ b/src/cpp/ripple/LoadManager.h @@ -66,11 +66,14 @@ protected: int mFlags; int mLastUpdate; int mLastWarning; + bool mLogged; public: - LoadSource(bool admin) : mBalance(0), mFlags(admin ? lsfPrivileged : 0), mLastUpdate(upTime()), mLastWarning(0) + LoadSource(bool admin) : + mBalance(0), mFlags(admin ? lsfPrivileged : 0), mLastUpdate(upTime()), mLastWarning(0), mLogged(false) { ; } - LoadSource(const std::string& name) : mName(name), mBalance(0), mFlags(0), mLastUpdate(upTime()), mLastWarning(0) + LoadSource(const std::string& name) : + mName(name), mBalance(0), mFlags(0), mLastUpdate(upTime()), mLastWarning(0), mLogged(false) { ; } void rename(const std::string& name) { mName = name; } @@ -80,6 +83,9 @@ public: void setPrivileged() { mFlags |= lsfPrivileged; } int getBalance() const { return mBalance; } + bool isLogged() const { return mLogged; } + void clearLogged() { mLogged = false; } + void setOutbound() { mFlags |= lsfOutbound; } bool isOutbound() const { return (mFlags & lsfOutbound) != 0; } }; diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index da49c25e1a..40868a6f37 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -374,27 +374,24 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans, if (r == tefFAILURE) throw Fault(IO_ERROR); - if (isTerRetry(r)) - { // transaction should be held - cLog(lsDEBUG) << "Transaction should be held: " << r; - trans->setStatus(HELD); - theApp->getMasterTransaction().canonicalize(trans, true); - mLedgerMaster->addHeldTransaction(trans); - return trans; - } - if (r == tefPAST_SEQ) - { // duplicate or conflict - cLog(lsINFO) << "Transaction is obsolete"; - trans->setStatus(OBSOLETE); - return trans; - } - if (r == tesSUCCESS) { cLog(lsINFO) << "Transaction is now included in open ledger"; trans->setStatus(INCLUDED); theApp->getMasterTransaction().canonicalize(trans, true); } + else if (r == tefPAST_SEQ) + { // duplicate or conflict + cLog(lsINFO) << "Transaction is obsolete"; + trans->setStatus(OBSOLETE); + } + else if (isTerRetry(r)) + { // transaction should be held + cLog(lsDEBUG) << "Transaction should be held: " << r; + trans->setStatus(HELD); + theApp->getMasterTransaction().canonicalize(trans, true); + mLedgerMaster->addHeldTransaction(trans); + } else { cLog(lsDEBUG) << "Status other than success " << r; @@ -1068,7 +1065,7 @@ std::vector< std::pair > std::string sql = str(boost::format("SELECT LedgerSeq,Status,RawTxn,TxnMeta FROM Transactions where TransID in " "(SELECT TransID from AccountTransactions " - " WHERE Account = '%s' AND LedgerSeq <= '%d' AND LedgerSeq >= '%d' ) ORDER BY LedgerSeq DESC LIMIT 200;") + " WHERE Account = '%s' AND LedgerSeq <= '%u' AND LedgerSeq >= '%u' ) ORDER BY LedgerSeq DESC LIMIT 200;") % account.humanAccountID() % maxLedger % minLedger); { @@ -1104,7 +1101,7 @@ std::vector NetworkOPs::getAccountTxsB( std::string sql = str(boost::format("SELECT LedgerSeq, RawTxn,TxnMeta FROM Transactions where TransID in (SELECT TransID from AccountTransactions " - " WHERE Account = '%s' AND LedgerSeq <= '%d' AND LedgerSeq >= '%d' ) ORDER BY LedgerSeq DESC LIMIT 500;") + " WHERE Account = '%s' AND LedgerSeq <= '%u' AND LedgerSeq >= '%u' ) ORDER BY LedgerSeq DESC LIMIT 500;") % account.humanAccountID() % maxLedger % minLedger); { @@ -1148,7 +1145,7 @@ std::vector { std::vector accounts; std::string sql = str(boost::format - ("SELECT DISTINCT Account FROM AccountTransactions INDEXED BY AcctLgrIndex WHERE LedgerSeq = '%d';") + ("SELECT DISTINCT Account FROM AccountTransactions INDEXED BY AcctLgrIndex WHERE LedgerSeq = '%u';") % ledgerSeq); RippleAddress acct; { diff --git a/src/cpp/ripple/SerializedTypes.h b/src/cpp/ripple/SerializedTypes.h index b618c952d7..30cb3b8fb2 100644 --- a/src/cpp/ripple/SerializedTypes.h +++ b/src/cpp/ripple/SerializedTypes.h @@ -427,6 +427,23 @@ public: static STAmount multiply(const STAmount& v1, const STAmount& v2) { return multiply(v1, v2, v1); } + // Add, subtract, multiply, or divide rounding result in specified direction + static STAmount addRound(const STAmount& v1, const STAmount& v2, bool roundUp); + static STAmount subRound(const STAmount& v1, const STAmount& v2, bool roundUp); + static STAmount mulRound(const STAmount& v1, const STAmount& v2, + const uint160& currency, const uint160& issuer, bool roundUp); + static STAmount divRound(const STAmount& v1, const STAmount& v2, + const uint160& currency, const uint160& issuer, bool roundUp); + + static STAmount mulRound(const STAmount& v1, const STAmount& v2, const STAmount& saUnit, bool roundUp) + { return mulRound(v1, v2, saUnit.getCurrency(), saUnit.getIssuer(), roundUp); } + static STAmount mulRound(const STAmount& v1, const STAmount& v2, bool roundUp) + { return mulRound(v1, v2, v1.getCurrency(), v1.getIssuer(), roundUp); } + static STAmount divRound(const STAmount& v1, const STAmount& v2, const STAmount& saUnit, bool roundUp) + { return divRound(v1, v2, saUnit.getCurrency(), saUnit.getIssuer(), roundUp); } + static STAmount divRound(const STAmount& v1, const STAmount& v2, bool roundUp) + { return divRound(v1, v2, v2.getCurrency(), v2.getIssuer(), roundUp); } + // Someone is offering X for Y, what is the rate? // Rate: smaller is better, the taker wants the most out: in/out static uint64 getRate(const STAmount& offerOut, const STAmount& offerIn); diff --git a/src/cpp/ripple/WSConnection.h b/src/cpp/ripple/WSConnection.h index 10b8be406b..6f3aa2cd2d 100644 --- a/src/cpp/ripple/WSConnection.h +++ b/src/cpp/ripple/WSConnection.h @@ -90,10 +90,12 @@ public: { if (theApp->getLoadManager().shouldCutoff(mLoadSource)) { +#if SHOULD_DISCONNECT connection_ptr ptr = mConnection.lock(); if (ptr) ptr->close(websocketpp::close::status::PROTOCOL_ERROR, "overload"); return rpcError(rpcSLOW_DOWN); +#endif } if (!jvRequest.isMember("command")) @@ -110,7 +112,7 @@ public: jvResult["id"] = jvRequest["id"]; } - theApp->getLoadManager().adjust(mLoadSource, 5); + theApp->getLoadManager().adjust(mLoadSource, -5); return jvResult; } @@ -131,7 +133,7 @@ public: jvResult["result"] = mRPCHandler.doCommand(jvRequest, iRole, cost); } - if (theApp->getLoadManager().adjust(mLoadSource, cost) && theApp->getLoadManager().shouldWarn(mLoadSource)) + if (theApp->getLoadManager().adjust(mLoadSource, -cost) && theApp->getLoadManager().shouldWarn(mLoadSource)) jvResult["warning"] = "load"; // Currently we will simply unwrap errors returned by the RPC diff --git a/src/js/index.js b/src/js/index.js index 4ff2b374d1..b603d6a531 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -3,6 +3,8 @@ exports.Amount = require('./amount').Amount; exports.UInt160 = require('./amount').UInt160; exports.Seed = require('./amount').Seed; +exports.utils = require('./utils'); + // Important: We do not guarantee any specific version of SJCL or for any // specific features to be included. The version and configuration may change at // any time without warning. diff --git a/src/js/orderbook.js b/src/js/orderbook.js index c78674e079..00ac29b23a 100644 --- a/src/js/orderbook.js +++ b/src/js/orderbook.js @@ -32,7 +32,7 @@ var OrderBook = function (remote, if (OrderBook.subscribe_events.indexOf(type) !== -1) { if (!self._subs && 'open' === self._remote._online_state) { self._remote.request_subscribe() - .books([self.to_json()]) + .books([self.to_json()], true) .request(); } self._subs += 1; @@ -54,7 +54,7 @@ var OrderBook = function (remote, this._remote.on('connect', function () { if (self._subs) { self._remote.request_subscribe() - .books([self.to_json()]) + .books([self.to_json()], true) .request(); } }); diff --git a/src/js/remote.js b/src/js/remote.js index 2f99748b2a..fed0252564 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -203,7 +203,7 @@ Request.prototype.rt_accounts = function (accounts) { return this.accounts(accounts, true); }; -Request.prototype.books = function (books) { +Request.prototype.books = function (books, state) { var procBooks = []; for (var i = 0, l = books.length; i < l; i++) { @@ -221,6 +221,8 @@ Request.prototype.books = function (books) { json["IssuerIn"] = UInt160.json_rewrite(book["IssuerIn"]); } + if (state || book["StateNow"]) json["StateNow"] = true; + procBooks.push(json); } this.message.books = procBooks; diff --git a/src/js/serializedtypes.js b/src/js/serializedtypes.js index 93009a9fb7..a3a2808d81 100644 --- a/src/js/serializedtypes.js +++ b/src/js/serializedtypes.js @@ -243,17 +243,25 @@ var STAccount = exports.Account = new SerializedType({ }); var STPathSet = exports.PathSet = new SerializedType({ + typeBoundary: 0xff, + typeEnd: 0x00, + typeAccount: 0x01, + typeCurrency: 0x10, + typeIssuer: 0x20, serialize: function (so, val) { // XXX for (var i = 0, l = val.length; i < l; i++) { + // Boundary + if (i) STInt8.serialize(so, this.typeBoundary); + for (var j = 0, l2 = val[i].length; j < l2; j++) { var entry = val[i][j]; var type = 0; - if (entry.account) type |= 0x01; - if (entry.currency) type |= 0x10; - if (entry.issuer) type |= 0x20; + if (entry.account) type |= this.typeAccount; + if (entry.currency) type |= this.typeCurrency; + if (entry.issuer) type |= this.typeIssuer; STInt8.serialize(so, type); @@ -268,10 +276,8 @@ var STPathSet = exports.PathSet = new SerializedType({ so.append(UInt160.from_json(entry.issuer).to_bytes()); } } - - if (j < l2) STInt8.serialize(so, 0xff); } - STInt8.serialize(so, 0x00); + STInt8.serialize(so, this.typeEnd); }, parse: function (so) { // XXX diff --git a/src/js/utils.js b/src/js/utils.js index d1779faf9a..462f3f2860 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -97,6 +97,15 @@ var assert = function (assertion, msg) { } }; +/** + * Convert a ripple epoch to a JavaScript timestamp. + * + * JavaScript timestamps are unix epoch in milliseconds. + */ +var toTimestamp = function (rpepoch) { + return (rpepoch + 0x386D4380) * 1000; +}; + exports.trace = trace; exports.arraySet = arraySet; exports.hexToString = hexToString; @@ -106,5 +115,6 @@ exports.stringToHex = stringToHex; exports.chunkString = chunkString; exports.logObject = logObject; exports.assert = assert; +exports.toTimestamp = toTimestamp; // vim:sw=2:sts=2:ts=8:et