Make this code work.

This commit is contained in:
JoelKatz
2012-06-18 17:01:35 -07:00
parent 74766e8884
commit d57236599e
2 changed files with 50 additions and 254 deletions

View File

@@ -1,226 +1,50 @@
#include "ValidationCollection.h"
#include "Application.h"
#include "Config.h"
#include "Conversion.h"
#include "Application.h"
#include <boost/foreach.hpp>
using namespace std;
#include "Log.h"
/*
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
*/
ValidationCollection::ValidationCollection()
bool ValidationCollection::addValidation(SerializedValidation::pointer val)
{
if(theApp->getUNL().nodeInUNL(val->getSignerPublic()))
val->setTrusted();
uint256 hash = val->getLedgerHash();
uint160 node = val->getSignerPublic().getNodeID();
boost::mutex::scoped_lock sl(mValidationLock);
bool ret = mValidations[hash].insert(std::make_pair(node, val)).second;
if (ret)
Log(lsINFO) << "Val for " << hash.GetHex() << " from " << node.GetHex() << " added " <<
(val->isTrusted() ? "trusted" : "UNtrusted");
return ret;
}
void ValidationCollection::save()
ValidationSet ValidationCollection::getValidations(const uint256& ledger)
{
}
void ValidationCollection::load()
{
}
void ValidationCollection::addToDB(newcoin::Validation& valid,int weCare)
{
Database* db=theApp->getDB();
uint256 hash=protobufTo256(valid.hash());
uint160 hanko=protobufTo160(valid.hanko());
uint256 sig=protobufTo256(valid.sig());
string hashStr,hankoStr,sigStr;
db->escape(hash.begin(),hash.GetSerializeSize(),hashStr);
db->escape(hanko.begin(),hanko.GetSerializeSize(),hankoStr);
db->escape(sig.begin(),sig.GetSerializeSize(),sigStr);
string sql=strprintf("INSERT INTO Validations (LedgerIndex,Hash,Hanko,SeqNum,Sig,WeCare) values (%d,%s,%s,%d,%s,%d)",valid.ledgerindex(),hashStr.c_str(),hankoStr.c_str(),valid.seqnum(),sigStr.c_str(),weCare);
db->executeSQL(sql);
}
bool ValidationCollection::hasValidation(uint32 ledgerIndex,uint160& hanko,uint32 seqnum)
{
string hankoStr;
Database* db=theApp->getDB();
db->escape(hanko.begin(),hanko.GetSerializeSize(),hankoStr);
string sql=strprintf("SELECT ValidationID,seqnum from Validations where LedgerIndex=%d and hanko=%s",
ledgerIndex,hankoStr.c_str());
if(db->executeSQL(sql) && db->startIterRows())
ValidationSet ret;
{
uint32 currentSeqNum=db->getInt(1);
if(currentSeqNum>=seqnum)
boost::mutex::scoped_lock sl(mValidationLock);
boost::unordered_map<uint256, ValidationSet>::iterator it = mValidations.find(ledger);
if (it != mValidations.end()) ret = it->second;
}
return ret;
}
void ValidationCollection::getValidationCount(const uint256& ledger, int& trusted, int &untrusted)
{
trusted = untrusted = 0;
boost::mutex::scoped_lock sl(mValidationLock);
boost::unordered_map<uint256, ValidationSet>::iterator it = mValidations.find(ledger);
if (it != mValidations.end())
{
for (ValidationSet::iterator vit = it->second.begin(), end = it->second.end();
vit != end; ++vit)
{
db->endIterRows();
return(true);
}
// delete the old validation we were storing
sql=strprintf("DELETE FROM Validations where ValidationID=%d",db->getInt(0));
db->endIterRows();
db->executeSQL(sql);
}
return(false);
}
// TODO: we are adding our own validation
// TODO: when do we check if we are with the consensus?
// TODO: throw out lower seqnums
void ValidationCollection::addValidation(newcoin::Validation& valid)
{
// TODO: make sure the validation is valid
uint256 hash=protobufTo256(valid.hash());
uint160 hanko=protobufTo160(valid.hanko());
// make sure we don't already have this validation
if(hasValidation(valid.ledgerindex(),hanko,valid.seqnum())) return;
// check if we care about this hanko
int validity=theApp->getUNL().checkValid(valid);
if( validity==1 )
{
addToDB(valid,true);
addToGroup(valid);
theApp->getLedgerMaster().checkConsensus(valid.ledgerindex());
}else if(validity==0)
{
addToDB(valid,false);
}else
{ // the signature wasn't valid
cout << "Invalid Validation" << endl;
}
}
bool ValidationCollection::Group::addIfCompatible(Ledger::pointer ledger,newcoin::Validation& valid)
{
if(mSuperLedger)
{
if(mSuperLedger->isCompatible(ledger))
{
mValidations.push_back(valid);
mSuperLedger->mergeIn(ledger);
if(vit->second->isTrusted())
++trusted;
else
++untrusted;
}
}
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(mIndexGroups.count(newValid.ledgerindex()))
{
bool canReturn=false;
// see if this hash is already on the list. If so add it there.
vector< Group >& groups=mIndexGroups[newValid.ledgerindex()];
BOOST_FOREACH(Group& group,groups)
{ // FIXME: Cannot modify *at* *all* inside a BOOST_FOREACH
BOOST_FOREACH(newcoin::Validation& valid,group.mValidations)
{
if(valid.hash()==newValid.hash())
{
group.mValidations.push_back(newValid);
canReturn=true;
break;
}
}
}
if(canReturn) return;
// this is a validation of a new ledger hash
uint256 newHash=protobufTo256(newValid.hash());
Ledger::pointer newLedger=theApp->getLedgerMaster().getLedger(newHash);
if(newLedger)
{ // see if this ledger is compatible with any groups
bool foundGroup=false;
BOOST_FOREACH(Group& group,groups)
{
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
vector<newcoin::Validation> retVec;
getValidations(newValid.ledgerindex(),retVec);
BOOST_FOREACH(newcoin::Validation& valid,retVec)
{
uint256 hash=protobufTo256(valid.hash());
Ledger::pointer ledger=theApp->getLedgerMaster().getLedger(hash);
newGroup.addIfCompatible(ledger,valid);
}
}
}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);
}
}else
{ // this is the first validation of this ledgerindex
uint256 newHash=protobufTo256(newValid.hash());
mIndexGroups[newValid.ledgerindex()][0].mValidations.push_back(newValid);
mIndexGroups[newValid.ledgerindex()][0].mSuperLedger=theApp->getLedgerMaster().getLedger(newHash);
}
}
void ValidationCollection::getValidations(uint32 ledgerIndex,vector<newcoin::Validation>& retVec)
{
string sql=strprintf("SELECT * From Validations where LedgerIndex=%d and wecare=1",ledgerIndex);
// TODO: ValidationCollection::getValidations(uint32 ledgerIndex)
}
// look through all the validated hashes at that index
// put the ledgers into compatible groups
// Pick the group with the most votes
bool ValidationCollection::getConsensusLedger(uint32 ledgerIndex, uint256& ourHash, Ledger::pointer& retLedger, uint256& retHash)
{
bool ret=false;
if(mIndexGroups.count(ledgerIndex))
{
unsigned int maxVotes=theConfig.MIN_VOTES_FOR_CONSENSUS;
vector< Group >& groups=mIndexGroups[ledgerIndex];
Group empty;
Group& maxGroup=empty;
BOOST_FOREACH(Group& group, groups)
{
if(group.mValidations.size()>maxVotes)
{
maxVotes=group.mValidations.size();
retLedger=group.mSuperLedger;
maxGroup=group;
if(!retLedger) retHash=protobufTo256(group.mValidations[0].hash());
ret=true;
}
}
if(ret)
{
// should also return false if we are in the consensus
BOOST_FOREACH(newcoin::Validation& valid, maxGroup.mValidations)
{
if(protobufTo256(valid.hash()) == ourHash) return(false);
}
}
}
return(ret);
}
// vim:ts=4

