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 +#include +#include + +#include +#include + +class SNTPQuery +{ +public: + bool mReceivedReply; + time_t mLocalTimeSent; + int mQueryNonce; + + SNTPQuery(time_t j = (time_t) -1) : mReceivedReply(false), mLocalTimeSent(j) { ; } +}; + +class SNTPClient +{ +protected: + std::map mQueries; + boost::mutex mLock; + + boost::asio::ip::udp::socket mSocket; + boost::asio::deadline_timer mTimer; + boost::asio::ip::udp::resolver mResolver; + + std::vector< std::pair > mServers; + + int mOffset; + time_t mLastOffsetUpdate; + std::list mOffsetList; + + std::vector mReceiveBuffer; + boost::asio::ip::udp::endpoint mReceiveEndpoint; + + void receivePacket(const boost::system::error_code& error, std::size_t bytes); + void resolveComplete(const boost::system::error_code& error, boost::asio::ip::udp::resolver::iterator iterator); + void sentPacket(boost::shared_ptr, const boost::system::error_code&, std::size_t); + void timerEntry(const boost::system::error_code&); + void sendComplete(const boost::system::error_code& error, std::size_t bytesTransferred); + void processReply(); + +public: + SNTPClient(boost::asio::io_service& service); + void init(const std::vector& servers); + void addServer(const std::string& mServer); + + void queryAll(); + bool doQuery(); + bool getOffset(int& offset); +}; + +#endif diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 48a7069da1..fc5e80d0dd 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -83,7 +83,7 @@ public: virtual void add(Serializer& s) const { return; } virtual bool isEquivalent(const SerializedType& t) const - { std::cerr << getSType() << std::endl; assert(getSType() == STI_NOTPRESENT); return t.getSType() == STI_NOTPRESENT; } + { assert(getSType() == STI_NOTPRESENT); return t.getSType() == STI_NOTPRESENT; } bool operator==(const SerializedType& t) const { return (getSType() == t.getSType()) && isEquivalent(t); } @@ -290,6 +290,7 @@ public: bool isNative() const { return mIsNative; } bool isZero() const { return mValue == 0; } + bool isNonZero() const { return mValue != 0; } bool isNegative() const { return mIsNegative && !isZero(); } bool isPositive() const { return !mIsNegative && !isZero(); } bool isGEZero() const { return !mIsNegative; } @@ -342,6 +343,7 @@ public: // Someone is offering X for Y, what is the rate? static uint64 getRate(const STAmount& offerOut, const STAmount& offerIn); + static STAmount setRate(uint64 rate, const uint160& currencyOut); // Someone is offering X for Y, I try to pay Z, how much do I get? // And what's left of the offer? And how much do I actually pay? @@ -706,7 +708,7 @@ public: int getLength() const; SerializedTypeID getSType() const { return STI_TL; } std::string getText() const; - void add(Serializer& s) const { if(s.addTaggedList(value)<0) throw(0); } + void add(Serializer& s) const { if (s.addTaggedList(value) < 0) throw(0); } const std::vector& peekValue() const { return value; } std::vector& peekValue() { return value; } diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 0f22432f14..92a6ddec94 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -1944,7 +1944,7 @@ bool TransactionEngine::calcNodeOfferRev( // Do a directory. // - Drive on computing saCurDlvAct to derive saPrvDlvAct. SLE::pointer sleDirectDir = entryCache(ltDIR_NODE, uDirectTip); - STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip)); // For correct ratio + STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip), uCurCurrencyID); // For correct ratio unsigned int uEntry = 0; uint256 uCurIndex; @@ -2027,7 +2027,7 @@ bool TransactionEngine::calcNodeOfferRev( // Do a directory. // - Drive on computing saCurDlvAct to derive saPrvDlvAct. SLE::pointer sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); -// ??? STAmount saOfrRate = STAmount::setRate(STAmount::getQuality(uNxtTip)); // For correct ratio +// ??? STAmount saOfrRate = STAmount::setRate(STAmount::getQuality(uNxtTip), uCurCurrencyID); // For correct ratio unsigned int uEntry = 0; uint256 uNxtIndex; @@ -2136,7 +2136,7 @@ bool TransactionEngine::calcNodeOfferFwd( // Do a directory. // - Drive on computing saPrvDlvAct to derive saCurDlvAct. SLE::pointer sleDirectDir = entryCache(ltDIR_NODE, uDirectTip); - STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip)); // For correct ratio + STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip), uCurCurrencyID); // For correct ratio unsigned int uEntry = 0; uint256 uCurIndex; diff --git a/src/TransactionMeta.cpp b/src/TransactionMeta.cpp index d1f4840e72..0ab28f391e 100644 --- a/src/TransactionMeta.cpp +++ b/src/TransactionMeta.cpp @@ -152,6 +152,17 @@ TransactionMetaNodeEntry* TransactionMetaNode::findEntry(int nodeType) return NULL; } +TMNEBalance* TransactionMetaNode::findBalance() +{ + for (boost::ptr_vector::iterator it = mEntries.begin(), end = mEntries.end(); + it != end; ++it) + if (it->getType() == TransactionMetaNodeEntry::TMNChangedBalance) + return dynamic_cast(&*it); + TMNEBalance* node = new TMNEBalance(); + mEntries.push_back(node); + return node; +} + void TransactionMetaNode::addNode(TransactionMetaNodeEntry* node) { mEntries.push_back(node); @@ -194,7 +205,7 @@ TransactionMetaSet::TransactionMetaSet(uint32 ledger, const std::vectorsecond; } +#if 0 void TransactionMetaSet::threadNode(const uint256& node, const uint256& prevTx, uint32 prevLgr) { modifyNode(node).thread(prevTx, prevLgr); } -bool TransactionMetaSet::deleteUnfunded(const uint256& nodeID, +void TransactionMetaSet::deleteUnfunded(const uint256& nodeID, const STAmount& firstBalance, const STAmount &secondBalance) { TransactionMetaNode& node = modifyNode(nodeID); @@ -270,5 +282,5 @@ bool TransactionMetaSet::deleteUnfunded(const uint256& nodeID, entry->setBalances(firstBalance, secondBalance); else node.addNode(new TMNEUnfunded(firstBalance, secondBalance)); - return true; } +#endif \ No newline at end of file diff --git a/src/TransactionMeta.h b/src/TransactionMeta.h index 5765034186..d863ccb701 100644 --- a/src/TransactionMeta.h +++ b/src/TransactionMeta.h @@ -36,11 +36,11 @@ public: bool operator>(const TransactionMetaNodeEntry&) const; bool operator>=(const TransactionMetaNodeEntry&) const; - std::auto_ptr clone() const - { return std::auto_ptr(clone()); } + virtual std::auto_ptr clone() const + { return std::auto_ptr(duplicate()); } protected: - virtual TransactionMetaNodeEntry* clone(void) = 0; + virtual TransactionMetaNodeEntry* duplicate(void) const = 0; }; class TMNEBalance : public TransactionMetaNodeEntry @@ -73,7 +73,7 @@ public: virtual Json::Value getJson(int) const; virtual int compare(const TransactionMetaNodeEntry&) const; - virtual TransactionMetaNodeEntry* clone(void) { return new TMNEBalance(*this); } + virtual TransactionMetaNodeEntry* duplicate(void) const { return new TMNEBalance(*this); } }; class TMNEUnfunded : public TransactionMetaNodeEntry @@ -88,7 +88,7 @@ public: virtual void addRaw(Serializer&) const; virtual Json::Value getJson(int) const; virtual int compare(const TransactionMetaNodeEntry&) const; - virtual TransactionMetaNodeEntry* clone(void) { return new TMNEUnfunded(*this); } + virtual TransactionMetaNodeEntry* duplicate(void) const { return new TMNEUnfunded(*this); } }; inline TransactionMetaNodeEntry* new_clone(const TransactionMetaNodeEntry& s) { return s.clone().release(); } @@ -114,6 +114,7 @@ public: const boost::ptr_vector& peekEntries() const { return mEntries; } TransactionMetaNodeEntry* findEntry(int nodeType); + TMNEBalance* findBalance(); void addNode(TransactionMetaNodeEntry*); bool operator<(const TransactionMetaNode& n) const { return mNode < n.mNode; } @@ -126,6 +127,11 @@ public: TransactionMetaNode(const uint256&node, SerializerIterator&); void addRaw(Serializer&); Json::Value getJson(int) const; + + void threadNode(const uint256& previousTransaction, uint32 previousLedger); + void deleteUnfunded(const STAmount& firstBalance, const STAmount& secondBalance); + void adjustBalance(unsigned flags, const STAmount &amount, bool signedBy); + void adjustBalances(unsigned flags, const STAmount &firstAmt, const STAmount &secondAmt); }; @@ -148,16 +154,11 @@ public: void swap(TransactionMetaSet&); bool isNodeAffected(const uint256&) const; + TransactionMetaNode& getAffectedNode(const uint256&); const TransactionMetaNode& peekAffectedNode(const uint256&) const; Json::Value getJson(int) const; void addRaw(Serializer&); - - void threadNode(const uint256& node, const uint256& previousTransaction, uint32 previousLedger); - bool signedBy(const uint256& node, const STAmount& fee); - bool deleteUnfunded(const uint256& node, const STAmount& firstBalance, const STAmount& secondBalance); - bool adjustBalance(const uint256& node, unsigned flags, const STAmount &amount); - bool adjustBalances(const uint256& node, unsigned flags, const STAmount &firstAmt, const STAmount &secondAmt); }; #endif diff --git a/src/ValidationCollection.cpp b/src/ValidationCollection.cpp index fcbe669b9c..466b3d7f2e 100644 --- a/src/ValidationCollection.cpp +++ b/src/ValidationCollection.cpp @@ -7,20 +7,22 @@ bool ValidationCollection::addValidation(SerializedValidation::pointer val) { + NewcoinAddress signer = val->getSignerPublic(); bool isCurrent = false; - if (theApp->getUNL().nodeInUNL(val->getSignerPublic())) + if (theApp->getUNL().nodeInUNL(signer)) { val->setTrusted(); - uint32 now = theApp->getOPs().getNetworkTimeNC(); + uint32 now = theApp->getOPs().getCloseTimeNC(); uint32 valClose = val->getCloseTime(); - if ((now > valClose) && (now < (valClose + LEDGER_MAX_INTERVAL))) + if ((now > (valClose - 4)) && (now < (valClose + LEDGER_MAX_INTERVAL))) isCurrent = true; else Log(lsWARNING) << "Received stale validation now=" << now << ", close=" << valClose; } + else Log(lsINFO) << "Node " << signer.humanNodePublic() << " not in UNL"; uint256 hash = val->getLedgerHash(); - uint160 node = val->getSignerPublic().getNodeID(); + uint160 node = signer.getNodeID(); { boost::mutex::scoped_lock sl(mValidationLock); @@ -28,18 +30,27 @@ bool ValidationCollection::addValidation(SerializedValidation::pointer val) return false; if (isCurrent) { - boost::unordered_map::iterator it = mCurrentValidations.find(node); - if ((it == mCurrentValidations.end()) || (val->getCloseTime() >= it->second->getCloseTime())) + boost::unordered_map::iterator it = mCurrentValidations.find(node); + if ((it == mCurrentValidations.end()) || (!it->second.newest) || + (val->getCloseTime() > it->second.newest->getCloseTime())) { if (it != mCurrentValidations.end()) - mStaleValidations.push_back(it->second); - mCurrentValidations[node] = val; - condWrite(); + { + if (it->second.oldest) + { + mStaleValidations.push_back(it->second.oldest); + condWrite(); + } + it->second.oldest = it->second.newest; + it->second.newest = val; + } + else + mCurrentValidations.insert(std::make_pair(node, ValidationPair(val))); } } } - Log(lsINFO) << "Val for " << hash.GetHex() << " from " << val->getSignerPublic().humanNodePublic() + Log(lsINFO) << "Val for " << hash.GetHex() << " from " << signer.humanNodePublic() << " added " << (val->isTrusted() ? "trusted" : "UNtrusted"); return isCurrent; } @@ -50,7 +61,8 @@ ValidationSet ValidationCollection::getValidations(const uint256& ledger) { boost::mutex::scoped_lock sl(mValidationLock); boost::unordered_map::iterator it = mValidations.find(ledger); - if (it != mValidations.end()) ret = it->second; + if (it != mValidations.end()) + ret = it->second; } return ret; } @@ -60,7 +72,7 @@ void ValidationCollection::getValidationCount(const uint256& ledger, bool curren trusted = untrusted = 0; boost::mutex::scoped_lock sl(mValidationLock); boost::unordered_map::iterator it = mValidations.find(ledger); - uint32 now = theApp->getOPs().getNetworkTimeNC(); + uint32 now = theApp->getOPs().getCloseTimeNC(); if (it != mValidations.end()) { for (ValidationSet::iterator vit = it->second.begin(), end = it->second.end(); vit != end; ++vit) @@ -70,7 +82,7 @@ void ValidationCollection::getValidationCount(const uint256& ledger, bool curren { uint32 closeTime = vit->second->getCloseTime(); if ((now < closeTime) || (now > (closeTime + 2 * LEDGER_MAX_INTERVAL))) - trusted = false; + isTrusted = false; } if (isTrusted) ++trusted; @@ -100,10 +112,10 @@ int ValidationCollection::getCurrentValidationCount(uint32 afterTime) { int count = 0; boost::mutex::scoped_lock sl(mValidationLock); - for (boost::unordered_map::iterator it = mCurrentValidations.begin(), + for (boost::unordered_map::iterator it = mCurrentValidations.begin(), end = mCurrentValidations.end(); it != end; ++it) { - if (it->second->isTrusted() && (it->second->getCloseTime() > afterTime)) + if (it->second.newest->isTrusted() && (it->second.newest->getCloseTime() > afterTime)) ++count; } return count; @@ -111,24 +123,45 @@ int ValidationCollection::getCurrentValidationCount(uint32 afterTime) boost::unordered_map ValidationCollection::getCurrentValidations() { - uint32 now = theApp->getOPs().getNetworkTimeNC(); + uint32 now = theApp->getOPs().getCloseTimeNC(); boost::unordered_map ret; { boost::mutex::scoped_lock sl(mValidationLock); - boost::unordered_map::iterator it = mCurrentValidations.begin(); + boost::unordered_map::iterator it = mCurrentValidations.begin(); bool anyNew = false; while (it != mCurrentValidations.end()) { - if (now > (it->second->getCloseTime() + LEDGER_MAX_INTERVAL)) + ValidationPair& pair = it->second; + + if (pair.oldest && (now > (pair.oldest->getCloseTime() + LEDGER_MAX_INTERVAL))) { - mStaleValidations.push_back(it->second); - it = mCurrentValidations.erase(it); + mStaleValidations.push_back(pair.oldest); + pair.oldest = SerializedValidation::pointer(); anyNew = true; } + if (pair.newest && (now > (pair.newest->getCloseTime() + LEDGER_MAX_INTERVAL))) + { + mStaleValidations.push_back(pair.newest); + pair.newest = SerializedValidation::pointer(); + anyNew = true; + } + if (!pair.newest && !pair.oldest) + it = mCurrentValidations.erase(it); else { - ++ret[it->second->getLedgerHash()]; + if (pair.oldest) + { + Log(lsTRACE) << "OLD " << pair.oldest->getLedgerHash().GetHex() << " " << + boost::lexical_cast(pair.oldest->getCloseTime()); + ++ret[pair.oldest->getLedgerHash()]; + } + if (pair.newest) + { + Log(lsTRACE) << "NEW " << pair.newest->getLedgerHash().GetHex() << " " << + boost::lexical_cast(pair.newest->getCloseTime()); + ++ret[pair.newest->getLedgerHash()]; + } ++it; } } @@ -139,14 +172,35 @@ boost::unordered_map ValidationCollection::getCurrentValidations() return ret; } +bool ValidationCollection::isDeadLedger(const uint256& ledger) +{ + for (std::list::iterator it = mDeadLedgers.begin(), end = mDeadLedgers.end(); it != end; ++it) + if (*it == ledger) + return true; + return false; +} + +void ValidationCollection::addDeadLedger(const uint256& ledger) +{ + if (isDeadLedger(ledger)) + return; + + mDeadLedgers.push_back(ledger); + if (mDeadLedgers.size() >= 128) + mDeadLedgers.pop_front(); +} + void ValidationCollection::flush() { boost::mutex::scoped_lock sl(mValidationLock); - boost::unordered_map::iterator it = mCurrentValidations.begin(); + boost::unordered_map::iterator it = mCurrentValidations.begin(); bool anyNew = false; while (it != mCurrentValidations.end()) { - mStaleValidations.push_back(it->second); + if (it->second.oldest) + mStaleValidations.push_back(it->second.oldest); + if (it->second.newest) + mStaleValidations.push_back(it->second.newest); ++it; anyNew = true; } diff --git a/src/ValidationCollection.h b/src/ValidationCollection.h index 2b2760ff36..2198a967fe 100644 --- a/src/ValidationCollection.h +++ b/src/ValidationCollection.h @@ -12,14 +12,24 @@ typedef boost::unordered_map ValidationSet; +class ValidationPair +{ +public: + SerializedValidation::pointer oldest, newest; + + ValidationPair(SerializedValidation::pointer v) : newest(v) { ; } +}; + class ValidationCollection { protected: boost::mutex mValidationLock; - boost::unordered_map mValidations; - boost::unordered_map mCurrentValidations; - std::vector mStaleValidations; + boost::unordered_map mValidations; + boost::unordered_map mCurrentValidations; + std::vector mStaleValidations; + std::list mDeadLedgers; + bool mWriting; void doWrite(); @@ -31,9 +41,16 @@ public: bool addValidation(SerializedValidation::pointer); ValidationSet getValidations(const uint256& ledger); void getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int& untrusted); + int getTrustedValidationCount(const uint256& ledger); int getCurrentValidationCount(uint32 afterTime); + boost::unordered_map getCurrentValidations(); + + void addDeadLedger(const uint256&); + bool isDeadLedger(const uint256&); + std::list getDeadLedgers() { return mDeadLedgers; } + void flush(); }; diff --git a/src/Version.h b/src/Version.h index 8fd1805a62..51603753c5 100644 --- a/src/Version.h +++ b/src/Version.h @@ -5,7 +5,7 @@ // #define SERVER_VERSION_MAJOR 0 -#define SERVER_VERSION_MINOR 3 +#define SERVER_VERSION_MINOR 4 #define SERVER_VERSION_SUB "-a" #define SERVER_NAME "NewCoin" @@ -16,11 +16,11 @@ // Version we prefer to speak: #define PROTO_VERSION_MAJOR 0 -#define PROTO_VERSION_MINOR 3 +#define PROTO_VERSION_MINOR 4 // Version we wil speak to: #define MIN_PROTO_MAJOR 0 -#define MIN_PROTO_MINOR 3 +#define MIN_PROTO_MINOR 4 #define MAKE_VERSION_INT(maj,min) ((maj << 16) | min) #define GET_VERSION_MAJOR(ver) (ver >> 16)