diff --git a/src/cpp/ripple/LedgerConsensus.cpp b/src/cpp/ripple/LedgerConsensus.cpp index cabccb7e0..971bb332a 100644 --- a/src/cpp/ripple/LedgerConsensus.cpp +++ b/src/cpp/ripple/LedgerConsensus.cpp @@ -27,6 +27,117 @@ typedef std::map::value_type u256_lct_pair; SETUP_LOG(); DECLARE_INSTANCE(LedgerConsensus); +void LCTransaction::setVote(const uint160& peer, bool votesYes) +{ // Track a peer's yes/no vote on a particular disputed transaction + std::pair::iterator, bool> res = + mVotes.insert(std::pair(peer, votesYes)); + + if (res.second) + { // new vote + if (votesYes) + { + cLog(lsDEBUG) << "Peer " << peer << " votes YES on " << mTransactionID; + ++mYays; + } + else + { + cLog(lsDEBUG) << "Peer " << peer << " votes NO on " << mTransactionID; + ++mNays; + } + } + else if (votesYes && !res.first->second) + { // changes vote to yes + cLog(lsDEBUG) << "Peer " << peer << " now votes YES on " << mTransactionID; + --mNays; + ++mYays; + res.first->second = true; + } + else if (!votesYes && res.first->second) + { // changes vote to no + cLog(lsDEBUG) << "Peer " << peer << " now votes NO on " << mTransactionID; + ++mNays; + --mYays; + res.first->second = false; + } +} + +void LCTransaction::unVote(const uint160& peer) +{ // Remove a peer's vote on this disputed transasction + boost::unordered_map::iterator it = mVotes.find(peer); + if (it != mVotes.end()) + { + if (it->second) + --mYays; + else + --mNays; + mVotes.erase(it); + } +} + +bool LCTransaction::updateVote(int percentTime, bool proposing) +{ + if (mOurVote && (mNays == 0)) + return false; + if (!mOurVote && (mYays == 0)) + return false; + + bool newPosition; + int weight; + if (proposing) // give ourselves full weight + { + // This is basically the percentage of nodes voting 'yes' (including us) + weight = (mYays * 100 + (mOurVote ? 100 : 0)) / (mNays + mYays + 1); + + // To prevent avalanche stalls, we increase the needed weight slightly over time + if (percentTime < AV_MID_CONSENSUS_TIME) + newPosition = weight > AV_INIT_CONSENSUS_PCT; + else if (percentTime < AV_LATE_CONSENSUS_TIME) + newPosition = weight > AV_MID_CONSENSUS_PCT; + else if (percentTime < AV_STUCK_CONSENSUS_TIME) + newPosition = weight > AV_LATE_CONSENSUS_PCT; + else + newPosition = weight > AV_STUCK_CONSENSUS_PCT; + } + else // don't let us outweigh a proposing node, just recognize consensus + { + weight = -1; + newPosition = mYays > mNays; + } + + if (newPosition == mOurVote) + { + cLog(lsINFO) << + "No change (" << (mOurVote ? "YES" : "NO") << ") : weight " << weight << ", percent " << percentTime; + cLog(lsDEBUG) << getJson(); + return false; + } + + mOurVote = newPosition; + cLog(lsDEBUG) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID; + cLog(lsDEBUG) << getJson(); + return true; +} + +Json::Value LCTransaction::getJson() +{ + Json::Value ret(Json::objectValue); + + ret["yays"] = mYays; + ret["nays"] = mNays; + ret["our_vote"] = mOurVote; + if (!mVotes.empty()) + { + Json::Value votesj(Json::objectValue); + typedef boost::unordered_map::value_type vt; + BOOST_FOREACH(vt& vote, mVotes) + { + votesj[vote.first.GetHex()] = vote.second; + } + ret["votes"] = votesj; + } + return ret; +} + 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), mConsensusFail(false), diff --git a/src/cpp/ripple/Transaction.h b/src/cpp/ripple/Transaction.h index 0ec8eadba..cc4a4bc77 100644 --- a/src/cpp/ripple/Transaction.h +++ b/src/cpp/ripple/Transaction.h @@ -77,7 +77,7 @@ public: bool checkSign() const; void updateID() { mTransactionID=mTransaction->getTransactionID(); } - SerializedTransaction::pointer getSTransaction() { return mTransaction; } + SerializedTransaction::ref getSTransaction() { return mTransaction; } const uint256& getID() const { return mTransactionID; } const RippleAddress& getFromAccount() const { return mAccountFrom; } diff --git a/src/cpp/ripple/TransactionAcquire.cpp b/src/cpp/ripple/TransactionAcquire.cpp index 3a5155f7a..69630d4d5 100644 --- a/src/cpp/ripple/TransactionAcquire.cpp +++ b/src/cpp/ripple/TransactionAcquire.cpp @@ -9,10 +9,10 @@ #include "Application.h" #include "NetworkOPs.h" -#include "LedgerTiming.h" #include "SerializedValidation.h" #include "Log.h" #include "SHAMapSync.h" +#include "HashPrefixes.h" #define TX_ACQUIRE_TIMEOUT 250 @@ -174,127 +174,46 @@ SMAddNode TransactionAcquire::takeNodes(const std::list& nodeIDs, } } -void LCTransaction::setVote(const uint160& peer, bool votesYes) -{ // Track a peer's yes/no vote on a particular disputed transaction - std::pair::iterator, bool> res = - mVotes.insert(std::pair(peer, votesYes)); - - if (res.second) - { // new vote - if (votesYes) - { - cLog(lsDEBUG) << "Peer " << peer << " votes YES on " << mTransactionID; - ++mYays; - } - else - { - cLog(lsDEBUG) << "Peer " << peer << " votes NO on " << mTransactionID; - ++mNays; - } - } - else if (votesYes && !res.first->second) - { // changes vote to yes - cLog(lsDEBUG) << "Peer " << peer << " now votes YES on " << mTransactionID; - --mNays; - ++mYays; - res.first->second = true; - } - else if (!votesYes && res.first->second) - { // changes vote to no - cLog(lsDEBUG) << "Peer " << peer << " now votes NO on " << mTransactionID; - ++mNays; - --mYays; - res.first->second = false; - } -} - -void LCTransaction::unVote(const uint160& peer) -{ // Remove a peer's vote on this disputed transasction - boost::unordered_map::iterator it = mVotes.find(peer); - if (it != mVotes.end()) - { - if (it->second) - --mYays; - else - --mNays; - mVotes.erase(it); - } -} - -bool LCTransaction::updateVote(int percentTime, bool proposing) -{ - if (mOurVote && (mNays == 0)) - return false; - if (!mOurVote && (mYays == 0)) - return false; - - bool newPosition; - int weight; - if (proposing) // give ourselves full weight - { - // This is basically the percentage of nodes voting 'yes' (including us) - weight = (mYays * 100 + (mOurVote ? 100 : 0)) / (mNays + mYays + 1); - - // To prevent avalanche stalls, we increase the needed weight slightly over time - if (percentTime < AV_MID_CONSENSUS_TIME) - newPosition = weight > AV_INIT_CONSENSUS_PCT; - else if (percentTime < AV_LATE_CONSENSUS_TIME) - newPosition = weight > AV_MID_CONSENSUS_PCT; - else if (percentTime < AV_STUCK_CONSENSUS_TIME) - newPosition = weight > AV_LATE_CONSENSUS_PCT; - else - newPosition = weight > AV_STUCK_CONSENSUS_PCT; - } - else // don't let us outweigh a proposing node, just recognize consensus - { - weight = -1; - newPosition = mYays > mNays; - } - - if (newPosition == mOurVote) - { - cLog(lsINFO) << - "No change (" << (mOurVote ? "YES" : "NO") << ") : weight " << weight << ", percent " << percentTime; - cLog(lsDEBUG) << getJson(); - return false; - } - - mOurVote = newPosition; - cLog(lsDEBUG) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID; - cLog(lsDEBUG) << getJson(); - return true; -} - -Json::Value LCTransaction::getJson() -{ - Json::Value ret(Json::objectValue); - - ret["yays"] = mYays; - ret["nays"] = mNays; - ret["our_vote"] = mOurVote; - if (!mVotes.empty()) - { - Json::Value votesj(Json::objectValue); - typedef boost::unordered_map::value_type vt; - BOOST_FOREACH(vt& vote, mVotes) - { - votesj[vote.first.GetHex()] = vote.second; - } - ret["votes"] = votesj; - } - return ret; -} - void ConsensusTransSetSF::gotNode(const SHAMapNode& id, const uint256& nodeHash, - const std::vector& nodeData, SHAMapTreeNode::TNType) + const std::vector& nodeData, SHAMapTreeNode::TNType type) { - // WRITEME: If 'isLeaf' is true, this is a transaction theApp->getTempNodeCache().store(nodeHash, nodeData); + if ((type == SHAMapTreeNode::tnTRANSACTION_NM) && (nodeData.size() > 16)) + { // this is a transaction, and we didn't have it + cLog(lsDEBUG) << "Node on our acquiring TX set is TXN we don't have"; + try + { + Serializer s(nodeData.begin() + 4, nodeData.end()); // skip prefix + SerializerIterator sit(s); + SerializedTransaction::pointer stx = boost::make_shared(boost::ref(sit)); + assert(stx->getTransactionID() == nodeHash); + theApp->getJobQueue().addJob(jtTRANSACTION, "TXS->TXN", + BIND_TYPE(&NetworkOPs::submitTransaction, &theApp->getOPs(), P_1, stx, NetworkOPs::stCallback())); + } + catch (...) + { + cLog(lsWARNING) << "Fetched invalid transaction in proposed set"; + } + } } bool ConsensusTransSetSF::haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector& nodeData) { - // WRITEME: We could check our own map, we could check transaction tables - return theApp->getTempNodeCache().retrieve(nodeHash, nodeData); + if (theApp->getTempNodeCache().retrieve(nodeHash, nodeData)) + return true; + + Transaction::pointer txn = Transaction::load(nodeHash); + if (txn) + { // this is a transaction, and we have it + cLog(lsDEBUG) << "Node in our acquiring TX set is TXN we have"; + Serializer s; + s.add32(sHP_TransactionID); + txn->getSTransaction()->add(s, true); + assert(s.getSHA512Half() == nodeHash); + nodeData = s.peekData(); + return true; + } + + return false; }