diff --git a/Builds/VisualStudio2012/RippleD.vcxproj b/Builds/VisualStudio2012/RippleD.vcxproj
index 5a6074141..85eb041d6 100644
--- a/Builds/VisualStudio2012/RippleD.vcxproj
+++ b/Builds/VisualStudio2012/RippleD.vcxproj
@@ -1445,7 +1445,7 @@
-
+
diff --git a/Builds/VisualStudio2012/RippleD.vcxproj.filters b/Builds/VisualStudio2012/RippleD.vcxproj.filters
index 894e9545a..e595901f8 100644
--- a/Builds/VisualStudio2012/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2012/RippleD.vcxproj.filters
@@ -97,9 +97,6 @@
{223ac5ce-c9a0-4196-9b75-4f5fbe8bfa00}
-
- {82a6d3f3-fa01-4cc6-9730-f928e61b9929}
-
{46bda3c0-166b-4188-8f6b-da6f6ff638fc}
@@ -145,6 +142,9 @@
{febf2e7e-f071-4a6c-9b81-68498fc8ea57}
+
+ {82a6d3f3-fa01-4cc6-9730-f928e61b9929}
+
@@ -535,10 +535,10 @@
[1] Ripple\ripple_app\_contracts
- [1] Ripple\ripple_app\_consensus
+ [1] Ripple\ripple_app\_misc\_consensus
- [1] Ripple\ripple_app\_consensus
+ [1] Ripple\ripple_app\_misc\_consensus
[1] Ripple\ripple_app\_data
@@ -1285,10 +1285,10 @@
[1] Ripple\ripple_app\_contracts
- [1] Ripple\ripple_app\_consensus
+ [1] Ripple\ripple_app\_misc\_consensus
- [1] Ripple\ripple_app\_consensus
+ [1] Ripple\ripple_app\_misc\_consensus
[1] Ripple\ripple_app\_data
@@ -1416,9 +1416,6 @@
[1] Ripple\ripple_app\_peers
-
- [1] Ripple\ripple_app\_peers
-
[1] Ripple\ripple_app\_peers
@@ -1584,6 +1581,9 @@
[1] Ripple\ripple_app\_network
+
+ [1] Ripple\ripple_app\_peers
+
diff --git a/TODO.txt b/TODO.txt
index 609a60feb..a0590091a 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -2,6 +2,9 @@
RIPPLE TODO
--------------------------------------------------------------------------------
+- Roll a simple wrapper for sqlite relational stuff like loading the UNL
+ Completely hide the specifics of SQLite and/or beast::db
+
- Tidy up convenience functions in RPC.h
- Maybe rename RPCServer to RPCClientServicer
diff --git a/modules/ripple_app/ripple_app.cpp b/modules/ripple_app/ripple_app.cpp
index 084759c1b..99a02391f 100644
--- a/modules/ripple_app/ripple_app.cpp
+++ b/modules/ripple_app/ripple_app.cpp
@@ -119,7 +119,7 @@ namespace ripple
#include "src/cpp/ripple/ripple_Peer.h" // VFALCO TODO Rename to IPeer
#include "src/cpp/ripple/ripple_IPeers.h"
#include "src/cpp/ripple/ripple_IProofOfWorkFactory.h"
-#include "src/cpp/ripple/ripple_IUniqueNodeList.h"
+#include "src/cpp/ripple/ripple_UniqueNodeList.h"
#include "src/cpp/ripple/ripple_IValidations.h"
#include "src/cpp/ripple/ripple_PeerSet.h"
#include "src/cpp/ripple/ripple_InboundLedger.h"
diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp
index 822ebc456..4900ea564 100644
--- a/src/cpp/ripple/RPCHandler.cpp
+++ b/src/cpp/ripple/RPCHandler.cpp
@@ -2492,13 +2492,13 @@ Json::Value RPCHandler::doUnlAdd (Json::Value params, LoadType* loadType, Scoped
if (raNodePublic.setNodePublic (strNode))
{
- getApp().getUNL ().nodeAddPublic (raNodePublic, IUniqueNodeList::vsManual, strComment);
+ getApp().getUNL ().nodeAddPublic (raNodePublic, UniqueNodeList::vsManual, strComment);
return "adding node by public key";
}
else
{
- getApp().getUNL ().nodeAddDomain (strNode, IUniqueNodeList::vsManual, strComment);
+ getApp().getUNL ().nodeAddDomain (strNode, UniqueNodeList::vsManual, strComment);
return "adding node by domain";
}
diff --git a/src/cpp/ripple/ripple_Application.cpp b/src/cpp/ripple/ripple_Application.cpp
index 8203cd1af..3af4299f6 100644
--- a/src/cpp/ripple/ripple_Application.cpp
+++ b/src/cpp/ripple/ripple_Application.cpp
@@ -128,7 +128,7 @@ public:
return *mValidations;
}
- IUniqueNodeList& getUNL ()
+ UniqueNodeList& getUNL ()
{
return *mUNL;
}
@@ -236,7 +236,7 @@ private:
beast::ScopedPointer mFeeTrack;
beast::ScopedPointer mHashRouter;
beast::ScopedPointer mValidations;
- beast::ScopedPointer mUNL;
+ beast::ScopedPointer mUNL;
beast::ScopedPointer mProofOfWorkFactory;
beast::ScopedPointer mPeers;
beast::ScopedPointer m_loadManager;
@@ -294,7 +294,7 @@ Application::Application ()
, mFeeTrack (ILoadFeeTrack::New ())
, mHashRouter (IHashRouter::New (IHashRouter::getDefaultHoldTime ()))
, mValidations (IValidations::New ())
- , mUNL (IUniqueNodeList::New (mIOService))
+ , mUNL (UniqueNodeList::New ())
, mProofOfWorkFactory (IProofOfWorkFactory::New ())
, mPeers (IPeers::New (mIOService))
, m_loadManager (ILoadManager::New ())
diff --git a/src/cpp/ripple/ripple_IApplication.h b/src/cpp/ripple/ripple_IApplication.h
index b8fb72272..d75d430b1 100644
--- a/src/cpp/ripple/ripple_IApplication.h
+++ b/src/cpp/ripple/ripple_IApplication.h
@@ -14,7 +14,7 @@ class IHashRouter;
class ILoadFeeTrack;
class IPeers;
class IProofOfWorkFactory;
-class IUniqueNodeList;
+class UniqueNodeList;
class IValidations;
class HashedObjectStore;
@@ -65,7 +65,7 @@ public:
virtual ILoadManager& getLoadManager () = 0;
virtual IPeers& getPeers () = 0;
virtual IProofOfWorkFactory& getProofOfWorkFactory () = 0;
- virtual IUniqueNodeList& getUNL () = 0;
+ virtual UniqueNodeList& getUNL () = 0;
virtual IValidations& getValidations () = 0;
virtual HashedObjectStore& getHashedObjectStore () = 0;
@@ -82,6 +82,12 @@ public:
virtual DatabaseCon* getRpcDB () = 0;
virtual DatabaseCon* getTxnDB () = 0;
virtual DatabaseCon* getLedgerDB () = 0;
+
+ /** Retrieve the "wallet database"
+
+ It looks like this is used to store the unique node list.
+ */
+ // VFALCO TODO Rename, document this
virtual DatabaseCon* getWalletDB () = 0;
// VFALCO NOTE It looks like this isn't used...
//virtual DatabaseCon* getNetNodeDB () = 0;
diff --git a/src/cpp/ripple/ripple_Peer.cpp b/src/cpp/ripple/ripple_Peer.cpp
index a1538a8d6..ded0d0f4f 100644
--- a/src/cpp/ripple/ripple_Peer.cpp
+++ b/src/cpp/ripple/ripple_Peer.cpp
@@ -1021,7 +1021,7 @@ void PeerImp::recvHello (protocol::TMHello& packet)
// Don't save IP address if the node wants privacy.
// Note: We don't go so far as to delete it. If a node which has previously announced itself now wants
// privacy, it should at least change its port.
- getApp().getPeers ().savePeer (strIP, iPort, IUniqueNodeList::vsInbound);
+ getApp().getPeers ().savePeer (strIP, iPort, UniqueNodeList::vsInbound);
}
}
@@ -1468,7 +1468,7 @@ void PeerImp::recvPeers (protocol::TMPeers& packet)
{
//WriteLog (lsINFO, Peer) << "Peer: Learning: " << addressToString(this) << ": " << i << ": " << strIP << " " << iPort;
- getApp().getPeers ().savePeer (strIP, iPort, IUniqueNodeList::vsTold);
+ getApp().getPeers ().savePeer (strIP, iPort, UniqueNodeList::vsTold);
}
}
}
diff --git a/src/cpp/ripple/ripple_Peers.cpp b/src/cpp/ripple/ripple_Peers.cpp
index 72e02688d..ade55fbc8 100644
--- a/src/cpp/ripple/ripple_Peers.cpp
+++ b/src/cpp/ripple/ripple_Peers.cpp
@@ -401,8 +401,8 @@ void Peers::connectTo (const std::string& strIp, int iPort)
db->executeSQL (str (boost::format ("REPLACE INTO PeerIps (IpPort,Score,Source,ScanNext) values (%s,%d,'%c',0);")
% sqlEscape (str (boost::format ("%s %d") % strIp % iPort))
- % getApp().getUNL ().iSourceScore (IUniqueNodeList::vsManual)
- % char (IUniqueNodeList::vsManual)));
+ % getApp().getUNL ().iSourceScore (UniqueNodeList::vsManual)
+ % char (UniqueNodeList::vsManual)));
}
scanRefresh ();
diff --git a/src/cpp/ripple/ripple_UniqueNodeList.cpp b/src/cpp/ripple/ripple_UniqueNodeList.cpp
index 354cc4f29..f5cb0342e 100644
--- a/src/cpp/ripple/ripple_UniqueNodeList.cpp
+++ b/src/cpp/ripple/ripple_UniqueNodeList.cpp
@@ -21,39 +21,32 @@
// Don't bother propagating past this number of rounds.
#define SCORE_ROUNDS 10
+// VFALCO TODO Replace macros with language constructs
+#define VALIDATORS_FETCH_SECONDS 30
+#define VALIDATORS_FILE_BYTES_MAX (50 << 10)
+
+// Gather string constants.
+#define SECTION_CURRENCIES "currencies"
+#define SECTION_DOMAIN "domain"
+#define SECTION_IPS "ips"
+#define SECTION_IPS_URL "ips_url"
+#define SECTION_PUBLIC_KEY "validation_public_key"
+#define SECTION_VALIDATORS "validators"
+#define SECTION_VALIDATORS_URL "validators_url"
+
+// Limit pollution of database.
+// YYY Move to config file.
+#define REFERRAL_VALIDATORS_MAX 50
+#define REFERRAL_IPS_MAX 50
+
+SETUP_LOG (UniqueNodeList)
+
// VFALCO TODO move all function definitions inlined into the class.
-class UniqueNodeList : public IUniqueNodeList
+class UniqueNodeListImp
+ : public UniqueNodeList
+ , public DeadlineTimer::Listener
{
-public:
- UniqueNodeList (boost::asio::io_service& io_service);
-
- // Begin processing.
- void start ();
-
- void nodeAddPublic (const RippleAddress& naNodePublic, ValidatorSource vsWhy, const std::string& strComment);
- void nodeAddDomain (std::string strDomain, ValidatorSource vsWhy, const std::string& strComment = "");
- void nodeRemovePublic (const RippleAddress& naNodePublic);
- void nodeRemoveDomain (std::string strDomain);
- void nodeReset ();
-
- void nodeScore ();
-
- bool nodeInUNL (const RippleAddress& naNodePublic);
- bool nodeInCluster (const RippleAddress& naNodePublic);
- bool nodeInCluster (const RippleAddress& naNodePublic, std::string& name);
-
- void nodeBootstrap ();
- bool nodeLoad (boost::filesystem::path pConfig);
- void nodeNetwork ();
-
- Json::Value getUnlJson ();
-
- int iSourceScore (ValidatorSource vsWhy);
-
private:
- bool miscLoad ();
- bool miscSave ();
-
// VFALCO TODO Rename these structs? Are they objects with static storage?
// This looks like C and not C++...
//
@@ -95,260 +88,683 @@ private:
typedef std::pair IPAndPortNumber;
typedef boost::unordered_map, score> epScore;
- void trustedLoad ();
-
- bool scoreRound (std::vector& vsnNodes);
-
- bool responseFetch (const std::string& strDomain, const boost::system::error_code& err, int iStatus, const std::string& strSiteFile);
-
- void scoreNext (bool bNow); // Update scoring timer.
- void scoreCompute ();
- void scoreTimerHandler (const boost::system::error_code& err);
-
- void fetchNext ();
- void fetchDirty ();
- void fetchFinish ();
- void fetchProcess (std::string strDomain);
- void fetchTimerHandler (const boost::system::error_code& err);
-
- void getValidatorsUrl (const RippleAddress& naNodePublic, Section secSite);
- void getIpsUrl (const RippleAddress& naNodePublic, Section secSite);
- bool responseIps (const std::string& strSite, const RippleAddress& naNodePublic, const boost::system::error_code& err, int iStatus, const std::string& strIpsFile);
- bool responseValidators (const std::string& strValidatorsUrl, const RippleAddress& naNodePublic, Section secSite, const std::string& strSite, const boost::system::error_code& err, int iStatus, const std::string& strValidatorsFile);
-
- void processIps (const std::string& strSite, const RippleAddress& naNodePublic, Section::mapped_type* pmtVecStrIps);
- int processValidators (const std::string& strSite, const std::string& strValidatorsSrc, const RippleAddress& naNodePublic, ValidatorSource vsWhy, Section::mapped_type* pmtVecStrValidators);
-
- void processFile (const std::string& strDomain, const RippleAddress& naNodePublic, Section secSite);
-
- bool getSeedDomains (const std::string& strDomain, seedDomain& dstSeedDomain);
- void setSeedDomains (const seedDomain& dstSeedDomain, bool bNext);
-
- bool getSeedNodes (const RippleAddress& naNodePublic, seedNode& dstSeedNode);
- void setSeedNodes (const seedNode& snSource, bool bNext);
-
- bool validatorsResponse (const boost::system::error_code& err, int iStatus, const std::string strResponse);
- void nodeProcess (const std::string& strSite, const std::string& strValidators, const std::string& strSource);
-
-private:
- // Misc persistent information
- boost::posix_time::ptime mtpScoreUpdated;
- boost::posix_time::ptime mtpFetchUpdated;
-
- boost::recursive_mutex mUNLLock;
- // XXX Make this faster, make this the contents vector unsigned char or raw public key.
- // XXX Contents needs to based on score.
- boost::unordered_set mUNL;
-
- boost::posix_time::ptime mtpScoreNext; // When to start scoring.
- boost::posix_time::ptime mtpScoreStart; // Time currently started scoring.
- boost::asio::deadline_timer mdtScoreTimer; // Timer to start scoring.
-
- boost::mutex mFetchLock;
- int mFetchActive; // Count of active fetches.
-
- boost::posix_time::ptime mtpFetchNext; // Time of to start next fetch.
- boost::asio::deadline_timer mdtFetchTimer; // Timer to start fetching.
-
- std::map sClusterNodes;
-};
-
-// VFALCO TODO Replace macros with language constructs
-#define VALIDATORS_FETCH_SECONDS 30
-#define VALIDATORS_FILE_BYTES_MAX (50 << 10)
-
-// Gather string constants.
-#define SECTION_CURRENCIES "currencies"
-#define SECTION_DOMAIN "domain"
-#define SECTION_IPS "ips"
-#define SECTION_IPS_URL "ips_url"
-#define SECTION_PUBLIC_KEY "validation_public_key"
-#define SECTION_VALIDATORS "validators"
-#define SECTION_VALIDATORS_URL "validators_url"
-
-// Limit pollution of database.
-// YYY Move to config file.
-#define REFERRAL_VALIDATORS_MAX 50
-#define REFERRAL_IPS_MAX 50
-
-SETUP_LOG (UniqueNodeList)
-
-UniqueNodeList::UniqueNodeList (boost::asio::io_service& io_service) :
- mdtScoreTimer (io_service),
- mFetchActive (0),
- mdtFetchTimer (io_service)
-{
-}
-
-// This is called when the application is started.
-// Get update times and start fetching and scoring as needed.
-void UniqueNodeList::start ()
-{
- miscLoad ();
-
- WriteLog (lsDEBUG, UniqueNodeList) << "Validator fetch updated: " << mtpFetchUpdated;
- WriteLog (lsDEBUG, UniqueNodeList) << "Validator score updated: " << mtpScoreUpdated;
-
- fetchNext (); // Start fetching.
- scoreNext (false); // Start scoring.
-}
-
-// Load information about when we last updated.
-bool UniqueNodeList::miscLoad ()
-{
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
- Database* db = getApp().getWalletDB ()->getDB ();
-
- if (!db->executeSQL ("SELECT * FROM Misc WHERE Magic=1;")) return false;
-
- bool bAvail = !!db->startIterRows ();
-
- mtpFetchUpdated = ptFromSeconds (bAvail ? db->getInt ("FetchUpdated") : -1);
- mtpScoreUpdated = ptFromSeconds (bAvail ? db->getInt ("ScoreUpdated") : -1);
-
- db->endIterRows ();
-
- trustedLoad ();
-
- return true;
-}
-
-// Persist update information.
-bool UniqueNodeList::miscSave ()
-{
- Database* db = getApp().getWalletDB ()->getDB ();
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- db->executeSQL (str (boost::format ("REPLACE INTO Misc (Magic,FetchUpdated,ScoreUpdated) VALUES (1,%d,%d);")
- % iToSeconds (mtpFetchUpdated)
- % iToSeconds (mtpScoreUpdated)));
-
- return true;
-}
-
-void UniqueNodeList::trustedLoad ()
-{
- boost::regex rNode ("\\`\\s*(\\S+)[\\s]*(.*)\\'");
- BOOST_FOREACH (const std::string & c, theConfig.CLUSTER_NODES)
+public:
+ UniqueNodeListImp ()
+ : m_scoreTimer (this)
+ , m_fetchTimer (this)
+ , mFetchActive (0)
{
- boost::smatch match;
+ }
- if (boost::regex_match (c, match, rNode))
+ //--------------------------------------------------------------------------
+
+ void onDeadlineTimer (DeadlineTimer& timer)
+ {
+ if (timer == m_scoreTimer)
{
- RippleAddress a = RippleAddress::createNodePublic (match[1]);
+ mtpScoreNext = boost::posix_time::ptime (boost::posix_time::not_a_date_time); // Timer not set.
+ mtpScoreStart = boost::posix_time::second_clock::universal_time (); // Scoring.
- if (a.isValid ())
- sClusterNodes.insert (std::make_pair (a, match[2]));
+ WriteLog (lsTRACE, UniqueNodeList) << "Scoring: Start";
+
+ scoreCompute ();
+
+ WriteLog (lsTRACE, UniqueNodeList) << "Scoring: End";
+
+ // Save update time.
+ mtpScoreUpdated = mtpScoreStart;
+ miscSave ();
+
+ mtpScoreStart = boost::posix_time::ptime (boost::posix_time::not_a_date_time); // Not scoring.
+
+ // Score again if needed.
+ scoreNext (false);
+
+ // Scan may be dirty due to new ips.
+ getApp().getPeers ().scanRefresh ();
}
- else
- WriteLog (lsWARNING, UniqueNodeList) << "Entry in cluster list invalid: '" << c << "'";
- }
-
- Database* db = getApp().getWalletDB ()->getDB ();
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
- boost::recursive_mutex::scoped_lock slUNL (mUNLLock);
-
- mUNL.clear ();
-
- // XXX Needs to limit by quanity and quality.
- SQL_FOREACH (db, "SELECT PublicKey FROM TrustedNodes WHERE Score != 0;")
- {
- mUNL.insert (db->getStrBinary ("PublicKey"));
- }
-}
-
-// For a round of scoring we destribute points from a node to nodes it refers to.
-// Returns true, iff scores were distributed.
-bool UniqueNodeList::scoreRound (std::vector& vsnNodes)
-{
- bool bDist = false;
-
- // For each node, distribute roundSeed to roundScores.
- BOOST_FOREACH (scoreNode & sn, vsnNodes)
- {
- int iEntries = sn.viReferrals.size ();
-
- if (sn.iRoundSeed && iEntries)
+ else if (timer == m_fetchTimer)
{
- score iTotal = (iEntries + 1) * iEntries / 2;
- score iBase = sn.iRoundSeed * iEntries / iTotal;
+ // Time to check for another fetch.
+ WriteLog (lsTRACE, UniqueNodeList) << "fetchTimerHandler";
+ fetchNext ();
+ }
+ }
- // Distribute the current entires' seed score to validators prioritized by mention order.
- for (int i = 0; i != iEntries; i++)
+ //--------------------------------------------------------------------------
+
+ // This is called when the application is started.
+ // Get update times and start fetching and scoring as needed.
+ void UniqueNodeListImp::start ()
+ {
+ miscLoad ();
+
+ WriteLog (lsDEBUG, UniqueNodeList) << "Validator fetch updated: " << mtpFetchUpdated;
+ WriteLog (lsDEBUG, UniqueNodeList) << "Validator score updated: " << mtpScoreUpdated;
+
+ fetchNext (); // Start fetching.
+ scoreNext (false); // Start scoring.
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Add a trusted node. Called by RPC or other source.
+ void nodeAddPublic (const RippleAddress& naNodePublic, ValidatorSource vsWhy, const std::string& strComment)
+ {
+ seedNode snCurrent;
+
+ bool bFound = getSeedNodes (naNodePublic, snCurrent);
+ bool bChanged = false;
+
+ if (!bFound)
+ {
+ snCurrent.naPublicKey = naNodePublic;
+ snCurrent.tpNext = boost::posix_time::second_clock::universal_time ();
+ }
+
+ // Promote source, if needed.
+ if (!bFound || iSourceScore (vsWhy) >= iSourceScore (snCurrent.vsSource))
+ {
+ snCurrent.vsSource = vsWhy;
+ snCurrent.strComment = strComment;
+ bChanged = true;
+ }
+
+ if (vsManual == vsWhy)
+ {
+ // A manual add forces immediate scan.
+ snCurrent.tpNext = boost::posix_time::second_clock::universal_time ();
+ bChanged = true;
+ }
+
+ if (bChanged)
+ setSeedNodes (snCurrent, true);
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Queue a domain for a single attempt fetch a ripple.txt.
+ // --> strComment: only used on vsManual
+ // YYY As a lot of these may happen at once, would be nice to wrap multiple calls in a transaction.
+ void nodeAddDomain (std::string strDomain, ValidatorSource vsWhy, const std::string& strComment)
+ {
+ boost::trim (strDomain);
+ boost::to_lower (strDomain);
+
+ // YYY Would be best to verify strDomain is a valid domain.
+ // WriteLog (lsTRACE) << str(boost::format("nodeAddDomain: '%s' %c '%s'")
+ // % strDomain
+ // % vsWhy
+ // % strComment);
+
+ seedDomain sdCurrent;
+
+ bool bFound = getSeedDomains (strDomain, sdCurrent);
+ bool bChanged = false;
+
+ if (!bFound)
+ {
+ sdCurrent.strDomain = strDomain;
+ sdCurrent.tpNext = boost::posix_time::second_clock::universal_time ();
+ }
+
+ // Promote source, if needed.
+ if (!bFound || iSourceScore (vsWhy) >= iSourceScore (sdCurrent.vsSource))
+ {
+ sdCurrent.vsSource = vsWhy;
+ sdCurrent.strComment = strComment;
+ bChanged = true;
+ }
+
+ if (vsManual == vsWhy)
+ {
+ // A manual add forces immediate scan.
+ sdCurrent.tpNext = boost::posix_time::second_clock::universal_time ();
+ bChanged = true;
+ }
+
+ if (bChanged)
+ setSeedDomains (sdCurrent, true);
+ }
+
+ //--------------------------------------------------------------------------
+
+ void nodeRemovePublic (const RippleAddress& naNodePublic)
+ {
+ {
+ Database* db = getApp().getWalletDB ()->getDB ();
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ db->executeSQL (str (boost::format ("DELETE FROM SeedNodes WHERE PublicKey=%s") % sqlEscape (naNodePublic.humanNodePublic ())));
+ db->executeSQL (str (boost::format ("DELETE FROM TrustedNodes WHERE PublicKey=%s") % sqlEscape (naNodePublic.humanNodePublic ())));
+ }
+
+ // YYY Only dirty on successful delete.
+ fetchDirty ();
+
+ boost::recursive_mutex::scoped_lock sl (mUNLLock);
+ mUNL.erase (naNodePublic.humanNodePublic ());
+ }
+
+ //--------------------------------------------------------------------------
+
+ void nodeRemoveDomain (std::string strDomain)
+ {
+ boost::trim (strDomain);
+ boost::to_lower (strDomain);
+
+ {
+ Database* db = getApp().getWalletDB ()->getDB ();
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ db->executeSQL (str (boost::format ("DELETE FROM SeedDomains WHERE Domain=%s") % sqlEscape (strDomain)));
+ }
+
+ // YYY Only dirty on successful delete.
+ fetchDirty ();
+ }
+
+ //--------------------------------------------------------------------------
+
+ void nodeReset ()
+ {
+ {
+ Database* db = getApp().getWalletDB ()->getDB ();
+
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ // XXX Check results.
+ db->executeSQL ("DELETE FROM SeedDomains");
+ db->executeSQL ("DELETE FROM SeedNodes");
+ }
+
+ fetchDirty ();
+ }
+
+ //--------------------------------------------------------------------------
+
+ // For debugging, schedule forced scoring.
+ void nodeScore ()
+ {
+ scoreNext (true);
+ }
+
+ //--------------------------------------------------------------------------
+
+ bool nodeInUNL (const RippleAddress& naNodePublic)
+ {
+ boost::recursive_mutex::scoped_lock sl (mUNLLock);
+
+ return mUNL.end () != mUNL.find (naNodePublic.humanNodePublic ());
+ }
+
+ //--------------------------------------------------------------------------
+
+ bool nodeInCluster (const RippleAddress& naNodePublic)
+ {
+ boost::recursive_mutex::scoped_lock sl (mUNLLock);
+ return m_clusterNodes.end () != m_clusterNodes.find (naNodePublic);
+ }
+
+ //--------------------------------------------------------------------------
+
+ bool nodeInCluster (const RippleAddress& naNodePublic, std::string& name)
+ {
+ boost::recursive_mutex::scoped_lock sl (mUNLLock);
+ std::map::iterator it = m_clusterNodes.find (naNodePublic);
+
+ if (it == m_clusterNodes.end ())
+ return false;
+
+ name = it->second;
+ return true;
+ }
+
+ //--------------------------------------------------------------------------
+
+ void nodeBootstrap ()
+ {
+ int iDomains = 0;
+ int iNodes = 0;
+ Database* db = getApp().getWalletDB ()->getDB ();
+
+ {
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ if (db->executeSQL (str (boost::format ("SELECT COUNT(*) AS Count FROM SeedDomains WHERE Source='%s' OR Source='%c';") % vsManual % vsValidator)) && db->startIterRows ())
+ iDomains = db->getInt ("Count");
+
+ db->endIterRows ();
+
+ if (db->executeSQL (str (boost::format ("SELECT COUNT(*) AS Count FROM SeedNodes WHERE Source='%s' OR Source='%c';") % vsManual % vsValidator)) && db->startIterRows ())
+ iNodes = db->getInt ("Count");
+
+ db->endIterRows ();
+ }
+
+ bool bLoaded = iDomains || iNodes;
+
+ // Always merge in the file specified in the config.
+ if (!theConfig.VALIDATORS_FILE.empty ())
+ {
+ WriteLog (lsINFO, UniqueNodeList) << "Bootstrapping UNL: loading from unl_default.";
+
+ bLoaded = nodeLoad (theConfig.VALIDATORS_FILE);
+ }
+
+ // If never loaded anything try the current directory.
+ if (!bLoaded && theConfig.VALIDATORS_FILE.empty ())
+ {
+ WriteLog (lsINFO, UniqueNodeList) << boost::str (boost::format ("Bootstrapping UNL: loading from '%s'.")
+ % theConfig.VALIDATORS_BASE);
+
+ bLoaded = nodeLoad (theConfig.VALIDATORS_BASE);
+ }
+
+ // Always load from rippled.cfg
+ if (!theConfig.VALIDATORS.empty ())
+ {
+ RippleAddress naInvalid; // Don't want a referrer on added entries.
+
+ WriteLog (lsINFO, UniqueNodeList) << boost::str (boost::format ("Bootstrapping UNL: loading from '%s'.")
+ % theConfig.CONFIG_FILE);
+
+ if (processValidators ("local", theConfig.CONFIG_FILE.string (), naInvalid, vsConfig, &theConfig.VALIDATORS))
+ bLoaded = true;
+ }
+
+ if (!bLoaded)
+ {
+ WriteLog (lsINFO, UniqueNodeList) << boost::str (boost::format ("Bootstrapping UNL: loading from '%s'.")
+ % theConfig.VALIDATORS_SITE);
+
+ nodeNetwork ();
+ }
+
+ if (!theConfig.IPS.empty ())
+ {
+ std::vector vstrValues;
+
+ vstrValues.reserve (theConfig.IPS.size ());
+
+ BOOST_FOREACH (const std::string & strPeer, theConfig.IPS)
{
- score iPoints = iBase * (iEntries - i) / iEntries;
+ std::string strIP;
+ int iPort;
- vsnNodes[sn.viReferrals[i]].iRoundScore += iPoints;
+ if (parseIpPort (strPeer, strIP, iPort))
+ {
+ vstrValues.push_back (str (boost::format ("(%s,'%c')")
+ % sqlEscape (str (boost::format ("%s %d") % strIP % iPort))
+ % static_cast (vsConfig)));
+ }
}
+
+ if (!vstrValues.empty ())
+ {
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ db->executeSQL (str (boost::format ("REPLACE INTO PeerIps (IpPort,Source) VALUES %s;")
+ % strJoin (vstrValues.begin (), vstrValues.end (), ",")));
+ }
+
+ fetchDirty ();
}
}
- if (ShouldLog (lsTRACE, UniqueNodeList))
+ //--------------------------------------------------------------------------
+
+ bool UniqueNodeListImp::nodeLoad (boost::filesystem::path pConfig)
{
- WriteLog (lsTRACE, UniqueNodeList) << "midway: ";
- BOOST_FOREACH (scoreNode & sn, vsnNodes)
+ if (pConfig.empty ())
{
- WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("%s| %d, %d, %d: [%s]")
- % sn.strValidator
- % sn.iScore
- % sn.iRoundScore
- % sn.iRoundSeed
- % strJoin (sn.viReferrals.begin (), sn.viReferrals.end (), ","));
+ WriteLog (lsINFO, UniqueNodeList) << VALIDATORS_FILE_NAME " path not specified.";
+
+ return false;
}
- }
- // 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;
- }
-
- if (ShouldLog (lsTRACE, UniqueNodeList))
- {
- WriteLog (lsTRACE, UniqueNodeList) << "finish: ";
- BOOST_FOREACH (scoreNode & sn, vsnNodes)
+ if (!boost::filesystem::exists (pConfig))
{
- WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("%s| %d, %d, %d: [%s]")
- % sn.strValidator
- % sn.iScore
- % sn.iRoundScore
- % sn.iRoundSeed
- % strJoin (sn.viReferrals.begin (), sn.viReferrals.end (), ","));
+ WriteLog (lsWARNING, UniqueNodeList) << str (boost::format (VALIDATORS_FILE_NAME " not found: %s") % pConfig);
+
+ return false;
+ }
+
+ if (!boost::filesystem::is_regular_file (pConfig))
+ {
+ WriteLog (lsWARNING, UniqueNodeList) << str (boost::format (VALIDATORS_FILE_NAME " not regular file: %s") % pConfig);
+
+ return false;
+ }
+
+ std::ifstream ifsDefault (pConfig.native ().c_str (), std::ios::in);
+
+ if (!ifsDefault)
+ {
+ WriteLog (lsFATAL, UniqueNodeList) << str (boost::format (VALIDATORS_FILE_NAME " failed to open: %s") % pConfig);
+
+ return false;
+ }
+
+ std::string strValidators;
+
+ strValidators.assign ((std::istreambuf_iterator (ifsDefault)),
+ std::istreambuf_iterator ());
+
+ if (ifsDefault.bad ())
+ {
+ WriteLog (lsFATAL, UniqueNodeList) << str (boost::format ("Failed to read: %s") % pConfig);
+
+ return false;
+ }
+
+ nodeProcess ("local", strValidators, pConfig.string ());
+
+ WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("Processing: %s") % pConfig);
+
+ return true;
+ }
+
+ //--------------------------------------------------------------------------
+
+ void nodeNetwork ()
+ {
+ if (!theConfig.VALIDATORS_SITE.empty ())
+ {
+ HttpsClient::httpsGet (
+ true,
+ getApp().getIOService (),
+ theConfig.VALIDATORS_SITE,
+ 443,
+ theConfig.VALIDATORS_URI,
+ VALIDATORS_FILE_BYTES_MAX,
+ boost::posix_time::seconds (VALIDATORS_FETCH_SECONDS),
+ BIND_TYPE (&UniqueNodeListImp::validatorsResponse, this, P_1, P_2, P_3));
}
}
- return bDist;
-}
+ //--------------------------------------------------------------------------
-// From SeedDomains and ValidatorReferrals compute scores and update TrustedNodes.
-void UniqueNodeList::scoreCompute ()
-{
- strIndex umPulicIdx; // Map of public key to index.
- strIndex umDomainIdx; // Map of domain to index.
- std::vector vsnNodes; // Index to scoring node.
+ Json::Value getUnlJson ()
+ {
+ Database* db = getApp().getWalletDB ()->getDB ();
- Database* db = getApp().getWalletDB ()->getDB ();
+ Json::Value ret (Json::arrayValue);
- // For each entry in SeedDomains with a PublicKey:
- // - Add an entry in umPulicIdx, umDomainIdx, and vsnNodes.
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+ SQL_FOREACH (db, "SELECT * FROM TrustedNodes;")
+ {
+ Json::Value node (Json::objectValue);
+
+ node["publicKey"] = db->getStrBinary ("PublicKey");
+ node["comment"] = db->getStrBinary ("Comment");
+
+ ret.append (node);
+ }
+
+ return ret;
+ }
+
+ //--------------------------------------------------------------------------
+
+ // For each kind of source, have a starting number of points to be distributed.
+ int iSourceScore (ValidatorSource vsWhy)
+ {
+ int iScore = 0;
+
+ switch (vsWhy)
+ {
+ case vsConfig:
+ iScore = 1500;
+ break;
+
+ case vsInbound:
+ iScore = 0;
+ break;
+
+ case vsManual:
+ iScore = 1500;
+ break;
+
+ case vsReferral:
+ iScore = 0;
+ break;
+
+ case vsTold:
+ iScore = 0;
+ break;
+
+ case vsValidator:
+ iScore = 1000;
+ break;
+
+ case vsWeb:
+ iScore = 200;
+ break;
+
+ default:
+ throw std::runtime_error ("Internal error: bad ValidatorSource.");
+ }
+
+ return iScore;
+ }
+
+ //--------------------------------------------------------------------------
+private:
+ // Load information about when we last updated.
+ bool miscLoad ()
{
boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+ Database* db = getApp().getWalletDB ()->getDB ();
- SQL_FOREACH (db, "SELECT Domain,PublicKey,Source FROM SeedDomains;")
+ if (!db->executeSQL ("SELECT * FROM Misc WHERE Magic=1;")) return false;
+
+ bool bAvail = !!db->startIterRows ();
+
+ mtpFetchUpdated = ptFromSeconds (bAvail ? db->getInt ("FetchUpdated") : -1);
+ mtpScoreUpdated = ptFromSeconds (bAvail ? db->getInt ("ScoreUpdated") : -1);
+
+ db->endIterRows ();
+
+ trustedLoad ();
+
+ return true;
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Persist update information.
+ bool miscSave ()
+ {
+ Database* db = getApp().getWalletDB ()->getDB ();
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ db->executeSQL (str (boost::format ("REPLACE INTO Misc (Magic,FetchUpdated,ScoreUpdated) VALUES (1,%d,%d);")
+ % iToSeconds (mtpFetchUpdated)
+ % iToSeconds (mtpScoreUpdated)));
+
+ return true;
+ }
+
+ //--------------------------------------------------------------------------
+
+ void trustedLoad ()
+ {
+ boost::regex rNode ("\\`\\s*(\\S+)[\\s]*(.*)\\'");
+ BOOST_FOREACH (const std::string & c, theConfig.CLUSTER_NODES)
{
- if (db->getNull ("PublicKey"))
+ boost::smatch match;
+
+ if (boost::regex_match (c, match, rNode))
{
- nothing (); // We ignore entries we don't have public keys for.
+ RippleAddress a = RippleAddress::createNodePublic (match[1]);
+
+ if (a.isValid ())
+ m_clusterNodes.insert (std::make_pair (a, match[2]));
}
else
+ WriteLog (lsWARNING, UniqueNodeList) << "Entry in cluster list invalid: '" << c << "'";
+ }
+
+ Database* db = getApp().getWalletDB ()->getDB ();
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+ boost::recursive_mutex::scoped_lock slUNL (mUNLLock);
+
+ mUNL.clear ();
+
+ // XXX Needs to limit by quanity and quality.
+ SQL_FOREACH (db, "SELECT PublicKey FROM TrustedNodes WHERE Score != 0;")
+ {
+ mUNL.insert (db->getStrBinary ("PublicKey"));
+ }
+ }
+
+ //--------------------------------------------------------------------------
+
+ // For a round of scoring we destribute points from a node to nodes it refers to.
+ // Returns true, iff scores were distributed.
+ //
+ bool scoreRound (std::vector& vsnNodes)
+ {
+ bool bDist = false;
+
+ // For each node, distribute roundSeed to roundScores.
+ BOOST_FOREACH (scoreNode & sn, vsnNodes)
+ {
+ int iEntries = sn.viReferrals.size ();
+
+ if (sn.iRoundSeed && iEntries)
+ {
+ score iTotal = (iEntries + 1) * iEntries / 2;
+ score iBase = sn.iRoundSeed * iEntries / iTotal;
+
+ // Distribute the current entires' seed score to validators prioritized by mention order.
+ for (int i = 0; i != iEntries; i++)
+ {
+ score iPoints = iBase * (iEntries - i) / iEntries;
+
+ vsnNodes[sn.viReferrals[i]].iRoundScore += iPoints;
+ }
+ }
+ }
+
+ if (ShouldLog (lsTRACE, UniqueNodeList))
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << "midway: ";
+ BOOST_FOREACH (scoreNode & sn, vsnNodes)
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("%s| %d, %d, %d: [%s]")
+ % sn.strValidator
+ % sn.iScore
+ % sn.iRoundScore
+ % sn.iRoundSeed
+ % strJoin (sn.viReferrals.begin (), sn.viReferrals.end (), ","));
+ }
+ }
+
+ // 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;
+ }
+
+ if (ShouldLog (lsTRACE, UniqueNodeList))
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << "finish: ";
+ BOOST_FOREACH (scoreNode & sn, vsnNodes)
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("%s| %d, %d, %d: [%s]")
+ % sn.strValidator
+ % sn.iScore
+ % sn.iRoundScore
+ % sn.iRoundSeed
+ % strJoin (sn.viReferrals.begin (), sn.viReferrals.end (), ","));
+ }
+ }
+
+ return bDist;
+ }
+
+ //--------------------------------------------------------------------------
+
+ // From SeedDomains and ValidatorReferrals compute scores and update TrustedNodes.
+ //
+ // VFALCO TODO Shrink this function, break it up
+ //
+ void scoreCompute ()
+ {
+ strIndex umPulicIdx; // Map of public key to index.
+ strIndex umDomainIdx; // Map of domain to index.
+ std::vector vsnNodes; // Index to scoring node.
+
+ Database* db = getApp().getWalletDB ()->getDB ();
+
+ // For each entry in SeedDomains with a PublicKey:
+ // - Add an entry in umPulicIdx, umDomainIdx, and vsnNodes.
+ {
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ SQL_FOREACH (db, "SELECT Domain,PublicKey,Source FROM SeedDomains;")
+ {
+ if (db->getNull ("PublicKey"))
+ {
+ nothing (); // We ignore entries we don't have public keys for.
+ }
+ else
+ {
+ std::string strDomain = db->getStrBinary ("Domain");
+ std::string strPublicKey = db->getStrBinary ("PublicKey");
+ std::string strSource = db->getStrBinary ("Source");
+ int iScore = iSourceScore (static_cast (strSource[0]));
+ strIndex::iterator siOld = umPulicIdx.find (strPublicKey);
+
+ if (siOld == umPulicIdx.end ())
+ {
+ // New node
+ int iNode = vsnNodes.size ();
+
+ umPulicIdx[strPublicKey] = iNode;
+ umDomainIdx[strDomain] = iNode;
+
+ scoreNode snCurrent;
+
+ snCurrent.strValidator = strPublicKey;
+ snCurrent.iScore = iScore;
+ snCurrent.iRoundSeed = snCurrent.iScore;
+ snCurrent.iRoundScore = 0;
+ snCurrent.iSeen = -1;
+
+ vsnNodes.push_back (snCurrent);
+ }
+ else
+ {
+ scoreNode& snOld = vsnNodes[siOld->second];
+
+ if (snOld.iScore < iScore)
+ {
+ // Update old node
+
+ snOld.iScore = iScore;
+ snOld.iRoundSeed = snOld.iScore;
+ }
+ }
+ }
+ }
+ }
+
+ // For each entry in SeedNodes:
+ // - Add an entry in umPulicIdx, umDomainIdx, and vsnNodes.
+ {
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ SQL_FOREACH (db, "SELECT PublicKey,Source FROM SeedNodes;")
{
- std::string strDomain = db->getStrBinary ("Domain");
std::string strPublicKey = db->getStrBinary ("PublicKey");
std::string strSource = db->getStrBinary ("Source");
int iScore = iSourceScore (static_cast (strSource[0]));
@@ -360,7 +776,6 @@ void UniqueNodeList::scoreCompute ()
int iNode = vsnNodes.size ();
umPulicIdx[strPublicKey] = iNode;
- umDomainIdx[strDomain] = iNode;
scoreNode snCurrent;
@@ -386,1545 +801,1190 @@ void UniqueNodeList::scoreCompute ()
}
}
}
- }
- // For each entry in SeedNodes:
- // - Add an entry in umPulicIdx, umDomainIdx, and vsnNodes.
- {
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- SQL_FOREACH (db, "SELECT PublicKey,Source FROM SeedNodes;")
+ // For debugging, print out initial scores.
+ if (ShouldLog (lsTRACE, UniqueNodeList))
{
- std::string strPublicKey = db->getStrBinary ("PublicKey");
- std::string strSource = db->getStrBinary ("Source");
- int iScore = iSourceScore (static_cast (strSource[0]));
- strIndex::iterator siOld = umPulicIdx.find (strPublicKey);
-
- if (siOld == umPulicIdx.end ())
+ BOOST_FOREACH (scoreNode & sn, vsnNodes)
{
- // New node
- int iNode = vsnNodes.size ();
-
- umPulicIdx[strPublicKey] = iNode;
-
- scoreNode snCurrent;
-
- snCurrent.strValidator = strPublicKey;
- snCurrent.iScore = iScore;
- snCurrent.iRoundSeed = snCurrent.iScore;
- snCurrent.iRoundScore = 0;
- snCurrent.iSeen = -1;
-
- vsnNodes.push_back (snCurrent);
- }
- else
- {
- scoreNode& snOld = vsnNodes[siOld->second];
-
- if (snOld.iScore < iScore)
- {
- // Update old node
-
- snOld.iScore = iScore;
- snOld.iRoundSeed = snOld.iScore;
- }
+ WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("%s| %d, %d, %d")
+ % sn.strValidator
+ % sn.iScore
+ % sn.iRoundScore
+ % sn.iRoundSeed);
}
}
- }
- // For debugging, print out initial scores.
- if (ShouldLog (lsTRACE, UniqueNodeList))
- {
- BOOST_FOREACH (scoreNode & sn, vsnNodes)
+ // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("vsnNodes.size=%d") % vsnNodes.size());
+
+ // Step through growing list of nodes adding each validation list.
+ // - Each validator may have provided referals. Add those referals as validators.
+ for (int iNode = 0; iNode != vsnNodes.size (); ++iNode)
{
- WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("%s| %d, %d, %d")
- % sn.strValidator
- % sn.iScore
- % sn.iRoundScore
- % sn.iRoundSeed);
- }
- }
+ scoreNode& sn = vsnNodes[iNode];
+ std::string& strValidator = sn.strValidator;
+ std::vector& viReferrals = sn.viReferrals;
- // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("vsnNodes.size=%d") % vsnNodes.size());
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
- // Step through growing list of nodes adding each validation list.
- // - Each validator may have provided referals. Add those referals as validators.
- for (int iNode = 0; iNode != vsnNodes.size (); ++iNode)
- {
- scoreNode& sn = vsnNodes[iNode];
- std::string& strValidator = sn.strValidator;
- std::vector& viReferrals = sn.viReferrals;
-
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- SQL_FOREACH (db, boost::str (boost::format ("SELECT Referral FROM ValidatorReferrals WHERE Validator=%s ORDER BY Entry;")
- % sqlEscape (strValidator)))
- {
- std::string strReferral = db->getStrBinary ("Referral");
- int iReferral;
-
- strIndex::iterator itEntry;
-
- RippleAddress na;
-
- if (na.setNodePublic (strReferral))
+ SQL_FOREACH (db, boost::str (boost::format ("SELECT Referral FROM ValidatorReferrals WHERE Validator=%s ORDER BY Entry;")
+ % sqlEscape (strValidator)))
{
- // Referring a public key.
- itEntry = umPulicIdx.find (strReferral);
+ std::string strReferral = db->getStrBinary ("Referral");
+ int iReferral;
- if (itEntry == umPulicIdx.end ())
+ strIndex::iterator itEntry;
+
+ RippleAddress na;
+
+ if (na.setNodePublic (strReferral))
{
- // Not found add public key to list of nodes.
- iReferral = vsnNodes.size ();
+ // Referring a public key.
+ itEntry = umPulicIdx.find (strReferral);
- umPulicIdx[strReferral] = iReferral;
+ if (itEntry == umPulicIdx.end ())
+ {
+ // Not found add public key to list of nodes.
+ iReferral = vsnNodes.size ();
- scoreNode snCurrent;
+ umPulicIdx[strReferral] = iReferral;
- snCurrent.strValidator = strReferral;
- snCurrent.iScore = iSourceScore (vsReferral);
- snCurrent.iRoundSeed = snCurrent.iScore;
- snCurrent.iRoundScore = 0;
- snCurrent.iSeen = -1;
+ 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;
+ }
+
+ // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("%s: Public=%s iReferral=%d") % strValidator % strReferral % iReferral);
- vsnNodes.push_back (snCurrent);
}
else
{
- iReferral = itEntry->second;
+ // Referring a domain.
+ itEntry = umDomainIdx.find (strReferral);
+ iReferral = itEntry == umDomainIdx.end ()
+ ? -1 // We ignore domains we can't find entires for.
+ : itEntry->second;
+
+ // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("%s: Domain=%s iReferral=%d") % strValidator % strReferral % iReferral);
}
- // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("%s: Public=%s iReferral=%d") % strValidator % strReferral % iReferral);
-
+ if (iReferral >= 0 && iNode != iReferral)
+ viReferrals.push_back (iReferral);
}
- else
+ }
+
+ //
+ // Distribute the points from the seeds.
+ //
+ bool bDist = true;
+
+ for (int i = SCORE_ROUNDS; bDist && i--;)
+ bDist = scoreRound (vsnNodes);
+
+ if (ShouldLog (lsTRACE, UniqueNodeList))
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << "Scored:";
+ BOOST_FOREACH (scoreNode & sn, vsnNodes)
{
- // Referring a domain.
- itEntry = umDomainIdx.find (strReferral);
- iReferral = itEntry == umDomainIdx.end ()
- ? -1 // We ignore domains we can't find entires for.
- : itEntry->second;
-
- // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("%s: Domain=%s iReferral=%d") % strValidator % strReferral % iReferral);
- }
-
- if (iReferral >= 0 && iNode != iReferral)
- viReferrals.push_back (iReferral);
- }
- }
-
- //
- // Distribute the points from the seeds.
- //
- bool bDist = true;
-
- for (int i = SCORE_ROUNDS; bDist && i--;)
- bDist = scoreRound (vsnNodes);
-
- if (ShouldLog (lsTRACE, UniqueNodeList))
- {
- WriteLog (lsTRACE, UniqueNodeList) << "Scored:";
- BOOST_FOREACH (scoreNode & sn, vsnNodes)
- {
- WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("%s| %d, %d, %d: [%s]")
- % sn.strValidator
- % sn.iScore
- % sn.iRoundScore
- % sn.iRoundSeed
- % strJoin (sn.viReferrals.begin (), sn.viReferrals.end (), ","));
- }
- }
-
- // Persist validator scores.
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- db->executeSQL ("BEGIN;");
- db->executeSQL ("UPDATE TrustedNodes SET Score = 0 WHERE Score != 0;");
-
- if (!vsnNodes.empty ())
- {
- // Load existing Seens from DB.
- std::vector vstrPublicKeys;
-
- vstrPublicKeys.resize (vsnNodes.size ());
-
- for (int iNode = vsnNodes.size (); iNode--;)
- {
- vstrPublicKeys[iNode] = sqlEscape (vsnNodes[iNode].strValidator);
- }
-
- SQL_FOREACH (db, str (boost::format ("SELECT PublicKey,Seen FROM TrustedNodes WHERE PublicKey IN (%s);")
- % strJoin (vstrPublicKeys.begin (), vstrPublicKeys.end (), ",")))
- {
- vsnNodes[umPulicIdx[db->getStrBinary ("PublicKey")]].iSeen = db->getNull ("Seen") ? -1 : db->getInt ("Seen");
- }
- }
-
- boost::unordered_set usUNL;
-
- if (!vsnNodes.empty ())
- {
- // Update the score old entries and add new entries as needed.
- std::vector vstrValues;
-
- vstrValues.resize (vsnNodes.size ());
-
- for (int iNode = vsnNodes.size (); iNode--;)
- {
- scoreNode& sn = vsnNodes[iNode];
- std::string strSeen = sn.iSeen >= 0 ? str (boost::format ("%d") % sn.iSeen) : "NULL";
-
- vstrValues[iNode] = str (boost::format ("(%s,%s,%s)")
- % sqlEscape (sn.strValidator)
- % sn.iScore
- % strSeen);
-
- usUNL.insert (sn.strValidator);
- }
-
- db->executeSQL (str (boost::format ("REPLACE INTO TrustedNodes (PublicKey,Score,Seen) VALUES %s;")
- % strJoin (vstrValues.begin (), vstrValues.end (), ",")));
- }
-
- {
- boost::recursive_mutex::scoped_lock sl (mUNLLock);
-
- // XXX Should limit to scores above a certain minimum and limit to a certain number.
- mUNL.swap (usUNL);
- }
-
- // Score IPs.
- db->executeSQL ("UPDATE PeerIps SET Score = 0 WHERE Score != 0;");
-
- boost::unordered_map umValidators;
-
- if (!vsnNodes.empty ())
- {
- std::vector vstrPublicKeys;
-
- // For every IpReferral add a score for the IP and PORT.
- SQL_FOREACH (db, "SELECT Validator,COUNT(*) AS Count FROM IpReferrals GROUP BY Validator;")
- {
- umValidators[db->getStrBinary ("Validator")] = db->getInt ("Count");
-
- // WriteLog (lsTRACE, UniqueNodeList) << strValidator << ":" << db->getInt("Count");
- }
- }
-
- // For each validator, get each referral and add its score to ip's score.
- // map of pair :: score
- epScore umScore;
-
- typedef boost::unordered_map::value_type vcType;
- BOOST_FOREACH (vcType & vc, umValidators)
- {
- std::string strValidator = vc.first;
-
- strIndex::iterator itIndex = umPulicIdx.find (strValidator);
-
- if (itIndex != umPulicIdx.end ())
- {
- int iSeed = vsnNodes[itIndex->second].iScore;
- int iEntries = vc.second;
- score iTotal = (iEntries + 1) * iEntries / 2;
- score iBase = iSeed * iEntries / iTotal;
- int iEntry = 0;
-
- SQL_FOREACH (db, str (boost::format ("SELECT IP,Port FROM IpReferrals WHERE Validator=%s ORDER BY Entry;")
- % sqlEscape (strValidator)))
- {
- score iPoints = iBase * (iEntries - iEntry) / iEntries;
- int iPort;
-
- iPort = db->getNull ("Port") ? -1 : db->getInt ("Port");
-
- std::pair< std::string, int> ep = std::make_pair (db->getStrBinary ("IP"), iPort);
-
- epScore::iterator itEp = umScore.find (ep);
-
- umScore[ep] = itEp == umScore.end () ? iPoints : itEp->second + iPoints;
- iEntry++;
+ WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("%s| %d, %d, %d: [%s]")
+ % sn.strValidator
+ % sn.iScore
+ % sn.iRoundScore
+ % sn.iRoundSeed
+ % strJoin (sn.viReferrals.begin (), sn.viReferrals.end (), ","));
}
}
- }
- // Apply validator scores to each IP.
- if (umScore.size ())
- {
- std::vector vstrValues;
-
- vstrValues.reserve (umScore.size ());
-
- typedef boost::unordered_map, score>::value_type ipScoreType;
- BOOST_FOREACH (ipScoreType & ipScore, umScore)
- {
- IPAndPortNumber ipEndpoint = ipScore.first;
- std::string strIpPort = str (boost::format ("%s %d") % ipEndpoint.first % ipEndpoint.second);
- score iPoints = ipScore.second;
-
- vstrValues.push_back (str (boost::format ("(%s,%d,'%c')")
- % sqlEscape (strIpPort)
- % iPoints
- % vsValidator));
- }
-
- // Set scores for each IP.
- db->executeSQL (str (boost::format ("REPLACE INTO PeerIps (IpPort,Score,Source) VALUES %s;")
- % strJoin (vstrValues.begin (), vstrValues.end (), ",")));
- }
-
- db->executeSQL ("COMMIT;");
-}
-
-// Begin scoring if timer was not cancelled.
-void UniqueNodeList::scoreTimerHandler (const boost::system::error_code& err)
-{
- if (!err)
- {
- mtpScoreNext = boost::posix_time::ptime (boost::posix_time::not_a_date_time); // Timer not set.
- mtpScoreStart = boost::posix_time::second_clock::universal_time (); // Scoring.
-
- WriteLog (lsTRACE, UniqueNodeList) << "Scoring: Start";
-
- scoreCompute ();
-
- WriteLog (lsTRACE, UniqueNodeList) << "Scoring: End";
-
- // Save update time.
- mtpScoreUpdated = mtpScoreStart;
- miscSave ();
-
- mtpScoreStart = boost::posix_time::ptime (boost::posix_time::not_a_date_time); // Not scoring.
-
- // Score again if needed.
- scoreNext (false);
-
- // Scan may be dirty due to new ips.
- getApp().getPeers ().scanRefresh ();
- }
-}
-
-// Start a timer to update scores.
-// <-- bNow: true, to force scoring for debugging.
-void UniqueNodeList::scoreNext (bool bNow)
-{
- // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("scoreNext: mtpFetchUpdated=%s mtpScoreStart=%s mtpScoreUpdated=%s mtpScoreNext=%s") % mtpFetchUpdated % mtpScoreStart % mtpScoreUpdated % mtpScoreNext);
- bool bCanScore = mtpScoreStart.is_not_a_date_time () // Not scoring.
- && !mtpFetchUpdated.is_not_a_date_time (); // Something to score.
-
- bool bDirty =
- (mtpScoreUpdated.is_not_a_date_time () || mtpScoreUpdated <= mtpFetchUpdated) // Not already scored.
- && (mtpScoreNext.is_not_a_date_time () // Timer is not fine.
- || mtpScoreNext < mtpFetchUpdated + boost::posix_time::seconds (SCORE_DELAY_SECONDS));
-
- if (!bCanScore)
- {
- nothing ();
- }
- else if (bNow || bDirty)
- {
- // Need to update or set timer.
- mtpScoreNext = boost::posix_time::second_clock::universal_time () // Past now too.
- + boost::posix_time::seconds (bNow ? 0 : SCORE_DELAY_SECONDS);
-
- // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("scoreNext: @%s") % mtpScoreNext);
- mdtScoreTimer.expires_at (mtpScoreNext);
- mdtScoreTimer.async_wait (BIND_TYPE (&UniqueNodeList::scoreTimerHandler, this, P_1));
- }
-}
-
-// For debugging, schedule forced scoring.
-void UniqueNodeList::nodeScore ()
-{
- scoreNext (true);
-}
-
-void UniqueNodeList::fetchFinish ()
-{
- {
- boost::mutex::scoped_lock sl (mFetchLock);
- mFetchActive--;
- }
-
- fetchNext ();
-}
-
-// Called when we need to update scores.
-void UniqueNodeList::fetchDirty ()
-{
- // Note update.
- mtpFetchUpdated = boost::posix_time::second_clock::universal_time ();
- miscSave ();
-
- // Update scores.
- scoreNext (false);
-}
-
-// Persist the IPs refered to by a Validator.
-// --> strSite: source of the IPs (for debugging)
-// --> naNodePublic: public key of the validating node.
-void UniqueNodeList::processIps (const std::string& strSite, const RippleAddress& naNodePublic, Section::mapped_type* pmtVecStrIps)
-{
- Database* db = getApp().getWalletDB ()->getDB ();
-
- std::string strEscNodePublic = sqlEscape (naNodePublic.humanNodePublic ());
-
- WriteLog (lsDEBUG, UniqueNodeList)
- << str (boost::format ("Validator: '%s' processing %d ips.")
- % strSite % ( pmtVecStrIps ? pmtVecStrIps->size () : 0));
-
- // Remove all current Validator's entries in IpReferrals
- {
+ // Persist validator scores.
boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
- db->executeSQL (str (boost::format ("DELETE FROM IpReferrals WHERE Validator=%s;") % strEscNodePublic));
- // XXX Check result.
+
+ db->executeSQL ("BEGIN;");
+ db->executeSQL ("UPDATE TrustedNodes SET Score = 0 WHERE Score != 0;");
+
+ if (!vsnNodes.empty ())
+ {
+ // Load existing Seens from DB.
+ std::vector vstrPublicKeys;
+
+ vstrPublicKeys.resize (vsnNodes.size ());
+
+ for (int iNode = vsnNodes.size (); iNode--;)
+ {
+ vstrPublicKeys[iNode] = sqlEscape (vsnNodes[iNode].strValidator);
+ }
+
+ SQL_FOREACH (db, str (boost::format ("SELECT PublicKey,Seen FROM TrustedNodes WHERE PublicKey IN (%s);")
+ % strJoin (vstrPublicKeys.begin (), vstrPublicKeys.end (), ",")))
+ {
+ vsnNodes[umPulicIdx[db->getStrBinary ("PublicKey")]].iSeen = db->getNull ("Seen") ? -1 : db->getInt ("Seen");
+ }
+ }
+
+ boost::unordered_set usUNL;
+
+ if (!vsnNodes.empty ())
+ {
+ // Update the score old entries and add new entries as needed.
+ std::vector vstrValues;
+
+ vstrValues.resize (vsnNodes.size ());
+
+ for (int iNode = vsnNodes.size (); iNode--;)
+ {
+ scoreNode& sn = vsnNodes[iNode];
+ std::string strSeen = sn.iSeen >= 0 ? str (boost::format ("%d") % sn.iSeen) : "NULL";
+
+ vstrValues[iNode] = str (boost::format ("(%s,%s,%s)")
+ % sqlEscape (sn.strValidator)
+ % sn.iScore
+ % strSeen);
+
+ usUNL.insert (sn.strValidator);
+ }
+
+ db->executeSQL (str (boost::format ("REPLACE INTO TrustedNodes (PublicKey,Score,Seen) VALUES %s;")
+ % strJoin (vstrValues.begin (), vstrValues.end (), ",")));
+ }
+
+ {
+ boost::recursive_mutex::scoped_lock sl (mUNLLock);
+
+ // XXX Should limit to scores above a certain minimum and limit to a certain number.
+ mUNL.swap (usUNL);
+ }
+
+ // Score IPs.
+ db->executeSQL ("UPDATE PeerIps SET Score = 0 WHERE Score != 0;");
+
+ boost::unordered_map umValidators;
+
+ if (!vsnNodes.empty ())
+ {
+ std::vector vstrPublicKeys;
+
+ // For every IpReferral add a score for the IP and PORT.
+ SQL_FOREACH (db, "SELECT Validator,COUNT(*) AS Count FROM IpReferrals GROUP BY Validator;")
+ {
+ umValidators[db->getStrBinary ("Validator")] = db->getInt ("Count");
+
+ // WriteLog (lsTRACE, UniqueNodeList) << strValidator << ":" << db->getInt("Count");
+ }
+ }
+
+ // For each validator, get each referral and add its score to ip's score.
+ // map of pair :: score
+ epScore umScore;
+
+ typedef boost::unordered_map::value_type vcType;
+ BOOST_FOREACH (vcType & vc, umValidators)
+ {
+ std::string strValidator = vc.first;
+
+ strIndex::iterator itIndex = umPulicIdx.find (strValidator);
+
+ if (itIndex != umPulicIdx.end ())
+ {
+ int iSeed = vsnNodes[itIndex->second].iScore;
+ int iEntries = vc.second;
+ score iTotal = (iEntries + 1) * iEntries / 2;
+ score iBase = iSeed * iEntries / iTotal;
+ int iEntry = 0;
+
+ SQL_FOREACH (db, str (boost::format ("SELECT IP,Port FROM IpReferrals WHERE Validator=%s ORDER BY Entry;")
+ % sqlEscape (strValidator)))
+ {
+ score iPoints = iBase * (iEntries - iEntry) / iEntries;
+ int iPort;
+
+ iPort = db->getNull ("Port") ? -1 : db->getInt ("Port");
+
+ std::pair< std::string, int> ep = std::make_pair (db->getStrBinary ("IP"), iPort);
+
+ epScore::iterator itEp = umScore.find (ep);
+
+ umScore[ep] = itEp == umScore.end () ? iPoints : itEp->second + iPoints;
+ iEntry++;
+ }
+ }
+ }
+
+ // Apply validator scores to each IP.
+ if (umScore.size ())
+ {
+ std::vector vstrValues;
+
+ vstrValues.reserve (umScore.size ());
+
+ typedef boost::unordered_map, score>::value_type ipScoreType;
+ BOOST_FOREACH (ipScoreType & ipScore, umScore)
+ {
+ IPAndPortNumber ipEndpoint = ipScore.first;
+ std::string strIpPort = str (boost::format ("%s %d") % ipEndpoint.first % ipEndpoint.second);
+ score iPoints = ipScore.second;
+
+ vstrValues.push_back (str (boost::format ("(%s,%d,'%c')")
+ % sqlEscape (strIpPort)
+ % iPoints
+ % vsValidator));
+ }
+
+ // Set scores for each IP.
+ db->executeSQL (str (boost::format ("REPLACE INTO PeerIps (IpPort,Score,Source) VALUES %s;")
+ % strJoin (vstrValues.begin (), vstrValues.end (), ",")));
+ }
+
+ db->executeSQL ("COMMIT;");
}
- // Add new referral entries.
- if (pmtVecStrIps && !pmtVecStrIps->empty ())
+ //--------------------------------------------------------------------------
+
+ // Begin scoring if timer was not cancelled.
+ void scoreTimerHandler (const boost::system::error_code& err)
{
- std::vector vstrValues;
-
- vstrValues.resize (std::min ((int) pmtVecStrIps->size (), REFERRAL_IPS_MAX));
-
- int iValues = 0;
- BOOST_FOREACH (const std::string & strReferral, *pmtVecStrIps)
+ if (!err)
{
- if (iValues == REFERRAL_VALIDATORS_MAX)
- break;
+ onDeadlineTimer (m_scoreTimer);
+ }
+ }
- std::string strIP;
- int iPort;
- bool bValid = parseIpPort (strReferral, strIP, iPort);
+ //--------------------------------------------------------------------------
- // XXX Filter out private network ips.
- // XXX http://en.wikipedia.org/wiki/Private_network
+ // Start a timer to update scores.
+ // <-- bNow: true, to force scoring for debugging.
+ void scoreNext (bool bNow)
+ {
+ // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("scoreNext: mtpFetchUpdated=%s mtpScoreStart=%s mtpScoreUpdated=%s mtpScoreNext=%s") % mtpFetchUpdated % mtpScoreStart % mtpScoreUpdated % mtpScoreNext);
+ bool bCanScore = mtpScoreStart.is_not_a_date_time () // Not scoring.
+ && !mtpFetchUpdated.is_not_a_date_time (); // Something to score.
- if (bValid)
+ bool bDirty =
+ (mtpScoreUpdated.is_not_a_date_time () || mtpScoreUpdated <= mtpFetchUpdated) // Not already scored.
+ && (mtpScoreNext.is_not_a_date_time () // Timer is not fine.
+ || mtpScoreNext < mtpFetchUpdated + boost::posix_time::seconds (SCORE_DELAY_SECONDS));
+
+ if (!bCanScore)
+ {
+ nothing ();
+ }
+ else if (bNow || bDirty)
+ {
+ // Need to update or set timer.
+ double const secondsFromNow = bNow ? 0 : SCORE_DELAY_SECONDS;
+ mtpScoreNext = boost::posix_time::second_clock::universal_time () // Past now too.
+ + boost::posix_time::seconds (secondsFromNow);
+
+ // WriteLog (lsTRACE, UniqueNodeList) << str(boost::format("scoreNext: @%s") % mtpScoreNext);
+#if 1
+ m_scoreTimer.setExpiration (secondsFromNow);
+#else
+ mdtScoreTimer.expires_at (mtpScoreNext);
+ mdtScoreTimer.async_wait (BIND_TYPE (&UniqueNodeListImp::scoreTimerHandler, this, P_1));
+#endif
+ }
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Given a ripple.txt, process it.
+ //
+ // VFALCO TODO Can't we take a filename or stream instead of a string?
+ //
+ bool responseFetch (const std::string& strDomain, const boost::system::error_code& err, int iStatus, const std::string& strSiteFile)
+ {
+ bool bReject = !err && iStatus != 200;
+
+ if (!bReject)
+ {
+ Section secSite = ParseSection (strSiteFile, true);
+ bool bGood = !err;
+
+ if (bGood)
{
- vstrValues[iValues] = str (boost::format ("(%s,%d,%s,%d)")
- % strEscNodePublic % iValues % sqlEscape (strIP) % iPort);
- iValues++;
+ WriteLog (lsTRACE, UniqueNodeList) << boost::format ("Validator: '%s' received " NODE_FILE_NAME ".") % strDomain;
}
else
{
WriteLog (lsTRACE, UniqueNodeList)
- << str (boost::format ("Validator: '%s' [" SECTION_IPS "]: rejecting '%s'")
- % strSite % strReferral);
+ << boost::format ("Validator: '%s' unable to retrieve " NODE_FILE_NAME ": %s")
+ % strDomain
+ % err.message ();
}
- }
- if (iValues)
- {
- vstrValues.resize (iValues);
+ //
+ // Verify file domain
+ //
+ std::string strSite;
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
- db->executeSQL (str (boost::format ("INSERT INTO IpReferrals (Validator,Entry,IP,Port) VALUES %s;")
- % strJoin (vstrValues.begin (), vstrValues.end (), ",")));
- // XXX Check result.
- }
- }
-
- fetchDirty ();
-}
-
-// Persist ValidatorReferrals.
-// --> strSite: source site for display
-// --> strValidatorsSrc: source details for display
-// --> naNodePublic: remote source public key - not valid for local
-// --> vsWhy: reason for adding validator to SeedDomains or SeedNodes.
-int UniqueNodeList::processValidators (const std::string& strSite, const std::string& strValidatorsSrc, const RippleAddress& naNodePublic, ValidatorSource vsWhy, Section::mapped_type* pmtVecStrValidators)
-{
- Database* db = getApp().getWalletDB ()->getDB ();
- std::string strNodePublic = naNodePublic.isValid () ? naNodePublic.humanNodePublic () : strValidatorsSrc;
- int iValues = 0;
-
- WriteLog (lsTRACE, UniqueNodeList)
- << str (boost::format ("Validator: '%s' : '%s' : processing %d validators.")
- % strSite
- % strValidatorsSrc
- % ( pmtVecStrValidators ? pmtVecStrValidators->size () : 0));
-
- // Remove all current Validator's entries in ValidatorReferrals
- {
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- db->executeSQL (str (boost::format ("DELETE FROM ValidatorReferrals WHERE Validator='%s';") % strNodePublic));
- // XXX Check result.
- }
-
- // Add new referral entries.
- if (pmtVecStrValidators && pmtVecStrValidators->size ())
- {
- std::vector vstrValues;
-
- vstrValues.reserve (std::min ((int) pmtVecStrValidators->size (), REFERRAL_VALIDATORS_MAX));
-
- BOOST_FOREACH (const std::string & strReferral, *pmtVecStrValidators)
- {
- if (iValues == REFERRAL_VALIDATORS_MAX)
- break;
-
- boost::smatch smMatch;
-
- // domain comment?
- // public_key comment?
- static boost::regex reReferral ("\\`\\s*(\\S+)(?:\\s+(.+))?\\s*\\'");
-
- if (!boost::regex_match (strReferral, smMatch, reReferral))
+ if (bGood && !SectionSingleB (secSite, SECTION_DOMAIN, strSite))
{
- WriteLog (lsWARNING, UniqueNodeList) << str (boost::format ("Bad validator: syntax error: %s: %s") % strSite % strReferral);
+ bGood = false;
+
+ WriteLog (lsTRACE, UniqueNodeList)
+ << boost::format ("Validator: '%s' bad " NODE_FILE_NAME " missing single entry for " SECTION_DOMAIN ".")
+ % strDomain;
}
- else
+
+ if (bGood && strSite != strDomain)
{
- std::string strRefered = smMatch[1];
- std::string strComment = smMatch[2];
- RippleAddress naValidator;
+ bGood = false;
- if (naValidator.setSeedGeneric (strRefered))
+ WriteLog (lsTRACE, UniqueNodeList)
+ << boost::format ("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_DOMAIN " does not match: %s")
+ % strDomain
+ % strSite;
+ }
+
+ //
+ // Process public key
+ //
+ std::string strNodePublicKey;
+
+ if (bGood && !SectionSingleB (secSite, SECTION_PUBLIC_KEY, strNodePublicKey))
+ {
+ // Bad [validation_public_key] Section.
+ bGood = false;
+
+ WriteLog (lsTRACE, UniqueNodeList)
+ << boost::format ("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " does not have single entry.")
+ % strDomain;
+ }
+
+ RippleAddress naNodePublic;
+
+ if (bGood && !naNodePublic.setNodePublic (strNodePublicKey))
+ {
+ // Bad public key.
+ bGood = false;
+
+ WriteLog (lsTRACE, UniqueNodeList)
+ << boost::format ("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " is bad: ")
+ % strDomain
+ % strNodePublicKey;
+ }
+
+ if (bGood)
+ {
+ // WriteLog (lsTRACE, UniqueNodeList) << boost::format("naNodePublic: '%s'") % naNodePublic.humanNodePublic();
+
+ seedDomain sdCurrent;
+
+ bool bFound = getSeedDomains (strDomain, sdCurrent);
+
+ assert (bFound);
+
+ uint256 iSha256 = Serializer::getSHA512Half (strSiteFile);
+ bool bChangedB = sdCurrent.iSha256 != iSha256;
+
+ sdCurrent.strDomain = strDomain;
+ // XXX If the node public key is changing, delete old public key information?
+ // XXX Only if no other refs to keep it arround, other wise we have an attack vector.
+ sdCurrent.naPublicKey = naNodePublic;
+
+ // WriteLog (lsTRACE, UniqueNodeList) << boost::format("sdCurrent.naPublicKey: '%s'") % sdCurrent.naPublicKey.humanNodePublic();
+
+ sdCurrent.tpFetch = boost::posix_time::second_clock::universal_time ();
+ sdCurrent.iSha256 = iSha256;
+
+ setSeedDomains (sdCurrent, true);
+
+ if (bChangedB)
{
-
- WriteLog (lsWARNING, UniqueNodeList) << str (boost::format ("Bad validator: domain or public key required: %s %s") % strRefered % strComment);
- }
- else if (naValidator.setNodePublic (strRefered))
- {
- // A public key.
- // XXX Schedule for CAS lookup.
- nodeAddPublic (naValidator, vsWhy, strComment);
-
- WriteLog (lsINFO, UniqueNodeList) << str (boost::format ("Node Public: %s %s") % strRefered % strComment);
-
- if (naNodePublic.isValid ())
- vstrValues.push_back (str (boost::format ("('%s',%d,'%s')") % strNodePublic % iValues % naValidator.humanNodePublic ()));
-
- iValues++;
+ WriteLog (lsTRACE, UniqueNodeList) << boost::format ("Validator: '%s' processing new " NODE_FILE_NAME ".") % strDomain;
+ processFile (strDomain, naNodePublic, secSite);
}
else
{
- // A domain: need to look it up.
- nodeAddDomain (strRefered, vsWhy, strComment);
-
- WriteLog (lsINFO, UniqueNodeList) << str (boost::format ("Node Domain: %s %s") % strRefered % strComment);
-
- if (naNodePublic.isValid ())
- vstrValues.push_back (str (boost::format ("('%s',%d,%s)") % strNodePublic % iValues % sqlEscape (strRefered)));
-
- iValues++;
+ WriteLog (lsTRACE, UniqueNodeList) << boost::format ("Validator: '%s' no change for " NODE_FILE_NAME ".") % strDomain;
+ fetchFinish ();
}
}
- }
-
- if (!vstrValues.empty ())
- {
- std::string strSql = str (boost::format ("INSERT INTO ValidatorReferrals (Validator,Entry,Referral) VALUES %s;")
- % strJoin (vstrValues.begin (), vstrValues.end (), ","));
-
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- db->executeSQL (strSql);
- // XXX Check result.
- }
- }
-
- fetchDirty ();
-
- return iValues;
-}
-
-// Given a Section with IPs, parse and persist it for a validator.
-bool UniqueNodeList::responseIps (const std::string& strSite, const RippleAddress& naNodePublic, const boost::system::error_code& err, int iStatus, const std::string& strIpsFile)
-{
- bool bReject = !err && iStatus != 200;
-
- if (!bReject)
- {
- if (!err)
- {
- Section secFile = ParseSection (strIpsFile, true);
-
- processIps (strSite, naNodePublic, SectionEntries (secFile, SECTION_IPS));
- }
-
- fetchFinish ();
- }
-
- return bReject;
-}
-
-// Process Section [ips_url].
-// If we have a Section with a single entry, fetch the url and process it.
-void UniqueNodeList::getIpsUrl (const RippleAddress& naNodePublic, Section secSite)
-{
- std::string strIpsUrl;
- std::string strScheme;
- std::string strDomain;
- int iPort;
- std::string strPath;
-
- if (SectionSingleB (secSite, SECTION_IPS_URL, strIpsUrl)
- && !strIpsUrl.empty ()
- && parseUrl (strIpsUrl, strScheme, strDomain, iPort, strPath)
- && -1 == iPort
- && strScheme == "https")
- {
- HttpsClient::httpsGet (
- true,
- getApp().getIOService (),
- strDomain,
- 443,
- strPath,
- NODE_FILE_BYTES_MAX,
- boost::posix_time::seconds (NODE_FETCH_SECONDS),
- BIND_TYPE (&UniqueNodeList::responseIps, this, strDomain, naNodePublic, P_1, P_2, P_3));
- }
- else
- {
- fetchFinish ();
- }
-}
-
-// After fetching a ripple.txt from a web site, given a Section with validators, parse and persist it.
-bool UniqueNodeList::responseValidators (const std::string& strValidatorsUrl, const RippleAddress& naNodePublic, Section secSite, const std::string& strSite, const boost::system::error_code& err, int iStatus, const std::string& strValidatorsFile)
-{
- bool bReject = !err && iStatus != 200;
-
- if (!bReject)
- {
- if (!err)
- {
- Section secFile = ParseSection (strValidatorsFile, true);
-
- processValidators (strSite, strValidatorsUrl, naNodePublic, vsValidator, SectionEntries (secFile, SECTION_VALIDATORS));
- }
-
- getIpsUrl (naNodePublic, secSite);
- }
-
- return bReject;
-}
-
-// Process Section [validators_url].
-void UniqueNodeList::getValidatorsUrl (const RippleAddress& naNodePublic, Section secSite)
-{
- std::string strValidatorsUrl;
- std::string strScheme;
- std::string strDomain;
- int iPort;
- std::string strPath;
-
- if (SectionSingleB (secSite, SECTION_VALIDATORS_URL, strValidatorsUrl)
- && !strValidatorsUrl.empty ()
- && parseUrl (strValidatorsUrl, strScheme, strDomain, iPort, strPath)
- && -1 == iPort
- && strScheme == "https")
- {
- HttpsClient::httpsGet (
- true,
- getApp().getIOService (),
- strDomain,
- 443,
- strPath,
- NODE_FILE_BYTES_MAX,
- boost::posix_time::seconds (NODE_FETCH_SECONDS),
- BIND_TYPE (&UniqueNodeList::responseValidators, this, strValidatorsUrl, naNodePublic, secSite, strDomain, P_1, P_2, P_3));
- }
- else
- {
- getIpsUrl (naNodePublic, secSite);
- }
-}
-
-// Process a ripple.txt.
-void UniqueNodeList::processFile (const std::string& strDomain, const RippleAddress& naNodePublic, Section secSite)
-{
- //
- // Process Validators
- //
- processValidators (strDomain, NODE_FILE_NAME, naNodePublic, vsReferral, SectionEntries (secSite, SECTION_VALIDATORS));
-
- //
- // Process ips
- //
- processIps (strDomain, naNodePublic, SectionEntries (secSite, SECTION_IPS));
-
- //
- // Process currencies
- //
- Section::mapped_type* pvCurrencies;
-
- if ((pvCurrencies = SectionEntries (secSite, SECTION_CURRENCIES)) && pvCurrencies->size ())
- {
- // XXX Process currencies.
- WriteLog (lsWARNING, UniqueNodeList) << "Ignoring currencies: not implemented.";
- }
-
- getValidatorsUrl (naNodePublic, secSite);
-}
-
-// Given a ripple.txt, process it.
-bool UniqueNodeList::responseFetch (const std::string& strDomain, const boost::system::error_code& err, int iStatus, const std::string& strSiteFile)
-{
- bool bReject = !err && iStatus != 200;
-
- if (!bReject)
- {
- Section secSite = ParseSection (strSiteFile, true);
- bool bGood = !err;
-
- if (bGood)
- {
- WriteLog (lsTRACE, UniqueNodeList) << boost::format ("Validator: '%s' received " NODE_FILE_NAME ".") % strDomain;
- }
- else
- {
- WriteLog (lsTRACE, UniqueNodeList)
- << boost::format ("Validator: '%s' unable to retrieve " NODE_FILE_NAME ": %s")
- % strDomain
- % err.message ();
- }
-
- //
- // Verify file domain
- //
- std::string strSite;
-
- if (bGood && !SectionSingleB (secSite, SECTION_DOMAIN, strSite))
- {
- bGood = false;
-
- WriteLog (lsTRACE, UniqueNodeList)
- << boost::format ("Validator: '%s' bad " NODE_FILE_NAME " missing single entry for " SECTION_DOMAIN ".")
- % strDomain;
- }
-
- if (bGood && strSite != strDomain)
- {
- bGood = false;
-
- WriteLog (lsTRACE, UniqueNodeList)
- << boost::format ("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_DOMAIN " does not match: %s")
- % strDomain
- % strSite;
- }
-
- //
- // Process public key
- //
- std::string strNodePublicKey;
-
- if (bGood && !SectionSingleB (secSite, SECTION_PUBLIC_KEY, strNodePublicKey))
- {
- // Bad [validation_public_key] Section.
- bGood = false;
-
- WriteLog (lsTRACE, UniqueNodeList)
- << boost::format ("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " does not have single entry.")
- % strDomain;
- }
-
- RippleAddress naNodePublic;
-
- if (bGood && !naNodePublic.setNodePublic (strNodePublicKey))
- {
- // Bad public key.
- bGood = false;
-
- WriteLog (lsTRACE, UniqueNodeList)
- << boost::format ("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " is bad: ")
- % strDomain
- % strNodePublicKey;
- }
-
- if (bGood)
- {
- // WriteLog (lsTRACE, UniqueNodeList) << boost::format("naNodePublic: '%s'") % naNodePublic.humanNodePublic();
-
- seedDomain sdCurrent;
-
- bool bFound = getSeedDomains (strDomain, sdCurrent);
-
- assert (bFound);
-
- uint256 iSha256 = Serializer::getSHA512Half (strSiteFile);
- bool bChangedB = sdCurrent.iSha256 != iSha256;
-
- sdCurrent.strDomain = strDomain;
- // XXX If the node public key is changing, delete old public key information?
- // XXX Only if no other refs to keep it arround, other wise we have an attack vector.
- sdCurrent.naPublicKey = naNodePublic;
-
- // WriteLog (lsTRACE, UniqueNodeList) << boost::format("sdCurrent.naPublicKey: '%s'") % sdCurrent.naPublicKey.humanNodePublic();
-
- sdCurrent.tpFetch = boost::posix_time::second_clock::universal_time ();
- sdCurrent.iSha256 = iSha256;
-
- setSeedDomains (sdCurrent, true);
-
- if (bChangedB)
- {
- WriteLog (lsTRACE, UniqueNodeList) << boost::format ("Validator: '%s' processing new " NODE_FILE_NAME ".") % strDomain;
- processFile (strDomain, naNodePublic, secSite);
- }
else
{
- WriteLog (lsTRACE, UniqueNodeList) << boost::format ("Validator: '%s' no change for " NODE_FILE_NAME ".") % strDomain;
+ // Failed: Update
+
+ // XXX If we have public key, perhaps try look up in CAS?
fetchFinish ();
}
}
- else
- {
- // Failed: Update
- // XXX If we have public key, perhaps try look up in CAS?
- fetchFinish ();
- }
+ return bReject;
}
- return bReject;
-}
+ //--------------------------------------------------------------------------
-// Get the ripple.txt and process it.
-void UniqueNodeList::fetchProcess (std::string strDomain)
-{
- WriteLog (lsTRACE, UniqueNodeList) << "Fetching '" NODE_FILE_NAME "' from '" << strDomain << "'.";
-
- std::deque deqSites;
-
- // Order searching from most specifically for purpose to generic.
- // This order allows the client to take the most burden rather than the servers.
- deqSites.push_back (str (boost::format (SYSTEM_NAME ".%s") % strDomain));
- deqSites.push_back (str (boost::format ("www.%s") % strDomain));
- deqSites.push_back (strDomain);
-
- HttpsClient::httpsGet (
- true,
- getApp().getIOService (),
- deqSites,
- 443,
- NODE_FILE_PATH,
- NODE_FILE_BYTES_MAX,
- boost::posix_time::seconds (NODE_FETCH_SECONDS),
- BIND_TYPE (&UniqueNodeList::responseFetch, this, strDomain, P_1, P_2, P_3));
-}
-
-void UniqueNodeList::fetchTimerHandler (const boost::system::error_code& err)
-{
- if (!err)
+ // Try to process the next fetch of a ripple.txt.
+ void fetchNext ()
{
- // Time to check for another fetch.
- WriteLog (lsTRACE, UniqueNodeList) << "fetchTimerHandler";
- fetchNext ();
- }
-}
+ bool bFull;
-// Try to process the next fetch of a ripple.txt.
-void UniqueNodeList::fetchNext ()
-{
- bool bFull;
-
- {
- boost::mutex::scoped_lock sl (mFetchLock);
-
- bFull = mFetchActive == NODE_FETCH_JOBS;
- }
-
- if (!bFull)
- {
- // Determine next scan.
- std::string strDomain;
- boost::posix_time::ptime tpNext;
- boost::posix_time::ptime tpNow;
-
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
- Database* db = getApp().getWalletDB ()->getDB ();
-
- if (db->executeSQL ("SELECT Domain,Next FROM SeedDomains INDEXED BY SeedDomainNext ORDER BY Next LIMIT 1;")
- && db->startIterRows ())
- {
- int iNext = db->getInt ("Next");
-
- tpNext = ptFromSeconds (iNext);
- tpNow = boost::posix_time::second_clock::universal_time ();
-
- WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: iNext=%s tpNext=%s tpNow=%s") % iNext % tpNext % tpNow);
- strDomain = db->getStrBinary ("Domain");
-
- db->endIterRows ();
- }
-
- if (!strDomain.empty ())
{
boost::mutex::scoped_lock sl (mFetchLock);
bFull = mFetchActive == NODE_FETCH_JOBS;
+ }
- if (!bFull && tpNext <= tpNow)
+ if (!bFull)
+ {
+ // Determine next scan.
+ std::string strDomain;
+ boost::posix_time::ptime tpNext;
+ boost::posix_time::ptime tpNow;
+
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+ Database* db = getApp().getWalletDB ()->getDB ();
+
+ if (db->executeSQL ("SELECT Domain,Next FROM SeedDomains INDEXED BY SeedDomainNext ORDER BY Next LIMIT 1;")
+ && db->startIterRows ())
{
- mFetchActive++;
+ int iNext = db->getInt ("Next");
+
+ tpNext = ptFromSeconds (iNext);
+ tpNow = boost::posix_time::second_clock::universal_time ();
+
+ WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: iNext=%s tpNext=%s tpNow=%s") % iNext % tpNext % tpNow);
+ strDomain = db->getStrBinary ("Domain");
+
+ db->endIterRows ();
+ }
+
+ if (!strDomain.empty ())
+ {
+ boost::mutex::scoped_lock sl (mFetchLock);
+
+ bFull = mFetchActive == NODE_FETCH_JOBS;
+
+ if (!bFull && tpNext <= tpNow)
+ {
+ mFetchActive++;
+ }
+ }
+
+ if (strDomain.empty () || bFull)
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: strDomain=%s bFull=%d") % strDomain % bFull);
+
+ nothing ();
+ }
+ else if (tpNext > tpNow)
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: set timer : strDomain=%s") % strDomain);
+ // Fetch needs to happen in the future. Set a timer to wake us.
+ mtpFetchNext = tpNext;
+
+#if 1
+ double const seconds = (tpNext - tpNow).seconds ();
+
+ m_fetchTimer.setExpiration (seconds);
+#else
+ mdtFetchTimer.expires_at (mtpFetchNext);
+ mdtFetchTimer.async_wait (BIND_TYPE (&UniqueNodeListImp::fetchTimerHandler, this, P_1));
+#endif
+ }
+ else
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: fetch now: strDomain=%s tpNext=%s tpNow=%s") % strDomain % tpNext % tpNow);
+ // Fetch needs to happen now.
+ mtpFetchNext = boost::posix_time::ptime (boost::posix_time::not_a_date_time);
+
+ seedDomain sdCurrent;
+ bool bFound = getSeedDomains (strDomain, sdCurrent);
+
+ assert (bFound);
+
+ // Update time of next fetch and this scan attempt.
+ sdCurrent.tpScan = tpNow;
+
+ // XXX Use a longer duration if we have lots of validators.
+ sdCurrent.tpNext = sdCurrent.tpScan + boost::posix_time::hours (7 * 24);
+
+ setSeedDomains (sdCurrent, false);
+
+ WriteLog (lsTRACE, UniqueNodeList) << "Validator: '" << strDomain << "' fetching " NODE_FILE_NAME ".";
+
+ fetchProcess (strDomain); // Go get it.
+
+ fetchNext (); // Look for more.
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Called when we need to update scores.
+ void fetchDirty ()
+ {
+ // Note update.
+ mtpFetchUpdated = boost::posix_time::second_clock::universal_time ();
+ miscSave ();
+
+ // Update scores.
+ scoreNext (false);
+ }
+
+
+ //--------------------------------------------------------------------------
+
+ void fetchFinish ()
+ {
+ {
+ boost::mutex::scoped_lock sl (mFetchLock);
+ mFetchActive--;
+ }
+
+ fetchNext ();
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Get the ripple.txt and process it.
+ void fetchProcess (std::string strDomain)
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << "Fetching '" NODE_FILE_NAME "' from '" << strDomain << "'.";
+
+ std::deque deqSites;
+
+ // Order searching from most specifically for purpose to generic.
+ // This order allows the client to take the most burden rather than the servers.
+ deqSites.push_back (str (boost::format (SYSTEM_NAME ".%s") % strDomain));
+ deqSites.push_back (str (boost::format ("www.%s") % strDomain));
+ deqSites.push_back (strDomain);
+
+ HttpsClient::httpsGet (
+ true,
+ getApp().getIOService (),
+ deqSites,
+ 443,
+ NODE_FILE_PATH,
+ NODE_FILE_BYTES_MAX,
+ boost::posix_time::seconds (NODE_FETCH_SECONDS),
+ BIND_TYPE (&UniqueNodeListImp::responseFetch, this, strDomain, P_1, P_2, P_3));
+ }
+
+ //--------------------------------------------------------------------------
+
+ void fetchTimerHandler (const boost::system::error_code& err)
+ {
+ if (!err)
+ {
+ onDeadlineTimer (m_fetchTimer);
+ }
+ }
+
+
+ //--------------------------------------------------------------------------
+
+ // Process Section [validators_url].
+ void getValidatorsUrl (const RippleAddress& naNodePublic, Section secSite)
+ {
+ std::string strValidatorsUrl;
+ std::string strScheme;
+ std::string strDomain;
+ int iPort;
+ std::string strPath;
+
+ if (SectionSingleB (secSite, SECTION_VALIDATORS_URL, strValidatorsUrl)
+ && !strValidatorsUrl.empty ()
+ && parseUrl (strValidatorsUrl, strScheme, strDomain, iPort, strPath)
+ && -1 == iPort
+ && strScheme == "https")
+ {
+ HttpsClient::httpsGet (
+ true,
+ getApp().getIOService (),
+ strDomain,
+ 443,
+ strPath,
+ NODE_FILE_BYTES_MAX,
+ boost::posix_time::seconds (NODE_FETCH_SECONDS),
+ BIND_TYPE (&UniqueNodeListImp::responseValidators, this, strValidatorsUrl, naNodePublic, secSite, strDomain, P_1, P_2, P_3));
+ }
+ else
+ {
+ getIpsUrl (naNodePublic, secSite);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Process Section [ips_url].
+ // If we have a Section with a single entry, fetch the url and process it.
+ void getIpsUrl (const RippleAddress& naNodePublic, Section secSite)
+ {
+ std::string strIpsUrl;
+ std::string strScheme;
+ std::string strDomain;
+ int iPort;
+ std::string strPath;
+
+ if (SectionSingleB (secSite, SECTION_IPS_URL, strIpsUrl)
+ && !strIpsUrl.empty ()
+ && parseUrl (strIpsUrl, strScheme, strDomain, iPort, strPath)
+ && -1 == iPort
+ && strScheme == "https")
+ {
+ HttpsClient::httpsGet (
+ true,
+ getApp().getIOService (),
+ strDomain,
+ 443,
+ strPath,
+ NODE_FILE_BYTES_MAX,
+ boost::posix_time::seconds (NODE_FETCH_SECONDS),
+ BIND_TYPE (&UniqueNodeListImp::responseIps, this, strDomain, naNodePublic, P_1, P_2, P_3));
+ }
+ else
+ {
+ fetchFinish ();
+ }
+ }
+
+
+ //--------------------------------------------------------------------------
+
+ // Given a Section with IPs, parse and persist it for a validator.
+ bool responseIps (const std::string& strSite, const RippleAddress& naNodePublic, const boost::system::error_code& err, int iStatus, const std::string& strIpsFile)
+ {
+ bool bReject = !err && iStatus != 200;
+
+ if (!bReject)
+ {
+ if (!err)
+ {
+ Section secFile = ParseSection (strIpsFile, true);
+
+ processIps (strSite, naNodePublic, SectionEntries (secFile, SECTION_IPS));
+ }
+
+ fetchFinish ();
+ }
+
+ return bReject;
+ }
+
+ // After fetching a ripple.txt from a web site, given a Section with validators, parse and persist it.
+ bool responseValidators (const std::string& strValidatorsUrl, const RippleAddress& naNodePublic, Section secSite, const std::string& strSite, const boost::system::error_code& err, int iStatus, const std::string& strValidatorsFile)
+ {
+ bool bReject = !err && iStatus != 200;
+
+ if (!bReject)
+ {
+ if (!err)
+ {
+ Section secFile = ParseSection (strValidatorsFile, true);
+
+ processValidators (strSite, strValidatorsUrl, naNodePublic, vsValidator, SectionEntries (secFile, SECTION_VALIDATORS));
+ }
+
+ getIpsUrl (naNodePublic, secSite);
+ }
+
+ return bReject;
+ }
+
+
+ //--------------------------------------------------------------------------
+
+ // Persist the IPs refered to by a Validator.
+ // --> strSite: source of the IPs (for debugging)
+ // --> naNodePublic: public key of the validating node.
+ void processIps (const std::string& strSite, const RippleAddress& naNodePublic, Section::mapped_type* pmtVecStrIps)
+ {
+ Database* db = getApp().getWalletDB ()->getDB ();
+
+ std::string strEscNodePublic = sqlEscape (naNodePublic.humanNodePublic ());
+
+ WriteLog (lsDEBUG, UniqueNodeList)
+ << str (boost::format ("Validator: '%s' processing %d ips.")
+ % strSite % ( pmtVecStrIps ? pmtVecStrIps->size () : 0));
+
+ // Remove all current Validator's entries in IpReferrals
+ {
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+ db->executeSQL (str (boost::format ("DELETE FROM IpReferrals WHERE Validator=%s;") % strEscNodePublic));
+ // XXX Check result.
+ }
+
+ // Add new referral entries.
+ if (pmtVecStrIps && !pmtVecStrIps->empty ())
+ {
+ std::vector vstrValues;
+
+ vstrValues.resize (std::min ((int) pmtVecStrIps->size (), REFERRAL_IPS_MAX));
+
+ int iValues = 0;
+ BOOST_FOREACH (const std::string & strReferral, *pmtVecStrIps)
+ {
+ if (iValues == REFERRAL_VALIDATORS_MAX)
+ break;
+
+ std::string strIP;
+ int iPort;
+ bool bValid = parseIpPort (strReferral, strIP, iPort);
+
+ // XXX Filter out private network ips.
+ // XXX http://en.wikipedia.org/wiki/Private_network
+
+ if (bValid)
+ {
+ vstrValues[iValues] = str (boost::format ("(%s,%d,%s,%d)")
+ % strEscNodePublic % iValues % sqlEscape (strIP) % iPort);
+ iValues++;
+ }
+ else
+ {
+ WriteLog (lsTRACE, UniqueNodeList)
+ << str (boost::format ("Validator: '%s' [" SECTION_IPS "]: rejecting '%s'")
+ % strSite % strReferral);
+ }
+ }
+
+ if (iValues)
+ {
+ vstrValues.resize (iValues);
+
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+ db->executeSQL (str (boost::format ("INSERT INTO IpReferrals (Validator,Entry,IP,Port) VALUES %s;")
+ % strJoin (vstrValues.begin (), vstrValues.end (), ",")));
+ // XXX Check result.
}
}
- if (strDomain.empty () || bFull)
- {
- WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: strDomain=%s bFull=%d") % strDomain % bFull);
-
- nothing ();
- }
- else if (tpNext > tpNow)
- {
- WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: set timer : strDomain=%s") % strDomain);
- // Fetch needs to happen in the future. Set a timer to wake us.
- mtpFetchNext = tpNext;
-
- mdtFetchTimer.expires_at (mtpFetchNext);
- mdtFetchTimer.async_wait (BIND_TYPE (&UniqueNodeList::fetchTimerHandler, this, P_1));
- }
- else
- {
- WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: fetch now: strDomain=%s tpNext=%s tpNow=%s") % strDomain % tpNext % tpNow);
- // Fetch needs to happen now.
- mtpFetchNext = boost::posix_time::ptime (boost::posix_time::not_a_date_time);
-
- seedDomain sdCurrent;
- bool bFound = getSeedDomains (strDomain, sdCurrent);
-
- assert (bFound);
-
- // Update time of next fetch and this scan attempt.
- sdCurrent.tpScan = tpNow;
-
- // XXX Use a longer duration if we have lots of validators.
- sdCurrent.tpNext = sdCurrent.tpScan + boost::posix_time::hours (7 * 24);
-
- setSeedDomains (sdCurrent, false);
-
- WriteLog (lsTRACE, UniqueNodeList) << "Validator: '" << strDomain << "' fetching " NODE_FILE_NAME ".";
-
- fetchProcess (strDomain); // Go get it.
-
- fetchNext (); // Look for more.
- }
- }
-}
-
-// For each kind of source, have a starting number of points to be distributed.
-int UniqueNodeList::iSourceScore (ValidatorSource vsWhy)
-{
- int iScore = 0;
-
- switch (vsWhy)
- {
- case vsConfig:
- iScore = 1500;
- break;
-
- case vsInbound:
- iScore = 0;
- break;
-
- case vsManual:
- iScore = 1500;
- break;
-
- case vsReferral:
- iScore = 0;
- break;
-
- case vsTold:
- iScore = 0;
- break;
-
- case vsValidator:
- iScore = 1000;
- break;
-
- case vsWeb:
- iScore = 200;
- break;
-
- default:
- throw std::runtime_error ("Internal error: bad ValidatorSource.");
+ fetchDirty ();
}
- return iScore;
-}
+ //--------------------------------------------------------------------------
-// Retrieve a SeedDomain from DB.
-bool UniqueNodeList::getSeedDomains (const std::string& strDomain, seedDomain& dstSeedDomain)
-{
- bool bResult;
- Database* db = getApp().getWalletDB ()->getDB ();
-
- std::string strSql = boost::str (boost::format ("SELECT * FROM SeedDomains WHERE Domain=%s;")
- % sqlEscape (strDomain));
-
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- bResult = db->executeSQL (strSql) && db->startIterRows ();
-
- if (bResult)
+ // Persist ValidatorReferrals.
+ // --> strSite: source site for display
+ // --> strValidatorsSrc: source details for display
+ // --> naNodePublic: remote source public key - not valid for local
+ // --> vsWhy: reason for adding validator to SeedDomains or SeedNodes.
+ int processValidators (const std::string& strSite, const std::string& strValidatorsSrc, const RippleAddress& naNodePublic, ValidatorSource vsWhy, Section::mapped_type* pmtVecStrValidators)
{
- std::string strPublicKey;
- int iNext;
- int iScan;
- int iFetch;
- std::string strSha256;
+ Database* db = getApp().getWalletDB ()->getDB ();
+ std::string strNodePublic = naNodePublic.isValid () ? naNodePublic.humanNodePublic () : strValidatorsSrc;
+ int iValues = 0;
- dstSeedDomain.strDomain = db->getStrBinary ("Domain");
+ WriteLog (lsTRACE, UniqueNodeList)
+ << str (boost::format ("Validator: '%s' : '%s' : processing %d validators.")
+ % strSite
+ % strValidatorsSrc
+ % ( pmtVecStrValidators ? pmtVecStrValidators->size () : 0));
- if (!db->getNull ("PublicKey") && db->getStr ("PublicKey", strPublicKey))
+ // Remove all current Validator's entries in ValidatorReferrals
{
- dstSeedDomain.naPublicKey.setNodePublic (strPublicKey);
- }
- else
- {
- dstSeedDomain.naPublicKey.clear ();
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ db->executeSQL (str (boost::format ("DELETE FROM ValidatorReferrals WHERE Validator='%s';") % strNodePublic));
+ // XXX Check result.
}
- std::string strSource = db->getStrBinary ("Source");
- dstSeedDomain.vsSource = static_cast (strSource[0]);
-
- iNext = db->getInt ("Next");
- dstSeedDomain.tpNext = ptFromSeconds (iNext);
- iScan = db->getInt ("Scan");
- dstSeedDomain.tpScan = ptFromSeconds (iScan);
- iFetch = db->getInt ("Fetch");
- dstSeedDomain.tpFetch = ptFromSeconds (iFetch);
-
- if (!db->getNull ("Sha256") && db->getStr ("Sha256", strSha256))
+ // Add new referral entries.
+ if (pmtVecStrValidators && pmtVecStrValidators->size ())
{
- dstSeedDomain.iSha256.SetHex (strSha256);
- }
- else
- {
- dstSeedDomain.iSha256.zero ();
+ std::vector vstrValues;
+
+ vstrValues.reserve (std::min ((int) pmtVecStrValidators->size (), REFERRAL_VALIDATORS_MAX));
+
+ BOOST_FOREACH (const std::string & strReferral, *pmtVecStrValidators)
+ {
+ if (iValues == REFERRAL_VALIDATORS_MAX)
+ break;
+
+ boost::smatch smMatch;
+
+ // domain comment?
+ // public_key comment?
+ static boost::regex reReferral ("\\`\\s*(\\S+)(?:\\s+(.+))?\\s*\\'");
+
+ if (!boost::regex_match (strReferral, smMatch, reReferral))
+ {
+ WriteLog (lsWARNING, UniqueNodeList) << str (boost::format ("Bad validator: syntax error: %s: %s") % strSite % strReferral);
+ }
+ else
+ {
+ std::string strRefered = smMatch[1];
+ std::string strComment = smMatch[2];
+ RippleAddress naValidator;
+
+ if (naValidator.setSeedGeneric (strRefered))
+ {
+
+ WriteLog (lsWARNING, UniqueNodeList) << str (boost::format ("Bad validator: domain or public key required: %s %s") % strRefered % strComment);
+ }
+ else if (naValidator.setNodePublic (strRefered))
+ {
+ // A public key.
+ // XXX Schedule for CAS lookup.
+ nodeAddPublic (naValidator, vsWhy, strComment);
+
+ WriteLog (lsINFO, UniqueNodeList) << str (boost::format ("Node Public: %s %s") % strRefered % strComment);
+
+ if (naNodePublic.isValid ())
+ vstrValues.push_back (str (boost::format ("('%s',%d,'%s')") % strNodePublic % iValues % naValidator.humanNodePublic ()));
+
+ iValues++;
+ }
+ else
+ {
+ // A domain: need to look it up.
+ nodeAddDomain (strRefered, vsWhy, strComment);
+
+ WriteLog (lsINFO, UniqueNodeList) << str (boost::format ("Node Domain: %s %s") % strRefered % strComment);
+
+ if (naNodePublic.isValid ())
+ vstrValues.push_back (str (boost::format ("('%s',%d,%s)") % strNodePublic % iValues % sqlEscape (strRefered)));
+
+ iValues++;
+ }
+ }
+ }
+
+ if (!vstrValues.empty ())
+ {
+ std::string strSql = str (boost::format ("INSERT INTO ValidatorReferrals (Validator,Entry,Referral) VALUES %s;")
+ % strJoin (vstrValues.begin (), vstrValues.end (), ","));
+
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ db->executeSQL (strSql);
+ // XXX Check result.
+ }
}
- dstSeedDomain.strComment = db->getStrBinary ("Comment");
+ fetchDirty ();
- db->endIterRows ();
+ return iValues;
}
- return bResult;
-}
+ //--------------------------------------------------------------------------
-// Persist a SeedDomain.
-void UniqueNodeList::setSeedDomains (const seedDomain& sdSource, bool bNext)
-{
- Database* db = getApp().getWalletDB ()->getDB ();
-
- int iNext = iToSeconds (sdSource.tpNext);
- int iScan = iToSeconds (sdSource.tpScan);
- int iFetch = iToSeconds (sdSource.tpFetch);
-
- // WriteLog (lsTRACE) << str(boost::format("setSeedDomains: iNext=%s tpNext=%s") % iNext % sdSource.tpNext);
-
- std::string strSql = boost::str (boost::format ("REPLACE INTO SeedDomains (Domain,PublicKey,Source,Next,Scan,Fetch,Sha256,Comment) VALUES (%s, %s, %s, %d, %d, %d, '%s', %s);")
- % sqlEscape (sdSource.strDomain)
- % (sdSource.naPublicKey.isValid () ? sqlEscape (sdSource.naPublicKey.humanNodePublic ()) : "NULL")
- % sqlEscape (std::string (1, static_cast (sdSource.vsSource)))
- % iNext
- % iScan
- % iFetch
- % sdSource.iSha256.GetHex ()
- % sqlEscape (sdSource.strComment)
- );
-
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- if (!db->executeSQL (strSql))
+ // Process a ripple.txt.
+ void processFile (const std::string& strDomain, const RippleAddress& naNodePublic, Section secSite)
{
- // XXX Check result.
- WriteLog (lsWARNING, UniqueNodeList) << "setSeedDomains: failed.";
- }
+ //
+ // Process Validators
+ //
+ processValidators (strDomain, NODE_FILE_NAME, naNodePublic, vsReferral, SectionEntries (secSite, SECTION_VALIDATORS));
- if (bNext && (mtpFetchNext.is_not_a_date_time () || mtpFetchNext > sdSource.tpNext))
- {
- // Schedule earlier wake up.
- fetchNext ();
- }
-}
+ //
+ // Process ips
+ //
+ processIps (strDomain, naNodePublic, SectionEntries (secSite, SECTION_IPS));
-// Queue a domain for a single attempt fetch a ripple.txt.
-// --> strComment: only used on vsManual
-// YYY As a lot of these may happen at once, would be nice to wrap multiple calls in a transaction.
-void UniqueNodeList::nodeAddDomain (std::string strDomain, ValidatorSource vsWhy, const std::string& strComment)
-{
- boost::trim (strDomain);
- boost::to_lower (strDomain);
+ //
+ // Process currencies
+ //
+ Section::mapped_type* pvCurrencies;
- // YYY Would be best to verify strDomain is a valid domain.
- // WriteLog (lsTRACE) << str(boost::format("nodeAddDomain: '%s' %c '%s'")
- // % strDomain
- // % vsWhy
- // % strComment);
-
- seedDomain sdCurrent;
-
- bool bFound = getSeedDomains (strDomain, sdCurrent);
- bool bChanged = false;
-
- if (!bFound)
- {
- sdCurrent.strDomain = strDomain;
- sdCurrent.tpNext = boost::posix_time::second_clock::universal_time ();
- }
-
- // Promote source, if needed.
- if (!bFound || iSourceScore (vsWhy) >= iSourceScore (sdCurrent.vsSource))
- {
- sdCurrent.vsSource = vsWhy;
- sdCurrent.strComment = strComment;
- bChanged = true;
- }
-
- if (vsManual == vsWhy)
- {
- // A manual add forces immediate scan.
- sdCurrent.tpNext = boost::posix_time::second_clock::universal_time ();
- bChanged = true;
- }
-
- if (bChanged)
- setSeedDomains (sdCurrent, true);
-}
-
-// Retrieve a SeedNode from DB.
-bool UniqueNodeList::getSeedNodes (const RippleAddress& naNodePublic, seedNode& dstSeedNode)
-{
- bool bResult;
- Database* db = getApp().getWalletDB ()->getDB ();
-
- std::string strSql = str (boost::format ("SELECT * FROM SeedNodes WHERE PublicKey='%s';")
- % naNodePublic.humanNodePublic ());
-
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- bResult = db->executeSQL (strSql) && db->startIterRows ();
-
- if (bResult)
- {
- std::string strPublicKey;
- std::string strSource;
- int iNext;
- int iScan;
- int iFetch;
- std::string strSha256;
-
- if (!db->getNull ("PublicKey") && db->getStr ("PublicKey", strPublicKey))
+ if ((pvCurrencies = SectionEntries (secSite, SECTION_CURRENCIES)) && pvCurrencies->size ())
{
- dstSeedNode.naPublicKey.setNodePublic (strPublicKey);
- }
- else
- {
- dstSeedNode.naPublicKey.clear ();
+ // XXX Process currencies.
+ WriteLog (lsWARNING, UniqueNodeList) << "Ignoring currencies: not implemented.";
}
- strSource = db->getStrBinary ("Source");
- dstSeedNode.vsSource = static_cast (strSource[0]);
-
- iNext = db->getInt ("Next");
- dstSeedNode.tpNext = ptFromSeconds (iNext);
- iScan = db->getInt ("Scan");
- dstSeedNode.tpScan = ptFromSeconds (iScan);
- iFetch = db->getInt ("Fetch");
- dstSeedNode.tpFetch = ptFromSeconds (iFetch);
-
- if (!db->getNull ("Sha256") && db->getStr ("Sha256", strSha256))
- {
- dstSeedNode.iSha256.SetHex (strSha256);
- }
- else
- {
- dstSeedNode.iSha256.zero ();
- }
-
- dstSeedNode.strComment = db->getStrBinary ("Comment");
-
- db->endIterRows ();
+ getValidatorsUrl (naNodePublic, secSite);
}
- return bResult;
-}
-
-// Persist a SeedNode.
-// <-- bNext: true, to do fetching if needed.
-void UniqueNodeList::setSeedNodes (const seedNode& snSource, bool bNext)
-{
- Database* db = getApp().getWalletDB ()->getDB ();
-
- int iNext = iToSeconds (snSource.tpNext);
- int iScan = iToSeconds (snSource.tpScan);
- int iFetch = iToSeconds (snSource.tpFetch);
-
- // WriteLog (lsTRACE) << str(boost::format("setSeedNodes: iNext=%s tpNext=%s") % iNext % sdSource.tpNext);
-
- assert (snSource.naPublicKey.isValid ());
-
- std::string strSql = str (boost::format ("REPLACE INTO SeedNodes (PublicKey,Source,Next,Scan,Fetch,Sha256,Comment) VALUES ('%s', '%c', %d, %d, %d, '%s', %s);")
- % snSource.naPublicKey.humanNodePublic ()
- % static_cast (snSource.vsSource)
- % iNext
- % iScan
- % iFetch
- % snSource.iSha256.GetHex ()
- % sqlEscape (snSource.strComment)
- );
+ //--------------------------------------------------------------------------
+ // Retrieve a SeedDomain from DB.
+ bool getSeedDomains (const std::string& strDomain, seedDomain& dstSeedDomain)
{
+ bool bResult;
+ Database* db = getApp().getWalletDB ()->getDB ();
+
+ std::string strSql = boost::str (boost::format ("SELECT * FROM SeedDomains WHERE Domain=%s;")
+ % sqlEscape (strDomain));
+
+ boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
+
+ bResult = db->executeSQL (strSql) && db->startIterRows ();
+
+ if (bResult)
+ {
+ std::string strPublicKey;
+ int iNext;
+ int iScan;
+ int iFetch;
+ std::string strSha256;
+
+ dstSeedDomain.strDomain = db->getStrBinary ("Domain");
+
+ if (!db->getNull ("PublicKey") && db->getStr ("PublicKey", strPublicKey))
+ {
+ dstSeedDomain.naPublicKey.setNodePublic (strPublicKey);
+ }
+ else
+ {
+ dstSeedDomain.naPublicKey.clear ();
+ }
+
+ std::string strSource = db->getStrBinary ("Source");
+ dstSeedDomain.vsSource = static_cast (strSource[0]);
+
+ iNext = db->getInt ("Next");
+ dstSeedDomain.tpNext = ptFromSeconds (iNext);
+ iScan = db->getInt ("Scan");
+ dstSeedDomain.tpScan = ptFromSeconds (iScan);
+ iFetch = db->getInt ("Fetch");
+ dstSeedDomain.tpFetch = ptFromSeconds (iFetch);
+
+ if (!db->getNull ("Sha256") && db->getStr ("Sha256", strSha256))
+ {
+ dstSeedDomain.iSha256.SetHex (strSha256);
+ }
+ else
+ {
+ dstSeedDomain.iSha256.zero ();
+ }
+
+ dstSeedDomain.strComment = db->getStrBinary ("Comment");
+
+ db->endIterRows ();
+ }
+
+ return bResult;
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Persist a SeedDomain.
+ void setSeedDomains (const seedDomain& sdSource, bool bNext)
+ {
+ Database* db = getApp().getWalletDB ()->getDB ();
+
+ int iNext = iToSeconds (sdSource.tpNext);
+ int iScan = iToSeconds (sdSource.tpScan);
+ int iFetch = iToSeconds (sdSource.tpFetch);
+
+ // WriteLog (lsTRACE) << str(boost::format("setSeedDomains: iNext=%s tpNext=%s") % iNext % sdSource.tpNext);
+
+ std::string strSql = boost::str (boost::format ("REPLACE INTO SeedDomains (Domain,PublicKey,Source,Next,Scan,Fetch,Sha256,Comment) VALUES (%s, %s, %s, %d, %d, %d, '%s', %s);")
+ % sqlEscape (sdSource.strDomain)
+ % (sdSource.naPublicKey.isValid () ? sqlEscape (sdSource.naPublicKey.humanNodePublic ()) : "NULL")
+ % sqlEscape (std::string (1, static_cast (sdSource.vsSource)))
+ % iNext
+ % iScan
+ % iFetch
+ % sdSource.iSha256.GetHex ()
+ % sqlEscape (sdSource.strComment)
+ );
+
boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
if (!db->executeSQL (strSql))
{
// XXX Check result.
- WriteLog (lsTRACE, UniqueNodeList) << "setSeedNodes: failed.";
+ WriteLog (lsWARNING, UniqueNodeList) << "setSeedDomains: failed.";
+ }
+
+ if (bNext && (mtpFetchNext.is_not_a_date_time () || mtpFetchNext > sdSource.tpNext))
+ {
+ // Schedule earlier wake up.
+ fetchNext ();
}
}
-#if 0
- // YYY When we have a cas schedule lookups similar to this.
- if (bNext && (mtpFetchNext.is_not_a_date_time () || mtpFetchNext > snSource.tpNext))
+ //--------------------------------------------------------------------------
+
+ // Retrieve a SeedNode from DB.
+ bool getSeedNodes (const RippleAddress& naNodePublic, seedNode& dstSeedNode)
{
- // Schedule earlier wake up.
- fetchNext ();
- }
+ bool bResult;
+ Database* db = getApp().getWalletDB ()->getDB ();
-#else
- fetchDirty ();
-#endif
-}
-
-// Add a trusted node. Called by RPC or other source.
-void UniqueNodeList::nodeAddPublic (const RippleAddress& naNodePublic, ValidatorSource vsWhy, const std::string& strComment)
-{
- seedNode snCurrent;
-
- bool bFound = getSeedNodes (naNodePublic, snCurrent);
- bool bChanged = false;
-
- if (!bFound)
- {
- snCurrent.naPublicKey = naNodePublic;
- snCurrent.tpNext = boost::posix_time::second_clock::universal_time ();
- }
-
- // Promote source, if needed.
- if (!bFound || iSourceScore (vsWhy) >= iSourceScore (snCurrent.vsSource))
- {
- snCurrent.vsSource = vsWhy;
- snCurrent.strComment = strComment;
- bChanged = true;
- }
-
- if (vsManual == vsWhy)
- {
- // A manual add forces immediate scan.
- snCurrent.tpNext = boost::posix_time::second_clock::universal_time ();
- bChanged = true;
- }
-
- if (bChanged)
- setSeedNodes (snCurrent, true);
-}
-
-void UniqueNodeList::nodeRemovePublic (const RippleAddress& naNodePublic)
-{
- {
- Database* db = getApp().getWalletDB ()->getDB ();
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- db->executeSQL (str (boost::format ("DELETE FROM SeedNodes WHERE PublicKey=%s") % sqlEscape (naNodePublic.humanNodePublic ())));
- db->executeSQL (str (boost::format ("DELETE FROM TrustedNodes WHERE PublicKey=%s") % sqlEscape (naNodePublic.humanNodePublic ())));
- }
-
- // YYY Only dirty on successful delete.
- fetchDirty ();
-
- boost::recursive_mutex::scoped_lock sl (mUNLLock);
- mUNL.erase (naNodePublic.humanNodePublic ());
-}
-
-void UniqueNodeList::nodeRemoveDomain (std::string strDomain)
-{
- boost::trim (strDomain);
- boost::to_lower (strDomain);
-
- {
- Database* db = getApp().getWalletDB ()->getDB ();
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- db->executeSQL (str (boost::format ("DELETE FROM SeedDomains WHERE Domain=%s") % sqlEscape (strDomain)));
- }
-
- // YYY Only dirty on successful delete.
- fetchDirty ();
-}
-
-void UniqueNodeList::nodeReset ()
-{
- {
- Database* db = getApp().getWalletDB ()->getDB ();
+ std::string strSql = str (boost::format ("SELECT * FROM SeedNodes WHERE PublicKey='%s';")
+ % naNodePublic.humanNodePublic ());
boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
- // XXX Check results.
- db->executeSQL ("DELETE FROM SeedDomains");
- db->executeSQL ("DELETE FROM SeedNodes");
- }
+ bResult = db->executeSQL (strSql) && db->startIterRows ();
- fetchDirty ();
-}
-
-Json::Value UniqueNodeList::getUnlJson ()
-{
- Database* db = getApp().getWalletDB ()->getDB ();
-
- Json::Value ret (Json::arrayValue);
-
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
- SQL_FOREACH (db, "SELECT * FROM TrustedNodes;")
- {
- Json::Value node (Json::objectValue);
-
- node["publicKey"] = db->getStrBinary ("PublicKey");
- node["comment"] = db->getStrBinary ("Comment");
-
- ret.append (node);
- }
-
- return ret;
-}
-
-bool UniqueNodeList::nodeLoad (boost::filesystem::path pConfig)
-{
- if (pConfig.empty ())
- {
- WriteLog (lsINFO, UniqueNodeList) << VALIDATORS_FILE_NAME " path not specified.";
-
- return false;
- }
-
- if (!boost::filesystem::exists (pConfig))
- {
- WriteLog (lsWARNING, UniqueNodeList) << str (boost::format (VALIDATORS_FILE_NAME " not found: %s") % pConfig);
-
- return false;
- }
-
- if (!boost::filesystem::is_regular_file (pConfig))
- {
- WriteLog (lsWARNING, UniqueNodeList) << str (boost::format (VALIDATORS_FILE_NAME " not regular file: %s") % pConfig);
-
- return false;
- }
-
- std::ifstream ifsDefault (pConfig.native ().c_str (), std::ios::in);
-
- if (!ifsDefault)
- {
- WriteLog (lsFATAL, UniqueNodeList) << str (boost::format (VALIDATORS_FILE_NAME " failed to open: %s") % pConfig);
-
- return false;
- }
-
- std::string strValidators;
-
- strValidators.assign ((std::istreambuf_iterator (ifsDefault)),
- std::istreambuf_iterator ());
-
- if (ifsDefault.bad ())
- {
- WriteLog (lsFATAL, UniqueNodeList) << str (boost::format ("Failed to read: %s") % pConfig);
-
- return false;
- }
-
- nodeProcess ("local", strValidators, pConfig.string ());
-
- WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("Processing: %s") % pConfig);
-
- return true;
-}
-
-bool UniqueNodeList::validatorsResponse (const boost::system::error_code& err, int iStatus, std::string strResponse)
-{
- bool bReject = !err && iStatus != 200;
-
- if (!bReject)
- {
- WriteLog (lsTRACE, UniqueNodeList) << "Fetch '" VALIDATORS_FILE_NAME "' complete.";
-
- if (!err)
+ if (bResult)
{
- nodeProcess ("network", strResponse, theConfig.VALIDATORS_SITE);
- }
- else
- {
- WriteLog (lsWARNING, UniqueNodeList) << "Error: " << err.message ();
- }
- }
+ std::string strPublicKey;
+ std::string strSource;
+ int iNext;
+ int iScan;
+ int iFetch;
+ std::string strSha256;
- return bReject;
-}
-
-void UniqueNodeList::nodeNetwork ()
-{
- if (!theConfig.VALIDATORS_SITE.empty ())
- {
- HttpsClient::httpsGet (
- true,
- getApp().getIOService (),
- theConfig.VALIDATORS_SITE,
- 443,
- theConfig.VALIDATORS_URI,
- VALIDATORS_FILE_BYTES_MAX,
- boost::posix_time::seconds (VALIDATORS_FETCH_SECONDS),
- BIND_TYPE (&UniqueNodeList::validatorsResponse, this, P_1, P_2, P_3));
- }
-}
-
-void UniqueNodeList::nodeBootstrap ()
-{
- int iDomains = 0;
- int iNodes = 0;
- Database* db = getApp().getWalletDB ()->getDB ();
-
- {
- boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
-
- if (db->executeSQL (str (boost::format ("SELECT COUNT(*) AS Count FROM SeedDomains WHERE Source='%s' OR Source='%c';") % vsManual % vsValidator)) && db->startIterRows ())
- iDomains = db->getInt ("Count");
-
- db->endIterRows ();
-
- if (db->executeSQL (str (boost::format ("SELECT COUNT(*) AS Count FROM SeedNodes WHERE Source='%s' OR Source='%c';") % vsManual % vsValidator)) && db->startIterRows ())
- iNodes = db->getInt ("Count");
-
- db->endIterRows ();
- }
-
- bool bLoaded = iDomains || iNodes;
-
- // Always merge in the file specified in the config.
- if (!theConfig.VALIDATORS_FILE.empty ())
- {
- WriteLog (lsINFO, UniqueNodeList) << "Bootstrapping UNL: loading from unl_default.";
-
- bLoaded = nodeLoad (theConfig.VALIDATORS_FILE);
- }
-
- // If never loaded anything try the current directory.
- if (!bLoaded && theConfig.VALIDATORS_FILE.empty ())
- {
- WriteLog (lsINFO, UniqueNodeList) << boost::str (boost::format ("Bootstrapping UNL: loading from '%s'.")
- % theConfig.VALIDATORS_BASE);
-
- bLoaded = nodeLoad (theConfig.VALIDATORS_BASE);
- }
-
- // Always load from rippled.cfg
- if (!theConfig.VALIDATORS.empty ())
- {
- RippleAddress naInvalid; // Don't want a referrer on added entries.
-
- WriteLog (lsINFO, UniqueNodeList) << boost::str (boost::format ("Bootstrapping UNL: loading from '%s'.")
- % theConfig.CONFIG_FILE);
-
- if (processValidators ("local", theConfig.CONFIG_FILE.string (), naInvalid, vsConfig, &theConfig.VALIDATORS))
- bLoaded = true;
- }
-
- if (!bLoaded)
- {
- WriteLog (lsINFO, UniqueNodeList) << boost::str (boost::format ("Bootstrapping UNL: loading from '%s'.")
- % theConfig.VALIDATORS_SITE);
-
- nodeNetwork ();
- }
-
- if (!theConfig.IPS.empty ())
- {
- std::vector vstrValues;
-
- vstrValues.reserve (theConfig.IPS.size ());
-
- BOOST_FOREACH (const std::string & strPeer, theConfig.IPS)
- {
- std::string strIP;
- int iPort;
-
- if (parseIpPort (strPeer, strIP, iPort))
+ if (!db->getNull ("PublicKey") && db->getStr ("PublicKey", strPublicKey))
{
- vstrValues.push_back (str (boost::format ("(%s,'%c')")
- % sqlEscape (str (boost::format ("%s %d") % strIP % iPort))
- % static_cast (vsConfig)));
+ dstSeedNode.naPublicKey.setNodePublic (strPublicKey);
}
+ else
+ {
+ dstSeedNode.naPublicKey.clear ();
+ }
+
+ strSource = db->getStrBinary ("Source");
+ dstSeedNode.vsSource = static_cast (strSource[0]);
+
+ iNext = db->getInt ("Next");
+ dstSeedNode.tpNext = ptFromSeconds (iNext);
+ iScan = db->getInt ("Scan");
+ dstSeedNode.tpScan = ptFromSeconds (iScan);
+ iFetch = db->getInt ("Fetch");
+ dstSeedNode.tpFetch = ptFromSeconds (iFetch);
+
+ if (!db->getNull ("Sha256") && db->getStr ("Sha256", strSha256))
+ {
+ dstSeedNode.iSha256.SetHex (strSha256);
+ }
+ else
+ {
+ dstSeedNode.iSha256.zero ();
+ }
+
+ dstSeedNode.strComment = db->getStrBinary ("Comment");
+
+ db->endIterRows ();
}
- if (!vstrValues.empty ())
+ return bResult;
+ }
+
+ //--------------------------------------------------------------------------
+
+ // Persist a SeedNode.
+ // <-- bNext: true, to do fetching if needed.
+ void setSeedNodes (const seedNode& snSource, bool bNext)
+ {
+ Database* db = getApp().getWalletDB ()->getDB ();
+
+ int iNext = iToSeconds (snSource.tpNext);
+ int iScan = iToSeconds (snSource.tpScan);
+ int iFetch = iToSeconds (snSource.tpFetch);
+
+ // WriteLog (lsTRACE) << str(boost::format("setSeedNodes: iNext=%s tpNext=%s") % iNext % sdSource.tpNext);
+
+ assert (snSource.naPublicKey.isValid ());
+
+ std::string strSql = str (boost::format ("REPLACE INTO SeedNodes (PublicKey,Source,Next,Scan,Fetch,Sha256,Comment) VALUES ('%s', '%c', %d, %d, %d, '%s', %s);")
+ % snSource.naPublicKey.humanNodePublic ()
+ % static_cast (snSource.vsSource)
+ % iNext
+ % iScan
+ % iFetch
+ % snSource.iSha256.GetHex ()
+ % sqlEscape (snSource.strComment)
+ );
+
{
boost::recursive_mutex::scoped_lock sl (getApp().getWalletDB ()->getDBLock ());
- db->executeSQL (str (boost::format ("REPLACE INTO PeerIps (IpPort,Source) VALUES %s;")
- % strJoin (vstrValues.begin (), vstrValues.end (), ",")));
+ if (!db->executeSQL (strSql))
+ {
+ // XXX Check result.
+ WriteLog (lsTRACE, UniqueNodeList) << "setSeedNodes: failed.";
+ }
}
+ #if 0
+
+ // YYY When we have a cas schedule lookups similar to this.
+ if (bNext && (mtpFetchNext.is_not_a_date_time () || mtpFetchNext > snSource.tpNext))
+ {
+ // Schedule earlier wake up.
+ fetchNext ();
+ }
+
+ #else
fetchDirty ();
+ #endif
}
-}
-// Process a validators.txt.
-// --> strSite: source of validators
-// --> strValidators: contents of a validators.txt
-void UniqueNodeList::nodeProcess (const std::string& strSite, const std::string& strValidators, const std::string& strSource)
-{
- Section secValidators = ParseSection (strValidators, true);
+ //--------------------------------------------------------------------------
- Section::mapped_type* pmtEntries = SectionEntries (secValidators, SECTION_VALIDATORS);
-
- if (pmtEntries)
+ bool validatorsResponse (const boost::system::error_code& err, int iStatus, std::string strResponse)
{
- RippleAddress naInvalid; // Don't want a referrer on added entries.
+ bool bReject = !err && iStatus != 200;
- // YYY Unspecified might be bootstrap or rpc command
- processValidators (strSite, strSource, naInvalid, vsValidator, pmtEntries);
+ if (!bReject)
+ {
+ WriteLog (lsTRACE, UniqueNodeList) << "Fetch '" VALIDATORS_FILE_NAME "' complete.";
+
+ if (!err)
+ {
+ nodeProcess ("network", strResponse, theConfig.VALIDATORS_SITE);
+ }
+ else
+ {
+ WriteLog (lsWARNING, UniqueNodeList) << "Error: " << err.message ();
+ }
+ }
+
+ return bReject;
}
- else
+
+ //--------------------------------------------------------------------------
+
+ // Process a validators.txt.
+ // --> strSite: source of validators
+ // --> strValidators: contents of a validators.txt
+ //
+ // VFALCO TODO Can't we name this processValidatorList?
+ //
+ void nodeProcess (const std::string& strSite, const std::string& strValidators, const std::string& strSource)
{
- WriteLog (lsWARNING, UniqueNodeList) << boost::str (boost::format ("'%s' missing [" SECTION_VALIDATORS "].")
- % theConfig.VALIDATORS_BASE);
+ Section secValidators = ParseSection (strValidators, true);
+
+ Section::mapped_type* pmtEntries = SectionEntries (secValidators, SECTION_VALIDATORS);
+
+ if (pmtEntries)
+ {
+ RippleAddress naInvalid; // Don't want a referrer on added entries.
+
+ // YYY Unspecified might be bootstrap or rpc command
+ processValidators (strSite, strSource, naInvalid, vsValidator, pmtEntries);
+ }
+ else
+ {
+ WriteLog (lsWARNING, UniqueNodeList) << boost::str (boost::format ("'%s' missing [" SECTION_VALIDATORS "].")
+ % theConfig.VALIDATORS_BASE);
+ }
}
-}
-bool UniqueNodeList::nodeInUNL (const RippleAddress& naNodePublic)
+ //--------------------------------------------------------------------------
+
+private:
+ // VFALCO TODO Replace ptime with beast::Time
+ // Misc persistent information
+ boost::posix_time::ptime mtpScoreUpdated;
+ boost::posix_time::ptime mtpFetchUpdated;
+
+ boost::recursive_mutex mUNLLock;
+ // XXX Make this faster, make this the contents vector unsigned char or raw public key.
+ // XXX Contents needs to based on score.
+ boost::unordered_set mUNL;
+
+ boost::posix_time::ptime mtpScoreNext; // When to start scoring.
+ boost::posix_time::ptime mtpScoreStart; // Time currently started scoring.
+ DeadlineTimer m_scoreTimer; // Timer to start scoring.
+
+ boost::mutex mFetchLock;
+ int mFetchActive; // Count of active fetches.
+
+ boost::posix_time::ptime mtpFetchNext; // Time of to start next fetch.
+ DeadlineTimer m_fetchTimer; // Timer to start fetching.
+
+ std::map m_clusterNodes;
+};
+
+UniqueNodeList* UniqueNodeList::New ()
{
- boost::recursive_mutex::scoped_lock sl (mUNLLock);
-
- return mUNL.end () != mUNL.find (naNodePublic.humanNodePublic ());
+ return new UniqueNodeListImp ();
}
-
-bool UniqueNodeList::nodeInCluster (const RippleAddress& naNodePublic)
-{
- boost::recursive_mutex::scoped_lock sl (mUNLLock);
- return sClusterNodes.end () != sClusterNodes.find (naNodePublic);
-}
-
-bool UniqueNodeList::nodeInCluster (const RippleAddress& naNodePublic, std::string& name)
-{
- boost::recursive_mutex::scoped_lock sl (mUNLLock);
- std::map::iterator it = sClusterNodes.find (naNodePublic);
-
- if (it == sClusterNodes.end ())
- return false;
-
- name = it->second;
- return true;
-}
-
-IUniqueNodeList* IUniqueNodeList::New (boost::asio::io_service& io_service)
-{
- return new UniqueNodeList (io_service);
-}
-
-// vim:ts=4
diff --git a/src/cpp/ripple/ripple_IUniqueNodeList.h b/src/cpp/ripple/ripple_UniqueNodeList.h
similarity index 89%
rename from src/cpp/ripple/ripple_IUniqueNodeList.h
rename to src/cpp/ripple/ripple_UniqueNodeList.h
index 163f8d39c..5480b27f3 100644
--- a/src/cpp/ripple/ripple_IUniqueNodeList.h
+++ b/src/cpp/ripple/ripple_UniqueNodeList.h
@@ -4,10 +4,10 @@
*/
//==============================================================================
-#ifndef RIPPLE_IUNIQUENODELIST_H_INCLUDED
-#define RIPPLE_IUNIQUENODELIST_H_INCLUDED
+#ifndef RIPPLE_UNIQUENODELIST_H_INCLUDED
+#define RIPPLE_UNIQUENODELIST_H_INCLUDED
-class IUniqueNodeList
+class UniqueNodeList
{
public:
enum ValidatorSource
@@ -26,9 +26,9 @@ public:
public:
// VFALCO TODO make this not use boost::asio...
- static IUniqueNodeList* New (boost::asio::io_service& io_service);
+ static UniqueNodeList* New ();
- virtual ~IUniqueNodeList () { }
+ virtual ~UniqueNodeList () { }
// VFALCO TODO Roll this into the constructor so there is one less state.
virtual void start () = 0;