Add scoring for TrustedNodes.

This commit is contained in:
Arthur Britto
2012-04-17 19:44:00 -07:00
parent 5d7a2ed8e3
commit 24088dbe9b
2 changed files with 336 additions and 44 deletions

View File

@@ -12,8 +12,8 @@
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/mem_fn.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/mem_fn.hpp>
// Gather string constants. // Gather string constants.
#define SECTION_CURRENCIES "currencies" #define SECTION_CURRENCIES "currencies"
@@ -48,12 +48,10 @@ void UniqueNodeList::start()
bool UniqueNodeList::miscLoad() bool UniqueNodeList::miscLoad()
{ {
std::string strSql("SELECT * FROM Misc;");
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
Database *db=theApp->getWalletDB()->getDB(); Database *db=theApp->getWalletDB()->getDB();
if (!db->executeSQL(strSql.c_str())) return false; if (!db->executeSQL("SELECT * FROM Misc;")) return false;
bool bAvail = !!db->startIterRows(); bool bAvail = !!db->startIterRows();
@@ -67,21 +65,309 @@ bool UniqueNodeList::miscLoad()
bool UniqueNodeList::miscSave() bool UniqueNodeList::miscSave()
{ {
std::string strSql = str(boost::format("REPLACE INTO Misc (FetchUpdated,ScoreUpdated) VALUES (%d,%d);")
% iToSeconds(mtpFetchUpdated)
% iToSeconds(mtpScoreUpdated));
Database* db=theApp->getWalletDB()->getDB(); Database* db=theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(strSql.c_str()); db->executeSQL(str(boost::format("REPLACE INTO Misc (FetchUpdated,ScoreUpdated) VALUES (%d,%d);")
% iToSeconds(mtpFetchUpdated)
% iToSeconds(mtpScoreUpdated)));
return true; return true;
} }
// Returns true, iff scores were distributed.
bool UniqueNodeList::scoreRound(std::vector<scoreNode>& vsnNodes)
{
bool bDist = false;
// For each node, distribute roundSeed to roundScores.
BOOST_FOREACH(scoreNode& sn, vsnNodes) {
long iEntries = sn.viReferrals.size();
if (sn.iRoundSeed && iEntries)
{
long iTotal = (iEntries + 1) * iEntries / 2;
long iBase = sn.iRoundSeed * iEntries / iTotal;
// Distribute the current entires' seed score to validators prioritized by mention order.
for (int i=0; i != iEntries; i++) {
vsnNodes[sn.viReferrals[i]].iRoundScore +=
iBase * (iEntries - i) / iEntries;
}
}
}
std::cerr << "midway: " << std::endl;
BOOST_FOREACH(scoreNode& sn, vsnNodes)
{
std::cerr << str(boost::format("%s| %d, %d, %d: [%s]")
% sn.strValidator
% sn.iScore
% sn.iRoundScore
% sn.iRoundSeed
% strJoin(sn.viReferrals.begin(), sn.viReferrals.end(), ","))
<< std::endl;
}
// Add roundScore to score.
// Make roundScore new roundSeed.
BOOST_FOREACH(scoreNode& sn, vsnNodes) {
if (!bDist && sn.iRoundScore)
bDist = true;
sn.iScore += sn.iRoundScore;
sn.iRoundSeed = sn.iRoundScore;
sn.iRoundScore = 0;
}
std::cerr << "finish: " << std::endl;
BOOST_FOREACH(scoreNode& sn, vsnNodes)
{
std::cerr << str(boost::format("%s| %d, %d, %d: [%s]")
% sn.strValidator
% sn.iScore
% sn.iRoundScore
% sn.iRoundSeed
% strJoin(sn.viReferrals.begin(), sn.viReferrals.end(), ","))
<< std::endl;
}
return bDist;
}
void UniqueNodeList::scoreCompute() void UniqueNodeList::scoreCompute()
{ {
strIndex umPulicIdx; // Map of public key to index.
strIndex umDomainIdx; // Map of domain to index.
std::vector<scoreNode> vsnNodes;
Database* db=theApp->getWalletDB()->getDB();
std::string strSql;
{
ScopedLock sl(theApp->getWalletDB()->getDBLock());
if (db->executeSQL("SELECT Domain,PublicKey,Source FROM SeedDomains;"))
{
bool bMore = db->startIterRows();
while (bMore)
{
if (db->getNull("PublicKey"))
{
nothing(); // We ignore entrys we don't have public keys for.
}
else
{
std::string strDomain;
std::string strPublicKey;
std::string strSource;
db->getStr("Domain", strDomain);
db->getStr("PublicKey", strPublicKey);
db->getStr("Source", strSource);
int iNode = vsnNodes.size();
umPulicIdx[strPublicKey] = iNode;
umDomainIdx[strDomain] = iNode;
scoreNode snCurrent;
snCurrent.strValidator = strPublicKey;
snCurrent.iScore = iSourceScore(static_cast<validatorSource>(strSource[0]));
snCurrent.iRoundSeed = snCurrent.iScore;
snCurrent.iRoundScore = 0;
snCurrent.iSeen = -1;
vsnNodes.push_back(snCurrent);
}
bMore = db->getNextRow();
}
db->endIterRows();
}
}
BOOST_FOREACH(scoreNode& sn, vsnNodes)
{
std::cerr << str(boost::format("%s| %d, %d, %d")
% sn.strValidator
% sn.iScore
% sn.iRoundScore
% sn.iRoundSeed)
<< std::endl;
}
// XXX When we have a CAS do SeedNodes here.
// std::cerr << str(boost::format("vsnNodes.size=%d") % vsnNodes.size()) << std::endl;
// Step through growing list of nodes adding each validation list.
for (int iNode=0; iNode != vsnNodes.size(); iNode++)
{
scoreNode& sn = vsnNodes[iNode];
std::string& strValidator = sn.strValidator;
std::vector<int>& viReferrals = sn.viReferrals;
strSql = str(boost::format("SELECT Referral FROM ValidatorReferrals WHERE Validator=%s ORDER BY Entry;")
% db->escape(strValidator));
ScopedLock sl(theApp->getWalletDB()->getDBLock());
if (db->executeSQL(strSql))
{
bool bMore = db->startIterRows();
while (bMore)
{
std::string strReferral;
int iReferral;
db->getStr("Referral", strReferral);
strIndex::iterator itEntry;
NewcoinAddress na;
if (na.setNodePublic(strReferral))
{
// Referring a public key.
itEntry = umPulicIdx.find(strReferral);
if (itEntry == umPulicIdx.end())
{
// Not found add public key to list of nodes.
iReferral = vsnNodes.size();
umPulicIdx[strReferral] = iReferral;
scoreNode snCurrent;
snCurrent.strValidator = strReferral;
snCurrent.iScore = iSourceScore(vsReferral);
snCurrent.iRoundSeed = snCurrent.iScore;
snCurrent.iRoundScore = 0;
snCurrent.iSeen = -1;
vsnNodes.push_back(snCurrent);
}
else
{
iReferral = itEntry->second;
}
// std::cerr << str(boost::format("%s: Public=%s iReferral=%d") % strValidator % strReferral % iReferral) << std::endl;
}
else
{
// Referring a domain.
itEntry = umDomainIdx.find(strReferral);
iReferral = itEntry == umDomainIdx.end()
? -1 // We ignore domains we can't find entires for.
: itEntry->second;
// std::cerr << str(boost::format("%s: Domain=%s iReferral=%d") % strValidator % strReferral % iReferral) << std::endl;
}
if (iReferral >= 0 && iNode != iReferral)
viReferrals.push_back(iReferral);
bMore = db->getNextRow();
}
db->endIterRows();
}
}
bool bDist = true;
for (int i = SCORE_ROUNDS; bDist && i--;)
bDist = scoreRound(vsnNodes);
std::cerr << "Scored:" << std::endl;
BOOST_FOREACH(scoreNode& sn, vsnNodes)
{
std::cerr << str(boost::format("%s| %d, %d, %d: [%s]")
% sn.strValidator
% sn.iScore
% sn.iRoundScore
% sn.iRoundSeed
% strJoin(sn.viReferrals.begin(), sn.viReferrals.end(), ","))
<< std::endl;
}
ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL("BEGIN;");
db->executeSQL("UPDATE TrustedNodes SET Score = 0 WHERE Score != 0;");
if (vsnNodes.size())
{
// Merge existing Seens.
std::vector<std::string> vstrPublicKeys;
vstrPublicKeys.resize(vsnNodes.size());
for (int iNode=vsnNodes.size(); iNode--;)
{
vstrPublicKeys[iNode] = db->escape(vsnNodes[iNode].strValidator);
}
if (db->executeSQL(str(boost::format("SELECT PublicKey,Seen FROM TrustedNodes WHERE PublicKey IN (%s);")
% strJoin(vstrPublicKeys.begin(), vstrPublicKeys.end(), ","))))
{
bool bMore = db->startIterRows();
while (bMore)
{
std::string strPublicKey;
db->getStr("PublicKey", strPublicKey);
vsnNodes[umPulicIdx[strPublicKey]].iSeen = db->getNull("Seen") ? -1 : db->getInt("Seen");
bMore = db->getNextRow();
}
db->endIterRows();
}
}
if (vsnNodes.size())
{
// Update old PublicKeys and add new ones.
std::vector<std::string> vstrValues;
vstrValues.resize(vsnNodes.size());
for (int iNode=vsnNodes.size(); iNode--;)
{
scoreNode& sn = vsnNodes[iNode];
if (sn.iSeen >= 0)
{
vstrValues[iNode] = str(boost::format("(%s,%s,%s)")
% db->escape(sn.strValidator)
% sn.iScore
% sn.iSeen);
}
else
{
vstrValues[iNode] = str(boost::format("(%s,%s,NULL)")
% db->escape(sn.strValidator)
% sn.iScore);
}
}
db->executeSQL(str(boost::format("REPLACE INTO TrustedNodes (PublicKey,Score,Seen) VALUES %s;")
% strJoin(vstrValues.begin(), vstrValues.end(), ",")));
}
db->executeSQL("COMMIT;");
} }
// Begin scoring if timer was not cancelled. // Begin scoring if timer was not cancelled.
@@ -94,6 +380,8 @@ void UniqueNodeList::scoreTimerHandler(const boost::system::error_code& err)
std::cerr << "Scoring start." << std::endl; std::cerr << "Scoring start." << std::endl;
scoreCompute();
std::cerr << "Scoring end." << std::endl; std::cerr << "Scoring end." << std::endl;
// Save update time. // Save update time.
@@ -169,13 +457,15 @@ void UniqueNodeList::processValidators(const std::string& strSite, const std::st
% strEscNodePublic); % strEscNodePublic);
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(strSql.c_str()); db->executeSQL(strSql);
// XXX Check result. // XXX Check result.
} }
// Add new referral entries. // Add new referral entries.
if (pmtVecStrValidators && pmtVecStrValidators->size()) { if (pmtVecStrValidators && pmtVecStrValidators->size()) {
std::ostringstream ossValues; std::vector<std::string> vstrValues;
vstrValues.resize(MIN(pmtVecStrValidators->size(), REFERRAL_VALIDATORS_MAX));
int i = 0; int i = 0;
BOOST_FOREACH(std::string strReferral, *pmtVecStrValidators) BOOST_FOREACH(std::string strReferral, *pmtVecStrValidators)
@@ -183,9 +473,7 @@ void UniqueNodeList::processValidators(const std::string& strSite, const std::st
if (i == REFERRAL_VALIDATORS_MAX) if (i == REFERRAL_VALIDATORS_MAX)
break; break;
ossValues << vstrValues[i] = str(boost::format("(%s,%d,%s)") % strEscNodePublic % i % db->escape(strReferral));
str(boost::format("%s(%s,%d,%s)")
% ( i ? "," : "") % strEscNodePublic % i % db->escape(strReferral));
i++; i++;
NewcoinAddress naValidator; NewcoinAddress naValidator;
@@ -203,11 +491,11 @@ void UniqueNodeList::processValidators(const std::string& strSite, const std::st
} }
std::string strSql = str(boost::format("INSERT INTO ValidatorReferrals (Validator,Entry,Referral) VALUES %s;") std::string strSql = str(boost::format("INSERT INTO ValidatorReferrals (Validator,Entry,Referral) VALUES %s;")
% ossValues.str()); % strJoin(vstrValues.begin(), vstrValues.end(), ","));
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(strSql.c_str()); db->executeSQL(strSql);
// XXX Check result. // XXX Check result.
} }
@@ -495,7 +783,6 @@ void UniqueNodeList::fetchNext()
if (!bFull) if (!bFull)
{ {
// Determine next scan. // Determine next scan.
std::string strSql("SELECT Domain,Next FROM SeedDomains ORDER BY Next ASC LIMIT 1;");
std::string strDomain; std::string strDomain;
boost::posix_time::ptime tpNext; boost::posix_time::ptime tpNext;
boost::posix_time::ptime tpNow; boost::posix_time::ptime tpNow;
@@ -503,7 +790,8 @@ void UniqueNodeList::fetchNext()
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
Database *db=theApp->getWalletDB()->getDB(); Database *db=theApp->getWalletDB()->getDB();
if (db->executeSQL(strSql.c_str()) && db->startIterRows()) if (db->executeSQL("SELECT Domain,Next FROM SeedDomains ORDER BY Next LIMIT 1;")
&& db->startIterRows())
{ {
int iNext = db->getInt("Next"); int iNext = db->getInt("Next");
@@ -632,7 +920,7 @@ bool UniqueNodeList::getSeedDomains(const std::string& strDomain, seedDomain& ds
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
bResult = db->executeSQL(strSql.c_str()) && db->startIterRows(); bResult = db->executeSQL(strSql) && db->startIterRows();
if (bResult) if (bResult)
{ {
std::string strPublicKey; std::string strPublicKey;
@@ -662,7 +950,7 @@ bool UniqueNodeList::getSeedDomains(const std::string& strDomain, seedDomain& ds
iFetch = db->getInt("Fetch"); iFetch = db->getInt("Fetch");
dstSeedDomain.tpFetch = ptFromSeconds(iFetch); dstSeedDomain.tpFetch = ptFromSeconds(iFetch);
db->getStr("Sha256", strSha256); db->getStr("Sha256", strSha256);
dstSeedDomain.iSha256.SetHex(strSha256.c_str()); dstSeedDomain.iSha256.SetHex(strSha256);
db->getStr("Comment", dstSeedDomain.strComment); db->getStr("Comment", dstSeedDomain.strComment);
db->endIterRows(); db->endIterRows();
@@ -694,7 +982,7 @@ void UniqueNodeList::setSeedDomains(const seedDomain& sdSource, bool bNext)
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
if (!db->executeSQL(strSql.c_str())) if (!db->executeSQL(strSql))
{ {
// XXX Check result. // XXX Check result.
std::cerr << "setSeedDomains: failed." << std::endl; std::cerr << "setSeedDomains: failed." << std::endl;
@@ -710,54 +998,41 @@ void UniqueNodeList::setSeedDomains(const seedDomain& sdSource, bool bNext)
// XXX allow update of comment. // XXX allow update of comment.
void UniqueNodeList::nodeAddPublic(NewcoinAddress naNodePublic, std::string strComment) void UniqueNodeList::nodeAddPublic(NewcoinAddress naNodePublic, std::string strComment)
{ {
Database* db=theApp->getWalletDB()->getDB();
std::string strPublicKey = naNodePublic.humanNodePublic(); std::string strPublicKey = naNodePublic.humanNodePublic();
std::string strTmp;
std::string strSql="INSERT INTO TrustedNodes (PublicKey,Comment) values (";
strSql.append(db->escape(strPublicKey));
strSql.append(",");
strSql.append(db->escape(strComment));
strSql.append(")");
Database* db=theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(strSql.c_str());
db->executeSQL(str(boost::format("INSERT INTO TrustedNodes (PublicKey,Comment) values (%s,%s);")
% db->escape(strPublicKey) % db->escape(strComment)));
} }
void UniqueNodeList::nodeRemove(NewcoinAddress naNodePublic) void UniqueNodeList::nodeRemove(NewcoinAddress naNodePublic)
{ {
Database* db=theApp->getWalletDB()->getDB();
std::string strPublic = naNodePublic.humanNodePublic(); std::string strPublic = naNodePublic.humanNodePublic();
std::string strTmp;
std::string strSql = "DELETE FROM TrustedNodes where PublicKey=";
db->escape(reinterpret_cast<const unsigned char*>(strPublic.c_str()), strPublic.size(), strTmp);
strSql.append(strTmp);
Database* db=theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(strSql.c_str());
db->executeSQL(str(boost::format("DELETE FROM TrustedNodes where PublicKey=%s") % db->escape(strPublic)));
} }
void UniqueNodeList::nodeReset() void UniqueNodeList::nodeReset()
{ {
Database* db=theApp->getWalletDB()->getDB(); Database* db=theApp->getWalletDB()->getDB();
std::string strSql = "DELETE FROM TrustedNodes";
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(strSql.c_str()); db->executeSQL("DELETE FROM TrustedNodes");
} }
Json::Value UniqueNodeList::getUnlJson() Json::Value UniqueNodeList::getUnlJson()
{ {
Database* db=theApp->getWalletDB()->getDB(); Database* db=theApp->getWalletDB()->getDB();
std::string strSql="SELECT * FROM TrustedNodes;";
Json::Value ret(Json::arrayValue); Json::Value ret(Json::arrayValue);
ScopedLock sl(theApp->getWalletDB()->getDBLock()); ScopedLock sl(theApp->getWalletDB()->getDBLock());
if (db->executeSQL(strSql.c_str())) if (db->executeSQL("SELECT * FROM TrustedNodes;"))
{ {
bool more = db->startIterRows(); bool more = db->startIterRows();
while (more) while (more)

View File

@@ -11,6 +11,7 @@
#include "ParseSection.h" #include "ParseSection.h"
#include <boost/thread/mutex.hpp> #include <boost/thread/mutex.hpp>
#include <boost/unordered_map.hpp>
#define SYSTEM_NAME "newcoin" #define SYSTEM_NAME "newcoin"
@@ -22,7 +23,11 @@
#define NODE_FILE_PATH "/" NODE_FILE_NAME #define NODE_FILE_PATH "/" NODE_FILE_NAME
// Wait for validation information to be stable before scoring. // Wait for validation information to be stable before scoring.
#define SCORE_DELAY_SECONDS 20 // #define SCORE_DELAY_SECONDS 20
#define SCORE_DELAY_SECONDS 5
// Don't bother propagating past this number of rounds.
#define SCORE_ROUNDS 10
class UniqueNodeList class UniqueNodeList
{ {
@@ -53,6 +58,18 @@ private:
std::string strComment; std::string strComment;
} seedDomain; } seedDomain;
typedef struct {
int iScore;
int iRoundScore;
int iRoundSeed;
int iSeen;
std::string strValidator; // The public key.
std::vector<int> viReferrals;
} scoreNode;
typedef boost::unordered_map<std::string,int> strIndex;
bool scoreRound(std::vector<scoreNode>& vsnNodes);
int iSourceScore(validatorSource vsWhy); int iSourceScore(validatorSource vsWhy);
void responseFetch(const std::string strDomain, const boost::system::error_code& err, const std::string strSiteFile); void responseFetch(const std::string strDomain, const boost::system::error_code& err, const std::string strSiteFile);