View File

@@ -1,56 +1,28 @@
#ifndef __VALIDATION_COLLECTION__
#define __VALIDATION_COLLECTION__
#include "../obj/src/newcoin.pb.h"
#include <boost/unordered_map.hpp>
#include <boost/thread/mutex.hpp>
#include "uint256.h"
#include "types.h"
#include "Ledger.h"
#include <list>
#include "SerializedValidation.h"
typedef boost::unordered_map<uint160, SerializedValidation::pointer> ValidationSet;
class ValidationCollection
{
protected:
// from ledger hash to the validation
//std::map<uint256, std::vector<newcoin::Validation> > mValidations;
//std::map<uint256, std::vector<newcoin::Validation> > mIgnoredValidations;
boost::mutex mValidationLock;
boost::unordered_map<uint256, ValidationSet> mValidations;
// 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
//
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(uint32 ledgerIndex,uint160& hanko,uint32 seqnum);
void addToGroup(newcoin::Validation& valid);
void addToDB(newcoin::Validation& valid,int weCare);
public:
ValidationCollection();
ValidationCollection() { ; }
void save();
void load();
void addValidation(newcoin::Validation& valid);
void getValidations(uint32 ledgerIndex,std::vector<newcoin::Validation>& retVec);
// It can miss some compatible ledgers of course if you don't know them
// 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);
bool addValidation(SerializedValidation::pointer);
ValidationSet getValidations(const uint256& ledger);
void getValidationCount(const uint256& ledger, int& trusted, int& untrusted);
};
#endif