mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-27 14:35:52 +00:00
.
This commit is contained in:
1
Ledger.h
1
Ledger.h
@@ -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);
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -247,7 +247,25 @@ 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;
|
||||||
|
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
|
||||||
|
// 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 &&
|
if( consensusHash &&
|
||||||
(ourAcceptedLedger->getHash()!= *consensusHash))
|
(ourAcceptedLedger->getHash()!= *consensusHash))
|
||||||
{
|
{
|
||||||
@@ -272,6 +290,5 @@ void LedgerMaster::checkConsensus(uint32 ledgerIndex)
|
|||||||
theApp->getConnectionPool().relayMessage(NULL,msg);
|
theApp->getConnectionPool().relayMessage(NULL,msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
}else
|
||||||
{ // we can't tell if it is compatible
|
{ // we don't have a ledger for this validation
|
||||||
compatible=false;
|
// add to its own group since we can't check if it is compatible
|
||||||
break;
|
int newIndex=groups.size();
|
||||||
|
mIndexGroups[newValid.ledgerindex()][newIndex].mValidations.push_back(newValid);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(compatible) groupList.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);
|
|
||||||
if(valids)
|
|
||||||
{
|
|
||||||
vector< pair<int, list<uint256> > > compatibleGroups;
|
|
||||||
|
|
||||||
map<uint256, int> voteCounts;
|
|
||||||
BOOST_FOREACH(newcoin::Validation valid,*valids)
|
|
||||||
{
|
|
||||||
uint256 hash=Transaction::protobufToInternalHash(valid.hash());
|
|
||||||
Ledger::pointer testLedger=theApp->getLedgerMaster().getLedger(hash);
|
|
||||||
if(testLedger)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
voteCounts[ ] += 1;
|
|
||||||
}
|
|
||||||
bool ret=false;
|
bool ret=false;
|
||||||
int maxVotes=theConfig.MIN_VOTES_FOR_CONSENSUS;
|
if(mIndexGroups.count(ledgerIndex))
|
||||||
pair<uint256, int>& vote=pair<uint256, int>();
|
|
||||||
BOOST_FOREACH(vote,voteCounts)
|
|
||||||
{
|
{
|
||||||
if(vote.second>maxVotes)
|
|
||||||
|
unsigned int maxVotes=theConfig.MIN_VOTES_FOR_CONSENSUS;
|
||||||
|
vector<newcoin::Validation>& mostValid=vector<newcoin::Validation>();
|
||||||
|
vector< Group >& groups=mIndexGroups[ledgerIndex];
|
||||||
|
Group& maxGroup=Group();
|
||||||
|
BOOST_FOREACH(Group& group,groups)
|
||||||
{
|
{
|
||||||
maxVotes=vote.second;
|
if(group.mValidations.size()>maxVotes)
|
||||||
retHash=vote.first;
|
{
|
||||||
|
maxVotes=group.mValidations.size();
|
||||||
|
retLedger=group.mSuperLedger;
|
||||||
|
maxGroup=group;
|
||||||
|
if(!retLedger) retHash=Transaction::protobufToInternalHash(group.mValidations[0].hash());
|
||||||
ret=true;
|
ret=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user