Split TransactionAcquire and DisputedTx

This commit is contained in:
Vinnie Falco
2013-06-12 13:43:47 -07:00
parent 35ec01d558
commit c31e2396f2
15 changed files with 308 additions and 208 deletions

View File

@@ -1,131 +1,12 @@
#define LEDGER_TOTAL_PASSES 8
#define LEDGER_RETRY_PASSES 5
#define TRUST_NETWORK
#define LC_DEBUG
typedef std::map<uint160, LedgerProposal::pointer>::value_type u160_prop_pair;
typedef std::map<uint256, LCTransaction::pointer>::value_type u256_lct_pair;
SETUP_LOG (LedgerConsensus)
DECLARE_INSTANCE(LedgerConsensus);
// VFALCO TODO move LCTransaction to its own file and rename to ConsensusTransactor
//
void LCTransaction::setVote(const uint160& peer, bool votesYes)
{ // Track a peer's yes/no vote on a particular disputed transaction
std::pair<boost::unordered_map<const uint160, bool>::iterator, bool> res =
mVotes.insert(std::pair<const uint160, bool>(peer, votesYes));
if (res.second)
{ // new vote
if (votesYes)
{
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " votes YES on " << mTransactionID;
++mYays;
}
else
{
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " votes NO on " << mTransactionID;
++mNays;
}
}
else if (votesYes && !res.first->second)
{ // changes vote to yes
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " now votes YES on " << mTransactionID;
--mNays;
++mYays;
res.first->second = true;
}
else if (!votesYes && res.first->second)
{ // changes vote to no
WriteLog (lsDEBUG, LedgerConsensus) << "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<uint160, bool>::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)
{
WriteLog (lsINFO, LedgerConsensus) <<
"No change (" << (mOurVote ? "YES" : "NO") << ") : weight " << weight << ", percent " << percentTime;
WriteLog (lsDEBUG, LedgerConsensus) << getJson();
return false;
}
mOurVote = newPosition;
WriteLog (lsDEBUG, LedgerConsensus) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID;
WriteLog (lsDEBUG, LedgerConsensus) << 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<uint160, bool>::value_type vt;
BOOST_FOREACH(vt& vote, mVotes)
{
votesj[vote.first.GetHex()] = vote.second;
}
ret["votes"] = votesj;
}
return ret;
}
LedgerConsensus::LedgerConsensus(uint256 const& 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),
@@ -848,7 +729,7 @@ void LedgerConsensus::addDisputedTransaction(uint256 const& txID, Blob const& tx
assert(false); // We don't have our own position?
}
LCTransaction::pointer txn = boost::make_shared<LCTransaction>(txID, tx, ourVote);
DisputedTx::pointer txn = boost::make_shared<DisputedTx>(txID, tx, ourVote);
mDisputes[txID] = txn;
BOOST_FOREACH(u160_prop_pair& pit, mPeerPositions)
@@ -1384,7 +1265,7 @@ Json::Value LedgerConsensus::getJson(bool full)
if (!mDisputes.empty())
{
typedef boost::unordered_map<uint256, LCTransaction::pointer>::value_type d_t;
typedef boost::unordered_map<uint256, DisputedTx::pointer>::value_type d_t;
Json::Value dsj(Json::objectValue);
BOOST_FOREACH(d_t& dt, mDisputes)
{

View File

@@ -1,71 +1,11 @@
#ifndef __LEDGER_CONSENSUS__
#define __LEDGER_CONSENSUS__
#ifndef RIPPLE_LEDGERCONSENSUS_H
#define RIPPLE_LEDGERCONSENSUS_H
#include "Transaction.h"
#include "LedgerProposal.h"
#include "TransactionEngine.h"
DEFINE_INSTANCE(LedgerConsensus);
DEFINE_INSTANCE(TransactionAcquire);
// VFALCO TODO rename to PeerTxRequest
// A transaction set we are trying to acquire
class TransactionAcquire
: private IS_INSTANCE (TransactionAcquire)
, public PeerSet
, public boost::enable_shared_from_this <TransactionAcquire>
{
public:
typedef boost::shared_ptr<TransactionAcquire> pointer;
public:
explicit TransactionAcquire(uint256 const& hash);
virtual ~TransactionAcquire() { ; }
SHAMap::ref getMap() { return mMap; }
SHAMapAddNode takeNodes(const std::list<SHAMapNode>& IDs,
const std::list< Blob >& data, Peer::ref);
private:
SHAMap::pointer mMap;
bool mHaveRoot;
void onTimer(bool progress);
void newPeer(Peer::ref peer) { trigger(peer); }
void done();
void trigger(Peer::ref);
boost::weak_ptr<PeerSet> pmDowncast();
};
// A transaction that may be disputed
class LCTransaction
{
public:
typedef boost::shared_ptr<LCTransaction> pointer;
LCTransaction(uint256 const& txID, Blob const& tx, bool ourVote) :
mTransactionID(txID), mYays(0), mNays(0), mOurVote(ourVote), transaction(tx) { ; }
uint256 const& getTransactionID() const { return mTransactionID; }
bool getOurVote() const { return mOurVote; }
Serializer& peekTransaction() { return transaction; }
void setOurVote(bool o) { mOurVote = o; }
void setVote(const uint160& peer, bool votesYes);
void unVote(const uint160& peer);
bool updateVote(int percentTime, bool proposing);
Json::Value getJson();
private:
uint256 mTransactionID;
int mYays, mNays;
bool mOurVote;
Serializer transaction;
boost::unordered_map<uint160, bool> mVotes;
};
enum LCState
{
@@ -179,7 +119,7 @@ private:
boost::unordered_map<uint256, std::vector< boost::weak_ptr<Peer> > > mPeerData;
// Disputed transactions
boost::unordered_map<uint256, LCTransaction::pointer> mDisputes;
boost::unordered_map<uint256, DisputedTx::pointer> mDisputes;
// Close time estimates
std::map<uint32, int> mCloseTimes;

View File

@@ -1,7 +1,7 @@
#ifndef ACCEPTED_LEDGER_H
#define ACCEPTED_LEDGER_H
#ifndef RIPPLE_ACCEPTEDLEDGER_H
#define RIPPLE_ACCEPTEDLEDGER_H
/**
/** A ledger that has become irrevocable.
An accepted ledger is a ledger that has a sufficient number of
validations to convince the local server that it is irrevocable.
@@ -9,6 +9,14 @@
The existence of an accepted ledger implies all preceding ledgers
are accepted.
*/
/* VFALCO TODO digest this terminology clarification:
Closed and accepted refer to ledgers that have not passed the
validation threshold yet. Once they pass the threshold, they are
"Validated". Closed just means its close time has passed and no
new transactions can get in. "Accepted" means we believe it to be
the result of the a consensus process (though haven't validated
it yet).
*/
class AcceptedLedger
{
public:

View File

@@ -0,0 +1,128 @@
#define TRUST_NETWORK
// Track a peer's yes/no vote on a particular disputed transaction
void DisputedTx::setVote(const uint160& peer, bool votesYes)
{
// VFALCO TODO Simplify this declaration. It doesn't exactly roll off the tongue!
std::pair <boost::unordered_map <const uint160, bool>::iterator, bool> res =
mVotes.insert(std::pair<const uint160, bool>(peer, votesYes));
// new vote
if (res.second)
{
if (votesYes)
{
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " votes YES on " << mTransactionID;
++mYays;
}
else
{
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " votes NO on " << mTransactionID;
++mNays;
}
}
// changes vote to yes
else if (votesYes && !res.first->second)
{
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " now votes YES on " << mTransactionID;
--mNays;
++mYays;
res.first->second = true;
}
// changes vote to no
else if (!votesYes && res.first->second)
{
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " now votes NO on " << mTransactionID;
++mNays;
--mYays;
res.first->second = false;
}
}
// Remove a peer's vote on this disputed transasction
void DisputedTx::unVote(const uint160& peer)
{
boost::unordered_map<uint160, bool>::iterator it = mVotes.find(peer);
if (it != mVotes.end())
{
if (it->second)
--mYays;
else
--mNays;
mVotes.erase(it);
}
}
bool DisputedTx::updateVote(int percentTime, bool proposing)
{
// VFALCO TODO Give the return value a descriptive local variable name
// and don't return from the middle.
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);
// VFALCO TODO Rename these macros and turn them into language constructs.
// consolidate them into a class that collects all these related values.
//
// 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)
{
WriteLog (lsINFO, LedgerConsensus) <<
"No change (" << (mOurVote ? "YES" : "NO") << ") : weight " << weight << ", percent " << percentTime;
WriteLog (lsDEBUG, LedgerConsensus) << getJson();
return false;
}
mOurVote = newPosition;
WriteLog (lsDEBUG, LedgerConsensus) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID;
WriteLog (lsDEBUG, LedgerConsensus) << getJson();
return true;
}
Json::Value DisputedTx::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<uint160, bool>::value_type vt;
BOOST_FOREACH(vt& vote, mVotes)
{
votesj[vote.first.GetHex()] = vote.second;
}
ret["votes"] = votesj;
}
return ret;
}

View File

@@ -0,0 +1,54 @@
#ifndef RIPPLE_DISPUTEDTX_H
#define RIPPLE_DISPUTEDTX_H
/** A transaction discovered to be in dispute during conensus.
During consensus, a @ref DisputedTx is created when a transaction
is discovered to be disputed. The object persists only as long as
the dispute.
Undisputed transactions have no corresponding @ref DisputedTx object.
*/
class DisputedTx
{
public:
typedef boost::shared_ptr <DisputedTx> pointer;
DisputedTx (uint256 const& txID,
Blob const& tx,
bool ourVote) :
mTransactionID(txID), mYays(0), mNays(0), mOurVote(ourVote), transaction(tx) { ; }
uint256 const& getTransactionID() const { return mTransactionID; }
bool getOurVote() const { return mOurVote; }
// VFALCO TODO make this const
Serializer& peekTransaction() { return transaction; }
void setOurVote(bool o) { mOurVote = o; }
// VFALCO NOTE its not really a peer, its the 160 bit hash of the validator's public key
//
void setVote(uint160 const& peer, bool votesYes);
void unVote (uint160 const& peer);
bool updateVote(int percentTime, bool proposing);
Json::Value getJson();
private:
uint256 mTransactionID;
int mYays;
int mNays;
bool mOurVote;
Serializer transaction;
boost::unordered_map <uint160, bool> mVotes;
};
// VFALCO TODO Rename and put these in a tidy place
typedef std::map<uint256, DisputedTx::pointer>::value_type u256_lct_pair;
typedef std::map<uint160, LedgerProposal::pointer>::value_type u160_prop_pair;
#define LEDGER_TOTAL_PASSES 8
#define LEDGER_RETRY_PASSES 5
#endif

View File

@@ -1,7 +1,4 @@
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
SETUP_LOG (JobQueue)

View File

@@ -3,7 +3,7 @@
/** Manages the lifetime of inbound ledgers.
*/
// VFALCO TODO Rename to InboundLedgerManager
// VFALCO TODO Rename to InboundLedgers
// VFALCO TODO Create abstract interface
class LedgerAcquireMaster
{

View File

@@ -1,12 +1,4 @@
#include <string>
#include <boost/test/unit_test.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <boost/regex.hpp>
#include <openssl/rand.h>
SETUP_LOG (ProofOfWork)

View File

@@ -1,5 +1,3 @@
#include <string>
#include <boost/test/unit_test.hpp>
class ProofOfWorkFactory : public IProofOfWorkFactory
{

View File

@@ -4,7 +4,7 @@ SETUP_LOG (TransactionAcquire)
#define TX_ACQUIRE_TIMEOUT 250
typedef std::map<uint160, LedgerProposal::pointer>::value_type u160_prop_pair;
typedef std::map<uint256, LCTransaction::pointer>::value_type u256_lct_pair;
typedef std::map<uint256, DisputedTx::pointer>::value_type u256_lct_pair;
DECLARE_INSTANCE(TransactionAcquire);

View File

@@ -0,0 +1,37 @@
#ifndef RIPPLE_TRANSACTIONACQUIRE_H
#define RIPPLE_TRANSACTIONACQUIRE_H
DEFINE_INSTANCE(TransactionAcquire);
// VFALCO TODO rename to PeerTxRequest
// A transaction set we are trying to acquire
class TransactionAcquire
: private IS_INSTANCE (TransactionAcquire)
, public PeerSet
, public boost::enable_shared_from_this <TransactionAcquire>
{
public:
typedef boost::shared_ptr<TransactionAcquire> pointer;
public:
explicit TransactionAcquire (uint256 const& hash);
virtual ~TransactionAcquire() { ; }
SHAMap::ref getMap() { return mMap; }
SHAMapAddNode takeNodes(const std::list<SHAMapNode>& IDs,
const std::list< Blob >& data, Peer::ref);
private:
SHAMap::pointer mMap;
bool mHaveRoot;
void onTimer(bool progress);
void newPeer(Peer::ref peer) { trigger(peer); }
void done();
void trigger(Peer::ref);
boost::weak_ptr<PeerSet> pmDowncast();
};
#endif