diff --git a/newcoin.vcxproj b/newcoin.vcxproj index fbd79cb56d..dfaccea231 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -175,6 +175,7 @@ + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 961347ae6f..2ccbac5b41 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -249,6 +249,9 @@ Source Files + + Source Files + Source Files diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj index 843c6f9dcf..d3e9aff5c2 100644 --- a/ripple2010.vcxproj +++ b/ripple2010.vcxproj @@ -175,6 +175,7 @@ + diff --git a/ripple2010.vcxproj.filters b/ripple2010.vcxproj.filters index 0ef380379d..bbdf822554 100644 --- a/ripple2010.vcxproj.filters +++ b/ripple2010.vcxproj.filters @@ -243,6 +243,9 @@ Source Files + + Source Files + Source Files diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index 2232fae789..7546aa9cfa 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -257,134 +258,77 @@ std::string STAmount::createHumanCurrency(const uint160& uCurrency) return sCurrency; } -// Assumes trusted input. bool STAmount::setValue(const std::string& sAmount) { // Note: mIsNative and mCurrency must be set already! - uint64 uValue; - int iOffset; - size_t uDecimal = sAmount.find_first_of(mIsNative ? "^" : "."); - size_t uExp = uDecimal == std::string::npos ? sAmount.find_first_of("e") : std::string::npos; - bool bInteger = uDecimal == std::string::npos && uExp == std::string::npos; - mIsNegative = false; - if (bInteger) + static boost::regex reNumber("\\`([+-]?)(\\d*)(\\.(\\d*))?([eE]([+-]?)(\\d+))?\\'"); + boost::smatch smMatch; + + if (!boost::regex_match(sAmount, smMatch, reNumber)) { - // Integer input: does not necessarily mean native. + cLog(lsWARNING) << "Number not valid: \"" << sAmount << "\""; + return false; + } - try + // Match fields: 0 = whole input, 1 = sign, 2 = integer portion, 3 = whole fraction (with '.') + // 4 = fraction (without '.'), 5 = whole exponent (with 'e'), 6 = exponent sign, 7 = exponent number + + try + { + if ((smMatch[2].length() + smMatch[4].length()) > 18) { - int64 a = sAmount.empty() ? 0 : lexical_cast_st(sAmount); - if (a >= 0) - { - uValue = static_cast(a); - } - else - { - uValue = static_cast(-a); - mIsNegative = true; - } - - } - catch (...) - { - cLog(lsINFO) << "Bad integer amount: " << sAmount; - + cLog(lsWARNING) << "Overlong number: " << sAmount; return false; } - iOffset = 0; - } - else if (uExp != std::string::npos) - { - // e input - try + mIsNegative = (smMatch[1].matched && (smMatch[1] == "-")); + + if (!smMatch[4].matched) // integer only { - int64 iInteger = uExp ? lexical_cast_st(sAmount.substr(0, uExp)) : 0; - if (iInteger >= 0) - { - uValue = static_cast(iInteger); - } + mValue = lexical_cast_s(smMatch[2]); + mOffset = 0; + } + else + { // integer and fraction + mValue = lexical_cast_s(smMatch[2] + smMatch[4]); + mOffset = -(smMatch[4].length()); + } + + if (smMatch[5].matched) + { // we have an exponent + if (smMatch[6].matched && (smMatch[6] == "-")) + mOffset -= lexical_cast_s(smMatch[7]); else - { - uValue = static_cast(-iInteger); - mIsNegative = true; - } - - iOffset = lexical_cast_st(sAmount.substr(uExp+1)); - } - catch (...) - { - cLog(lsINFO) << "Bad e amount: " << sAmount; - - return false; + mOffset += lexical_cast_s(smMatch[7]); } } - else + catch (...) { - // Float input: has a decimal - - // Example size decimal size-decimal offset - // ^1 2 0 2 -1 - // 123^ 4 3 1 0 - // 1^23 4 1 3 -2 - try - { - iOffset = -int(sAmount.size() - uDecimal - 1); - - // Issolate integer and fraction. - uint64 uInteger; - int64 iInteger = uDecimal ? lexical_cast_st(sAmount.substr(0, uDecimal)) : 0; - if (iInteger >= 0) - { - uInteger = static_cast(iInteger); - } - else - { - uInteger = static_cast(-iInteger); - mIsNegative = true; - } - - uint64 uFraction = iOffset ? lexical_cast_st(sAmount.substr(uDecimal+1)) : 0; - - // Scale the integer portion to the same offset as the fraction. - uValue = uInteger; - for (int i = -iOffset; i--;) - uValue *= 10; - - // Add in the fraction. - uValue += uFraction; - } - catch (...) - { - cLog(lsINFO) << "Bad float amount: " << sAmount; - - return false; - } + cLog(lsWARNING) << "Number not parsed: \"" << sAmount << "\""; + return false; } + cLog(lsTRACE) << "Float \"" << sAmount << "\" parsed to " << mValue << " : " << mOffset; + if (mIsNative) { - if (bInteger) - iOffset = -SYSTEM_CURRENCY_PRECISION; + if (smMatch[3].matched) + mOffset -= SYSTEM_CURRENCY_PRECISION; - while (iOffset > -SYSTEM_CURRENCY_PRECISION) { - uValue *= 10; - --iOffset; + while (mOffset > 0) + { + mValue *= 10; + --mOffset; } - while (iOffset < -SYSTEM_CURRENCY_PRECISION) { - uValue /= 10; - ++iOffset; + while (mOffset < 0) { + mValue /= 10; + ++mOffset; } - - mValue = uValue; } else - { - mValue = uValue; - mOffset = iOffset; canonicalize(); - } + return true; } @@ -1287,11 +1231,13 @@ BOOST_AUTO_TEST_CASE( setValue_test ) { STAmount saTmp; +#if 0 // Check native floats saTmp.setFullValue("1^0"); BOOST_CHECK_MESSAGE(SYSTEM_CURRENCY_PARTS == saTmp.getNValue(), "float integer failed"); saTmp.setFullValue("0^1"); BOOST_CHECK_MESSAGE(SYSTEM_CURRENCY_PARTS/10 == saTmp.getNValue(), "float fraction failed"); saTmp.setFullValue("0^12"); BOOST_CHECK_MESSAGE(12*SYSTEM_CURRENCY_PARTS/100 == saTmp.getNValue(), "float fraction failed"); saTmp.setFullValue("1^2"); BOOST_CHECK_MESSAGE(SYSTEM_CURRENCY_PARTS+(2*SYSTEM_CURRENCY_PARTS/10) == saTmp.getNValue(), "float combined failed"); +#endif // Check native integer saTmp.setFullValue("1"); BOOST_CHECK_MESSAGE(1 == saTmp.getNValue(), "integer failed"); diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 0eb3eab404..0a9b408b92 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -564,7 +564,7 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre // void NetworkOPs::setStateTimer() -{ // set timer early if ledger is closing +{ mNetTimer.expires_from_now(boost::posix_time::milliseconds(LEDGER_GRANULARITY)); mNetTimer.async_wait(boost::bind(&NetworkOPs::checkState, this, boost::asio::placeholders::error)); } @@ -594,36 +594,41 @@ void NetworkOPs::checkState(const boost::system::error_code& result) { // Network state machine if ((result == boost::asio::error::operation_aborted) || theConfig.RUN_STANDALONE) + { + cLog(lsFATAL) << "Network state timer error: " << result; return; + } + + { + ScopedLock(theApp->getMasterLock()); + + std::vector peerList = theApp->getConnectionPool().getPeerVector(); + + // do we have sufficient peers? If not, we are disconnected. + if (peerList.size() < theConfig.NETWORK_QUORUM) + { + if (mMode != omDISCONNECTED) + { + setMode(omDISCONNECTED); + cLog(lsWARNING) << "Node count (" << peerList.size() << + ") has fallen below quorum (" << theConfig.NETWORK_QUORUM << ")."; + } + return; + } + if (mMode == omDISCONNECTED) + { + setMode(omCONNECTED); + cLog(lsINFO) << "Node count (" << peerList.size() << ") is sufficient."; + } + + if (!mConsensus) + tryStartConsensus(); + + if (mConsensus) + mConsensus->timerEntry(); + } setStateTimer(); - - ScopedLock(theApp->getMasterLock()); - - std::vector peerList = theApp->getConnectionPool().getPeerVector(); - - // do we have sufficient peers? If not, we are disconnected. - if (peerList.size() < theConfig.NETWORK_QUORUM) - { - if (mMode != omDISCONNECTED) - { - setMode(omDISCONNECTED); - cLog(lsWARNING) << "Node count (" << peerList.size() << - ") has fallen below quorum (" << theConfig.NETWORK_QUORUM << ")."; - } - return; - } - if (mMode == omDISCONNECTED) - { - setMode(omCONNECTED); - cLog(lsINFO) << "Node count (" << peerList.size() << ") is sufficient."; - } - - if (!mConsensus) - tryStartConsensus(); - - if (mConsensus) - mConsensus->timerEntry(); } void NetworkOPs::tryStartConsensus() diff --git a/src/cpp/ripple/TransactionCheck.cpp b/src/cpp/ripple/TransactionCheck.cpp new file mode 100644 index 0000000000..50b1a7f7cf --- /dev/null +++ b/src/cpp/ripple/TransactionCheck.cpp @@ -0,0 +1,11 @@ + +#include "TransactionErr.h" +#include "TransactionEngine.h" + +// Double check a transaction's metadata to make sure no system invariants were broken +// Call right before 'calcRawMeta' + +bool TransactionEngine::checkInvariants(TER result, const SerializedTransaction& txn, TransactionEngineParams params) +{ + return true; +} diff --git a/src/cpp/ripple/TransactionEngine.cpp b/src/cpp/ripple/TransactionEngine.cpp index 1e0beac41d..79ce67c1f2 100644 --- a/src/cpp/ripple/TransactionEngine.cpp +++ b/src/cpp/ripple/TransactionEngine.cpp @@ -93,7 +93,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa } #endif - UPTR_T transactor = Transactor::makeTransactor(txn,params,this); + UPTR_T transactor = Transactor::makeTransactor(txn, params, this); if (transactor.get() != NULL) { uint256 txID = txn.getTransactionID(); @@ -153,28 +153,39 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa if (didApply) { - // Transaction succeeded fully or (retries are not allowed and the transaction could claim a fee) - Serializer m; - mNodes.calcRawMeta(m, terResult, mTxnSeq++); - - txnWrite(); - - Serializer s; - txn.add(s); - - if (isSetBit(params, tapOPEN_LEDGER)) + if (!checkInvariants(terResult, txn, params)) { - if (!mLedger->addTransaction(txID, s)) - assert(false); + cLog(lsFATAL) << "Transaction violates invariants"; + cLog(lsFATAL) << txn.getJson(0); + cLog(lsFATAL) << transToken(terResult) << ": " << transHuman(terResult); + cLog(lsFATAL) << mNodes.getJson(0); + didApply = false; } else { - if (!mLedger->addTransaction(txID, s, m)) + // Transaction succeeded fully or (retries are not allowed and the transaction could claim a fee) + Serializer m; + mNodes.calcRawMeta(m, terResult, mTxnSeq++); + + txnWrite(); + + Serializer s; + txn.add(s); + + if (isSetBit(params, tapOPEN_LEDGER)) + { + if (!mLedger->addTransaction(txID, s)) + assert(false); + } + else + { + if (!mLedger->addTransaction(txID, s, m)) assert(false); - // Charge whatever fee they specified. - STAmount saPaid = txn.getTransactionFee(); - mLedger->destroyCoins(saPaid.getNValue()); + // Charge whatever fee they specified. + STAmount saPaid = txn.getTransactionFee(); + mLedger->destroyCoins(saPaid.getNValue()); + } } } diff --git a/src/cpp/ripple/TransactionEngine.h b/src/cpp/ripple/TransactionEngine.h index 2ad074efb6..17e1e02f94 100644 --- a/src/cpp/ripple/TransactionEngine.h +++ b/src/cpp/ripple/TransactionEngine.h @@ -65,6 +65,7 @@ public: void entryModify(SLE::ref sleEntry) { mNodes.entryModify(sleEntry); } TER applyTransaction(const SerializedTransaction&, TransactionEngineParams, bool& didApply); + bool checkInvariants(TER result, const SerializedTransaction& txn, TransactionEngineParams params); }; inline TransactionEngineParams operator|(const TransactionEngineParams& l1, const TransactionEngineParams& l2)