diff --git a/src/cpp/ripple/JobQueue.h b/src/cpp/ripple/JobQueue.h index 26a72dca2..bdd23bc69 100644 --- a/src/cpp/ripple/JobQueue.h +++ b/src/cpp/ripple/JobQueue.h @@ -17,13 +17,15 @@ enum JobType { // must be in priority order, low to high jtINVALID, - jtVALIDATION_ut, - jtTRANSACTION, - jtPROPOSAL_ut, - jtVALIDATION_t, - jtTRANSACTION_l, - jtPROPOSAL_t, - jtADMIN, + jtVALIDATION_ut, // A validation from an untrusted source + jtCLIENTOP_ut, // A client operation from a non-local/untrusted source + jtTRANSACTION, // A transaction received from the network + jtPROPOSAL_ut, // A proposal from an untrusted source + jtCLIENTOP_t, // A client operation from a trusted source + jtVALIDATION_t, // A validation from a trusted source + jtTRANSACTION_l, // A local transaction + jtPROPOSAL_t, // A proposal from a trusted source + jtADMIN, // An administrative operation jtDEATH, // job of death, used internally }; diff --git a/src/cpp/ripple/LedgerConsensus.cpp b/src/cpp/ripple/LedgerConsensus.cpp index e5a877b3b..8daade3bd 100644 --- a/src/cpp/ripple/LedgerConsensus.cpp +++ b/src/cpp/ripple/LedgerConsensus.cpp @@ -267,7 +267,7 @@ bool LCTransaction::updateVote(int percentTime, bool proposing) LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previousLedger, uint32 closeTime) : mState(lcsPRE_CLOSE), mCloseTime(closeTime), mPrevLedgerHash(prevLCLHash), mPreviousLedger(previousLedger), - mValPublic(theConfig.VALIDATION_PUB), mValPrivate(theConfig.VALIDATION_PRIV), + mValPublic(theConfig.VALIDATION_PUB), mValPrivate(theConfig.VALIDATION_PRIV), mConsensusFail(false), mCurrentMSeconds(0), mClosePercent(0), mHaveCloseTimeConsensus(false), mConsensusStartTime(boost::posix_time::microsec_clock::universal_time()) { @@ -305,6 +305,36 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou } } +void LedgerConsensus::checkOurValidation() +{ // This only covers some cases - Fix for the case where we can't ever acquire the consensus ledger + if (!mHaveCorrectLCL || !mValPublic.isValid() || !mValPrivate.isValid()) + return; + + SerializedValidation::pointer lastVal = theApp->getOPs().getLastValidation(); + if (lastVal) + { + if (lastVal->getFieldU32(sfLedgerSequence) == mPreviousLedger->getLedgerSeq()) + return; + if (lastVal->getLedgerHash() == mPrevLedgerHash) + return; + } + + uint256 signingHash; + SerializedValidation::pointer v = boost::make_shared + (mPreviousLedger->getHash(), theApp->getOPs().getValidationTimeNC(), mValPublic, false); + v->setTrusted(); + v->sign(signingHash, mValPrivate); + theApp->isNew(signingHash); + theApp->getValidations().addValidation(v); + std::vector validation = v->getSigned(); + ripple::TMValidation val; + val.set_validation(&validation[0], validation.size()); + theApp->getConnectionPool().relayMessage(NULL, + boost::make_shared(val, ripple::mtVALIDATION)); + theApp->getOPs().setLastValidation(v); + cLog(lsWARNING) << "Sending partial validation"; +} + void LedgerConsensus::checkLCL() { uint256 netLgr = mPrevLedgerHash; @@ -575,12 +605,13 @@ void LedgerConsensus::statePreClose() void LedgerConsensus::closeLedger() { - mState = lcsESTABLISH; - mConsensusStartTime = boost::posix_time::microsec_clock::universal_time(); - mCloseTime = theApp->getOPs().getCloseTimeNC(); - theApp->getOPs().setLastCloseTime(mCloseTime); - statusChange(ripple::neCLOSING_LEDGER, *mPreviousLedger); - takeInitialPosition(*theApp->getMasterLedger().closeLedger(true)); + checkOurValidation(); + mState = lcsESTABLISH; + mConsensusStartTime = boost::posix_time::microsec_clock::universal_time(); + mCloseTime = theApp->getOPs().getCloseTimeNC(); + theApp->getOPs().setLastCloseTime(mCloseTime); + statusChange(ripple::neCLOSING_LEDGER, *mPreviousLedger); + takeInitialPosition(*theApp->getMasterLedger().closeLedger(true)); } void LedgerConsensus::stateEstablish() @@ -769,7 +800,7 @@ bool LedgerConsensus::haveConsensus(bool forReal) cLog(lsDEBUG) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree; return ContinuousLedgerTiming::haveConsensus(mPreviousProposers, agree + disagree, agree, currentValidations, - mPreviousMSeconds, mCurrentMSeconds, forReal); + mPreviousMSeconds, mCurrentMSeconds, forReal, mConsensusFail); } SHAMap::pointer LedgerConsensus::getTransactionTree(const uint256& hash, bool doAcquire) @@ -1193,15 +1224,17 @@ void LedgerConsensus::accept(SHAMap::ref set) } statusChange(ripple::neACCEPTED_LEDGER, *newLCL); - if (mValidating) + if (mValidating && !mConsensusFail) { uint256 signingHash; SerializedValidation::pointer v = boost::make_shared - (newLCLHash, theApp->getOPs().getValidationTimeNC(), mValPublic, mValPrivate, - mProposing, boost::ref(signingHash)); + (newLCLHash, theApp->getOPs().getValidationTimeNC(), mValPublic, mProposing); + v->setFieldU32(sfLedgerSequence, newLCL->getLedgerSeq()); + v->sign(signingHash, mValPrivate); v->setTrusted(); theApp->isNew(signingHash); // suppress it if we receive it theApp->getValidations().addValidation(v); + theApp->getOPs().setLastValidation(v); std::vector validation = v->getSigned(); ripple::TMValidation val; val.set_validation(&validation[0], validation.size()); @@ -1238,7 +1271,7 @@ void LedgerConsensus::accept(SHAMap::ref set) cLog(lsINFO) << "Applying transactions from current ledger"; applyTransactions(theApp->getMasterLedger().getCurrentLedger()->peekTransactionMap(), newOL, newLCL, failedTransactions, true); - theApp->getMasterLedger().pushLedger(newLCL, newOL, true); + theApp->getMasterLedger().pushLedger(newLCL, newOL, !mConsensusFail); mNewLedgerHash = newLCL->getHash(); mState = lcsACCEPTED; sl.unlock(); diff --git a/src/cpp/ripple/LedgerConsensus.h b/src/cpp/ripple/LedgerConsensus.h index 064d5800c..3b66fa9f4 100644 --- a/src/cpp/ripple/LedgerConsensus.h +++ b/src/cpp/ripple/LedgerConsensus.h @@ -91,7 +91,7 @@ protected: LedgerAcquire::pointer mAcquiringLedger; LedgerProposal::pointer mOurPosition; RippleAddress mValPublic, mValPrivate; - bool mProposing, mValidating, mHaveCorrectLCL; + bool mProposing, mValidating, mHaveCorrectLCL, mConsensusFail; int mCurrentMSeconds, mClosePercent, mCloseResolution; bool mHaveCloseTimeConsensus; @@ -148,6 +148,7 @@ protected: void playbackProposals(); int getThreshold(); void closeLedger(); + void checkOurValidation(); void beginAccept(bool synchronous); void endConsensus(); diff --git a/src/cpp/ripple/LedgerTiming.cpp b/src/cpp/ripple/LedgerTiming.cpp index 6119123a4..dd92c953c 100644 --- a/src/cpp/ripple/LedgerTiming.cpp +++ b/src/cpp/ripple/LedgerTiming.cpp @@ -63,7 +63,8 @@ bool ContinuousLedgerTiming::haveConsensus( int currentFinished, // proposers who have validated a ledger after this one int previousAgreeTime, // how long it took to agree on the last ledger int currentAgreeTime, // how long we've been trying to agree - bool forReal) // deciding whether to stop consensus process + bool forReal, // deciding whether to stop consensus process + bool& failed) // we can't reach a consensus { cLog(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d validated=%d time=%d/%d%s") % currentProposers % previousProposers % currentAgree % currentFinished % currentAgreeTime % previousAgreeTime % @@ -85,13 +86,15 @@ bool ContinuousLedgerTiming::haveConsensus( if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80) { tLog(forReal, lsINFO) << "normal consensus"; + failed = false; return true; } - // If 50% of the nodes on your UNL have moved on, you should declare consensus - if (((currentFinished * 100) / (currentProposers + 1)) > 50) + // If 80% of the nodes on your UNL have moved on, you should declare consensus + if (((currentFinished * 100) / (currentProposers + 1)) > 80) { - tLog(forReal, lsWARNING) << "We see no consensus, but 50% of nodes have moved on"; + tLog(forReal, lsWARNING) << "We see no consensus, but 80% of nodes have moved on"; + failed = true; return true; } diff --git a/src/cpp/ripple/LedgerTiming.h b/src/cpp/ripple/LedgerTiming.h index 6cf535c40..821eb6073 100644 --- a/src/cpp/ripple/LedgerTiming.h +++ b/src/cpp/ripple/LedgerTiming.h @@ -66,7 +66,7 @@ public: int previousProposers, int currentProposers, int currentAgree, int currentClosed, int previousAgreeTime, int currentAgreeTime, - bool forReal); + bool forReal, bool& failed); static int getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq); }; diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index bf5be14e2..56f6f428c 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -85,6 +85,8 @@ protected: uint256 mLastCloseHash; uint32 mLastCloseTime; uint32 mLastValidationTime; + SerializedValidation::pointer mLastValidation; + // XXX Split into more locks. boost::interprocess::interprocess_upgradable_mutex mMonitorLock; @@ -131,8 +133,10 @@ public: Ledger::pointer getLedgerByHash(const uint256& hash) { return mLedgerMaster->getLedgerByHash(hash); } Ledger::pointer getLedgerBySeq(const uint32 seq) { return mLedgerMaster->getLedgerBySeq(seq); } - uint256 getClosedLedgerHash() - { return mLedgerMaster->getClosedLedger()->getHash(); } + uint256 getClosedLedgerHash() { return mLedgerMaster->getClosedLedger()->getHash(); } + + SerializedValidation::ref getLastValidation() { return mLastValidation; } + void setLastValidation(SerializedValidation::ref v) { mLastValidation = v; } SLE::pointer getSLE(Ledger::pointer lpLedger, const uint256& uHash) { return lpLedger->getSLE(uHash); } diff --git a/src/cpp/ripple/RippleAddress.cpp b/src/cpp/ripple/RippleAddress.cpp index a194a16bc..ecb152857 100644 --- a/src/cpp/ripple/RippleAddress.cpp +++ b/src/cpp/ripple/RippleAddress.cpp @@ -115,7 +115,7 @@ uint160 RippleAddress::getNodeID() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - getNodeID"); case VER_NODE_PUBLIC: // Note, we are encoding the left. @@ -129,7 +129,7 @@ const std::vector& RippleAddress::getNodePublic() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - getNodePublic"); case VER_NODE_PUBLIC: return vchData; @@ -143,7 +143,7 @@ std::string RippleAddress::humanNodePublic() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - humanNodePublic"); case VER_NODE_PUBLIC: return ToString(); @@ -209,7 +209,7 @@ const std::vector& RippleAddress::getNodePrivateData() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - getNodePrivateData"); case VER_NODE_PRIVATE: return vchData; @@ -223,7 +223,7 @@ uint256 RippleAddress::getNodePrivate() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source = getNodePrivate"); case VER_NODE_PRIVATE: return uint256(vchData); @@ -237,7 +237,7 @@ std::string RippleAddress::humanNodePrivate() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - humanNodePrivate"); case VER_NODE_PRIVATE: return ToString(); @@ -279,7 +279,7 @@ uint160 RippleAddress::getAccountID() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - getAccountID"); case VER_ACCOUNT_ID: return uint160(vchData); @@ -297,7 +297,7 @@ std::string RippleAddress::humanAccountID() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - humanAccountID"); case VER_ACCOUNT_ID: return ToString(); @@ -353,7 +353,7 @@ const std::vector& RippleAddress::getAccountPublic() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - getAccountPublic"); case VER_ACCOUNT_ID: throw std::runtime_error("public not available from account id"); @@ -371,7 +371,7 @@ std::string RippleAddress::humanAccountPublic() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - humanAccountPublic"); case VER_ACCOUNT_ID: throw std::runtime_error("public not available from account id"); @@ -446,7 +446,7 @@ uint256 RippleAddress::getAccountPrivate() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - getAccountPrivate"); case VER_ACCOUNT_PRIVATE: return uint256(vchData); @@ -460,7 +460,7 @@ std::string RippleAddress::humanAccountPrivate() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - humanAccountPrivate"); case VER_ACCOUNT_PRIVATE: return ToString(); @@ -606,7 +606,7 @@ BIGNUM* RippleAddress::getGeneratorBN() const { // returns the public generator switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - getGeneratorBN"); case VER_FAMILY_GENERATOR: // Do nothing. @@ -625,7 +625,7 @@ const std::vector& RippleAddress::getGenerator() const { // returns the public generator switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - getGenerator"); case VER_FAMILY_GENERATOR: // Do nothing. @@ -640,7 +640,7 @@ std::string RippleAddress::humanGenerator() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - humanGenerator"); case VER_FAMILY_GENERATOR: return ToString(); @@ -678,7 +678,7 @@ uint128 RippleAddress::getSeed() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - getSeed"); case VER_FAMILY_SEED: return uint128(vchData); @@ -692,7 +692,7 @@ std::string RippleAddress::humanSeed1751() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - humanSeed1751"); case VER_FAMILY_SEED: { @@ -719,7 +719,7 @@ std::string RippleAddress::humanSeed() const { switch (nVersion) { case VER_NONE: - throw std::runtime_error("unset source"); + throw std::runtime_error("unset source - humanSeed"); case VER_FAMILY_SEED: return ToString(); diff --git a/src/cpp/ripple/SerializedValidation.cpp b/src/cpp/ripple/SerializedValidation.cpp index 75d8249ab..c344e1002 100644 --- a/src/cpp/ripple/SerializedValidation.cpp +++ b/src/cpp/ripple/SerializedValidation.cpp @@ -39,26 +39,32 @@ SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSi } SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint32 signTime, - const RippleAddress& naPub, const RippleAddress& naPriv, bool isFull, uint256& signingHash) + const RippleAddress& raPub, bool isFull) : STObject(sValidationFormat, sfValidation), mTrusted(false) -{ +{ // Does not sign setFieldH256(sfLedgerHash, ledgerHash); setFieldU32(sfSigningTime, signTime); - setFieldVL(sfSigningPubKey, naPub.getNodePublic()); - mNodeID = naPub.getNodeID(); + setFieldVL(sfSigningPubKey, raPub.getNodePublic()); + mNodeID = raPub.getNodeID(); assert(mNodeID.isNonZero()); if (!isFull) setFlag(sFullFlag); +} +void SerializedValidation::sign(const RippleAddress& raPriv) +{ + uint256 signingHash; + sign(signingHash, raPriv); +} + +void SerializedValidation::sign(uint256& signingHash, const RippleAddress& raPriv) +{ signingHash = getSigningHash(); std::vector signature; - naPriv.signNodePrivate(signingHash, signature); + raPriv.signNodePrivate(signingHash, signature); setFieldVL(sfSignature, signature); - // XXX Check if this can fail. - // if (!RippleAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), mSignature.peekValue())) - // throw std::runtime_error("Unable to sign validation"); } uint256 SerializedValidation::getSigningHash() const @@ -90,8 +96,8 @@ bool SerializedValidation::isValid(const uint256& signingHash) const { try { - RippleAddress naPublicKey = RippleAddress::createNodePublic(getFieldVL(sfSigningPubKey)); - return naPublicKey.isValid() && naPublicKey.verifyNodePublic(signingHash, getFieldVL(sfSignature)); + RippleAddress raPublicKey = RippleAddress::createNodePublic(getFieldVL(sfSigningPubKey)); + return raPublicKey.isValid() && raPublicKey.verifyNodePublic(signingHash, getFieldVL(sfSignature)); } catch (...) { diff --git a/src/cpp/ripple/SerializedValidation.h b/src/cpp/ripple/SerializedValidation.h index 57b3b42a8..72e1fc50e 100644 --- a/src/cpp/ripple/SerializedValidation.h +++ b/src/cpp/ripple/SerializedValidation.h @@ -24,13 +24,14 @@ public: // These throw if the object is not valid SerializedValidation(SerializerIterator& sit, bool checkSignature = true); - SerializedValidation(const uint256& ledgerHash, uint32 signTime, const RippleAddress& naPub, - const RippleAddress& naPriv, bool isFull, uint256& signingHash); + + // Does not sign the validation + SerializedValidation(const uint256& ledgerHash, uint32 signTime, const RippleAddress& raPub, bool isFull); uint256 getLedgerHash() const; uint32 getSignTime() const; uint32 getFlags() const; - RippleAddress getSignerPublic() const; + RippleAddress getSignerPublic() const; uint160 getNodeID() const { return mNodeID; } bool isValid() const; bool isFull() const; @@ -41,6 +42,8 @@ public: void setTrusted() { mTrusted = true; } std::vector getSigned() const; std::vector getSignature() const; + void sign(uint256& signingHash, const RippleAddress& raPrivate); + void sign(const RippleAddress& raPrivate); // The validation this replaced const uint256& getPreviousHash() { return mPreviousHash; } diff --git a/src/cpp/ripple/ValidationCollection.cpp b/src/cpp/ripple/ValidationCollection.cpp index f4c7b941b..acb6c2c4b 100644 --- a/src/cpp/ripple/ValidationCollection.cpp +++ b/src/cpp/ripple/ValidationCollection.cpp @@ -97,12 +97,12 @@ void ValidationCollection::getValidationCount(const uint256& ledger, bool curren uint32 now = theApp->getOPs().getNetworkTimeNC(); if (set) { - for (ValidationSet::iterator vit = set->begin(), end = set->end(); vit != end; ++vit) + BOOST_FOREACH(u160_val_pair& it, *set) { - bool isTrusted = vit->second->isTrusted(); + bool isTrusted = it.second->isTrusted(); if (isTrusted && currentOnly) { - uint32 closeTime = vit->second->getSignTime(); + uint32 closeTime = it.second->getSignTime(); if ((now < (closeTime - LEDGER_EARLY_INTERVAL)) || (now > (closeTime + LEDGER_VAL_INTERVAL))) isTrusted = false; else @@ -119,6 +119,28 @@ void ValidationCollection::getValidationCount(const uint256& ledger, bool curren cLog(lsTRACE) << "VC: " << ledger << "t:" << trusted << " u:" << untrusted; } +void ValidationCollection::getValidationTypes(const uint256& ledger, int& full, int& partial) +{ + full = partial = 0; + boost::mutex::scoped_lock sl(mValidationLock); + VSpointer set = findSet(ledger); + if (set) + { + BOOST_FOREACH(u160_val_pair& it, *set) + { + if (it.second->isTrusted()) + { + if (it.second->isFull()) + ++full; + else + ++partial; + } + } + } + cLog(lsTRACE) << "VC: " << ledger << "f:" << full << " p:" << partial; +} + + int ValidationCollection::getTrustedValidationCount(const uint256& ledger) { int trusted = 0; @@ -126,9 +148,9 @@ int ValidationCollection::getTrustedValidationCount(const uint256& ledger) VSpointer set = findSet(ledger); if (set) { - for (ValidationSet::iterator vit = set->begin(), end = set->end(); vit != end; ++vit) + BOOST_FOREACH(u160_val_pair& it, *set) { - if (vit->second->isTrusted()) + if (it.second->isTrusted()) ++trusted; } } @@ -167,6 +189,36 @@ int ValidationCollection::getLoadRatio(bool overLoaded) return (goodNodes * 100) / (goodNodes + badNodes); } +std::list ValidationCollection::getCurrentTrustedValidations() +{ + uint32 cutoff = theApp->getOPs().getNetworkTimeNC() - LEDGER_VAL_INTERVAL; + + std::list ret; + + boost::mutex::scoped_lock sl(mValidationLock); + boost::unordered_map::iterator it = mCurrentValidations.begin(); + while (it != mCurrentValidations.end()) + { + if (!it->second) // contains no record + it = mCurrentValidations.erase(it); + else if (it->second->getSignTime() < cutoff) + { // contains a stale record + mStaleValidations.push_back(it->second); + it->second.reset(); + condWrite(); + it = mCurrentValidations.erase(it); + } + else + { // contains a live record + if (it->second->isTrusted()) + ret.push_back(it->second); + ++it; + } + } + + return ret; +} + boost::unordered_map ValidationCollection::getCurrentValidations(uint256 currentLedger) { diff --git a/src/cpp/ripple/ValidationCollection.h b/src/cpp/ripple/ValidationCollection.h index e4a22ec4f..d8ecd791e 100644 --- a/src/cpp/ripple/ValidationCollection.h +++ b/src/cpp/ripple/ValidationCollection.h @@ -38,6 +38,7 @@ public: bool addValidation(const SerializedValidation::pointer&); ValidationSet getValidations(const uint256& ledger); void getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int& untrusted); + void getValidationTypes(const uint256& ledger, int& full, int& partial); int getTrustedValidationCount(const uint256& ledger); @@ -45,6 +46,7 @@ public: int getLoadRatio(bool overLoaded); boost::unordered_map getCurrentValidations(uint256 currentLedger); + std::list getCurrentTrustedValidations(); void flush(); void sweep() { mValidations.sweep(); }