Merge branch 'master' into websocket

This commit is contained in:
Arthur Britto
2012-06-18 17:50:03 -07:00
15 changed files with 162 additions and 267 deletions

View File

@@ -68,7 +68,7 @@ PROTO_SRCS = env.Protoc([], 'src/newcoin.proto', PROTOCOUTDIR='obj', PROTOCP
env.Clean(PROTO_SRCS, 'site_scons/site_tools/protoc.pyc')
# Remove unused source files.
UNUSED_SRCS = ['src/HttpReply.cpp', 'src/ValidationCollection.cpp']
UNUSED_SRCS = ['src/HttpReply.cpp']
for file in UNUSED_SRCS:
NEWCOIN_SRCS.remove(file)

View File

@@ -119,3 +119,6 @@
[validation_seed]
snTBDmrhUK3znvF8AaQURLm4UCkbS
[unl_default]
validators.txt

View File

@@ -13,6 +13,7 @@
#include "Peer.h"
#include "NetworkOPs.h"
#include "TaggedCache.h"
#include "ValidationCollection.h"
#include "../database/database.h"
@@ -44,6 +45,7 @@ class Application
TransactionMaster mMasterTransaction;
NetworkOPs mNetOps;
NodeCache mNodeCache;
ValidationCollection mValidations;
DatabaseCon *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB;
@@ -74,6 +76,7 @@ public:
LedgerAcquireMaster& getMasterLedgerAcquire() { return mMasterLedgerAcquire; }
TransactionMaster& getMasterTransaction() { return mMasterTransaction; }
NodeCache& getNodeCache() { return mNodeCache; }
ValidationCollection& getValidations() { return mValidations; }
DatabaseCon* getTxnDB() { return mTxnDB; }
DatabaseCon* getLedgerDB() { return mLedgerDB; }

View File

@@ -11,6 +11,9 @@
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
// How often to enforce policies.
#define POLICY_INTERVAL_SECONDS 5
void splitIpPort(const std::string& strIpPort, std::string& strIp, int& iPort)
{
std::vector<std::string> vIpPort;
@@ -23,7 +26,8 @@ void splitIpPort(const std::string& strIpPort, std::string& strIp, int& iPort)
ConnectionPool::ConnectionPool(boost::asio::io_service& io_service) :
mCtx(boost::asio::ssl::context::sslv23),
bScanning(false),
mScanTimer(io_service)
mScanTimer(io_service),
mPolicyTimer(io_service)
{
mCtx.set_options(
boost::asio::ssl::context::default_workarounds
@@ -36,7 +40,7 @@ ConnectionPool::ConnectionPool(boost::asio::io_service& io_service) :
void ConnectionPool::start()
{
// XXX Start running policy.
// Start running policy.
policyEnforce();
// Start scanning.
@@ -157,7 +161,41 @@ void ConnectionPool::policyLowWater()
void ConnectionPool::policyEnforce()
{
boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time();
std::cerr << "policyEnforce: begin: " << tpNow << std::endl;
// Cancel any in progrss timer.
(void) mPolicyTimer.cancel();
// Enforce policies.
policyLowWater();
// Schedule next enforcement.
boost::posix_time::ptime tpNext;
tpNext = boost::posix_time::second_clock::universal_time()+boost::posix_time::seconds(POLICY_INTERVAL_SECONDS);
std::cerr << "policyEnforce: schedule : " << tpNext << std::endl;
mPolicyTimer.expires_at(tpNext);
mPolicyTimer.async_wait(boost::bind(&ConnectionPool::policyHandler, this, _1));
}
void ConnectionPool::policyHandler(const boost::system::error_code& ecResult)
{
if (ecResult == boost::asio::error::operation_aborted)
{
nothing();
}
else if (!ecResult)
{
policyEnforce();
}
else
{
throw std::runtime_error("Internal error: unexpected deadline error.");
}
}
// XXX Broken: also don't send a message to a peer if we got it from the peer.

View File

@@ -34,6 +34,10 @@ private:
void scanHandler(const boost::system::error_code& ecResult);
boost::asio::deadline_timer mPolicyTimer;
void policyHandler(const boost::system::error_code& ecResult);
// Peers we are establishing a connection with as a client.
// int miConnectStarting;

View File

@@ -281,7 +281,7 @@ void LedgerConsensus::mapComplete(const uint256& hash, SHAMap::pointer map)
}
if (!peers.empty())
adjustCount(map, peers);
else
else if (!hash)
Log(lsWARNING) << "By the time we got the map, no peers were proposing it";
sendHaveTxSet(hash, true);
@@ -807,8 +807,11 @@ void LedgerConsensus::accept(SHAMap::pointer set)
}
#endif
SerializedValidation v(newLCLHash, mOurPosition->peekSeed(), true);
std::vector<unsigned char> validation = v.getSigned();
SerializedValidation::pointer v = boost::make_shared<SerializedValidation>
(newLCLHash, mOurPosition->peekSeed(), true);
v->setTrusted();
theApp->getValidations().addValidation(v);
std::vector<unsigned char> validation = v->getSigned();
newcoin::TMValidation val;
val.set_validation(&validation[0], validation.size());
theApp->getConnectionPool().relayMessage(NULL, boost::make_shared<PackedMessage>(val, newcoin::mtVALIDATION));

View File

@@ -308,11 +308,18 @@ void NetworkOPs::checkState(const boost::system::error_code& result)
if (!!peerLedger)
{
// FIXME: If we have this ledger, don't count it if it's too far past its close time
bool isNew = ledgers.find(peerLedger) == ledgers.end();
ValidationCount& vc = ledgers[peerLedger];
if (isNew)
{
theApp->getValidations().getValidationCount(peerLedger,
vc.trustedValidations, vc.untrustedValidations);
Log(lsTRACE) << peerLedger.GetHex() << " has " << vc.trustedValidations <<
" trusted validations and " << vc.untrustedValidations << " untrusted";
}
if ((vc.nodesUsing == 0) || ((*it)->getNodePublic() > vc.highNode))
vc.highNode = (*it)->getNodePublic();
++vc.nodesUsing;
// WRITEME: Validations, trusted peers
}
}
}
@@ -533,8 +540,8 @@ void NetworkOPs::setMode(OperatingMode om)
if (mMode == om) return;
Log l((om < mMode) ? lsWARNING : lsINFO);
if (om == omDISCONNECTED) l << "STATE->Disonnected";
else if (om==omCONNECTED) l << "STATE->Connected";
else if (om==omTRACKING) l << "STATE->Tracking";
else if (om == omCONNECTED) l << "STATE->Connected";
else if (om == omTRACKING) l << "STATE->Tracking";
else l << "STATE->Full";
mMode = om;
}
@@ -562,4 +569,9 @@ std::vector< std::pair<uint32, uint256> >
return affectedAccounts;
}
bool NetworkOPs::recvValidation(SerializedValidation::pointer val)
{
return theApp->getValidations().addValidation(val);
}
// vim:ts=4

