This commit is contained in:
jed
2011-10-25 16:56:15 -07:00
parent 11a964ffed
commit fed4350ccc
5 changed files with 139 additions and 82 deletions

View File

@@ -51,6 +51,7 @@ public:
typedef boost::shared_ptr<Ledger> pointer; typedef boost::shared_ptr<Ledger> pointer;
Ledger(uint32 index); Ledger(uint32 index);
Ledger(newcoin::FullLedger& ledger); Ledger(newcoin::FullLedger& ledger);
Ledger(Ledger::pointer other);
void setTo(newcoin::FullLedger& ledger); void setTo(newcoin::FullLedger& ledger);
void mergeIn(Ledger::pointer other); void mergeIn(Ledger::pointer other);

View File

@@ -4,6 +4,9 @@
/* /*
Soon we should support saving the ledger in a real DB Soon we should support saving the ledger in a real DB
For now save them all in For now save them all in
For all the various ledgers we can save just the delta from the combined ledger for that index.
*/ */
void LedgerHistory::load() void LedgerHistory::load()

View File

@@ -247,31 +247,48 @@ void LedgerMaster::checkConsensus(uint32 ledgerIndex)
Ledger::pointer ourAcceptedLedger=mLedgerHistory.getAcceptedLedger(ledgerIndex); Ledger::pointer ourAcceptedLedger=mLedgerHistory.getAcceptedLedger(ledgerIndex);
if(ourAcceptedLedger) if(ourAcceptedLedger)
{ {
uint256* consensusHash=theApp->getValidationCollection().getConsensusLedgerHash(ledgerIndex); Ledger::pointer consensusLedger;
if( consensusHash && uint256 consensusHash;
(ourAcceptedLedger->getHash()!= *consensusHash))
{
Ledger::pointer consensusLedger=mLedgerHistory.getLedger(*consensusHash);
if(consensusLedger)
{ // see if these are compatible
if(ourAcceptedLedger->isCompatible(consensusLedger))
{ // try to merge any transactions from the consensus one into ours
ourAcceptedLedger->mergeIn(consensusLedger);
// Ledger::pointer child=ourAcceptedLedger->getChild();
Ledger::pointer child=mLedgerHistory.getAcceptedLedger(ledgerIndex+1);
if(child) child->recalculate();
}else
{ // switch to this ledger. Re-validate
mLedgerHistory.addAcceptedLedger(consensusLedger);
consensusLedger->publishValidation();
}
if( theApp->getValidationCollection().getConsensusLedger(ledgerIndex,ourAcceptedLedger->getHash(), consensusLedger, consensusHash) )
{ // our accepted ledger isn't compatible with the consensus
if(consensusLedger)
{ // switch to this ledger. Re-validate
mLedgerHistory.addAcceptedLedger(consensusLedger);
consensusLedger->publishValidation();
}else }else
{ // we don't know the consensus one. Ask peers for it { // we don't know the consensus one. Ask peers for it
PackedMessage::pointer msg=Peer::createGetFullLedger(*consensusHash); // TODO: make sure this isn't sent many times before we have a chance to get a reply
PackedMessage::pointer msg=Peer::createGetFullLedger(consensusHash);
theApp->getConnectionPool().relayMessage(NULL,msg); theApp->getConnectionPool().relayMessage(NULL,msg);
} }
} }
} }
} }
/*
if( consensusHash &&
(ourAcceptedLedger->getHash()!= *consensusHash))
{
Ledger::pointer consensusLedger=mLedgerHistory.getLedger(*consensusHash);
if(consensusLedger)
{ // see if these are compatible
if(ourAcceptedLedger->isCompatible(consensusLedger))
{ // try to merge any transactions from the consensus one into ours
ourAcceptedLedger->mergeIn(consensusLedger);
// Ledger::pointer child=ourAcceptedLedger->getChild();
Ledger::pointer child=mLedgerHistory.getAcceptedLedger(ledgerIndex+1);
if(child) child->recalculate();
}else
{ // switch to this ledger. Re-validate
mLedgerHistory.addAcceptedLedger(consensusLedger);
consensusLedger->publishValidation();
}
}else
{ // we don't know the consensus one. Ask peers for it
PackedMessage::pointer msg=Peer::createGetFullLedger(*consensusHash);
theApp->getConnectionPool().relayMessage(NULL,msg);
}
}
*/

View File

