From fed4350ccccefcbfc36705aa44642d257771f731 Mon Sep 17 00:00:00 2001 From: jed Date: Tue, 25 Oct 2011 16:56:15 -0700 Subject: [PATCH] . --- Ledger.h | 1 + LedgerHistory.cpp | 3 + LedgerMaster.cpp | 55 ++++++++++------ ValidationCollection.cpp | 137 ++++++++++++++++++++++----------------- ValidationCollection.h | 25 +++++-- 5 files changed, 139 insertions(+), 82 deletions(-) diff --git a/Ledger.h b/Ledger.h index 73ad4f8337..8919be0375 100644 --- a/Ledger.h +++ b/Ledger.h @@ -51,6 +51,7 @@ public: typedef boost::shared_ptr pointer; Ledger(uint32 index); Ledger(newcoin::FullLedger& ledger); + Ledger(Ledger::pointer other); void setTo(newcoin::FullLedger& ledger); void mergeIn(Ledger::pointer other); diff --git a/LedgerHistory.cpp b/LedgerHistory.cpp index 0e2e858b15..e1ac7ed5bb 100644 --- a/LedgerHistory.cpp +++ b/LedgerHistory.cpp @@ -4,6 +4,9 @@ /* Soon we should support saving the ledger in a real DB 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() diff --git a/LedgerMaster.cpp b/LedgerMaster.cpp index 7da0bde0c8..88859d2241 100644 --- a/LedgerMaster.cpp +++ b/LedgerMaster.cpp @@ -247,31 +247,48 @@ void LedgerMaster::checkConsensus(uint32 ledgerIndex) Ledger::pointer ourAcceptedLedger=mLedgerHistory.getAcceptedLedger(ledgerIndex); if(ourAcceptedLedger) { - uint256* consensusHash=theApp->getValidationCollection().getConsensusLedgerHash(ledgerIndex); - 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(); - } + Ledger::pointer consensusLedger; + uint256 consensusHash; + 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 { // 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); } } } } +/* +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); +} +} +*/ diff --git a/ValidationCollection.cpp b/ValidationCollection.cpp index 898bd4498c..f4584392f6 100644 --- a/ValidationCollection.cpp +++ b/ValidationCollection.cpp @@ -6,6 +6,14 @@ #include 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) { @@ -46,6 +54,8 @@ void ValidationCollection::addValidation(newcoin::Validation& valid) if( theApp->getUNL().findHanko(valid.hanko()) ) { mValidations[hash].push_back(valid); + mIndexValidations[valid.ledgerindex()].push_back(valid); + addToGroup(valid); theApp->getLedgerMaster().checkConsensus(valid.ledgerindex()); }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 // a validation can be in multiple groups since compatibility isn't transitive // Sometimes things are just complex void ValidationCollection::addToGroup(newcoin::Validation& newValid) { - if(mGroupValidations.count(newValid.ledgerindex())) + + if(mIndexGroups.count(newValid.ledgerindex())) { bool canReturn=false; // see if this hash is already on the list. If so add it there. - vector< vector >& groups=mGroupValidations[newValid.ledgerindex()]; - vector& groupList=vector(); - BOOST_FOREACH(groupList,groups) + vector< Group >& groups=mIndexGroups[newValid.ledgerindex()]; + BOOST_FOREACH(Group& group,groups) { - BOOST_FOREACH(newcoin::Validation& valid,groupList) + BOOST_FOREACH(newcoin::Validation& valid,group.mValidations) { if(valid.hash()==newValid.hash()) { - groupList.push_back(newValid); + group.mValidations.push_back(newValid); canReturn=true; break; } @@ -86,49 +109,47 @@ void ValidationCollection::addToGroup(newcoin::Validation& newValid) Ledger::pointer newLedger=theApp->getLedgerMaster().getLedger(newHash); if(newLedger) { // see if this ledger is compatible with any groups - BOOST_FOREACH(groupList,groups) + bool foundGroup=false; + BOOST_FOREACH(Group& group,groups) { - bool compatible=true; - BOOST_FOREACH(newcoin::Validation& valid,groupList) + if(group.addIfCompatible(newLedger,newValid)) foundGroup=true; + } + + 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()); Ledger::pointer ledger=theApp->getLedgerMaster().getLedger(hash); - if(ledger) - { - if(!ledger->isCompatible(newLedger)) - { // not compatible with this group - compatible=false; - break; - } - }else - { // we can't tell if it is compatible - compatible=false; - break; - } + newGroup.addIfCompatible(ledger,valid); } - - 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 { // 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* ValidationCollection::getValidations(uint32 ledgerIndex) { - if(mGroupValidations.count(ledgerIndex)) + if(mIndexValidations.count(ledgerIndex)) { - return(&(mGroupValidations[ledgerIndex])); + return(&(mIndexValidations[ledgerIndex])); } return(NULL); } @@ -137,38 +158,36 @@ vector* ValidationCollection::getValidations(uint32 ledgerI // look through all the validated hashes at that index // put the ledgers into compatible groups // Pick the group with the most votes -bool ValidationCollection::getConsensusLedgers(uint32 ledgerIndex, list& retHashs) +bool ValidationCollection::getConsensusLedger(uint32 ledgerIndex, uint256& ourHash, Ledger::pointer& retLedger, uint256& retHash) { - vector* valids=getValidations(ledgerIndex); - if(valids) + bool ret=false; + if(mIndexGroups.count(ledgerIndex)) { - vector< pair > > compatibleGroups; - - map voteCounts; - BOOST_FOREACH(newcoin::Validation valid,*valids) + + unsigned int maxVotes=theConfig.MIN_VOTES_FOR_CONSENSUS; + vector& mostValid=vector(); + vector< Group >& groups=mIndexGroups[ledgerIndex]; + Group& maxGroup=Group(); + BOOST_FOREACH(Group& group,groups) { - uint256 hash=Transaction::protobufToInternalHash(valid.hash()); - Ledger::pointer testLedger=theApp->getLedgerMaster().getLedger(hash); - if(testLedger) + if(group.mValidations.size()>maxVotes) { - - } - - voteCounts[ ] += 1; - } - bool ret=false; - int maxVotes=theConfig.MIN_VOTES_FOR_CONSENSUS; - pair& vote=pair(); - BOOST_FOREACH(vote,voteCounts) - { - if(vote.second>maxVotes) - { - maxVotes=vote.second; - retHash=vote.first; + maxVotes=group.mValidations.size(); + retLedger=group.mSuperLedger; + maxGroup=group; + if(!retLedger) retHash=Transaction::protobufToInternalHash(group.mValidations[0].hash()); 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); } \ No newline at end of file diff --git a/ValidationCollection.h b/ValidationCollection.h index b732cbdd6c..f2ff481169 100644 --- a/ValidationCollection.h +++ b/ValidationCollection.h @@ -4,6 +4,8 @@ #include "newcoin.pb.h" #include "uint256.h" #include "types.h" +#include "Ledger.h" +#include class ValidationCollection { @@ -15,22 +17,37 @@ class ValidationCollection // 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 // - std::map > > mGroupValidations; + class Group + { + public: + std::vector mValidations; + Ledger::pointer mSuperLedger; + + bool addIfCompatible(Ledger::pointer ledger,newcoin::Validation& valid); + }; + + std::map > mIndexGroups; // all the groups at each index + std::map > mIndexValidations; // all the validations at each index bool hasValidation(uint256& ledgerHash,uint160& hanko,uint32 seqnum); void addToGroup(newcoin::Validation& valid); public: ValidationCollection(); + void save(); + void load(); + void addValidation(newcoin::Validation& valid); std::vector* getValidations(uint32 ledgerIndex); // 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 - bool getConsensusLedgers(uint32 ledgerIndex, std::list& retHashs); + + // fills out retLedger if there is a consensusLedger you can check + // 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); };