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)