View File

@@ -5,6 +5,7 @@
#include "AccountState.h"
#include "RippleState.h"
#include "NicknameState.h"
#include "SerializedValidation.h"
// #include <boost/asio.hpp>
@@ -115,6 +116,7 @@ public:
const std::string& pubKey, const std::string& signature);
bool gotTXData(boost::shared_ptr<Peer> peer, const uint256& hash,
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData);
bool recvValidation(SerializedValidation::pointer val);
SHAMap::pointer getTXMap(const uint256& hash);
bool hasTXSet(boost::shared_ptr<Peer> peer, const uint256& set, newcoin::TxSetStatus status);
void mapComplete(const uint256& hash, SHAMap::pointer map);

View File

@@ -595,7 +595,7 @@ void Peer::recvTransaction(newcoin::TMTransaction& packet)
{
#endif
std::string rawTx = packet.rawtransaction();
Serializer s(std::vector<unsigned char>(rawTx.begin(), rawTx.end()));
Serializer s(rawTx);
SerializerIterator sit(s);
SerializedTransaction::pointer stx = boost::make_shared<SerializedTransaction>(boost::ref(sit));
@@ -668,6 +668,31 @@ void Peer::recvHaveTxSet(newcoin::TMHaveTransactionSet& packet)
void Peer::recvValidation(newcoin::TMValidation& packet)
{
if (packet.validation().size() < 50)
{
punishPeer(PP_UNKNOWN_REQUEST);
return;
}
try
{
Serializer s(packet.validation());
SerializerIterator sit(s);
SerializedValidation::pointer val = boost::make_shared<SerializedValidation>(boost::ref(sit));
if (!val->isValid())
{
punishPeer(PP_UNKNOWN_REQUEST);
return;
}
if (theApp->getOPs().recvValidation(val))
{
PackedMessage::pointer message = boost::make_shared<PackedMessage>(packet, newcoin::mtVALIDATION);
theApp->getConnectionPool().relayMessage(this, message);
}
}
catch (...)
{
punishPeer(PP_UNKNOWN_REQUEST);
}
}
void Peer::recvGetValidation(newcoin::TMGetValidations& packet)

View File

@@ -13,13 +13,13 @@ const uint32 SerializedValidation::sFullFlag = 0x00010000;
const uint32 SerializedValidation::sValidationMagic = 0x4c575200; // "LGR"
SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSignature)
: STObject(sValidationFormat, sit), mSignature(sit, "Signature")
: STObject(sValidationFormat, sit), mSignature(sit, "Signature"), mTrusted(false)
{
if (!isValid()) throw std::runtime_error("Invalid validation");
}
SerializedValidation::SerializedValidation(const uint256& ledgerHash, const NewcoinAddress& naSeed, bool isFull)
: STObject(sValidationFormat), mSignature("Signature")
: STObject(sValidationFormat), mSignature("Signature"), mTrusted(false)
{
setValueFieldH256(sfLedgerHash, ledgerHash);
setValueFieldVL(sfSigningKey, NewcoinAddress::createNodePublic(naSeed).getNodePublic());

View File

@@ -8,6 +8,9 @@ class SerializedValidation : public STObject
{
protected:
STVariableLength mSignature;
bool mTrusted;
void setNode();
public:
typedef boost::shared_ptr<SerializedValidation> pointer;
@@ -26,9 +29,11 @@ public:
NewcoinAddress getSignerPublic() const;
bool isValid() const;
bool isFull() const;
bool isTrusted() const { return mTrusted; }
CKey::pointer getSigningKey() const;
uint256 getSigningHash() const;
void setTrusted() { mTrusted = true; }
void addSigned(Serializer&) const;
void addSignature(Serializer&) const;
std::vector<unsigned char> getSigned() const;

View File

@@ -24,6 +24,10 @@ public:
Serializer(int n = 256) { mData.reserve(n); }
Serializer(const std::vector<unsigned char> &data) : mData(data) { ; }
Serializer(const std::string& data) : mData(data.data(), (data.data()) + data.size()) { ; }
Serializer(std::vector<unsigned char>::iterator begin, std::vector<unsigned char>::iterator end) :
mData(begin, end) { ; }
Serializer(std::vector<unsigned char>::const_iterator begin, std::vector<unsigned char>::const_iterator end) :
mData(begin, end) { ; }
// assemble functions
int add8(unsigned char byte);

View File

@@ -1532,7 +1532,7 @@ bool UniqueNodeList::nodeLoad(boost::filesystem::path pConfig)
return false;
}
nodeProcess("local", strValidators, pConfig.native());
nodeProcess("local", strValidators, pConfig.string());
std::cerr << str(boost::format("Processing: %s") % pConfig) << std::endl;

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