@@ -6,6 +6,14 @@
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
using namespace std; using namespace std;
/*
We need to group validations into compatible groups.
We can make one super ledger of all the transactions in each compatible group.
Then we just have to check this ledger to see if a new ledger is compatible
This is also the ledger we hand back when we ask for the consensus ledger
*/
bool ValidationCollection::hasValidation(uint256& ledgerHash,uint160& hanko,uint32 seqnum) bool ValidationCollection::hasValidation(uint256& ledgerHash,uint160& hanko,uint32 seqnum)
{ {
@@ -46,6 +54,8 @@ void ValidationCollection::addValidation(newcoin::Validation& valid)
if( theApp->getUNL().findHanko(valid.hanko()) ) if( theApp->getUNL().findHanko(valid.hanko()) )
{ {
mValidations[hash].push_back(valid); mValidations[hash].push_back(valid);
mIndexValidations[valid.ledgerindex()].push_back(valid);
addToGroup(valid);
theApp->getLedgerMaster().checkConsensus(valid.ledgerindex()); theApp->getLedgerMaster().checkConsensus(valid.ledgerindex());
}else }else
@@ -55,24 +65,37 @@ void ValidationCollection::addValidation(newcoin::Validation& valid)
} }
bool ValidationCollection::Group::addIfCompatible(Ledger::pointer ledger,newcoin::Validation& valid)
{
if(mSuperLedger)
{
if(mSuperLedger->isCompatible(ledger))
{
mValidations.push_back(valid);
mSuperLedger->mergeIn(ledger);
}
}
return(false);
}
// TODO: optimize. We can at least cache what ledgers are compatible // TODO: optimize. We can at least cache what ledgers are compatible
// a validation can be in multiple groups since compatibility isn't transitive // a validation can be in multiple groups since compatibility isn't transitive
// Sometimes things are just complex // Sometimes things are just complex
void ValidationCollection::addToGroup(newcoin::Validation& newValid) void ValidationCollection::addToGroup(newcoin::Validation& newValid)
{ {
if(mGroupValidations.count(newValid.ledgerindex()))
if(mIndexGroups.count(newValid.ledgerindex()))
{ {
bool canReturn=false; bool canReturn=false;
// see if this hash is already on the list. If so add it there. // see if this hash is already on the list. If so add it there.
vector< vector<newcoin::Validation> >& groups=mGroupValidations[newValid.ledgerindex()]; vector< Group >& groups=mIndexGroups[newValid.ledgerindex()];
vector<newcoin::Validation>& groupList=vector<newcoin::Validation>(); BOOST_FOREACH(Group& group,groups)
BOOST_FOREACH(groupList,groups)
{ {
BOOST_FOREACH(newcoin::Validation& valid,groupList) BOOST_FOREACH(newcoin::Validation& valid,group.mValidations)
{ {
if(valid.hash()==newValid.hash()) if(valid.hash()==newValid.hash())
{ {
groupList.push_back(newValid); group.mValidations.push_back(newValid);
canReturn=true; canReturn=true;
break; break;
} }
@@ -86,49 +109,47 @@ void ValidationCollection::addToGroup(newcoin::Validation& newValid)
Ledger::pointer newLedger=theApp->getLedgerMaster().getLedger(newHash); Ledger::pointer newLedger=theApp->getLedgerMaster().getLedger(newHash);
if(newLedger) if(newLedger)
{ // see if this ledger is compatible with any groups { // see if this ledger is compatible with any groups
BOOST_FOREACH(groupList,groups) bool foundGroup=false;
BOOST_FOREACH(Group& group,groups)
{ {
bool compatible=true; if(group.addIfCompatible(newLedger,newValid)) foundGroup=true;
BOOST_FOREACH(newcoin::Validation& valid,groupList) }
if(!foundGroup)
{ // this validation didn't fit in any of the other groups
// we need to make a new group for it and see what validations fit it
Group& newGroup=mIndexGroups[newValid.ledgerindex()][groups.size()];
newGroup.mValidations.push_back(newValid);
newGroup.mSuperLedger=Ledger::pointer(new Ledger(newLedger)); // since this super ledger gets modified and we don't want to screw the original
BOOST_FOREACH(newcoin::Validation& valid,mIndexValidations[newValid.ledgerindex()])
{ {
uint256 hash=Transaction::protobufToInternalHash(valid.hash()); uint256 hash=Transaction::protobufToInternalHash(valid.hash());
Ledger::pointer ledger=theApp->getLedgerMaster().getLedger(hash); Ledger::pointer ledger=theApp->getLedgerMaster().getLedger(hash);
if(ledger) newGroup.addIfCompatible(ledger,valid);
{
if(!ledger->isCompatible(newLedger))
{ // not compatible with this group
compatible=false;
break;
}
}else
{ // we can't tell if it is compatible
compatible=false;
break;
}
} }
if(compatible) groupList.push_back(newValid);
} }
}else
{ // we don't have a ledger for this validation
// add to its own group since we can't check if it is compatible
int newIndex=groups.size();
mIndexGroups[newValid.ledgerindex()][newIndex].mValidations.push_back(newValid);
} }
// also add to its own group in case
groupList.push_back(newValid);
groups.push_back(groupList);
}else }else
{ // this is the first validation of this ledgerindex { // this is the first validation of this ledgerindex
mGroupValidations[newValid.ledgerindex()][0].push_back(newValid); uint256 newHash=Transaction::protobufToInternalHash(newValid.hash());
mIndexGroups[newValid.ledgerindex()][0].mValidations.push_back(newValid);
mIndexGroups[newValid.ledgerindex()][0].mSuperLedger=theApp->getLedgerMaster().getLedger(newHash);
} }
} }
vector<newcoin::Validation>* ValidationCollection::getValidations(uint32 ledgerIndex) vector<newcoin::Validation>* ValidationCollection::getValidations(uint32 ledgerIndex)
{ {
if(mGroupValidations.count(ledgerIndex)) if(mIndexValidations.count(ledgerIndex))
{ {
return(&(mGroupValidations[ledgerIndex])); return(&(mIndexValidations[ledgerIndex]));
} }
return(NULL); return(NULL);
} }
@@ -137,38 +158,36 @@ vector<newcoin::Validation>* ValidationCollection::getValidations(uint32 ledgerI
// look through all the validated hashes at that index // look through all the validated hashes at that index
// put the ledgers into compatible groups // put the ledgers into compatible groups
// Pick the group with the most votes // Pick the group with the most votes
bool ValidationCollection::getConsensusLedgers(uint32 ledgerIndex, list<uint256>& retHashs) bool ValidationCollection::getConsensusLedger(uint32 ledgerIndex, uint256& ourHash, Ledger::pointer& retLedger, uint256& retHash)
{ {
vector<newcoin::Validation>* valids=getValidations(ledgerIndex); bool ret=false;
if(valids) if(mIndexGroups.count(ledgerIndex))
{ {
vector< pair<int, list<uint256> > > compatibleGroups;
unsigned int maxVotes=theConfig.MIN_VOTES_FOR_CONSENSUS;
map<uint256, int> voteCounts; vector<newcoin::Validation>& mostValid=vector<newcoin::Validation>();
BOOST_FOREACH(newcoin::Validation valid,*valids) vector< Group >& groups=mIndexGroups[ledgerIndex];
Group& maxGroup=Group();
BOOST_FOREACH(Group& group,groups)
{ {
uint256 hash=Transaction::protobufToInternalHash(valid.hash()); if(group.mValidations.size()>maxVotes)
Ledger::pointer testLedger=theApp->getLedgerMaster().getLedger(hash);
if(testLedger)
{ {
maxVotes=group.mValidations.size();
} retLedger=group.mSuperLedger;
maxGroup=group;
voteCounts[ ] += 1; if(!retLedger) retHash=Transaction::protobufToInternalHash(group.mValidations[0].hash());
}
bool ret=false;
int maxVotes=theConfig.MIN_VOTES_FOR_CONSENSUS;
pair<uint256, int>& vote=pair<uint256, int>();
BOOST_FOREACH(vote,voteCounts)
{
if(vote.second>maxVotes)
{
maxVotes=vote.second;
retHash=vote.first;
ret=true; ret=true;
} }
} }
return(ret); if(ret)
{
// should also return false if we are in the consensus
BOOST_FOREACH(newcoin::Validation& valid, maxGroup.mValidations)
{
if(Transaction::protobufToInternalHash(valid.hash()) == ourHash) return(false);
}
}
} }
return(false);
return(ret);
} }

View File

@@ -4,6 +4,8 @@
#include "newcoin.pb.h" #include "newcoin.pb.h"
#include "uint256.h" #include "uint256.h"
#include "types.h" #include "types.h"
#include "Ledger.h"
#include <list>
class ValidationCollection class ValidationCollection
{ {
@@ -15,22 +17,37 @@ class ValidationCollection
// this maps ledgerIndex to an array of groups. Each group is a list of validations. // this maps ledgerIndex to an array of groups. Each group is a list of validations.
// a validation can be in multiple groups since compatibility isn't transitive // a validation can be in multiple groups since compatibility isn't transitive
// //
std::map<uint32, std::vector< std::vector<newcoin::Validation> > > mGroupValidations; class Group
{
public:
std::vector<newcoin::Validation> mValidations;
Ledger::pointer mSuperLedger;
bool addIfCompatible(Ledger::pointer ledger,newcoin::Validation& valid);
};
std::map<uint32, std::vector< Group > > mIndexGroups; // all the groups at each index
std::map<uint32, std::vector< newcoin::Validation > > mIndexValidations; // all the validations at each index
bool hasValidation(uint256& ledgerHash,uint160& hanko,uint32 seqnum); bool hasValidation(uint256& ledgerHash,uint160& hanko,uint32 seqnum);
void addToGroup(newcoin::Validation& valid); void addToGroup(newcoin::Validation& valid);
public: public:
ValidationCollection(); ValidationCollection();
void save();
void load();
void addValidation(newcoin::Validation& valid); void addValidation(newcoin::Validation& valid);
std::vector<newcoin::Validation>* getValidations(uint32 ledgerIndex); std::vector<newcoin::Validation>* getValidations(uint32 ledgerIndex);
// It can miss some compatible ledgers of course if you don't know them // It can miss some compatible ledgers of course if you don't know them
// gets a list of all the compatible ledgers that were voted for the most
// returns false if there isn't a consensus yet // fills out retLedger if there is a consensusLedger you can check
bool getConsensusLedgers(uint32 ledgerIndex, std::list<uint256>& retHashs); // fills out retHash if there isn't a consensusLedger to check. We need to fetch this ledger
// returns false if there isn't a consensus yet or we are in the consensus
bool getConsensusLedger(uint32 ledgerIndex, uint256& ourHash, Ledger::pointer& retLedger, uint256& retHash);
int getSeqNum(uint32 ledgerIndex); int getSeqNum(uint32 ledgerIndex);
}; };