diff --git a/newcoin.vcxproj b/newcoin.vcxproj
index c053c90f59..d6c37b34c1 100644
--- a/newcoin.vcxproj
+++ b/newcoin.vcxproj
@@ -146,6 +146,7 @@
+
@@ -248,17 +249,13 @@
Designer
-
-
-
Document
..\protoc-2.4.1-win32\protoc -I=..\newcoin\src --cpp_out=..\newcoin\obj\src ..\newcoin\src\newcoin.proto
obj\src\newcoin.pb.h
-
diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters
index b8c3acee1e..2116e89379 100644
--- a/newcoin.vcxproj.filters
+++ b/newcoin.vcxproj.filters
@@ -273,6 +273,9 @@
Source Files
+
+ Source Files
+
@@ -506,16 +509,12 @@
-
html
-
-
-
diff --git a/newcoind.cfg b/newcoind.cfg
index 01b72581eb..774a32129d 100644
--- a/newcoind.cfg
+++ b/newcoind.cfg
@@ -85,6 +85,10 @@
# 192.168.0.1 3939
# 2001:0db8:0100:f101:0210:a4ff:fee3:9566
#
+# [sntp_servers]
+# IP address or domain of servers to use for time synchronization.
+# The default time servers are suitable for servers located in the United States
+#
# [peer_ip]:
# IP address or domain to bind to allow external connections from peers.
# Defaults to not allow external connections from peers.
@@ -140,6 +144,12 @@
[debug_logfile]
debug.log
+[sntp_servers]
+time.windows.com
+time.apple.com
+time.nist.gov
+pool.ntp.org
+
[validation_seed]
shh1D4oj5czH3PUEjYES8c7Bay3tE
diff --git a/src/Amount.cpp b/src/Amount.cpp
index 7d95eeffca..1e2db3124c 100644
--- a/src/Amount.cpp
+++ b/src/Amount.cpp
@@ -446,7 +446,7 @@ bool STAmount::operator==(const STAmount& a) const
bool STAmount::operator!=(const STAmount& a) const
{
- return (mOffset != a.mOffset) || (mValue != a.mValue) || (mIsNegative!= a.mIsNegative) || !isComparable(a);
+ return (mOffset != a.mOffset) || (mValue != a.mValue) || (mIsNegative != a.mIsNegative) || !isComparable(a);
}
bool STAmount::operator<(const STAmount& a) const
@@ -762,6 +762,14 @@ uint64 STAmount::getRate(const STAmount& offerOut, const STAmount& offerIn)
return (ret << (64 - 8)) | r.getMantissa();
}
+STAmount STAmount::setRate(uint64 rate, const uint160& currencyOut)
+{
+ uint64 mantissa = rate & ~(255ull << (64 - 8));
+ int exponent = static_cast(rate >> (64 - 8)) - 100;
+
+ return STAmount(currencyOut, mantissa, exponent);
+}
+
// Taker gets all taker can pay for with saTakerFunds, limited by saOfferPays and saOfferFunds.
// --> saOfferFunds: Limit for saOfferPays
// --> saTakerFunds: Limit for saOfferGets : How much taker really wants. : Driver
@@ -1105,6 +1113,12 @@ BOOST_AUTO_TEST_CASE( CustomCurrency_test )
if (STAmount::divide(STAmount(currency, 60) , STAmount(currency, 3), uint160()).getText() != "20")
BOOST_FAIL("STAmount divide fail");
+ STAmount a1(currency, 60), a2 (currency, 10, -1);
+ if (STAmount::divide(a2, a1, currency) != STAmount::setRate(STAmount::getRate(a1, a2), currency))
+ BOOST_FAIL("STAmount setRate(getRate) fail");
+ if (STAmount::divide(a1, a2, currency) != STAmount::setRate(STAmount::getRate(a2, a1), currency))
+ BOOST_FAIL("STAmount setRate(getRate) fail");
+
BOOST_TEST_MESSAGE("Amount CC Complete");
}
@@ -1115,21 +1129,21 @@ BOOST_AUTO_TEST_CASE( CurrencyMulDivTests )
uint160 c(1);
if (STAmount::getRate(STAmount(1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul))
- BOOST_FAIL("STAmount getrate fail");
+ BOOST_FAIL("STAmount getRate fail");
if (STAmount::getRate(STAmount(10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul))
- BOOST_FAIL("STAmount getrate fail");
+ BOOST_FAIL("STAmount getRate fail");
if (STAmount::getRate(STAmount(c, 1), STAmount(c, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul))
- BOOST_FAIL("STAmount getrate fail");
+ BOOST_FAIL("STAmount getRate fail");
if (STAmount::getRate(STAmount(c, 10), STAmount(c, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul))
- BOOST_FAIL("STAmount getrate fail");
+ BOOST_FAIL("STAmount getRate fail");
if (STAmount::getRate(STAmount(c, 1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul))
- BOOST_FAIL("STAmount getrate fail");
+ BOOST_FAIL("STAmount getRate fail");
if (STAmount::getRate(STAmount(c, 10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul))
- BOOST_FAIL("STAmount getrate fail");
+ BOOST_FAIL("STAmount getRate fail");
if (STAmount::getRate(STAmount(1), STAmount(c, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul))
- BOOST_FAIL("STAmount getrate fail");
+ BOOST_FAIL("STAmount getRate fail");
if (STAmount::getRate(STAmount(10), STAmount(c, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul))
- BOOST_FAIL("STAmount getrate fail");
+ BOOST_FAIL("STAmount getRate fail");
}
diff --git a/src/Application.cpp b/src/Application.cpp
index dd4fd40a26..daac98dd54 100644
--- a/src/Application.cpp
+++ b/src/Application.cpp
@@ -38,7 +38,8 @@ DatabaseCon::~DatabaseCon()
Application::Application() :
mUNL(mIOService),
mNetOps(mIOService, &mMasterLedger), mTempNodeCache(16384, 90), mHashedObjectStore(16384, 300),
- mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL),
+ mSNTPClient(mAuxService), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL),
+ mHashNodeDB(NULL), mNetNodeDB(NULL),
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL)
{
RAND_bytes(mNonce256.begin(), mNonce256.size());
@@ -50,6 +51,7 @@ extern int RpcDBCount, TxnDBCount, LedgerDBCount, WalletDBCount, HashNodeDBCount
void Application::stop()
{
+ mAuxService.stop();
mIOService.stop();
mHashedObjectStore.bulkWrite();
mValidations.flush();
@@ -68,6 +70,11 @@ void Application::run()
if (!theConfig.DEBUG_LOGFILE.empty())
Log::setLogFile(theConfig.DEBUG_LOGFILE);
+ mSNTPClient.init(theConfig.SNTP_SERVERS);
+
+ boost::thread auxThread(boost::bind(&boost::asio::io_service::run, &mAuxService));
+ auxThread.detach();
+
//
// Construct databases.
//
@@ -90,6 +97,7 @@ void Application::run()
//
getUNL().nodeBootstrap();
+
//
// Allow peer connections.
//
@@ -146,6 +154,7 @@ void Application::run()
mNetOps.setLastCloseNetTime(secondLedger->getCloseTimeNC());
}
+
mNetOps.setStateTimer();
mIOService.run(); // This blocks
diff --git a/src/Application.h b/src/Application.h
index 14281abbff..79ffb8cf34 100644
--- a/src/Application.h
+++ b/src/Application.h
@@ -16,6 +16,7 @@
#include "TaggedCache.h"
#include "ValidationCollection.h"
#include "Suppression.h"
+#include "SNTPClient.h"
#include "../database/database.h"
@@ -38,7 +39,7 @@ public:
class Application
{
- boost::asio::io_service mIOService;
+ boost::asio::io_service mIOService, mAuxService;
Wallet mWallet;
UniqueNodeList mUNL;
@@ -50,6 +51,7 @@ class Application
ValidationCollection mValidations;
SuppressionTable mSuppressions;
HashedObjectStore mHashedObjectStore;
+ SNTPClient mSNTPClient;
DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB;
@@ -86,6 +88,7 @@ public:
bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); }
bool isNew(const uint160& s) { return mSuppressions.addSuppression(s); }
bool running() { return mTxnDB != NULL; }
+ bool getSystemTimeOffset(int& offset) { return mSNTPClient.getOffset(offset); }
DatabaseCon* getRpcDB() { return mRpcDB; }
DatabaseCon* getTxnDB() { return mTxnDB; }
diff --git a/src/Config.cpp b/src/Config.cpp
index e8e81747d3..cf9c135bfe 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -23,6 +23,7 @@
#define SECTION_RPC_ALLOW_REMOTE "rpc_allow_remote"
#define SECTION_RPC_IP "rpc_ip"
#define SECTION_RPC_PORT "rpc_port"
+#define SECTION_SNTP "sntp_servers"
#define SECTION_UNL_DEFAULT "unl_default"
#define SECTION_VALIDATION_QUORUM "validation_quorum"
#define SECTION_VALIDATION_SEED "validation_seed"
@@ -193,6 +194,12 @@ void Config::load()
// sectionEntriesPrint(&IPS, SECTION_IPS);
}
+ smtTmp = sectionEntries(secConfig, SECTION_SNTP);
+ if (smtTmp)
+ {
+ SNTP_SERVERS = *smtTmp;
+ }
+
(void) sectionSingleB(secConfig, SECTION_VALIDATORS_SITE, VALIDATORS_SITE);
(void) sectionSingleB(secConfig, SECTION_PEER_IP, PEER_IP);
diff --git a/src/Config.h b/src/Config.h
index 85de649e2d..585980b92b 100644
--- a/src/Config.h
+++ b/src/Config.h
@@ -54,6 +54,7 @@ public:
std::string VALIDATORS_SITE; // Where to find validators.txt on the Internet.
std::vector VALIDATORS; // Validators from newcoind.cfg.
std::vector IPS; // Peer IPs from newcoind.cfg.
+ std::vector SNTP_SERVERS; // SNTP servers from newcoind.cfg.
// Network parameters
int NETWORK_START_TIME; // The Unix time we start ledger 0.
diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp
index 8c9ef0ac7c..9dc7e6e92c 100644
--- a/src/HashedObject.cpp
+++ b/src/HashedObject.cpp
@@ -37,7 +37,6 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index,
t.detach();
}
}
- Log(lsTRACE) << "HOS: " << hash.GetHex() << " store: deferred";
return true;
}
diff --git a/src/Ledger.cpp b/src/Ledger.cpp
index aa279f8a46..131e7d3b91 100644
--- a/src/Ledger.cpp
+++ b/src/Ledger.cpp
@@ -71,7 +71,7 @@ Ledger::Ledger(bool dummy, Ledger& prevLedger) :
prevLedger.getCloseAgree(), mLedgerSeq);
if (prevLedger.mCloseTime == 0)
{
- mCloseTime = theApp->getOPs().getNetworkTimeNC();
+ mCloseTime = theApp->getOPs().getCloseTimeNC() - mCloseResolution;
mCloseTime -= (mCloseTime % mCloseResolution);
}
else
diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp
index 088c8c6638..2407616bd6 100644
--- a/src/LedgerAcquire.cpp
+++ b/src/LedgerAcquire.cpp
@@ -10,7 +10,7 @@
#include "SHAMapSync.h"
#include "HashPrefixes.h"
-#define LA_DEBUG
+// #define LA_DEBUG
#define LEDGER_ACQUIRE_TIMEOUT 2
#define TRUST_NETWORK
@@ -88,13 +88,23 @@ void PeerSet::TimerEntry(boost::weak_ptr wptr, const boost::system::err
}
LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT),
- mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false)
+ mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false), mSignaled(false)
{
#ifdef LA_DEBUG
Log(lsTRACE) << "Acquiring ledger " << mHash.GetHex();
#endif
}
+void LedgerAcquire::onTimer()
+{
+ if (getTimeouts() > 6)
+ {
+ setFailed();
+ done();
+ }
+ else trigger(Peer::pointer(), true);
+}
+
boost::weak_ptr LedgerAcquire::pmDowncast()
{
return boost::shared_polymorphic_downcast(shared_from_this());
@@ -102,6 +112,9 @@ boost::weak_ptr LedgerAcquire::pmDowncast()
void LedgerAcquire::done()
{
+ if (mSignaled)
+ return;
+ mSignaled = true;
#ifdef LA_DEBUG
Log(lsTRACE) << "Done acquiring ledger " << mHash.GetHex();
#endif
@@ -113,9 +126,10 @@ void LedgerAcquire::done()
mOnComplete.empty();
mLock.unlock();
- theApp->getMasterLedger().storeLedger(mLedger);
+ if (mLedger)
+ theApp->getMasterLedger().storeLedger(mLedger);
- for (int i = 0; i < triggers.size(); ++i)
+ for (unsigned int i = 0; i < triggers.size(); ++i)
triggers[i](shared_from_this());
}
@@ -126,33 +140,25 @@ void LedgerAcquire::addOnComplete(boost::function
mLock.unlock();
}
-void LedgerAcquire::trigger(Peer::pointer peer)
+void LedgerAcquire::trigger(Peer::pointer peer, bool timer)
{
- if (mAborted)
+ if (mAborted || mComplete || mFailed)
return;
#ifdef LA_DEBUG
if(peer) Log(lsTRACE) << "Trigger acquiring ledger " << mHash.GetHex() << " from " << peer->getIP();
else Log(lsTRACE) << "Trigger acquiring ledger " << mHash.GetHex();
- Log(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed;
- Log(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState;
-#endif
if (mComplete || mFailed)
- return;
+ Log(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed;
+ else
+ Log(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState;
+#endif
if (!mHaveBase)
{
-#ifdef LA_DEBUG
- Log(lsTRACE) << "need base";
-#endif
newcoin::TMGetLedger tmGL;
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
tmGL.set_itype(newcoin::liBASE);
*(tmGL.add_nodeids()) = SHAMapNode().getRawString();
- if (peer)
- {
- sendRequest(tmGL, peer);
- return;
- }
- else sendRequest(tmGL);
+ sendRequest(tmGL, peer);
}
if (mHaveBase && !mHaveTransactions)
@@ -168,12 +174,7 @@ void LedgerAcquire::trigger(Peer::pointer peer)
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
tmGL.set_itype(newcoin::liTX_NODE);
*(tmGL.add_nodeids()) = SHAMapNode().getRawString();
- if (peer)
- {
- sendRequest(tmGL, peer);
- return;
- }
- sendRequest(tmGL);
+ sendRequest(tmGL, peer);
}
else
{
@@ -187,7 +188,8 @@ void LedgerAcquire::trigger(Peer::pointer peer)
else
{
mHaveTransactions = true;
- if (mHaveState) mComplete = true;
+ if (mHaveState)
+ mComplete = true;
}
}
else
@@ -198,12 +200,7 @@ void LedgerAcquire::trigger(Peer::pointer peer)
tmGL.set_itype(newcoin::liTX_NODE);
for (std::vector::iterator it = nodeIDs.begin(); it != nodeIDs.end(); ++it)
*(tmGL.add_nodeids()) = it->getRawString();
- if (peer)
- {
- sendRequest(tmGL, peer);
- return;
- }
- sendRequest(tmGL);
+ sendRequest(tmGL, peer);
}
}
}
@@ -221,12 +218,7 @@ void LedgerAcquire::trigger(Peer::pointer peer)
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
tmGL.set_itype(newcoin::liAS_NODE);
*(tmGL.add_nodeids()) = SHAMapNode().getRawString();
- if (peer)
- {
- sendRequest(tmGL, peer);
- return;
- }
- sendRequest(tmGL);
+ sendRequest(tmGL, peer);
}
else
{
@@ -240,7 +232,8 @@ void LedgerAcquire::trigger(Peer::pointer peer)
else
{
mHaveState = true;
- if (mHaveTransactions) mComplete = true;
+ if (mHaveTransactions)
+ mComplete = true;
}
}
else
@@ -251,25 +244,23 @@ void LedgerAcquire::trigger(Peer::pointer peer)
tmGL.set_itype(newcoin::liAS_NODE);
for (std::vector::iterator it = nodeIDs.begin(); it != nodeIDs.end(); ++it)
*(tmGL.add_nodeids()) = it->getRawString();
- if (peer)
- {
- sendRequest(tmGL, peer);
- return;
- }
- sendRequest(tmGL);
+ sendRequest(tmGL, peer);
}
}
}
if (mComplete || mFailed)
done();
- else
+ else if (timer)
resetTimer();
}
void PeerSet::sendRequest(const newcoin::TMGetLedger& tmGL, Peer::pointer peer)
{
- peer->sendPacket(boost::make_shared(tmGL, newcoin::mtGET_LEDGER));
+ if (!peer)
+ sendRequest(tmGL);
+ else
+ peer->sendPacket(boost::make_shared(tmGL, newcoin::mtGET_LEDGER));
}
void PeerSet::sendRequest(const newcoin::TMGetLedger& tmGL)
@@ -320,8 +311,10 @@ bool LedgerAcquire::takeBase(const std::string& data)
theApp->getHashedObjectStore().store(LEDGER, mLedger->getLedgerSeq(), s.peekData(), mHash);
progress();
- if (!mLedger->getTransHash()) mHaveTransactions = true;
- if (!mLedger->getAccountHash()) mHaveState = true;
+ if (!mLedger->getTransHash())
+ mHaveTransactions = true;
+ if (!mLedger->getAccountHash())
+ mHaveState = true;
mLedger->setAcquiring();
return true;
}
@@ -337,7 +330,7 @@ bool LedgerAcquire::takeTxNode(const std::list& nodeIDs,
{
if (nodeIDit->isRoot())
{
- if (!mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), *nodeDatait, STN_ARF_WIRE))
+ if (!mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), *nodeDatait, snfWIRE))
return false;
}
else if (!mLedger->peekTransactionMap()->addKnownNode(*nodeIDit, *nodeDatait, &tFilter))
@@ -372,7 +365,7 @@ bool LedgerAcquire::takeAsNode(const std::list& nodeIDs,
{
if (nodeIDit->isRoot())
{
- if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), *nodeDatait, STN_ARF_WIRE))
+ if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), *nodeDatait, snfWIRE))
return false;
}
else if (!mLedger->peekAccountStateMap()->addKnownNode(*nodeIDit, *nodeDatait, &tFilter))
@@ -396,13 +389,13 @@ bool LedgerAcquire::takeAsNode(const std::list& nodeIDs,
bool LedgerAcquire::takeAsRootNode(const std::vector& data)
{
if (!mHaveBase) return false;
- return mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), data, STN_ARF_WIRE);
+ return mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), data, snfWIRE);
}
bool LedgerAcquire::takeTxRootNode(const std::vector& data)
{
if (!mHaveBase) return false;
- return mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), data, STN_ARF_WIRE);
+ return mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), data, snfWIRE);
}
LedgerAcquire::pointer LedgerAcquireMaster::findCreate(const uint256& hash)
@@ -468,23 +461,21 @@ bool LedgerAcquireMaster::gotLedgerData(newcoin::TMLedgerData& packet, Peer::poi
return false;
if (packet.nodes_size() == 1)
{
- ledger->trigger(peer);
+ ledger->trigger(peer, false);
return true;
}
- Log(lsDEBUG) << "liBASE includes ASbase";
if (!ledger->takeAsRootNode(strCopy(packet.nodes(1).nodedata())))
{
Log(lsWARNING) << "Included ASbase invalid";
}
if (packet.nodes().size() == 2)
{
- ledger->trigger(peer);
+ ledger->trigger(peer, false);
return true;
}
- Log(lsDEBUG) << "liBASE includes TXbase";
if (!ledger->takeTxRootNode(strCopy(packet.nodes(2).nodedata())))
Log(lsWARNING) << "Invcluded TXbase invalid";
- ledger->trigger(peer);
+ ledger->trigger(peer, false);
return true;
}
@@ -508,7 +499,7 @@ bool LedgerAcquireMaster::gotLedgerData(newcoin::TMLedgerData& packet, Peer::poi
else
ret = ledger->takeAsNode(nodeIDs, nodeData);
if (ret)
- ledger->trigger(peer);
+ ledger->trigger(peer, false);
return ret;
}
diff --git a/src/LedgerAcquire.h b/src/LedgerAcquire.h
index 62540172b3..1497ca41ad 100644
--- a/src/LedgerAcquire.h
+++ b/src/LedgerAcquire.h
@@ -65,14 +65,14 @@ public:
protected:
Ledger::pointer mLedger;
- bool mHaveBase, mHaveState, mHaveTransactions, mAborted;
+ bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled;
std::vector< boost::function > mOnComplete;
void done();
- void onTimer() { trigger(Peer::pointer()); }
+ void onTimer();
- void newPeer(Peer::pointer peer) { trigger(peer); }
+ void newPeer(Peer::pointer peer) { trigger(peer, false); }
boost::weak_ptr pmDowncast();
@@ -92,7 +92,7 @@ public:
bool takeTxRootNode(const std::vector& data);
bool takeAsNode(const std::list& IDs, const std::list >& data);
bool takeAsRootNode(const std::vector& data);
- void trigger(Peer::pointer);
+ void trigger(Peer::pointer, bool timer);
};
class LedgerAcquireMaster
diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp
index c4eeab81db..b9bd543450 100644
--- a/src/LedgerConsensus.cpp
+++ b/src/LedgerConsensus.cpp
@@ -36,7 +36,7 @@ boost::weak_ptr TransactionAcquire::pmDowncast()
return boost::shared_polymorphic_downcast(shared_from_this());
}
-void TransactionAcquire::trigger(Peer::pointer peer)
+void TransactionAcquire::trigger(Peer::pointer peer, bool timer)
{
if (mComplete || mFailed)
return;
@@ -76,7 +76,7 @@ void TransactionAcquire::trigger(Peer::pointer peer)
}
if (mComplete || mFailed)
done();
- else
+ else if (timer)
resetTimer();
}
@@ -101,7 +101,7 @@ bool TransactionAcquire::takeNodes(const std::list& nodeIDs,
Log(lsWARNING) << "Got root TXS node, already have it";
return false;
}
- if (!mMap->addRootNode(getHash(), *nodeDatait, STN_ARF_WIRE))
+ if (!mMap->addRootNode(getHash(), *nodeDatait, snfWIRE))
return false;
else mHaveRoot = true;
}
@@ -110,7 +110,7 @@ bool TransactionAcquire::takeNodes(const std::list& nodeIDs,
++nodeIDit;
++nodeDatait;
}
- trigger(peer);
+ trigger(peer, false);
progress();
return true;
}
@@ -146,7 +146,7 @@ void LCTransaction::setVote(const uint160& peer, bool votesYes)
++mYays;
res.first->second = true;
}
- else if(!votesYes && res.first->second)
+ else if (!votesYes && res.first->second)
{ // changes vote to no
Log(lsTRACE) << "Peer " << peer.GetHex() << " now votes NO on " << mTransactionID.GetHex();
++mNays;
@@ -201,12 +201,14 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::pointer pre
assert(mPreviousMSeconds);
mCloseResolution = ContinuousLedgerTiming::getNextLedgerTimeResolution(
- previousLedger->getCloseResolution(), previousLedger->getCloseAgree(), previousLedger->getLedgerSeq() + 1);
+ mPreviousLedger->getCloseResolution(), mPreviousLedger->getCloseAgree(), previousLedger->getLedgerSeq() + 1);
mHaveCorrectLCL = previousLedger->getHash() == prevLCLHash;
if (!mHaveCorrectLCL)
{
+ Log(lsINFO) << "Entering consensus with: " << previousLedger->getHash().GetHex();
+ Log(lsINFO) << "Correct LCL is: " << prevLCLHash.GetHex();
mHaveCorrectLCL = mProposing = mValidating = false;
mAcquiringLedger = theApp->getMasterLedgerAcquire().findCreate(prevLCLHash);
std::vector peerList = theApp->getConnectionPool().getPeerVector();
@@ -216,16 +218,50 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::pointer pre
}
else if (mValSeed.isValid())
{
+ Log(lsINFO) << "Entering consensus process, validating";
mHaveCorrectLCL = mValidating = true;
mProposing = theApp->getOPs().getOperatingMode() == NetworkOPs::omFULL;
}
else
{
+ Log(lsINFO) << "Entering consensus process, proposing";
mHaveCorrectLCL = true;
mProposing = mValidating = false;
}
}
+void LedgerConsensus::checkLCL()
+{
+ uint256 netLgr = mPrevLedgerHash;
+ int netLgrCount = 0;
+ {
+ boost::unordered_map vals = theApp->getValidations().getCurrentValidations();
+ for (boost::unordered_map::iterator it = vals.begin(), end = vals.end(); it != end; ++it)
+ if ((it->second > netLgrCount) && !theApp->getValidations().isDeadLedger(it->first))
+ {
+ netLgr = it->first;
+ netLgrCount = it->second;
+ }
+ }
+ if (netLgr != mPrevLedgerHash)
+ { // LCL change
+ Log(lsWARNING) << "View of consensus changed during consensus (" << netLgrCount << ")";
+ mPrevLedgerHash = netLgr;
+ mAcquiringLedger = theApp->getMasterLedgerAcquire().findCreate(mPrevLedgerHash);
+ std::vector peerList = theApp->getConnectionPool().getPeerVector();
+ bool found = false;
+ for (std::vector::const_iterator it = peerList.begin(), end = peerList.end(); it != end; ++it)
+ if ((*it)->hasLedger(mPrevLedgerHash))
+ {
+ found = true;
+ mAcquiringLedger->peerHas(*it);
+ }
+ if (!found)
+ for (std::vector::const_iterator it = peerList.begin(), end = peerList.end(); it != end; ++it)
+ mAcquiringLedger->peerHas(*it);
+ }
+}
+
void LedgerConsensus::takeInitialPosition(Ledger& initialLedger)
{
SHAMap::pointer initialSet = initialLedger.peekTransactionMap()->snapShot(false);
@@ -234,7 +270,7 @@ void LedgerConsensus::takeInitialPosition(Ledger& initialLedger)
// if any peers have taken a contrary position, process disputes
boost::unordered_set found;
- for(boost::unordered_map::iterator it = mPeerPositions.begin(),
+ for (boost::unordered_map::iterator it = mPeerPositions.begin(),
end = mPeerPositions.end(); it != end; ++it)
{
uint256 set = it->second->getCurrentHash();
@@ -260,14 +296,14 @@ void LedgerConsensus::createDisputes(SHAMap::pointer m1, SHAMap::pointer m2)
{
SHAMap::SHAMapDiff differences;
m1->compare(m2, differences, 16384);
- for(SHAMap::SHAMapDiff::iterator pos = differences.begin(), end = differences.end(); pos != end; ++pos)
+ for (SHAMap::SHAMapDiff::iterator pos = differences.begin(), end = differences.end(); pos != end; ++pos)
{ // create disputed transactions (from the ledger that has them)
if (pos->second.first)
{
assert(!pos->second.second);
addDisputedTransaction(pos->first, pos->second.first->peekData());
}
- else if(pos->second.second)
+ else if (pos->second.second)
{
assert(!pos->second.first);
addDisputedTransaction(pos->first, pos->second.second->peekData());
@@ -335,7 +371,7 @@ void LedgerConsensus::adjustCount(SHAMap::pointer map, const std::vectorhasItem(it->second->getTransactionID());
- for(std::vector::const_iterator pit = peers.begin(), pend = peers.end(); pit != pend; ++pit)
+ for (std::vector::const_iterator pit = peers.begin(), pend = peers.end(); pit != pend; ++pit)
it->second->setVote(*pit, setHas);
}
}
@@ -369,7 +405,7 @@ void LedgerConsensus::statePreClose()
int proposersClosed = mPeerPositions.size();
// This ledger is open. This computes how long since the last ledger closed
- int sinceClose = 1000 * (theApp->getOPs().getNetworkTimeNC() - theApp->getOPs().getLastCloseNetTime());
+ int sinceClose = 1000 * (theApp->getOPs().getCloseTimeNC() - theApp->getOPs().getLastCloseNetTime());
if (sinceClose >= ContinuousLedgerTiming::shouldClose(anyTransactions, mPreviousProposers, proposersClosed,
mPreviousMSeconds, sinceClose))
@@ -377,7 +413,7 @@ void LedgerConsensus::statePreClose()
Log(lsINFO) << "CLC: closing ledger";
mState = lcsESTABLISH;
mConsensusStartTime = boost::posix_time::second_clock::universal_time();
- mCloseTime = theApp->getOPs().getNetworkTimeNC();
+ mCloseTime = theApp->getOPs().getCloseTimeNC();
theApp->getOPs().setLastCloseNetTime(mCloseTime);
statusChange(newcoin::neCLOSING_LEDGER, *mPreviousLedger);
takeInitialPosition(*theApp->getMasterLedger().closeLedger());
@@ -391,9 +427,10 @@ void LedgerConsensus::stateEstablish()
updateOurPositions();
if (!mHaveCloseTimeConsensus)
{
- Log(lsINFO) << "No close time consensus";
+ if (haveConsensus())
+ Log(lsINFO) << "We have TX consensus but not CT consensus";
}
- else if (haveConsensus())
+ if (haveConsensus())
{
Log(lsINFO) << "Converge cutoff";
mState = lcsFINISHED;
@@ -418,17 +455,21 @@ void LedgerConsensus::timerEntry()
{
if (!mHaveCorrectLCL)
{
- Log(lsINFO) << "Checking for consensus ledger " << mPrevLedgerHash.GetHex();
+ checkLCL();
Ledger::pointer consensus = theApp->getMasterLedger().getLedgerByHash(mPrevLedgerHash);
if (consensus)
{
- Log(lsINFO) << "We have acquired the consensus ledger";
+ Log(lsINFO) << "Acquired the consensus ledger " << mPrevLedgerHash.GetHex();
if (theApp->getMasterLedger().getClosedLedger()->getHash() != mPrevLedgerHash)
- theApp->getOPs().switchLastClosedLedger(consensus);
+ theApp->getOPs().switchLastClosedLedger(consensus, true);
mPreviousLedger = consensus;
mHaveCorrectLCL = true;
+ mCloseResolution = ContinuousLedgerTiming::getNextLedgerTimeResolution(
+ mPreviousLedger->getCloseResolution(), mPreviousLedger->getCloseAgree(),
+ mPreviousLedger->getLedgerSeq() + 1);
}
- else Log(lsINFO) << "We still don't have it";
+ else
+ Log(lsINFO) << "Need consensus ledger " << mPrevLedgerHash.GetHex();
}
mCurrentMSeconds = (mCloseTime == 0) ? 0 :
@@ -437,9 +478,9 @@ void LedgerConsensus::timerEntry()
switch (mState)
{
- case lcsPRE_CLOSE: statePreClose(); if (mState != lcsESTABLISH) return;
- case lcsESTABLISH: stateEstablish(); if (mState != lcsFINISHED) return;
- case lcsFINISHED: stateFinished(); if (mState != lcsACCEPTED) return;
+ case lcsPRE_CLOSE: statePreClose(); if (mState != lcsESTABLISH) return; fallthru();
+ case lcsESTABLISH: stateEstablish(); if (mState != lcsFINISHED) return; fallthru();
+ case lcsFINISHED: stateFinished(); if (mState != lcsACCEPTED) return; fallthru();
case lcsACCEPTED: stateAccepted(); return;
}
assert(false);
@@ -447,12 +488,11 @@ void LedgerConsensus::timerEntry()
void LedgerConsensus::updateOurPositions()
{
- Log(lsINFO) << "Updating our positions";
bool changes = false;
SHAMap::pointer ourPosition;
std::vector addedTx, removedTx;
- for(boost::unordered_map::iterator it = mDisputes.begin(),
+ for (boost::unordered_map::iterator it = mDisputes.begin(),
end = mDisputes.end(); it != end; ++it)
{
if (it->second->updatePosition(mClosePercent, mProposing))
@@ -479,8 +519,6 @@ void LedgerConsensus::updateOurPositions()
for (boost::unordered_map::iterator it = mPeerPositions.begin(),
end = mPeerPositions.end(); it != end; ++it)
++closeTimes[it->second->getCloseTime() - (it->second->getCloseTime() % mCloseResolution)];
- ++closeTimes[mOurPosition->getCloseTime() - (mOurPosition->getCloseTime() % mCloseResolution)];
-
int neededWeight;
if (mClosePercent < AV_MID_CONSENSUS_TIME)
@@ -489,18 +527,38 @@ void LedgerConsensus::updateOurPositions()
neededWeight = AV_MID_CONSENSUS_PCT;
else neededWeight = AV_LATE_CONSENSUS_PCT;
- int thresh = mPeerPositions.size() * neededWeight / 100;
uint32 closeTime = 0;
+ mHaveCloseTimeConsensus = false;
+
+ int thresh = mPeerPositions.size();
+ if (thresh == 0)
+ { // no other times
+ mHaveCloseTimeConsensus = true;
+ closeTime = mOurPosition->getCloseTime() - (mOurPosition->getCloseTime() % mCloseResolution);
+ }
+ if (mProposing)
+ {
+ ++closeTimes[mOurPosition->getCloseTime() - (mOurPosition->getCloseTime() % mCloseResolution)];
+ ++thresh;
+ }
+ thresh = thresh * neededWeight / 100;
+
for (std::map::iterator it = closeTimes.begin(), end = closeTimes.end(); it != end; ++it)
{
+ Log(lsINFO) << "CCTime: " << it->first << " has " << it->second << " out of " << thresh;
if (it->second > thresh)
{
+ Log(lsINFO) << "Close time consensus reached: " << closeTime;
mHaveCloseTimeConsensus = true;
closeTime = it->first;
}
}
+
if (closeTime != (mOurPosition->getCloseTime() - (mOurPosition->getCloseTime() % mCloseResolution)))
+ {
+ ourPosition = mComplete[mOurPosition->getCurrentHash()]->snapShot(true);
changes = true;
+ }
if (changes)
{
@@ -508,7 +566,7 @@ void LedgerConsensus::updateOurPositions()
mOurPosition->changePosition(newHash, closeTime);
if (mProposing) propose(addedTx, removedTx);
mapComplete(newHash, ourPosition, false);
- Log(lsINFO) << "We change our position to " << newHash.GetHex();
+ Log(lsINFO) << "Position change: CTime " << closeTime << ", tx " << newHash.GetHex();
}
}
@@ -652,6 +710,7 @@ bool LedgerConsensus::peerPosition(LedgerProposal::pointer newPosition)
}
else if (newPosition->getProposeSeq() == 0)
{ // new initial close time estimate
+ Log(lsTRACE) << "Peer reports close time as " << newPosition->getCloseTime();
++mCloseTimes[newPosition->getCloseTime()];
}
Log(lsINFO) << "Processing peer proposal " << newPosition->getProposeSeq() << "/"
@@ -818,13 +877,15 @@ void LedgerConsensus::accept(SHAMap::pointer set)
applyTransactions(set, newLCL, newLCL, failedTransactions, true);
newLCL->setClosed();
- uint32 closeTime = mOurPosition->getCloseTime();
+ uint32 closeTime = mOurPosition->getCloseTime() - (mOurPosition->getCloseTime() & mCloseResolution);
bool closeTimeCorrect = true;
if (closeTime == 0)
- { // we didn't agree
+ { // we agreed to disagree
closeTimeCorrect = false;
closeTime = mPreviousLedger->getCloseTimeNC() + 1;
+ Log(lsINFO) << "Consensus close time (good) " << closeTime;
}
+ else Log(lsINFO) << "Consensus close time (bad) " << closeTime;
newLCL->setAccepted(closeTime, mCloseResolution, closeTimeCorrect);
newLCL->updateHash();
@@ -833,7 +894,6 @@ void LedgerConsensus::accept(SHAMap::pointer set)
statusChange(newcoin::neACCEPTED_LEDGER, *newLCL);
if (mValidating)
{
- assert (theApp->getOPs().getNetworkTimeNC() > newLCL->getCloseTimeNC());
SerializedValidation::pointer v = boost::make_shared
(newLCLHash, newLCL->getCloseTimeNC(), mValSeed, mProposing);
v->setTrusted();
@@ -844,7 +904,6 @@ void LedgerConsensus::accept(SHAMap::pointer set)
theApp->getConnectionPool().relayMessage(NULL, boost::make_shared(val, newcoin::mtVALIDATION));
Log(lsINFO) << "Validation sent " << newLCLHash.GetHex();
}
- else Log(lsWARNING) << "Not validating";
Ledger::pointer newOL = boost::make_shared(true, boost::ref(*newLCL));
ScopedLock sl = theApp->getMasterLedger().getLock();
@@ -878,21 +937,72 @@ void LedgerConsensus::accept(SHAMap::pointer set)
mState = lcsACCEPTED;
sl.unlock();
+ if (mValidating && mOurPosition->getCurrentHash().isNonZero())
+ { // see how close our close time is to other node's close time reports
+ Log(lsINFO) << "We closed at " << boost::lexical_cast(mCloseTime);
+ uint64 closeTotal = mCloseTime;
+ int closeCount = 1;
+ for (std::map::iterator it = mCloseTimes.begin(), end = mCloseTimes.end(); it != end; ++it)
+ {
+ Log(lsINFO) << boost::lexical_cast(it->second) << " time votes for "
+ << boost::lexical_cast(it->first);
+ closeCount += it->second;
+ closeTotal += static_cast(it->first) * static_cast(it->second);
+ }
+ closeTotal += (closeCount / 2);
+ closeTotal /= closeCount;
+ int offset = static_cast(closeTotal) - static_cast(mCloseTime);
+ Log(lsINFO) << "Our close offset is estimated at " << offset << " (" << closeCount << ")";
+ }
+
#ifdef DEBUG
- Json::StyledStreamWriter ssw;
- if (1)
{
+ Json::StyledStreamWriter ssw;
Log(lsTRACE) << "newLCL";
Json::Value p;
newLCL->addJson(p, LEDGER_JSON_DUMP_TXNS | LEDGER_JSON_DUMP_STATE);
ssw.write(Log(lsTRACE).ref(), p);
}
#endif
- // FIXME: If necessary, change state to TRACKING/FULL
}
void LedgerConsensus::endConsensus()
{
- theApp->getOPs().endConsensus();
+ theApp->getOPs().endConsensus(mHaveCorrectLCL);
}
+
+Json::Value LedgerConsensus::getJson()
+{
+ Json::Value ret(Json::objectValue);
+ ret["proposing"] = mProposing ? "yes" : "no";
+ ret["validating"] = mValidating ? "yes" : "no";
+ ret["proposers"] = static_cast(mPeerPositions.size());
+
+ if (mHaveCorrectLCL)
+ {
+ ret["synched"] = "yes";
+ ret["ledger_seq"] = mPreviousLedger->getLedgerSeq() + 1;
+ ret["close_granularity"] = mCloseResolution;
+ }
+ else
+ ret["synched"] = "no";
+
+ switch (mState)
+ {
+ case lcsPRE_CLOSE: ret["state"] = "open"; break;
+ case lcsESTABLISH: ret["state"] = "consensus"; break;
+ case lcsFINISHED: ret["state"] = "finished"; break;
+ case lcsACCEPTED: ret["state"] = "accepted"; break;
+ }
+
+ int v = mDisputes.size();
+ if (v != 0)
+ ret["disputes"] = v;
+
+ if (mOurPosition)
+ ret["our_position"] = mOurPosition->getJson();
+
+ return ret;
+}
+
// vim:ts=4
diff --git a/src/LedgerConsensus.h b/src/LedgerConsensus.h
index 215175d8e3..7e104a43b7 100644
--- a/src/LedgerConsensus.h
+++ b/src/LedgerConsensus.h
@@ -8,6 +8,8 @@
#include
#include
+#include "../json/value.h"
+
#include "key.h"
#include "Transaction.h"
#include "LedgerAcquire.h"
@@ -25,11 +27,11 @@ protected:
SHAMap::pointer mMap;
bool mHaveRoot;
- void onTimer() { trigger(Peer::pointer()); }
- void newPeer(Peer::pointer peer) { trigger(peer); }
+ void onTimer() { trigger(Peer::pointer(), true); }
+ void newPeer(Peer::pointer peer) { trigger(peer, false); }
void done();
- void trigger(Peer::pointer);
+ void trigger(Peer::pointer, bool timer);
boost::weak_ptr pmDowncast();
public:
@@ -142,6 +144,7 @@ public:
LedgerConsensus(const uint256& prevLCLHash, Ledger::pointer previousLedger, uint32 closeTime);
int startup();
+ Json::Value getJson();
Ledger::pointer peekPreviousLedger() { return mPreviousLedger; }
uint256 getLCL() { return mPrevLedgerHash; }
@@ -149,6 +152,7 @@ public:
SHAMap::pointer getTransactionTree(const uint256& hash, bool doAcquire);
TransactionAcquire::pointer getAcquiring(const uint256& hash);
void mapComplete(const uint256& hash, SHAMap::pointer map, bool acquired);
+ void checkLCL();
void timerEntry();
diff --git a/src/LedgerEntrySet.cpp b/src/LedgerEntrySet.cpp
index 10c3e31b19..a43ea72bf3 100644
--- a/src/LedgerEntrySet.cpp
+++ b/src/LedgerEntrySet.cpp
@@ -169,9 +169,11 @@ void LedgerEntrySet::entryDelete(SLE::pointer& sle, bool unfunded)
if (unfunded)
{
assert(sle->getType() == ltOFFER); // only offers can be unfunded
+#if 0
mSet.deleteUnfunded(sle->getIndex(),
sle->getIValueFieldAmount(sfTakerPays),
sle->getIValueFieldAmount(sfTakerGets));
+#endif
}
break;
@@ -192,4 +194,64 @@ bool LedgerEntrySet::intersect(const LedgerEntrySet& lesLeft, const LedgerEntryS
return true; // XXX Needs implementation
}
+Json::Value LedgerEntrySet::getJson(int) const
+{
+ Json::Value ret(Json::objectValue);
+
+ Json::Value nodes(Json::arrayValue);
+ for (boost::unordered_map::const_iterator it = mEntries.begin(),
+ end = mEntries.end(); it != end; ++it)
+ {
+ Json::Value entry(Json::objectValue);
+ entry["node"] = it->first.GetHex();
+ switch (it->second.mEntry->getType())
+ {
+ case ltINVALID: entry["type"] = "invalid"; break;
+ case ltACCOUNT_ROOT: entry["type"] = "acccount_root"; break;
+ case ltDIR_NODE: entry["type"] = "dir_node"; break;
+ case ltGENERATOR_MAP: entry["type"] = "generator_map"; break;
+ case ltRIPPLE_STATE: entry["type"] = "ripple_state"; break;
+ case ltNICKNAME: entry["type"] = "nickname"; break;
+ case ltOFFER: entry["type"] = "offer"; break;
+ default: assert(false);
+ }
+ switch (it->second.mAction)
+ {
+ case taaCACHED: entry["action"] = "cache"; break;
+ case taaMODIFY: entry["action"] = "modify"; break;
+ case taaDELETE: entry["action"] = "delete"; break;
+ case taaCREATE: entry["action"] = "create"; break;
+ default: assert(false);
+ }
+ nodes.append(entry);
+ }
+ ret["nodes" ] = nodes;
+
+ return ret;
+}
+
+void LedgerEntrySet::addRawMeta(Serializer& s)
+{
+ for (boost::unordered_map::const_iterator it = mEntries.begin(),
+ end = mEntries.end(); it != end; ++it)
+ {
+ switch (it->second.mAction)
+ {
+ case taaMODIFY:
+ // WRITEME
+ break;
+ case taaDELETE:
+ // WRITEME
+ break;
+ case taaCREATE:
+ // WRITEME
+ break;
+ default:
+ // ignore these
+ break;
+ }
+ }
+ mSet.addRaw(s);
+}
+
// vim:ts=4
diff --git a/src/LedgerEntrySet.h b/src/LedgerEntrySet.h
index 14f4a38f49..a6784d946d 100644
--- a/src/LedgerEntrySet.h
+++ b/src/LedgerEntrySet.h
@@ -58,6 +58,9 @@ public:
void entryDelete(SLE::pointer&, bool unfunded);
void entryModify(SLE::pointer&); // This entry will be modified
+ Json::Value getJson(int) const;
+ void addRawMeta(Serializer&);
+
// iterator functions
bool isEmpty() const { return mEntries.empty(); }
boost::unordered_map::const_iterator begin() const { return mEntries.begin(); }
diff --git a/src/LedgerProposal.cpp b/src/LedgerProposal.cpp
index 55c7f0afc6..70d9458f6c 100644
--- a/src/LedgerProposal.cpp
+++ b/src/LedgerProposal.cpp
@@ -72,4 +72,18 @@ std::vector LedgerProposal::sign(void)
return ret;
}
+Json::Value LedgerProposal::getJson() const
+{
+ Json::Value ret = Json::objectValue;
+ ret["previous_ledger"] = mPreviousLedger.GetHex();
+ ret["transaction_hash"] = mCurrentHash.GetHex();
+ ret["close_time"] = mCloseTime;
+ ret["propose_seq"] = mProposeSeq;
+
+ if (mPublicKey.isValid())
+ ret["peer_id"] = mPublicKey.humanNodePublic();
+
+ return ret;
+}
+
// vim:ts=4
diff --git a/src/LedgerProposal.h b/src/LedgerProposal.h
index 1490e9d5ee..790b61fb4d 100644
--- a/src/LedgerProposal.h
+++ b/src/LedgerProposal.h
@@ -5,6 +5,8 @@
#include
+#include "../json/value.h"
+
#include "NewcoinAddress.h"
#include "Serializer.h"
@@ -48,6 +50,7 @@ public:
std::vector sign();
void changePosition(const uint256& newPosition, uint32 newCloseTime);
+ Json::Value getJson() const;
};
#endif
diff --git a/src/LedgerTiming.cpp b/src/LedgerTiming.cpp
index e3ea061640..ecff1f441a 100644
--- a/src/LedgerTiming.cpp
+++ b/src/LedgerTiming.cpp
@@ -19,13 +19,15 @@ int ContinuousLedgerTiming::shouldClose(
int previousMSeconds, // seconds the previous ledger took to reach consensus
int currentMSeconds) // seconds since the previous ledger closed
{
- assert((previousMSeconds > 0) && (previousMSeconds < 600000));
- assert((currentMSeconds >= 0) && (currentMSeconds < 600000));
-
-#if 0
- Log(lsTRACE) << boost::str(boost::format("CLC::shouldClose Trans=%s, Prop: %d/%d, Secs: %d (last:%d)") %
- (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed % currentMSeconds % previousMSeconds);
-#endif
+ if ((previousMSeconds < -1000) || (previousMSeconds > 600000) ||
+ (currentMSeconds < -1000) || (currentMSeconds > 600000))
+ {
+ Log(lsFATAL) <<
+ boost::str(boost::format("CLC::shouldClose range error Trans=%s, Prop: %d/%d, Secs: %d (last:%d)")
+ % (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed
+ % currentMSeconds % previousMSeconds);
+ return currentMSeconds;
+ }
if (!anyTransactions)
{ // no transactions so far this interval
@@ -44,12 +46,6 @@ int ContinuousLedgerTiming::shouldClose(
return LEDGER_IDLE_INTERVAL * 1000; // normal idle
}
- if (previousMSeconds == (1000 * LEDGER_IDLE_INTERVAL)) // coming out of idle, close now
- {
- Log(lsTRACE) << "leaving idle, close now";
- return currentMSeconds;
- }
-
Log(lsTRACE) << "close now";
return currentMSeconds; // this ledger should close now
}
@@ -101,7 +97,6 @@ bool ContinuousLedgerTiming::haveConsensus(
int ContinuousLedgerTiming::getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq)
{
assert(ledgerSeq);
- assert(previousAgree); // TEMPORARY
if ((!previousAgree) && ((ledgerSeq % LEDGER_RES_DECREASE) == 0))
{ // reduce resolution
int i = 1;
diff --git a/src/LedgerTiming.h b/src/LedgerTiming.h
index add26558f1..41e85762a1 100644
--- a/src/LedgerTiming.h
+++ b/src/LedgerTiming.h
@@ -5,7 +5,7 @@
# define LEDGER_IDLE_INTERVAL 15
// The number of seconds a validation remains current
-# define LEDGER_MAX_INTERVAL 60
+# define LEDGER_MAX_INTERVAL (LEDGER_IDLE_INTERVAL * 4)
// The number of milliseconds we wait minimum to ensure participation
# define LEDGER_MIN_CONSENSUS 2000
diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp
index a802b8b08f..875834a1e5 100644
--- a/src/NetworkOPs.cpp
+++ b/src/NetworkOPs.cpp
@@ -24,14 +24,16 @@
// there's a functional network.
NetworkOPs::NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster) :
- mMode(omDISCONNECTED),mNetTimer(io_service), mLedgerMaster(pLedgerMaster),
+ mMode(omDISCONNECTED),mNetTimer(io_service), mLedgerMaster(pLedgerMaster), mCloseTimeOffset(0),
mLastCloseProposers(0), mLastCloseConvergeTime(LEDGER_IDLE_INTERVAL)
{
}
boost::posix_time::ptime NetworkOPs::getNetworkTimePT()
{
- return boost::posix_time::second_clock::universal_time();
+ int offset = 0;
+ theApp->getSystemTimeOffset(offset);
+ return boost::posix_time::second_clock::universal_time() + boost::posix_time::seconds(offset);
}
uint32 NetworkOPs::getNetworkTimeNC()
@@ -39,6 +41,11 @@ uint32 NetworkOPs::getNetworkTimeNC()
return iToSeconds(getNetworkTimePT());
}
+uint32 NetworkOPs::getCloseTimeNC()
+{
+ return iToSeconds(getNetworkTimePT() + boost::posix_time::seconds(mCloseTimeOffset));
+}
+
uint32 NetworkOPs::getCurrentLedgerID()
{
return mLedgerMaster->getCurrentLedger()->getLedgerSeq();
@@ -415,6 +422,7 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis
Ledger::pointer ourClosed = mLedgerMaster->getClosedLedger();
uint256 closedLedger = ourClosed->getHash();
+ uint256 prevClosedLedger = ourClosed->getParentHash();
ValidationCount& ourVC = ledgers[closedLedger];
if ((theConfig.LEDGER_CREATOR) && (mMode >= omTRACKING))
@@ -447,19 +455,26 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis
// 3) Is there a network ledger we'd like to switch to? If so, do we have it?
bool switchLedgers = false;
- for(boost::unordered_map::iterator it = ledgers.begin(), end = ledgers.end();
+ for (boost::unordered_map::iterator it = ledgers.begin(), end = ledgers.end();
it != end; ++it)
{
Log(lsTRACE) << "L: " << it->first.GetHex() <<
" t=" << it->second.trustedValidations << ", n=" << it->second.nodesUsing;
- if (it->second > bestVC)
+ if ((it->second > bestVC) && !theApp->getValidations().isDeadLedger(it->first))
{
bestVC = it->second;
closedLedger = it->first;
switchLedgers = true;
}
}
- networkClosed = closedLedger;
+
+ if (switchLedgers && (closedLedger == prevClosedLedger))
+ { // don't switch to our own previous ledger
+ networkClosed = ourClosed->getHash();
+ switchLedgers = false;
+ }
+ else
+ networkClosed = closedLedger;
if (!switchLedgers)
{
@@ -518,15 +533,18 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis
// FIXME: If this rewinds the ledger sequence, or has the same sequence, we should update the status on
// any stored transactions in the invalidated ledgers.
- switchLastClosedLedger(consensus);
+ switchLastClosedLedger(consensus, false);
return true;
}
-void NetworkOPs::switchLastClosedLedger(Ledger::pointer newLedger)
+void NetworkOPs::switchLastClosedLedger(Ledger::pointer newLedger, bool duringConsensus)
{ // set the newledger as our last closed ledger -- this is abnormal code
- Log(lsERROR) << "ABNORMAL Switching last closed ledger to " << newLedger->getHash().GetHex();
+ if (duringConsensus)
+ Log(lsERROR) << "JUMPdc last closed ledger to " << newLedger->getHash().GetHex();
+ else
+ Log(lsERROR) << "JUMP last closed ledger to " << newLedger->getHash().GetHex();
newLedger->setClosed();
Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*newLedger));
@@ -586,12 +604,6 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, uint
if (!theApp->isNew(s.getSHA512Half()))
return false;
- if ((mMode != omFULL) && (mMode != omTRACKING))
- {
- Log(lsINFO) << "Received proposal when not full/tracking: " << mMode;
- return true;
- }
-
if (!mConsensus)
{ // FIXME: CLC
Log(lsWARNING) << "Received proposal when full but not during consensus window";
@@ -643,10 +655,11 @@ void NetworkOPs::mapComplete(const uint256& hash, SHAMap::pointer map)
mConsensus->mapComplete(hash, map, true);
}
-void NetworkOPs::endConsensus()
+void NetworkOPs::endConsensus(bool correctLCL)
{
uint256 deadLedger = theApp->getMasterLedger().getClosedLedger()->getParentHash();
Log(lsTRACE) << "Ledger " << deadLedger.GetHex() << " is now dead";
+ theApp->getValidations().addDeadLedger(deadLedger);
std::vector peerList = theApp->getConnectionPool().getPeerVector();
for (std::vector::const_iterator it = peerList.begin(), end = peerList.end(); it != end; ++it)
if (*it && ((*it)->getClosedLedgerHash() == deadLedger))
@@ -735,6 +748,9 @@ Json::Value NetworkOPs::getServerInfo()
if (!theConfig.VALIDATION_SEED.isValid()) info["serverState"] = "none";
else info["validationPKey"] = NewcoinAddress::createNodePublic(theConfig.VALIDATION_SEED).humanNodePublic();
+ if (mConsensus)
+ info["consensus"] = mConsensus->getJson();
+
return info;
}
diff --git a/src/NetworkOPs.h b/src/NetworkOPs.h
index 36c1941455..8e8e45c24c 100644
--- a/src/NetworkOPs.h
+++ b/src/NetworkOPs.h
@@ -46,6 +46,10 @@ public:
};
protected:
+ typedef boost::unordered_map > subInfoMapType;
+ typedef boost::unordered_map >::value_type subInfoMapValue;
+ typedef boost::unordered_map >::iterator subInfoMapIterator;
+
OperatingMode mMode;
boost::posix_time::ptime mConnectTime;
boost::asio::deadline_timer mNetTimer;
@@ -54,16 +58,12 @@ protected:
LedgerMaster* mLedgerMaster;
LedgerAcquire::pointer mAcquiringLedger;
- void setMode(OperatingMode);
-
- typedef boost::unordered_map > subInfoMapType;
- typedef boost::unordered_map >::value_type subInfoMapValue;
- typedef boost::unordered_map >::iterator subInfoMapIterator;
+ int mCloseTimeOffset;
// last ledger close
- int mLastCloseProposers, mLastCloseConvergeTime;
- uint256 mLastCloseHash;
- uint32 mLastCloseNetTime;
+ int mLastCloseProposers, mLastCloseConvergeTime;
+ uint256 mLastCloseHash;
+ uint32 mLastCloseNetTime;
// XXX Split into more locks.
boost::interprocess::interprocess_upgradable_mutex mMonitorLock;
@@ -75,6 +75,8 @@ protected:
boost::unordered_set mSubTransaction; // all transactions
// subInfoMapType mSubTransactionAccounts;
+ void setMode(OperatingMode);
+
Json::Value transJson(const SerializedTransaction& stTxn, TransactionEngineResult terResult, const std::string& strStatus, int iSeq, const std::string& strType);
void pubTransactionAll(const Ledger::pointer& lpCurrent, const SerializedTransaction& stTxn, TransactionEngineResult terResult, const char* pState);
void pubTransactionAccounts(const Ledger::pointer& lpCurrent, const SerializedTransaction& stTxn, TransactionEngineResult terResult, const char* pState);
@@ -86,6 +88,7 @@ public:
// network information
uint32 getNetworkTimeNC();
+ uint32 getCloseTimeNC();
boost::posix_time::ptime getNetworkTimePT();
uint32 getCurrentLedgerID();
OperatingMode getOperatingMode() { return mMode; }
@@ -181,10 +184,10 @@ public:
// network state machine
void checkState(const boost::system::error_code& result);
- void switchLastClosedLedger(Ledger::pointer newLedger); // Used for the "jump" case
+ void switchLastClosedLedger(Ledger::pointer newLedger, bool duringConsensus); // Used for the "jump" case
bool checkLastClosedLedger(const std::vector&, uint256& networkClosed);
int beginConsensus(const uint256& networkClosed, Ledger::pointer closingLedger);
- void endConsensus();
+ void endConsensus(bool correctLCL);
void setStateTimer();
void newLCL(int proposers, int convergeTime, const uint256& ledgerHash);
int getPreviousProposers() { return mLastCloseProposers; }
diff --git a/src/Peer.cpp b/src/Peer.cpp
index 22994c3cf4..991d18456b 100644
--- a/src/Peer.cpp
+++ b/src/Peer.cpp
@@ -571,15 +571,24 @@ void Peer::recvHello(newcoin::TMHello& packet)
// Cancel verification timeout.
(void) mVerifyTimer.cancel();
- uint32 minTime = theApp->getOPs().getNetworkTimeNC() - 4;
- uint32 maxTime = minTime + 8;
+ uint32 ourTime = theApp->getOPs().getNetworkTimeNC();
+ uint32 minTime = ourTime - 10;
+ uint32 maxTime = ourTime + 10;
+
+#ifdef DEBUG
+ if (packet.has_nettime())
+ {
+ int64 to = ourTime;
+ to -= packet.nettime();
+ Log(lsDEBUG) << "Connect: time offset " << to;
+ }
+#endif
if (packet.has_nettime() && ((packet.nettime() < minTime) || (packet.nettime() > maxTime)))
{
Log(lsINFO) << "Recv(Hello): Disconnect: Clock is far off";
}
-
- if (packet.protoversionmin() < MAKE_VERSION_INT(MIN_PROTO_MAJOR, MIN_PROTO_MINOR))
+ else if (packet.protoversionmin() < MAKE_VERSION_INT(MIN_PROTO_MAJOR, MIN_PROTO_MINOR))
{
Log(lsINFO) << "Recv(Hello): Server requires protocol version " <<
GET_VERSION_MAJOR(packet.protoversion()) << "." << GET_VERSION_MINOR(packet.protoversion())
@@ -998,13 +1007,13 @@ void Peer::recvGetLedger(newcoin::TMGetLedger& packet)
reply.add_nodes()->set_nodedata(nData.getDataPtr(), nData.getLength());
if (packet.nodeids().size() != 0)
- {
+ { // new-style root request
Log(lsINFO) << "Ledger root w/map roots request";
SHAMap::pointer map = ledger->peekAccountStateMap();
if (map)
- {
+ { // return account state root node if possible
Serializer rootNode(768);
- if (map->getRootNode(rootNode, STN_ARF_WIRE))
+ if (map->getRootNode(rootNode, snfWIRE))
{
reply.add_nodes()->set_nodedata(rootNode.getDataPtr(), rootNode.getLength());
if (ledger->getTransHash().isNonZero())
@@ -1013,7 +1022,7 @@ void Peer::recvGetLedger(newcoin::TMGetLedger& packet)
if (map)
{
rootNode.resize(0);
- if (map->getRootNode(rootNode, STN_ARF_WIRE))
+ if (map->getRootNode(rootNode, snfWIRE))
reply.add_nodes()->set_nodedata(rootNode.getDataPtr(), rootNode.getLength());
}
}
diff --git a/src/PubKeyCache.cpp b/src/PubKeyCache.cpp
index 0119969544..f6a4096d38 100644
--- a/src/PubKeyCache.cpp
+++ b/src/PubKeyCache.cpp
@@ -6,7 +6,6 @@
CKey::pointer PubKeyCache::locate(const NewcoinAddress& id)
{
- if(1)
{ // is it in cache
boost::mutex::scoped_lock sl(mLock);
std::map::iterator it(mCache.find(id));
diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp
index 738d1858ef..b360908f2d 100644
--- a/src/SHAMap.cpp
+++ b/src/SHAMap.cpp
@@ -262,7 +262,7 @@ SHAMapItem::pointer SHAMap::firstBelow(SHAMapTreeNode* node)
break;
}
if (!foundNode) return SHAMapItem::pointer();
- } while (1);
+ } while (true);
}
SHAMapItem::pointer SHAMap::lastBelow(SHAMapTreeNode* node)
@@ -284,7 +284,7 @@ SHAMapItem::pointer SHAMap::lastBelow(SHAMapTreeNode* node)
break;
}
if (!foundNode) return SHAMapItem::pointer();
- } while (1);
+ } while (true);
}
SHAMapItem::pointer SHAMap::onlyBelow(SHAMapTreeNode* node)
@@ -359,22 +359,23 @@ SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id)
boost::recursive_mutex::scoped_lock sl(mLock);
std::stack stack = getStack(id, true, false);
- while(!stack.empty())
+ while (!stack.empty())
{
SHAMapTreeNode::pointer node = stack.top();
stack.pop();
- if(node->isLeaf())
+ if (node->isLeaf())
{
- if(node->peekItem()->getTag()>id)
+ if (node->peekItem()->getTag() > id)
return node->peekItem();
}
- else for(int i = node->selectBranch(id) + 1; i < 16; ++i)
- if(!node->isEmptyBranch(i))
+ else for (int i = node->selectBranch(id) + 1; i < 16; ++i)
+ if (!node->isEmptyBranch(i))
{
node = getNode(node->getChildNodeID(i), node->getChildHash(i), false);
SHAMapItem::pointer item = firstBelow(node.get());
- if (!item) throw std::runtime_error("missing node");
+ if (!item)
+ throw std::runtime_error("missing node");
return item;
}
}
@@ -392,17 +393,18 @@ SHAMapItem::pointer SHAMap::peekPrevItem(const uint256& id)
SHAMapTreeNode::pointer node = stack.top();
stack.pop();
- if(node->isLeaf())
+ if (node->isLeaf())
{
- if(node->peekItem()->getTag()peekItem()->getTag() < id)
return node->peekItem();
}
- else for(int i = node->selectBranch(id) - 1; i >= 0; --i)
- if(!node->isEmptyBranch(i))
+ else for (int i = node->selectBranch(id) - 1; i >= 0; --i)
+ if (!node->isEmptyBranch(i))
{
node = getNode(node->getChildNodeID(i), node->getChildHash(i), false);
SHAMapItem::pointer item = firstBelow(node.get());
- if (!item) throw std::runtime_error("missing node");
+ if (!item)
+ throw std::runtime_error("missing node");
return item;
}
}
@@ -414,7 +416,8 @@ SHAMapItem::pointer SHAMap::peekItem(const uint256& id)
{
boost::recursive_mutex::scoped_lock sl(mLock);
SHAMapTreeNode* leaf = walkToPointer(id);
- if (!leaf) return SHAMapItem::pointer();
+ if (!leaf)
+ return SHAMapItem::pointer();
return leaf->peekItem();
}
@@ -432,7 +435,7 @@ bool SHAMap::delItem(const uint256& id)
assert(mState != Immutable);
std::stack stack = getStack(id, true, false);
- if(stack.empty())
+ if (stack.empty())
throw std::runtime_error("missing node");
SHAMapTreeNode::pointer leaf=stack.top();
@@ -442,7 +445,7 @@ bool SHAMap::delItem(const uint256& id)
SHAMapTreeNode::TNType type=leaf->getType();
returnNode(leaf, true);
- if(mTNByID.erase(*leaf)==0)
+ if (mTNByID.erase(*leaf) == 0)
assert(false);
uint256 prevHash;
@@ -460,8 +463,8 @@ bool SHAMap::delItem(const uint256& id)
}
if (!node->isRoot())
{ // we may have made this a node with 1 or 0 children
- int bc=node->getBranchCount();
- if(bc==0)
+ int bc = node->getBranchCount();
+ if (bc == 0)
{
#ifdef DEBUG
std::cerr << "delItem makes empty node" << std::endl;
@@ -470,10 +473,10 @@ bool SHAMap::delItem(const uint256& id)
if (!mTNByID.erase(*node))
assert(false);
}
- else if(bc==1)
+ else if (bc == 1)
{ // pull up on the thread
SHAMapItem::pointer item = onlyBelow(node.get());
- if(item)
+ if (item)
{
eraseChildren(node);
#ifdef ST_DEBUG
@@ -521,7 +524,7 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction, bool hasM
uint256 prevHash;
returnNode(node, true);
- if(node->isInner())
+ if (node->isInner())
{ // easy case, we end on an inner node
#ifdef ST_DEBUG
std::cerr << "aGI inner " << node->getString() << std::endl;
@@ -530,7 +533,7 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction, bool hasM
assert(node->isEmptyBranch(branch));
SHAMapTreeNode::pointer newNode =
boost::make_shared(node->getChildNodeID(branch), item, type, mSeq);
- if(!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
+ if (!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
{
std::cerr << "Node: " << node->getString() << std::endl;
std::cerr << "NewNode: " << newNode->getString() << std::endl;
@@ -562,7 +565,7 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction, bool hasM
SHAMapTreeNode::pointer newNode =
boost::make_shared(mSeq, node->getChildNodeID(b1));
newNode->makeInner();
- if(!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
+ if (!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
assert(false);
stack.push(node);
node = newNode;
@@ -579,7 +582,7 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction, bool hasM
newNode = boost::make_shared(node->getChildNodeID(b2), otherItem, type, mSeq);
assert(newNode->isValid() && newNode->isLeaf());
- if(!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
+ if (!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
assert(false);
node->setChildHash(b2, newNode->getNodeHash());
}
@@ -635,13 +638,13 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui
throw SHAMapMissingNode(id, hash);
HashedObject::pointer obj(theApp->getHashedObjectStore().retrieve(hash));
- if(!obj)
+ if (!obj)
throw SHAMapMissingNode(id, hash);
assert(Serializer::getSHA512Half(obj->getData()) == hash);
try
{
- SHAMapTreeNode::pointer ret = boost::make_shared(id, obj->getData(), mSeq, STN_ARF_PREFIXED);
+ SHAMapTreeNode::pointer ret = boost::make_shared(id, obj->getData(), mSeq, snfPREFIX);
#ifdef DEBUG
assert((ret->getNodeHash() == hash) && (id == *ret));
#endif
@@ -665,14 +668,14 @@ int SHAMap::flushDirty(int maxNodes, HashedObjectType t, uint32 seq)
int flushed = 0;
Serializer s;
- if(mDirtyNodes)
+ if (mDirtyNodes)
{
boost::unordered_map& dirtyNodes = *mDirtyNodes;
boost::unordered_map::iterator it = dirtyNodes.begin();
while (it != dirtyNodes.end())
{
s.erase();
- it->second->addRaw(s, STN_ARF_PREFIXED);
+ it->second->addRaw(s, snfPREFIX);
theApp->getHashedObjectStore().store(t, seq, s.peekData(), s.getSHA512Half());
if (flushed++ >= maxNodes)
return flushed;
@@ -714,10 +717,10 @@ void SHAMap::dump(bool hash)
#if 0
std::cerr << "SHAMap::dump" << std::endl;
SHAMapItem::pointer i=peekFirstItem();
- while(i)
+ while (i)
{
std::cerr << "Item: id=" << i->getTag().GetHex() << std::endl;
- i=peekNextItem(i->getTag());
+ i = peekNextItem(i->getTag());
}
std::cerr << "SHAMap::dump done" << std::endl;
#endif
@@ -728,7 +731,8 @@ void SHAMap::dump(bool hash)
it != mTNByID.end(); ++it)
{
std::cerr << it->second->getString() << std::endl;
- if(hash) std::cerr << " " << it->second->getNodeHash().GetHex() << std::endl;
+ if (hash)
+ std::cerr << " " << it->second->getNodeHash().GetHex() << std::endl;
}
}
@@ -756,8 +760,8 @@ BOOST_AUTO_TEST_CASE( SHAMap_test )
SHAMap sMap;
SHAMapItem i1(h1, IntToVUC(1)), i2(h2, IntToVUC(2)), i3(h3, IntToVUC(3)), i4(h4, IntToVUC(4)), i5(h5, IntToVUC(5));
- if(!sMap.addItem(i2, true, false)) BOOST_FAIL("no add");
- if(!sMap.addItem(i1, true, false)) BOOST_FAIL("no add");
+ if (!sMap.addItem(i2, true, false)) BOOST_FAIL("no add");
+ if (!sMap.addItem(i1, true, false)) BOOST_FAIL("no add");
SHAMapItem::pointer i;
diff --git a/src/SHAMap.h b/src/SHAMap.h
index 0130a624d7..4bfd7999db 100644
--- a/src/SHAMap.h
+++ b/src/SHAMap.h
@@ -122,6 +122,12 @@ public:
virtual void dump();
};
+enum SHANodeFormat
+{
+ snfPREFIX = 1, // Form that hashes to its official hash
+ snfWIRE = 2, // Compressed form used on the wire
+};
+
class SHAMapTreeNode : public SHAMapNode
{
friend class SHAMap;
@@ -156,12 +162,9 @@ public:
SHAMapTreeNode(const SHAMapTreeNode& node, uint32 seq); // copy node from older tree
SHAMapTreeNode(const SHAMapNode& nodeID, SHAMapItem::pointer item, TNType type, uint32 seq);
-#define STN_ARF_PREFIXED 1
-#define STN_ARF_WIRE 2
-
// raw node functions
- SHAMapTreeNode(const SHAMapNode& id, const std::vector& contents, uint32 seq, int format);
- void addRaw(Serializer &, int format);
+ SHAMapTreeNode(const SHAMapNode& id, const std::vector& data, uint32 seq, SHANodeFormat format);
+ void addRaw(Serializer &, SHANodeFormat format);
virtual bool isPopulated() const { return true; }
@@ -325,9 +328,9 @@ public:
SHAMapSyncFilter* filter);
bool getNodeFat(const SHAMapNode& node, std::vector& nodeIDs,
std::list >& rawNode, bool fatLeaves);
- bool getRootNode(Serializer& s, int format);
- bool addRootNode(const uint256& hash, const std::vector& rootNode, int format);
- bool addRootNode(const std::vector& rootNode, int format);
+ bool getRootNode(Serializer& s, SHANodeFormat format);
+ bool addRootNode(const uint256& hash, const std::vector& rootNode, SHANodeFormat format);
+ bool addRootNode(const std::vector& rootNode, SHANodeFormat format);
bool addKnownNode(const SHAMapNode& nodeID, const std::vector& rawNode,
SHAMapSyncFilter* filter);
diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp
index 95fdcb1f9d..689745995c 100644
--- a/src/SHAMapNodes.cpp
+++ b/src/SHAMapNodes.cpp
@@ -27,50 +27,50 @@ uint256 SHAMapNode::smMasks[65];
bool SHAMapNode::operator<(const SHAMapNode &s) const
{
- if(s.mDepthmDepth) return false;
- return mNodeID mDepth) return false;
+ return mNodeID < s.mNodeID;
}
bool SHAMapNode::operator>(const SHAMapNode &s) const
{
- if(s.mDepthmDepth) return true;
- return mNodeID>s.mNodeID;
+ if (s.mDepth < mDepth) return false;
+ if (s.mDepth > mDepth) return true;
+ return mNodeID > s.mNodeID;
}
bool SHAMapNode::operator<=(const SHAMapNode &s) const
{
- if(s.mDepthmDepth) return false;
- return mNodeID<=s.mNodeID;
+ if (s.mDepth < mDepth) return true;
+ if (s.mDepth > mDepth) return false;
+ return mNodeID <= s.mNodeID;
}
bool SHAMapNode::operator>=(const SHAMapNode &s) const
{
- if(s.mDepthmDepth) return true;
- return mNodeID>=s.mNodeID;
+ if (s.mDepth < mDepth) return false;
+ if (s.mDepth > mDepth) return true;
+ return mNodeID >= s.mNodeID;
}
bool SHAMapNode::operator==(const SHAMapNode &s) const
{
- return (s.mDepth==mDepth) && (s.mNodeID==mNodeID);
+ return (s.mDepth == mDepth) && (s.mNodeID == mNodeID);
}
bool SHAMapNode::operator!=(const SHAMapNode &s) const
{
- return (s.mDepth!=mDepth) || (s.mNodeID!=mNodeID);
+ return (s.mDepth != mDepth) || (s.mNodeID != mNodeID);
}
bool SHAMapNode::operator==(const uint256 &s) const
{
- return s==mNodeID;
+ return s == mNodeID;
}
bool SHAMapNode::operator!=(const uint256 &s) const
{
- return s!=mNodeID;
+ return s != mNodeID;
}
static bool j = SHAMapNode::ClassInit();
@@ -78,7 +78,7 @@ static bool j = SHAMapNode::ClassInit();
bool SHAMapNode::ClassInit()
{ // set up the depth masks
uint256 selector;
- for(int i = 0; i < 64; i += 2)
+ for (int i = 0; i < 64; i += 2)
{
smMasks[i] = selector;
*(selector.begin() + (i / 2)) = 0xF0;
@@ -189,10 +189,10 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& node, SHAMapItem::pointer item,
updateHash();
}
-SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector& rawNode, uint32 seq, int format)
- : SHAMapNode(id), mSeq(seq), mType(tnERROR), mFullBelow(false)
+SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector& rawNode, uint32 seq,
+ SHANodeFormat format) : SHAMapNode(id), mSeq(seq), mType(tnERROR), mFullBelow(false)
{
- if (format == STN_ARF_WIRE)
+ if (format == snfWIRE)
{
Serializer s(rawNode);
int type = s.removeLastByte();
@@ -256,7 +256,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vectoraddRaw(s);
@@ -400,7 +400,7 @@ void SHAMapTreeNode::addRaw(Serializer& s, int format)
}
else if (mType == tnTRANSACTION_NM)
{
- if (format == STN_ARF_PREFIXED)
+ if (format == snfPREFIX)
{
s.add32(sHP_TransactionID);
mItem->addRaw(s);
@@ -413,7 +413,7 @@ void SHAMapTreeNode::addRaw(Serializer& s, int format)
}
else if (mType == tnTRANSACTION_MD)
{
- if (format == STN_ARF_PREFIXED)
+ if (format == snfPREFIX)
{
s.add32(sHP_TransactionNode);
mItem->addRaw(s);
@@ -476,7 +476,7 @@ std::string SHAMapTreeNode::getString() const
ret += ")";
if (isInner())
{
- for(int i = 0; i < 16; ++i)
+ for (int i = 0; i < 16; ++i)
if (!isEmptyBranch(i))
{
ret += "\nb";
diff --git a/src/SHAMapSync.cpp b/src/SHAMapSync.cpp
index ab230c79fb..25c2d019a0 100644
--- a/src/SHAMapSync.cpp
+++ b/src/SHAMapSync.cpp
@@ -58,7 +58,7 @@ void SHAMap::getMissingNodes(std::vector& nodeIDs, std::vector nodeData;
if (filter->haveNode(childID, childHash, nodeData))
{
- d = boost::make_shared(childID, nodeData, mSeq, STN_ARF_PREFIXED);
+ d = boost::make_shared(childID, nodeData, mSeq, snfPREFIX);
if (childHash != d->getNodeHash())
{
Log(lsERROR) << "Wrong hash from cached object";
@@ -99,7 +99,7 @@ bool SHAMap::getNodeFat(const SHAMapNode& wanted, std::vector& nodeI
nodeIDs.push_back(*node);
Serializer s;
- node->addRaw(s, STN_ARF_WIRE);
+ node->addRaw(s, snfWIRE);
rawNodes.push_back(s.peekData());
if (node->isRoot() || node->isLeaf()) // don't get a fat root, can't get a fat leaf
@@ -114,7 +114,7 @@ bool SHAMap::getNodeFat(const SHAMapNode& wanted, std::vector& nodeI
{
nodeIDs.push_back(*nextNode);
Serializer s;
- nextNode->addRaw(s, STN_ARF_WIRE);
+ nextNode->addRaw(s, snfWIRE);
rawNodes.push_back(s.peekData());
}
}
@@ -122,14 +122,14 @@ bool SHAMap::getNodeFat(const SHAMapNode& wanted, std::vector& nodeI
return true;
}
-bool SHAMap::getRootNode(Serializer& s, int format)
+bool SHAMap::getRootNode(Serializer& s, SHANodeFormat format)
{
boost::recursive_mutex::scoped_lock sl(mLock);
root->addRaw(s, format);
return true;
}
-bool SHAMap::addRootNode(const std::vector& rootNode, int format)
+bool SHAMap::addRootNode(const std::vector& rootNode, SHANodeFormat format)
{
boost::recursive_mutex::scoped_lock sl(mLock);
@@ -160,7 +160,7 @@ bool SHAMap::addRootNode(const std::vector& rootNode, int format)
return true;
}
-bool SHAMap::addRootNode(const uint256& hash, const std::vector& rootNode, int format)
+bool SHAMap::addRootNode(const uint256& hash, const std::vector& rootNode, SHANodeFormat format)
{
boost::recursive_mutex::scoped_lock sl(mLock);
@@ -236,14 +236,14 @@ bool SHAMap::addKnownNode(const SHAMapNode& node, const std::vectorgetChildHash(branch);
if (!hash) return false;
- SHAMapTreeNode::pointer newNode = boost::make_shared(node, rawNode, mSeq, STN_ARF_WIRE);
+ SHAMapTreeNode::pointer newNode = boost::make_shared(node, rawNode, mSeq, snfWIRE);
if (hash != newNode->getNodeHash()) // these aren't the droids we're looking for
return false;
if (filter)
{
Serializer s;
- newNode->addRaw(s, STN_ARF_PREFIXED);
+ newNode->addRaw(s, snfPREFIX);
filter->gotNode(node, hash, s.peekData(), newNode->isLeaf());
}
@@ -399,7 +399,7 @@ std::list > SHAMap::getTrustedPath(const uint256& ind
Serializer s;
while (!stack.empty())
{
- stack.top()->addRaw(s, STN_ARF_WIRE);
+ stack.top()->addRaw(s, snfWIRE);
path.push_back(s.getData());
s.erase();
stack.pop();
@@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE( SHAMapSync_test )
Log(lsFATAL) << "Didn't get root node " << gotNodes.size();
BOOST_FAIL("NodeSize");
}
- if (!destination.addRootNode(*gotNodes.begin(), STN_ARF_WIRE))
+ if (!destination.addRootNode(*gotNodes.begin(), snfWIRE))
{
Log(lsFATAL) << "AddRootNode fails";
BOOST_FAIL("AddRootNode");
diff --git a/src/SNTPClient.cpp b/src/SNTPClient.cpp
new file mode 100644
index 0000000000..444b7b6c73
--- /dev/null
+++ b/src/SNTPClient.cpp
@@ -0,0 +1,249 @@
+#include "SNTPClient.h"
+
+#include
+#include
+
+#include
+
+#include "utils.h"
+#include "Log.h"
+
+// #define SNTP_DEBUG
+
+static uint8_t SNTPQueryData[48] =
+{ 0x1B,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+
+// NTP query frequency - 5 minutes
+#define NTP_QUERY_FREQUENCY (5 * 60)
+
+// NTP minimum interval to query same servers - 3 minutes
+#define NTP_MIN_QUERY (3 * 60)
+
+// NTP sample window (should be odd)
+#define NTP_SAMPLE_WINDOW 9
+
+// NTP timestamp constant
+#define NTP_UNIX_OFFSET 0x83AA7E80
+
+// SNTP packet offsets
+#define NTP_OFF_INFO 0
+#define NTP_OFF_ROOTDELAY 1
+#define NTP_OFF_ROOTDISP 2
+#define NTP_OFF_REFERENCEID 3
+#define NTP_OFF_REFTS_INT 4
+#define NTP_OFF_REFTS_FRAC 5
+#define NTP_OFF_ORGTS_INT 6
+#define NTP_OFF_ORGTS_FRAC 7
+#define NTP_OFF_RECVTS_INT 8
+#define NTP_OFF_RECVTS_FRAC 9
+#define NTP_OFF_XMITTS_INT 10
+#define NTP_OFF_XMITTS_FRAC 11
+
+
+SNTPClient::SNTPClient(boost::asio::io_service& service) : mSocket(service), mTimer(service), mResolver(service),
+ mOffset(0), mLastOffsetUpdate((time_t) -1), mReceiveBuffer(256)
+{
+ mSocket.open(boost::asio::ip::udp::v4());
+ mSocket.async_receive_from(boost::asio::buffer(mReceiveBuffer, 256), mReceiveEndpoint,
+ boost::bind(&SNTPClient::receivePacket, this, boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+
+ mTimer.expires_from_now(boost::posix_time::seconds(NTP_QUERY_FREQUENCY));
+ mTimer.async_wait(boost::bind(&SNTPClient::timerEntry, this, boost::asio::placeholders::error));
+}
+
+void SNTPClient::resolveComplete(const boost::system::error_code& error, boost::asio::ip::udp::resolver::iterator it)
+{
+ if (!error)
+ {
+ boost::asio::ip::udp::resolver::iterator sel = it;
+ int i = 1;
+ while (++it != boost::asio::ip::udp::resolver::iterator())
+ if ((rand() % ++i) == 0)
+ sel = it;
+ if (sel != boost::asio::ip::udp::resolver::iterator())
+ {
+ boost::mutex::scoped_lock sl(mLock);
+ SNTPQuery& query = mQueries[*sel];
+ time_t now = time(NULL);
+ if ((query.mLocalTimeSent == now) || ((query.mLocalTimeSent + 1) == now))
+ { // This can happen if the same IP address is reached through multiple names
+ Log(lsTRACE) << "SNTP: Redundant query suppressed";
+ return;
+ }
+ query.mReceivedReply = false;
+ query.mLocalTimeSent = now;
+ RAND_bytes(reinterpret_cast(&query.mQueryNonce), sizeof(query.mQueryNonce));
+ reinterpret_cast(SNTPQueryData)[NTP_OFF_XMITTS_INT] = time(NULL) + NTP_UNIX_OFFSET;
+ reinterpret_cast(SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.mQueryNonce;
+ mSocket.async_send_to(boost::asio::buffer(SNTPQueryData, 48), *sel,
+ boost::bind(&SNTPClient::sendComplete, this,
+ boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
+ }
+ }
+}
+
+void SNTPClient::receivePacket(const boost::system::error_code& error, std::size_t bytes_xferd)
+{
+ if (!error)
+ {
+ boost::mutex::scoped_lock sl(mLock);
+#ifdef SNTP_DEBUG
+ Log(lsTRACE) << "SNTP: Packet from " << mReceiveEndpoint;
+#endif
+ std::map::iterator query = mQueries.find(mReceiveEndpoint);
+ if (query == mQueries.end())
+ Log(lsDEBUG) << "SNTP: Reply from " << mReceiveEndpoint << " found without matching query";
+ else if (query->second.mReceivedReply)
+ Log(lsDEBUG) << "SNTP: Duplicate response from " << mReceiveEndpoint;
+ else
+ {
+ query->second.mReceivedReply = true;
+ if (time(NULL) > (query->second.mLocalTimeSent + 1))
+ Log(lsWARNING) << "SNTP: Late response from " << mReceiveEndpoint;
+ else if (bytes_xferd < 48)
+ Log(lsWARNING) << "SNTP: Short reply from " << mReceiveEndpoint
+ << " (" << bytes_xferd << ") " << mReceiveBuffer.size();
+ else if (reinterpret_cast(&mReceiveBuffer[0])[NTP_OFF_ORGTS_FRAC] != query->second.mQueryNonce)
+ Log(lsWARNING) << "SNTP: Reply from " << mReceiveEndpoint << "had wrong nonce";
+ else
+ processReply();
+ }
+ }
+
+ mSocket.async_receive_from(boost::asio::buffer(mReceiveBuffer, 256), mReceiveEndpoint,
+ boost::bind(&SNTPClient::receivePacket, this, boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+}
+
+void SNTPClient::sendComplete(const boost::system::error_code& error, std::size_t)
+{
+ if (error)
+ Log(lsWARNING) << "SNTP: Send error";
+}
+
+void SNTPClient::processReply()
+{
+ assert(mReceiveBuffer.size() >= 48);
+ uint32 *recvBuffer = reinterpret_cast(&mReceiveBuffer.front());
+
+ unsigned info = ntohl(recvBuffer[NTP_OFF_INFO]);
+ int64_t timev = ntohl(recvBuffer[NTP_OFF_RECVTS_INT]);
+ unsigned stratum = (info >> 16) & 0xff;
+
+ if ((info >> 30) == 3)
+ {
+ Log(lsINFO) << "SNTP: Alarm condition " << mReceiveEndpoint;
+ return;
+ }
+ if ((stratum == 0) || (stratum > 14))
+ {
+ Log(lsINFO) << "SNTP: Unreasonable stratum (" << stratum << ") from " << mReceiveEndpoint;
+ return;
+ }
+
+ time_t now = time(NULL);
+ timev -= now;
+ timev -= NTP_UNIX_OFFSET;
+
+ // add offset to list, replacing oldest one if appropriate
+ mOffsetList.push_back(timev);
+ if (mOffsetList.size() >= NTP_SAMPLE_WINDOW)
+ mOffsetList.pop_front();
+ mLastOffsetUpdate = now;
+
+ // select median time
+ std::list offsetList = mOffsetList;
+ offsetList.sort();
+ int j = offsetList.size();
+ std::list::iterator it = offsetList.begin();
+ for (int i = 0; i < (j / 2); ++i)
+ ++it;
+ mOffset = *it;
+ if ((j % 2) == 0)
+ mOffset = (mOffset + (*--it)) / 2;
+
+ if ((mOffset == -1) || (mOffset == 1)) // small corrections likely do more harm than good
+ mOffset = 0;
+
+#ifndef SNTP_DEBUG
+ if (timev || mOffset)
+#endif
+ Log(lsTRACE) << "SNTP: Offset is " << timev << ", new system offset is " << mOffset;
+}
+
+void SNTPClient::timerEntry(const boost::system::error_code& error)
+{
+ if (!error)
+ {
+ doQuery();
+ mTimer.expires_from_now(boost::posix_time::seconds(NTP_QUERY_FREQUENCY));
+ mTimer.async_wait(boost::bind(&SNTPClient::timerEntry, this, boost::asio::placeholders::error));
+ }
+}
+
+void SNTPClient::addServer(const std::string& server)
+{
+ boost::mutex::scoped_lock sl(mLock);
+ mServers.push_back(std::make_pair(server, (time_t) -1));
+}
+
+void SNTPClient::init(const std::vector& servers)
+{
+ std::vector::const_iterator it = servers.begin();
+ if (it == servers.end())
+ {
+ Log(lsINFO) << "SNTP: no server specified";
+ return;
+ }
+ do
+ addServer(*it++);
+ while (it != servers.end());
+ queryAll();
+}
+
+void SNTPClient::queryAll()
+{
+ while (doQuery())
+ nothing();
+}
+
+bool SNTPClient::getOffset(int& offset)
+{
+ boost::mutex::scoped_lock sl(mLock);
+ if ((mLastOffsetUpdate == (time_t) -1) || ((mLastOffsetUpdate + 90) < time(NULL)))
+ return false;
+ offset = mOffset;
+ return true;
+}
+
+bool SNTPClient::doQuery()
+{
+ boost::mutex::scoped_lock sl(mLock);
+ std::vector< std::pair >::iterator best = mServers.end();
+ for (std::vector< std::pair >::iterator it = mServers.begin(), end = best;
+ it != end; ++it)
+ if ((best == end) || (it->second == (time_t) -1) || (it->second < best->second))
+ best = it;
+ if (best == mServers.end())
+ {
+ Log(lsINFO) << "SNTP: No server to query";
+ return false;
+ }
+ time_t now = time(NULL);
+ if ((best->second != (time_t) -1) && ((best->second + NTP_MIN_QUERY) >= now))
+ {
+ Log(lsTRACE) << "SNTP: All servers recently queried";
+ return false;
+ }
+ best->second = now;
+
+ boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), best->first, "ntp");
+ mResolver.async_resolve(query,
+ boost::bind(&SNTPClient::resolveComplete, this,
+ boost::asio::placeholders::error, boost::asio::placeholders::iterator));
+#ifdef SNTP_DEBUG
+ Log(lsTRACE) << "SNTP: Resolve pending for " << best->first;
+#endif
+ return true;
+}
diff --git a/src/SNTPClient.h b/src/SNTPClient.h
new file mode 100644
index 0000000000..51adf3ceb2
--- /dev/null
+++ b/src/SNTPClient.h
@@ -0,0 +1,58 @@
+#ifndef __SNTPCLIENT__
+#define __SNTPCLIENT__
+
+#include
+#include