From d861cf5719777f1d6a0067b10f2a853da2aede4e Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 13 Apr 2012 20:40:25 -0700 Subject: [PATCH 1/8] Add support for detecting NULL entires to sqlite. --- database/SqliteDatabase.cpp | 8 +++++++- database/SqliteDatabase.h | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/database/SqliteDatabase.cpp b/database/SqliteDatabase.cpp index 9639dd0be2..d3625e7384 100644 --- a/database/SqliteDatabase.cpp +++ b/database/SqliteDatabase.cpp @@ -120,6 +120,11 @@ bool SqliteDatabase::getNextRow() } } +bool SqliteDatabase::getNull(int colIndex) +{ + return(SQLITE_NULL == sqlite3_column_type(mCurrentStmt, colIndex)); +} + char* SqliteDatabase::getStr(int colIndex,std::string& retStr) { retStr=(char*)sqlite3_column_text(mCurrentStmt, colIndex); @@ -172,4 +177,5 @@ void SqliteDatabase::escape(const unsigned char* start, int size, std::string& r } retStr.push_back('\''); -} \ No newline at end of file +} +// vim:ts=4 diff --git a/database/SqliteDatabase.h b/database/SqliteDatabase.h index 49525e1e52..f615aed578 100644 --- a/database/SqliteDatabase.h +++ b/database/SqliteDatabase.h @@ -29,6 +29,7 @@ public: // will return false if there are no more rows bool getNextRow(); + bool getNull(int colIndex); char* getStr(int colIndex,std::string& retStr); int32 getInt(int colIndex); float getFloat(int colIndex); @@ -39,4 +40,4 @@ public: void escape(const unsigned char* start,int size,std::string& retStr); -}; \ No newline at end of file +}; From 4d7f86c43e0ba18d63d3c90a3c86285d73f03b57 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 13 Apr 2012 20:41:03 -0700 Subject: [PATCH 2/8] Add support for escaping String and detecting nulls to database. --- database/database.cpp | 21 ++++++++++++++++++++- database/database.h | 5 ++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/database/database.cpp b/database/database.cpp index 52d7738200..0e88bc0704 100644 --- a/database/database.cpp +++ b/database/database.cpp @@ -14,6 +14,15 @@ Database::~Database() { } +bool Database::getNull(const char* colName) +{ + int index; + if(getColNumber(colName,&index)) + { + return(getNull(index)); + } + return true; +} char* Database::getStr(const char* colName,std::string& retStr) { @@ -134,4 +143,14 @@ char* Database::getSingleDBValueStr(const char* sql,std::string& retStr) ret=0; } return(ret); -} \ No newline at end of file +} + +std::string Database::escape(const std::string strValue) +{ + std::string strReturn; + + escape(reinterpret_cast(strValue.c_str()), strValue.size(), strReturn); + + return strReturn; +} +// vim:ts=4 diff --git a/database/database.h b/database/database.h index d06ac10c89..ea0dc083f6 100644 --- a/database/database.h +++ b/database/database.h @@ -30,7 +30,8 @@ public: std::string& getPass(){ return(mDBPass); } virtual void escape(const unsigned char* start,int size,std::string& retStr)=0; - + std::string escape(const std::string strValue); + // returns true if the query went ok virtual bool executeSQL(const char* sql, bool fail_okay=false)=0; @@ -47,6 +48,7 @@ public: virtual bool getNextRow()=0; // get Data from the current row + bool getNull(const char* colName); char* getStr(const char* colName,std::string& retStr); int32 getInt(const char* colName); float getFloat(const char* colName); @@ -55,6 +57,7 @@ public: int getBinary(const char* colName,unsigned char* buf,int maxSize); uint64 getBigInt(const char* colName); + virtual bool getNull(int colIndex)=0; virtual char* getStr(int colIndex,std::string& retStr)=0; virtual int32 getInt(int colIndex)=0; virtual float getFloat(int colIndex)=0; From c01ca8bf394b52aee8cdc0a1d0ccec6a7a4d6e65 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 13 Apr 2012 20:42:05 -0700 Subject: [PATCH 3/8] Comment out unused config variables. --- src/Config.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Config.h b/src/Config.h index 4e2628b9e5..0270c0a694 100644 --- a/src/Config.h +++ b/src/Config.h @@ -3,14 +3,13 @@ class Config { public: - // core software parameters int VERSION; std::string VERSION_STR; // network parameters - std::string NETWORK_ID; - std::string NETWORK_DNS_SEEDS; +// std::string NETWORK_ID; +// std::string NETWORK_DNS_SEEDS; int NETWORK_START_TIME; // The Unix time we start ledger 0 int TRANSACTION_FEE_BASE; int LEDGER_SECONDS; @@ -23,13 +22,13 @@ public: std::string PEER_IP; int PEER_PORT; int NUMBER_CONNECTIONS; - bool NODE_INBOUND; // we accept inbound connections - bool NODE_DATABASE; // we offer historical data services - bool NODE_PUBLIC; // we do not attempt to hide our identity - bool NODE_DUMB; // we are a 'dumb' client - bool NODE_SMART; // we offer services to 'dumb' clients +// bool NODE_INBOUND; // we accept inbound connections +// bool NODE_DATABASE; // we offer historical data services +// bool NODE_PUBLIC; // we do not attempt to hide our identity +// bool NODE_DUMB; // we are a 'dumb' client +// bool NODE_SMART; // we offer services to 'dumb' clients - std::string HANKO_PRIVATE; +// std::string HANKO_PRIVATE; // RPC parameters std::string RPC_IP; From 1d8d60b6a6a18bc109cdef50a14b8039efba8061 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 13 Apr 2012 20:42:55 -0700 Subject: [PATCH 4/8] Comment out debugging. --- src/HttpsClient.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/HttpsClient.cpp b/src/HttpsClient.cpp index 01d8590612..baa395a270 100644 --- a/src/HttpsClient.cpp +++ b/src/HttpsClient.cpp @@ -44,13 +44,13 @@ void HttpsClient::httpsGet( void HttpsClient::httpsNext() { - std::cerr << "Fetch: " << mDeqSites[0] << std::endl; + // std::cerr << "Fetch: " << mDeqSites[0] << std::endl; boost::shared_ptr query(new boost::asio::ip::tcp::resolver::query(mDeqSites[0], boost::lexical_cast(mPort), ip::resolver_query_base::numeric_service|ip::resolver_query_base::numeric_service)); mQuery = query; mCtx.set_default_verify_paths(mShutdown); - if (!mShutdown) + if (mShutdown) { std::cerr << "set_default_verify_paths: " << mShutdown.message() << std::endl; } @@ -59,7 +59,7 @@ void HttpsClient::httpsNext() { mDeadline.expires_from_now(mTimeout, mShutdown); - std::cerr << "expires_from_now: " << mShutdown.message() << std::endl; + // std::cerr << "expires_from_now: " << mShutdown.message() << std::endl; } if (!mShutdown) @@ -73,7 +73,7 @@ void HttpsClient::httpsNext() if (!mShutdown) { - std::cerr << "Resolving: " << mDeqSites[0] << std::endl; + // std::cerr << "Resolving: " << mDeqSites[0] << std::endl; mResolver.async_resolve(*mQuery, boost::bind( @@ -92,9 +92,9 @@ void HttpsClient::handleDeadline(const boost::system::error_code& ecResult) if (ecResult == boost::asio::error::operation_aborted) { // Timer canceled because deadline no longer needed. - std::cerr << "Deadline cancelled." << std::endl; + // std::cerr << "Deadline cancelled." << std::endl; - // Do nothing. Aborter is done. + // nothing(); // Aborter is done. } else if (ecResult) { @@ -142,7 +142,7 @@ void HttpsClient::handleResolve( } else { - std::cerr << "Resolve complete." << std::endl; + // std::cerr << "Resolve complete." << std::endl; boost::asio::async_connect( mSocketSsl.lowest_layer(), @@ -166,7 +166,7 @@ void HttpsClient::handleConnect(const boost::system::error_code& ecResult) if (!mShutdown) { - std::cerr << "Connected." << std::endl; + // std::cerr << "Connectted." << std::endl; mSocketSsl.lowest_layer().set_option(boost::asio::ip::tcp::no_delay(true)); mSocketSsl.set_verify_mode(boost::asio::ssl::verify_peer); @@ -206,7 +206,7 @@ void HttpsClient::handleRequest(const boost::system::error_code& ecResult) } else { - std::cerr << "SSL session started." << std::endl; + // std::cerr << "SSL session started." << std::endl; std::ostream osRequest(&mRequest); @@ -238,7 +238,7 @@ void HttpsClient::handleWrite(const boost::system::error_code& ecResult) } else { - std::cerr << "Wrote." << std::endl; + // std::cerr << "Wrote." << std::endl; boost::asio::async_read( mSocketSsl, @@ -265,7 +265,8 @@ void HttpsClient::handleData(const boost::system::error_code& ecResult) { if (mShutdown) { - std::cerr << "Complete." << std::endl; + // std::cerr << "Complete." << std::endl; + // nothing(); } else { From 12349122e5fcebf5cc4a4526ea32672cda147ef6 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 13 Apr 2012 20:43:29 -0700 Subject: [PATCH 5/8] Improve error reporting and add clearing to NewcoinAddress. --- src/NewcoinAddress.cpp | 44 ++++++++++++++++++++++++------------------ src/NewcoinAddress.h | 3 ++- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/NewcoinAddress.cpp b/src/NewcoinAddress.cpp index a22b7d763b..0ea477e6fd 100644 --- a/src/NewcoinAddress.cpp +++ b/src/NewcoinAddress.cpp @@ -9,17 +9,23 @@ #include #include #include +#include NewcoinAddress::NewcoinAddress() { nVersion = VER_NONE; } -bool NewcoinAddress::IsValid() +bool NewcoinAddress::IsValid() const { return !vchData.empty(); } +void NewcoinAddress::clear() +{ + nVersion = VER_NONE; +} + // // Hanko // @@ -38,7 +44,7 @@ uint160 NewcoinAddress::getHanko() const return Hash160(vchData); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -61,7 +67,7 @@ std::string NewcoinAddress::humanHanko() const } default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -96,7 +102,7 @@ const std::vector& NewcoinAddress::getNodePublic() const return vchData; default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -113,7 +119,7 @@ std::string NewcoinAddress::humanNodePublic() const return ToString(); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -141,7 +147,7 @@ uint256 NewcoinAddress::getNodePrivate() const return uint256(vchData); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -155,7 +161,7 @@ std::string NewcoinAddress::humanNodePrivate() const return ToString(); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -191,7 +197,7 @@ uint160 NewcoinAddress::getAccountID() const return Hash160(vchData); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -214,7 +220,7 @@ std::string NewcoinAddress::humanAccountID() const } default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -246,7 +252,7 @@ const std::vector& NewcoinAddress::getAccountPublic() const return vchData; default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -263,7 +269,7 @@ std::string NewcoinAddress::humanAccountPublic() const return ToString(); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -298,7 +304,7 @@ uint256 NewcoinAddress::getAccountPrivate() const return uint256(vchData); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -312,7 +318,7 @@ std::string NewcoinAddress::humanAccountPrivate() const return ToString(); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -346,7 +352,7 @@ BIGNUM* NewcoinAddress::getFamilyGeneratorBN() const break; default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } BIGNUM* ret = BN_bin2bn(&vchData[0], vchData.size(), NULL); @@ -366,7 +372,7 @@ const std::vector& NewcoinAddress::getFamilyGenerator() const return vchData; default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -380,7 +386,7 @@ std::string NewcoinAddress::humanFamilyGenerator() const return ToString(); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -426,7 +432,7 @@ uint128 NewcoinAddress::getFamilySeed() const return uint128(vchData); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -462,7 +468,7 @@ std::string NewcoinAddress::humanFamilySeed1751() const } default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } @@ -476,7 +482,7 @@ std::string NewcoinAddress::humanFamilySeed() const return ToString(); default: - throw std::runtime_error("bad source"); + throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } diff --git a/src/NewcoinAddress.h b/src/NewcoinAddress.h index c98568c6e9..b4bc8c2481 100644 --- a/src/NewcoinAddress.h +++ b/src/NewcoinAddress.h @@ -27,7 +27,8 @@ private: public: NewcoinAddress(); - bool IsValid(); + bool IsValid() const; + void clear(); // // hanko From 96f23534c3a37e9ebea00ac6eb24fbf8bf4a919c Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 13 Apr 2012 20:44:11 -0700 Subject: [PATCH 6/8] Add const to some ParseSection functions. --- src/ParseSection.cpp | 4 ++-- src/ParseSection.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ParseSection.cpp b/src/ParseSection.cpp index 2258da1dbd..3fe6efbc3f 100644 --- a/src/ParseSection.cpp +++ b/src/ParseSection.cpp @@ -69,7 +69,7 @@ void PrintSection(section secInput) std::cerr << "PrintSection<" << std::endl; } -section::mapped_type* sectionEntries(section& secSource, std::string strSection) +section::mapped_type* sectionEntries(section& secSource, const std::string strSection) { section::iterator it; section::mapped_type* smtResult; @@ -96,7 +96,7 @@ int sectionCount(section& secSource, std::string strSection) return pmtEntries ? -1 : pmtEntries->size(); } -bool sectionSingleB(section& secSource, std::string strSection, std::string& strValue) +bool sectionSingleB(section& secSource, const std::string strSection, std::string& strValue) { section::mapped_type* pmtEntries = sectionEntries(secSource, strSection); bool bSingle = pmtEntries && 1 == pmtEntries->size(); diff --git a/src/ParseSection.h b/src/ParseSection.h index 1f7a4aa7c9..027f29f261 100644 --- a/src/ParseSection.h +++ b/src/ParseSection.h @@ -9,8 +9,8 @@ typedef std::map > section; section ParseSection(const std::string strInput, const bool bTrim); void PrintSection(section secInput); -bool sectionSingleB(section& secSource, std::string strSection, std::string& strValue); +bool sectionSingleB(section& secSource, const std::string strSection, std::string& strValue); int sectionCount(section& secSource, std::string strSection); -section::mapped_type* sectionEntries(section& secSource, std::string strSection); +section::mapped_type* sectionEntries(section& secSource, const std::string strSection); #endif From 658cd9cf309f3ab3dcbefcedd0530f5be6d60f3b Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 13 Apr 2012 20:45:08 -0700 Subject: [PATCH 7/8] Add support for getting a Sha256 from a String. --- src/Serializer.cpp | 5 +++++ src/Serializer.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/Serializer.cpp b/src/Serializer.cpp index 0962338b48..16d14a829a 100644 --- a/src/Serializer.cpp +++ b/src/Serializer.cpp @@ -213,6 +213,11 @@ uint256 Serializer::getSHA512Half(const unsigned char *data, int len) return j[0]; } +uint256 Serializer::getSHA512Half(const std::string& strData) +{ + return getSHA512Half(reinterpret_cast(strData.c_str()), strData.size()); +} + bool Serializer::checkSignature(int pubkeyOffset, int signatureOffset) const { std::vector pubkey, signature; diff --git a/src/Serializer.h b/src/Serializer.h index 0573ef3be0..1a038251c0 100644 --- a/src/Serializer.h +++ b/src/Serializer.h @@ -68,6 +68,7 @@ class Serializer uint256 getSHA512Half(int size=-1) const; static uint256 getSHA512Half(const std::vector& data, int size=-1); static uint256 getSHA512Half(const unsigned char *data, int len); + static uint256 getSHA512Half(const std::string& strData); // totality functions int getLength() const { return mData.size(); } From 85b592260358e6843c50355b44499d94965cce7a Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 13 Apr 2012 20:47:34 -0700 Subject: [PATCH 8/8] Work toward automated UNL maintenance. --- SConstruct | 1 + src/Application.cpp | 17 + src/Application.h | 4 +- src/DBInit.cpp | 141 +++++++- src/RPCServer.cpp | 31 +- src/UniqueNodeList.cpp | 713 +++++++++++++++++++++++++++++------------ src/UniqueNodeList.h | 62 ++-- src/Wallet.cpp | 71 +++- src/Wallet.h | 13 +- src/main.cpp | 2 +- 10 files changed, 802 insertions(+), 253 deletions(-) diff --git a/SConstruct b/SConstruct index d4b1abb8a9..a656f1f018 100644 --- a/SConstruct +++ b/SConstruct @@ -31,6 +31,7 @@ for dir in ['src', 'database', 'json', 'util']: env.ParseConfig('pkg-config --cflags --libs openssl') env.Append(LIBS = [ + 'boost_date_time-mt', 'boost_filesystem-mt', 'boost_program_options-mt', 'boost_regex-mt', diff --git a/src/Application.cpp b/src/Application.cpp index 9e33711dfe..e092151cbe 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -41,6 +41,7 @@ DatabaseCon::~DatabaseCon() } Application::Application() : + mUNL(mIOService), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL), mPeerDoor(NULL), mRPCDoor(NULL) { @@ -61,17 +62,32 @@ void Application::run() { assert(mTxnDB==NULL); + // + // Construct databases. + // mTxnDB=new DatabaseCon("transaction.db", TxnDBInit, TxnDBCount); mLedgerDB=new DatabaseCon("ledger.db", LedgerDBInit, LedgerDBCount); mWalletDB=new DatabaseCon("wallet.db", WalletDBInit, WalletDBCount); mHashNodeDB=new DatabaseCon("hashnode.db", HashNodeDBInit, HashNodeDBCount); mNetNodeDB=new DatabaseCon("netnode.db", NetNodeDBInit, NetNodeDBCount); + // + // Begin validation and ip maintenance. + // + + mWallet.start(); + + // + // Allow peer connections. + // if(theConfig.PEER_PORT) { mPeerDoor=new PeerDoor(mIOService); }//else BOOST_LOG_TRIVIAL(info) << "No Peer Port set. Not listening for connections."; + // + // Allow RPC connections. + // if(theConfig.RPC_PORT) { mRPCDoor=new RPCDoor(mIOService); @@ -79,6 +95,7 @@ void Application::run() mConnectionPool.connectToNetwork(mKnownNodes, mIOService); mTimingService.start(mIOService); + std::cout << "Before Run." << std::endl; // Temporary root account will be ["This is my payphrase."]:0 diff --git a/src/Application.h b/src/Application.h index 2060cfcc9f..76f641a865 100644 --- a/src/Application.h +++ b/src/Application.h @@ -38,6 +38,8 @@ class Application NetworkOPs mNetOps; Wallet mWallet; + boost::asio::io_service mIOService; + TimingService mTimingService; UniqueNodeList mUNL; KnownNodeList mKnownNodes; @@ -55,8 +57,6 @@ class Application std::map mPeerMap; boost::recursive_mutex mPeerMapLock; - boost::asio::io_service mIOService; - public: Application(); ~Application(); diff --git a/src/DBInit.cpp b/src/DBInit.cpp index fab5cecc1f..b86092c0f1 100644 --- a/src/DBInit.cpp +++ b/src/DBInit.cpp @@ -55,14 +55,144 @@ const char *WalletDBInit[] = { Seq BIGINT UNSIGNED, \ Comment TEXT \ );", + + // XXX Don't really need this. + // We should generate communication identity per launch. + // Validation id is provided via rpc or stored in config. "CREATE TABLE NodeIdentity ( \ PublicKey CHARACTER(53), \ PrivateKey CHARACTER(52) \ );", - "CREATE TABLE TrustedNodes ( \ - PublicKey CHARACTER(53) PRIMARY KEY, \ + + // Miscellaneous persistent information + // ScoresUpdated: when scores was last updated. + // Scores need updating if: + // - This time is 0 + // - This time is prior to the most recent SeedDomains or SeedNodes fetch. + "CREATE TABLE Misc ( \ + ScoresUpdated DATETIME \ + );", + + // Domain: + // Domain source for https. + // PublicKey: + // Set if ever succeeded. + // XXX Use NULL in place of "" + // Source: + // 'M' = Manually added. : 1500 + // 'V' = validators.txt : 1000 + // 'W' = Web browsing. : 200 + // 'R' = Referral : 0 + // Next: + // Time of next fetch attempt. + // Scan: + // Time of last fetch attempt. + // Fetch: + // Time of last successful fetch. + // Sha256: + // Checksum of last fetch. + // Comment: + // User supplied comment. + // Table of Domains user has asked to trust. + "CREATE TABLE SeedDomains ( \ + Domain TEXT PRIMARY KEY NOT NULL, \ + PublicKey CHARACTER(53), \ + Source CHARACTER(1) NOT NULL, \ + Next DATETIME, \ + Scan DATETIME, \ + Fetch DATETIME, \ + Sha256 CHARACTER[64], \ Comment TEXT \ - );" + );", + + // Allow us to easily find the next SeedDomain to fetch. + "CREATE INDEX SeedDomainNext ON SeedDomains (Next);", + + // Table of PublicKeys user has asked to trust. + // Fetches are made to the CAS. + // Next: + // Time of next fetch attempt. + // Scan: + // Time of last fetch attempt. + // Fetch: + // Time of last successful fetch. + // Sha256: + // Checksum of last fetch. + // Comment: + // User supplied comment. + "CREATE TABLE SeedNodes ( \ + PublicKey CHARACTER(53) PRIMARY KEY NOT NULL, \ + Next DATETIME, \ + Scan DATETIME, \ + Fetch DATETIME, \ + Sha256 CHARACTER[64], \ + Comment TEXT \ + );", + + // Allow us to easily find the next SeedNode to fetch. + "CREATE INDEX SeedNodeNext ON SeedNodes (Next);", + + // Table of trusted nodes. + // Score: + // Computed trust score. Higher is better. + // Seen: + // Last validation received. + "CREATE TABLE TrustedNodes ( \ + PublicKey CHARACTER(53) PRIMARY KEY NOT NULL, \ + Score INTEGER, \ + Seen DATETIME \ + );", + + // List of referrals. + // - There may be multiple sources for a Validator. The last source is used. + // Validator: + // Public key of referrer. + // Index: + // Entry index in [validators] table. + // Referral: + // Public key of referred or Domain. + // XXX Index by validator for fast delete + "CREATE TABLE ValidatorReferrals ( \ + Validator CHARACTER(53), \ + Entry INTEGER, \ + Referral TEXT \ + );", + + // List of referrals. + // Validator: + // Public key of referree. + // Index: + // Entry index in [validators] table. + // IP: + // IP of referred. + "CREATE TABLE IpReferrals ( \ + Validator CHARACTER(53), \ + Index INTEGER, \ + IP TEXT \ + );", + + // Table of IPs to contact the nextwork. + // IP: + // IP address to contact. + // Port: + // Port to contact. 0=default. + // Score: + // Computed trust score. Higher is better. + // Source: + // 'V' = Validation file + // 'M' = Manually added. + // 'I' = Inbound connection. + // 'O' = Other. + // Contact: + // Time of last contact. + // XXX Update on connect and hourly. + "CREATE TABLE PeerIps ( \ + IP TEXT PRIMARY KEY, \ + PORT INTEGER, \ + Score INTEGER, \ + Source CHARACTER(1), \ + Contact DATETIME \ + );", }; #if 0 @@ -78,7 +208,6 @@ const char *WalletDBInit[] = { int WalletDBCount=sizeof(WalletDBInit)/sizeof(const char *); - // Hash node database holds nodes indexed by hash const char *HashNodeDBInit[] = { "CREATE TABLE CommittedObjects \ @@ -88,11 +217,13 @@ const char *HashNodeDBInit[] = { Object BLOB \ );", "CREATE INDEX ObjectLocate ON \ - CommittedObjects(LedgerIndex, ObjType);" }; + CommittedObjects(LedgerIndex, ObjType);" +}; int HashNodeDBCount=sizeof(HashNodeDBInit)/sizeof(const char *); // Net node database holds nodes seen on the network +// XXX Not really used needs replacement. const char *NetNodeDBInit[] = { "CREATE TABLE KnownNodes ( \ Hanko CHARACTER(35) PRIMARY KEY, \ diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index cc555b4725..5f257a9830 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -505,27 +505,28 @@ Json::Value RPCServer::doLedger(Json::Value& params) return "not implemented"; } -// unl_add -// unl_add +// unl_add [] Json::Value RPCServer::doUnlAdd(Json::Value& params) { if(params.size()==1 || params.size()==2) { - std::string strNodePublic=params[0u].asString(); + std::string strNode = params[0u].asString(); std::string strComment=params.size() == 2 ? "" : params[1u].asString(); NewcoinAddress nodePublic; - if(nodePublic.setNodePublic(strNodePublic)) + if(nodePublic.setNodePublic(strNode)) { - theApp->getUNL().nodeAdd(nodePublic, strComment); + theApp->getUNL().nodeAddPublic(nodePublic, strComment); - return "adding node"; + return "adding node by public key"; } else { - return "invalid public key"; + theApp->getUNL().nodeAddDomain(strNode, UniqueNodeList::vsManual, strComment); + + return "adding node by domain"; } } else return "invalid params"; @@ -535,6 +536,9 @@ Json::Value RPCServer::doUnlAdd(Json::Value& params) { // validation_create // validation_create // validation_create +// +// NOTE: It is poor security to specify secret information on the command line. This information might be saved in the command +// shell history file (e.g. .bash_history) and it may be leaked via the process status command (i.e. ps). Json::Value RPCServer::doValidatorCreate(Json::Value& params) { NewcoinAddress familySeed; NewcoinAddress familyGenerator; @@ -675,18 +679,6 @@ Json::Value RPCServer::doUnlDelete(Json::Value& params) { else return "invalid params"; } -Json::Value RPCServer::doUnlFetch(Json::Value& params) { - if(params.size() == 1) - { - std::string strDomain=params[0u].asString(); - - theApp->getUNL().nodeFetch(strDomain); - - return "fetching domain"; - } - else return "invalid params"; -} - Json::Value RPCServer::doUnlList(Json::Value& params) { return theApp->getUNL().getUnlJson(); } @@ -716,7 +708,6 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params if(command=="unl_add") return doUnlAdd(params); if(command=="unl_default") return doUnlDefault(params); if(command=="unl_delete") return doUnlDelete(params); - if(command=="unl_fetch") return doUnlFetch(params); if(command=="unl_list") return doUnlList(params); if(command=="unl_reset") return doUnlReset(params); diff --git a/src/UniqueNodeList.cpp b/src/UniqueNodeList.cpp index d4b22788a5..c0cb52b207 100644 --- a/src/UniqueNodeList.cpp +++ b/src/UniqueNodeList.cpp @@ -2,208 +2,362 @@ #include "Conversion.h" #include "HttpsClient.h" #include "ParseSection.h" +#include "Serializer.h" #include "UniqueNodeList.h" #include #include #include #include +#include +#include // 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 "node_public_key" +#define SECTION_PUBLIC_KEY "validation_public_key" #define SECTION_VALIDATORS "validators" #define SECTION_VALIDATORS_URL "validators_url" -UniqueNodeList::UniqueNodeList() : mFetchActive(0) +// Limit pollution of database. +#define REFERRAL_VALIDATORS_MAX 50 +#define REFERRAL_IPS_MAX 50 + +static boost::posix_time::ptime ptEpoch() +{ + return boost::posix_time::ptime(boost::gregorian::date(2000, boost::gregorian::Jan, 1)); +} + +static int iToSeconds(boost::posix_time::ptime ptWhen) +{ + boost::posix_time::time_duration td = ptWhen - ptEpoch(); + + return td.total_seconds(); +} + +static boost::posix_time::ptime ptFromSeconds(int iSeconds) +{ + return ptEpoch() + boost::posix_time::seconds(iSeconds); +} + +UniqueNodeList::UniqueNodeList(boost::asio::io_service& io_service) : + mFetchActive(0), + mdtFetchTimer(io_service) { } -void UniqueNodeList::processIps(section::mapped_type& vecStrIps) { - std::cerr << "Processing ips." << std::endl; - - // XXX Do something with ips. -} - -void UniqueNodeList::processValidators(section::mapped_type& vecStrValidators) +void UniqueNodeList::start() { - std::cerr << "Processing validators." << std::endl; - - // XXX Do something with validators. + fetchNext(); // Start fetching. } -void UniqueNodeList::responseIps(const boost::system::error_code& err, const std::string strIpsFile) -{ - section secFile = ParseSection(strIpsFile, true); - section::mapped_type* pmtEntries = sectionEntries(secFile, SECTION_IPS); - - if (pmtEntries) - processIps(*pmtEntries); - - getValidatorsUrl(); -} - -void UniqueNodeList::responseValidators(const boost::system::error_code& err, const std::string strValidatorsFile) -{ - section secFile = ParseSection(strValidatorsFile, true); - section::mapped_type* pmtEntries = sectionEntries(secFile, SECTION_VALIDATORS); - - if (pmtEntries) - processValidators(*pmtEntries); - - getFinish(); -} - -void UniqueNodeList::getIpsUrl() -{ - std::string strDomain; - std::string strPath; - - if (!mStrIpsUrl.empty() && HttpsClient::httpsParseUrl(mStrIpsUrl, strDomain, strPath)) - { - HttpsClient::httpsGet( - theApp->getIOService(), - strDomain, - 443, - strPath, - NODE_FILE_BYTES_MAX, - boost::posix_time::seconds(NODE_FETCH_SECONDS), - boost::bind(&UniqueNodeList::responseIps, this, _1, _2)); - } - else - { - getValidatorsUrl(); - } -} - -void UniqueNodeList::getValidatorsUrl() -{ - std::string strDomain; - std::string strPath; - - if (!mStrValidatorsUrl.empty() && HttpsClient::httpsParseUrl(mStrValidatorsUrl, strDomain, strPath)) - { - HttpsClient::httpsGet( - theApp->getIOService(), - strDomain, - 443, - strPath, - NODE_FILE_BYTES_MAX, - boost::posix_time::seconds(NODE_FETCH_SECONDS), - boost::bind(&UniqueNodeList::responseValidators, this, _1, _2)); - } - else - { - getFinish(); - } -} - -void UniqueNodeList::getFinish() +void UniqueNodeList::fetchFinish() { { boost::mutex::scoped_lock sl(mFetchLock); mFetchActive--; } - std::cerr << "Fetch active: " << mFetchActive << std::endl; fetchNext(); } -void UniqueNodeList::responseFetch(const std::string strDomain, const boost::system::error_code& err, const std::string strSiteFile) +void UniqueNodeList::processIps(const std::string& strSite, section::mapped_type* pmtVecStrIps) { - std::cerr << "Fetch complete." << std::endl; - std::cerr << "Error: " << err.message() << std::endl; + std::cerr + << str(boost::format("Validator: '%s' processing %d ips.") + % strSite % ( pmtVecStrIps ? pmtVecStrIps->size() : 0)) + << std::endl; - section secSite = ParseSection(strSiteFile, true); - bool bGood = !err; + // XXX Do something with ips. +} - // - // Verify file domain - // - std::string strSite; +void UniqueNodeList::processValidators(const std::string& strSite, NewcoinAddress naNodePublic, section::mapped_type* pmtVecStrValidators) +{ + Database* db=theApp->getWalletDB()->getDB(); - if (bGood) - { - bGood = sectionSingleB(secSite, SECTION_DOMAIN, strSite); - if (strSite != strDomain) + std::string strEscNodePublic = db->escape(naNodePublic.humanNodePublic()); + + std::cerr + << str(boost::format("Validator: '%s' processing %d validators.") + % strSite % ( pmtVecStrValidators ? pmtVecStrValidators->size() : 0)) + << std::endl; + + // Remove all current entries Validator in ValidatorReferrals + // XXX INDEX BY ValidatorReferralsIndex + std::string strSql = str(boost::format("DELETE FROM ValidatorReferrals WHERE Validator=%s;") + % strEscNodePublic); + + ScopedLock sl(theApp->getWalletDB()->getDBLock()); + db->executeSQL(strSql.c_str()); + // XXX Check result. + + // Add new referral entries. + if (pmtVecStrValidators->size()) { + std::ostringstream ossValues; + + int i = 0; + BOOST_FOREACH(std::string strReferral, *pmtVecStrValidators) { - bGood = false; + if (i == REFERRAL_VALIDATORS_MAX) + break; - std::cerr << "Warning: Site '" << strDomain << "' provides '" NODE_FILE_NAME "' for '" << strSite << "', ignoring." << std::endl; - } - else - { - std::cerr << "Processing: Site '" << strDomain << "' '" NODE_FILE_NAME "'" << std::endl; + ossValues << + str(boost::format("%s(%s,%d,%s)") + % ( i ? "," : "") % strEscNodePublic % i % db->escape(strReferral)); + i++; + + NewcoinAddress naValidator; + + if (naValidator.setNodePublic(strReferral)) + { + // A public key. + // XXX Schedule for CAS lookup. + } + else + { + // A domain. + nodeAddDomain(strReferral, vsReferral); + } } + + std::string strSql = str(boost::format("INSERT INTO ValidatorReferrals (Validator,Entry,Referral) VALUES %s;") + % ossValues.str()); + + ScopedLock sl(theApp->getWalletDB()->getDBLock()); + + db->executeSQL(strSql.c_str()); + // XXX Check result. } - // - // Process public key - // - std::string strNodePublicKey; + // XXX Set timer to cause rebuild. +} - if (bGood && (bGood = sectionSingleB(secSite, SECTION_PUBLIC_KEY, strNodePublicKey))) +void UniqueNodeList::responseIps(const std::string& strSite, const boost::system::error_code& err, const std::string strIpsFile) +{ + if (!err) { - NewcoinAddress naNodePublic; + section secFile = ParseSection(strIpsFile, true); - if (naNodePublic.setNodePublic(strNodePublicKey)) - { - std::cerr << strDomain << ": " << strNodePublicKey << std::endl; - - nodeAdd(naNodePublic, strDomain); - } + processIps(strSite, sectionEntries(secFile, SECTION_IPS)); } + fetchFinish(); +} + +// +// Process [ips_url] +// +void UniqueNodeList::getIpsUrl(section secSite) +{ + std::string strIpsUrl; + std::string strDomain; + std::string strPath; + + if (sectionSingleB(secSite, SECTION_IPS_URL, strIpsUrl) + && !strIpsUrl.empty() + && HttpsClient::httpsParseUrl(strIpsUrl, strDomain, strPath)) + { + HttpsClient::httpsGet( + theApp->getIOService(), + strDomain, + 443, + strPath, + NODE_FILE_BYTES_MAX, + boost::posix_time::seconds(NODE_FETCH_SECONDS), + boost::bind(&UniqueNodeList::responseIps, this, strDomain, _1, _2)); + } + else + { + fetchFinish(); + } +} + +void UniqueNodeList::responseValidators(NewcoinAddress naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string strValidatorsFile) +{ + if (!err) + { + section secFile = ParseSection(strValidatorsFile, true); + + processValidators(strSite, naNodePublic, sectionEntries(secFile, SECTION_VALIDATORS)); + } + + getIpsUrl(secSite); +} + +// +// Process [validators_url] +// +void UniqueNodeList::getValidatorsUrl(NewcoinAddress naNodePublic, section secSite) +{ + std::string strValidatorsUrl; + std::string strDomain; + std::string strPath; + + if (sectionSingleB(secSite, SECTION_VALIDATORS_URL, strValidatorsUrl) + && !strValidatorsUrl.empty() + && HttpsClient::httpsParseUrl(strValidatorsUrl, strDomain, strPath)) + { + HttpsClient::httpsGet( + theApp->getIOService(), + strDomain, + 443, + strPath, + NODE_FILE_BYTES_MAX, + boost::posix_time::seconds(NODE_FETCH_SECONDS), + boost::bind(&UniqueNodeList::responseValidators, this, naNodePublic, secSite, strDomain, _1, _2)); + } + else + { + getIpsUrl(secSite); + } +} + +// Process a newcoin.txt. +void UniqueNodeList::processFile(const std::string strDomain, NewcoinAddress naNodePublic, section secSite) +{ + // + // Process Validators + // + processValidators(strDomain, naNodePublic, sectionEntries(secSite, SECTION_VALIDATORS)); + // // Process ips // - section::mapped_type* pvIps; - - if (bGood && (pvIps = sectionEntries(secSite, SECTION_IPS)) && pvIps->size()) - { - std::cerr << "Ips: " << pvIps->size() << std::endl; - - processIps(*pvIps); - } - - // - // Process ips_url - // - if (bGood) - (void) sectionSingleB(secSite, SECTION_IPS_URL, mStrIpsUrl); - - // - // Process Validators - // - section::mapped_type* pvValidators; - - if (bGood && (pvValidators = sectionEntries(secSite, SECTION_VALIDATORS)) && pvValidators->size()) - { - processValidators(*pvValidators); - } - - // - // Process validators_url - // - - if (bGood) - (void) sectionSingleB(secSite, SECTION_VALIDATORS_URL, mStrValidatorsUrl); + processIps(strDomain, sectionEntries(secSite, SECTION_IPS)); // // Process currencies // section::mapped_type* pvCurrencies; - if (bGood && (pvCurrencies = sectionEntries(secSite, SECTION_CURRENCIES)) && pvCurrencies->size()) + if ((pvCurrencies = sectionEntries(secSite, SECTION_CURRENCIES)) && pvCurrencies->size()) { // XXX Process currencies. std::cerr << "Ignoring currencies: not implemented." << std::endl; } - getIpsUrl(); + getValidatorsUrl(naNodePublic, secSite); +} + +void UniqueNodeList::responseFetch(const std::string strDomain, const boost::system::error_code& err, const std::string strSiteFile) +{ + section secSite = ParseSection(strSiteFile, true); + bool bGood = !err; + + if (bGood) + { + std::cerr << boost::format("Validator: '%s' received " NODE_FILE_NAME ".") % strDomain << std::endl; + } + else + { + std::cerr + << boost::format("Validator: '%s' unabile to retrieve " NODE_FILE_NAME ": %s") + % strDomain + % err.message() + << std::endl; + } + + // + // Verify file domain + // + std::string strSite; + + if (bGood && !sectionSingleB(secSite, SECTION_DOMAIN, strSite)) + { + bGood = false; + + std::cerr + << boost::format("Validator: '%s' bad " NODE_FILE_NAME " missing single entry for " SECTION_DOMAIN ".") + % strDomain + << std::endl; + } + + if (bGood && strSite != strDomain) + { + bGood = false; + + std::cerr + << boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_DOMAIN " does not match: %s") + % strDomain + % strSite + << std::endl; + } + + // + // Process public key + // + std::string strNodePublicKey; + + if (bGood && !sectionSingleB(secSite, SECTION_PUBLIC_KEY, strNodePublicKey)) + { + // Bad [validation_public_key] section. + bGood = false; + + std::cerr + << boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " does not have single entry.") + % strDomain + << std::endl; + } + + NewcoinAddress naNodePublic; + + if (bGood && !naNodePublic.setNodePublic(strNodePublicKey)) + { + // Bad public key. + bGood = false; + + std::cerr + << boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " is bad: ") + % strDomain + % strNodePublicKey + << std::endl; + } + + if (bGood) + { +// std::cerr << boost::format("naNodePublic: '%s'") % naNodePublic.humanNodePublic() << std::endl; + + seedDomain sdCurrent; + + bool bFound = getSeedDomans(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; + +// std::cerr << boost::format("sdCurrent.naPublicKey: '%s'") % sdCurrent.naPublicKey.humanNodePublic() << std::endl; + + sdCurrent.tpFetch = boost::posix_time::second_clock::universal_time(); + sdCurrent.iSha256 = iSha256; + + setSeedDomans(sdCurrent); + + if (bChangedB) + { + std::cerr << boost::format("Validator: '%s' processing new " NODE_FILE_NAME ".") % strDomain << std::endl; + processFile(strDomain, naNodePublic, secSite); + } + else + { + std::cerr << boost::format("Validator: '%s' no change for " NODE_FILE_NAME ".") % strDomain << std::endl; + fetchFinish(); + } + } + else + { + // Failed: Update + + // XXX If we have public key, perhaps try look up in CAS? + fetchFinish(); + } } // Get the newcoin.txt and process it. @@ -211,15 +365,9 @@ void UniqueNodeList::fetchProcess(std::string strDomain) { std::cerr << "Fetching '" NODE_FILE_NAME "' from '" << strDomain << "'." << std::endl; - { - boost::mutex::scoped_lock sl(mFetchLock); - mFetchActive++; - } - std::deque deqSites; - - std::ostringstream ossNewcoin; - std::ostringstream ossWeb; + std::ostringstream ossNewcoin; + std::ostringstream ossWeb; ossNewcoin << SYSTEM_NAME "." << strDomain; ossWeb << "www." << strDomain; @@ -238,40 +386,234 @@ void UniqueNodeList::fetchProcess(std::string strDomain) boost::bind(&UniqueNodeList::responseFetch, this, strDomain, _1, _2)); } +void UniqueNodeList::fetchTimerHandler(const boost::system::error_code& err) +{ + if (!err) + { + // Time to check for another fetch. + std::cerr << "fetchTimerHandler" << std::endl; + fetchNext(); + } +} + // Try to process the next fetch. void UniqueNodeList::fetchNext() { - bool work; - std::string strDomain; + bool bFull; + { boost::mutex::scoped_lock sl(mFetchLock); - work = mFetchActive != NODE_FETCH_JOBS && !mFetchPending.empty(); - if (work) { - strDomain = mFetchPending.front(); - mFetchPending.pop_front(); - } + + bFull = mFetchActive == NODE_FETCH_JOBS; } - if (!strDomain.empty()) + if (!bFull) { - fetchProcess(strDomain); + // Determine next scan. + std::string strSql("SELECT Domain,Next FROM SeedDomains ORDER BY Next ASC LIMIT 1;"); + std::string strDomain; + boost::posix_time::ptime tpNext; + boost::posix_time::ptime tpNow; + + ScopedLock sl(theApp->getWalletDB()->getDBLock()); + Database *db=theApp->getWalletDB()->getDB(); + + if (db->executeSQL(strSql.c_str()) && db->startIterRows()) + { + int iNext = db->getInt("Next"); + + tpNext = ptFromSeconds(iNext); + tpNow = boost::posix_time::second_clock::universal_time(); + + db->getStr("Domain", strDomain); + + 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) + { + // nothing(); + } + else if (tpNext > tpNow) + { + // Fetch needs to happen in the future. Set a timer to wake us. + mtpFetchNext = tpNext; + + mdtFetchTimer.expires_at(mtpFetchNext); + mdtFetchTimer.async_wait(boost::bind(&UniqueNodeList::fetchTimerHandler, this, _1)); + } + else + { + // Fetch needs to happen now. + mtpFetchNext = boost::posix_time::ptime(boost::posix_time::not_a_date_time); + + seedDomain sdCurrent; + bool bFound = getSeedDomans(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); + + setSeedDomans(sdCurrent); + + std::cerr << "Validator: '" << strDomain << "' fetching " NODE_FILE_NAME "." << std::endl; + + fetchProcess(strDomain); // Go get it. + + fetchNext(); // Look for more. + } } } -// Get newcoin.txt from a domain's web server. -void UniqueNodeList::nodeFetch(std::string strDomain) +int UniqueNodeList::iSourceScore(validatorSource vsWhy) { - { - boost::mutex::scoped_lock sl(mFetchLock); + int iScore = 0; - mFetchPending.push_back(strDomain); + switch (vsWhy) { + case vsManual: iScore = 1500; break; + case vsValidator: iScore = 1000; break; + case vsWeb: iScore = 200; break; + case vsReferral: iScore = 0; break; + default: + throw std::runtime_error("Internal error: bad validatorSource."); } - fetchNext(); + return iScore; +} + +// Queue a domain for a single attempt fetch a newcoin.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, std::string strComment) +{ + // YYY Would be best to verify strDomain is a valid domain. + seedDomain sdCurrent; + + bool bFound = getSeedDomans(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; + bChanged = true; + } + + if (vsManual == vsWhy) + { + // A manual add forces immediate scan. + sdCurrent.tpNext = boost::posix_time::second_clock::universal_time(); + sdCurrent.strComment = strComment; + bChanged = true; + } + + if (bChanged) + setSeedDomans(sdCurrent); +} + +bool UniqueNodeList::getSeedDomans(const std::string& strDomain, seedDomain& dstSeedDomain) +{ + bool bResult; + Database* db=theApp->getWalletDB()->getDB(); + + std::string strSql = str(boost::format("SELECT * FROM SeedDomains WHERE Domain=%s;") + % db->escape(strDomain)); + + ScopedLock sl(theApp->getWalletDB()->getDBLock()); + + bResult = db->executeSQL(strSql.c_str()) && db->startIterRows(); + if (bResult) + { + std::string strPublicKey; + std::string strSource; + int iNext; + int iScan; + int iFetch; + std::string strSha256; + + db->getStr("Domain", dstSeedDomain.strDomain); + + if (!db->getNull("PublicKey") && db->getStr("PublicKey", strPublicKey)) + { + dstSeedDomain.naPublicKey.setNodePublic(strPublicKey); + } + else + { + dstSeedDomain.naPublicKey.clear(); + } + + db->getStr("Source", strSource); + 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); + db->getStr("Sha256", strSha256); + dstSeedDomain.iSha256.SetHex(strSha256.c_str()); + db->getStr("Comment", dstSeedDomain.strComment); + + db->endIterRows(); + } + + return bResult; +} + +void UniqueNodeList::setSeedDomans(const seedDomain& sdSource) +{ + Database* db=theApp->getWalletDB()->getDB(); + + int iNext = iToSeconds(sdSource.tpNext); + int iScan = iToSeconds(sdSource.tpScan); + int iFetch = iToSeconds(sdSource.tpFetch); + + std::string strSql = str(boost::format("REPLACE INTO SeedDomains (Domain,PublicKey,Source,Next,Scan,Fetch,Sha256,Comment) VALUES (%s, %s, %s, %d, %d, %d, '%s', %s);") + % db->escape(sdSource.strDomain) + % (sdSource.naPublicKey.IsValid() ? db->escape(sdSource.naPublicKey.humanNodePublic()) : "NULL") + % db->escape(std::string(1, static_cast(sdSource.vsSource))) + % iNext + % iScan + % iFetch + % sdSource.iSha256.GetHex() + % db->escape(sdSource.strComment) + ); + + ScopedLock sl(theApp->getWalletDB()->getDBLock()); + + db->executeSQL(strSql.c_str()); + // XXX Check result. + + if (mtpFetchNext.is_not_a_date_time() || mtpFetchNext > sdSource.tpNext) + { + // Schedule earlier wake up. + fetchNext(); + } } // XXX allow update of comment. -void UniqueNodeList::nodeAdd(NewcoinAddress naNodePublic, std::string strComment) +void UniqueNodeList::nodeAddPublic(NewcoinAddress naNodePublic, std::string strComment) { Database* db=theApp->getWalletDB()->getDB(); @@ -279,11 +621,9 @@ void UniqueNodeList::nodeAdd(NewcoinAddress naNodePublic, std::string strComment std::string strTmp; std::string strSql="INSERT INTO TrustedNodes (PublicKey,Comment) values ("; - db->escape(reinterpret_cast(strPublicKey.c_str()), strPublicKey.size(), strTmp); - strSql.append(strTmp); + strSql.append(db->escape(strPublicKey)); strSql.append(","); - db->escape(reinterpret_cast(strComment.c_str()), strComment.size(), strTmp); - strSql.append(strTmp); + strSql.append(db->escape(strComment)); strSql.append(")"); ScopedLock sl(theApp->getWalletDB()->getDBLock()); @@ -314,33 +654,6 @@ void UniqueNodeList::nodeReset() db->executeSQL(strSql.c_str()); } -// 0- we don't care, 1- we care and is valid, 2-invalid signature -#if 0 -int UniqueNodeList::checkValid(newcoin::Validation& valid) -{ - Database* db=theApp->getWalletDB()->getDB(); - std::string strSql="SELECT pubkey from TrustedNodes where hanko="; - std::string hashStr; - db->escape((unsigned char*) &(valid.hanko()[0]),valid.hanko().size(),hashStr); - strSql.append(hashStr); - - ScopedLock sl(theApp->getWalletDB()->getDBLock()); - if( db->executeSQL(strSql.c_str()) ) - { - if(db->startIterRows() && db->getNextRow()) - { - //theApp->getDB()->getBytes(); - - // TODO: check that the public key makes the correct signature of the validation - db->endIterRows(); - return(1); - } - else db->endIterRows(); - } - return(0); // not on our list -} -#endif - Json::Value UniqueNodeList::getUnlJson() { Database* db=theApp->getWalletDB()->getDB(); @@ -384,7 +697,7 @@ void UniqueNodeList::nodeDefault(std::string strValidators) { { BOOST_FOREACH(std::string strValidator, *pmtEntries) { - nodeFetch(strValidator); + nodeAddDomain(strValidator, vsValidator); } } } diff --git a/src/UniqueNodeList.h b/src/UniqueNodeList.h index fe9f63f5c9..e5e9de528d 100644 --- a/src/UniqueNodeList.h +++ b/src/UniqueNodeList.h @@ -23,40 +23,66 @@ class UniqueNodeList { +public: + typedef enum { + vsManual = 'M', + vsValidator = 'V', // validators.txt + vsWeb = 'W', + vsReferral = 'R', + } validatorSource; + private: + typedef struct { + std::string strDomain; + NewcoinAddress naPublicKey; + validatorSource vsSource; + boost::posix_time::ptime tpNext; + boost::posix_time::ptime tpScan; + boost::posix_time::ptime tpFetch; + uint256 iSha256; + std::string strComment; + } seedDomain; + + int iSourceScore(validatorSource vsWhy); + void responseFetch(const std::string strDomain, const boost::system::error_code& err, const std::string strSiteFile); - boost::mutex mFetchLock; - int mFetchActive; // count of active fetches - std::deque mFetchPending; + boost::mutex mFetchLock; + int mFetchActive; // count of active fetches - std::string mStrIpsUrl; - std::string mStrValidatorsUrl; + boost::posix_time::ptime mtpFetchNext; + boost::asio::deadline_timer mdtFetchTimer; void fetchNext(); + void fetchFinish(); void fetchProcess(std::string strDomain); + void fetchTimerHandler(const boost::system::error_code& err); - void getValidatorsUrl(); - void getIpsUrl(); - void getFinish(); - void responseIps(const boost::system::error_code& err, const std::string strIpsFile); - void responseValidators(const boost::system::error_code& err, const std::string strValidatorsFile); + void getValidatorsUrl(NewcoinAddress naNodePublic, section secSite); + void getIpsUrl(section secSite); + void responseIps(const std::string& strSite, const boost::system::error_code& err, const std::string strIpsFile); + void responseValidators(NewcoinAddress naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string strValidatorsFile); - void processIps(section::mapped_type& vecStrIps); - void processValidators(section::mapped_type& vecStrValidators); + void processIps(const std::string& strSite, section::mapped_type* pmtVecStrIps); + void processValidators(const std::string& strSite, NewcoinAddress naNodePublic, section::mapped_type* pmtVecStrValidators); + + void processFile(const std::string strDomain, NewcoinAddress naNodePublic, section secSite); + + bool getSeedDomans(const std::string& strDomain, seedDomain& dstSeedDomain); + void setSeedDomans(const seedDomain& dstSeedDomain); public: - UniqueNodeList(); + UniqueNodeList(boost::asio::io_service& io_service); - void nodeAdd(NewcoinAddress naNodePublic, std::string strComment); - void nodeFetch(std::string strDomain); + // Begin processing. + void start(); + + void nodeAddPublic(NewcoinAddress naNodePublic, std::string strComment); + void nodeAddDomain(std::string strDomain, validatorSource vsWhy, std::string strComment=""); void nodeRemove(NewcoinAddress naNodePublic); void nodeDefault(std::string strValidators); void nodeReset(); - // 2- we don't care, 1- we care and is valid, 2-invalid signature -// int checkValid(newcoin::Validation& valid); - Json::Value getUnlJson(); }; diff --git a/src/Wallet.cpp b/src/Wallet.cpp index e05fadb848..378ef67401 100644 --- a/src/Wallet.cpp +++ b/src/Wallet.cpp @@ -4,10 +4,10 @@ #include "openssl/ec.h" -#include "boost/foreach.hpp" -#include "boost/lexical_cast.hpp" -#include "boost/interprocess/sync/scoped_lock.hpp" -#include "boost/make_shared.hpp" +#include +#include +#include +#include #include "Wallet.h" #include "Ledger.h" @@ -244,6 +244,10 @@ LocalAccount::pointer LocalAccountFamily::get(int seq) return ret; } +Wallet::Wallet() : mLedger(0) { + mPtScoresUpdated = boost::posix_time::from_time_t(0); +} + NewcoinAddress Wallet::addFamily(const NewcoinAddress& familySeed, bool lock) { LocalAccountFamily::pointer fam(doPrivate(familySeed, true, !lock)); @@ -330,12 +334,67 @@ Json::Value Wallet::getFamilyJson(const NewcoinAddress& family) return fit->second->getJson(); } +void Wallet::start() +{ + miscLoad(); + + struct tm tmScoresUpdated = to_tm(mPtScoresUpdated); + time_t ttScoresUpdated = mktime(&tmScoresUpdated); + + std::cerr << "Validator scores updated: " + << (ttScoresUpdated + ? "Never" + : boost::posix_time::to_simple_string(mPtScoresUpdated) + ) + << std::endl; + + theApp->getUNL().start(); +} + +bool Wallet::miscLoad() +{ + std::string strSql("SELECT * FROM Misc;"); + + ScopedLock sl(theApp->getWalletDB()->getDBLock()); + Database *db=theApp->getWalletDB()->getDB(); + + if(!db->executeSQL(strSql.c_str())) return false; + if(!db->startIterRows()) return false; + + time_t ttScoresUpdated = db->getInt("ScoresUpdated"); + + mPtScoresUpdated = boost::posix_time::from_time_t(ttScoresUpdated); + + db->endIterRows(); + + return true; +} + +bool Wallet::miscSave() +{ + struct tm tmScoresUpdated = to_tm(mPtScoresUpdated); + time_t ttScoresUpdated = mktime(&tmScoresUpdated); + std::string strScoresUpdate = boost::lexical_cast(ttScoresUpdated); + + std::string strSql("REPLACE INTO Misc (ScoresUpdated) VALUES ("); + // Should be a parameter. + strSql.append(strScoresUpdate); + strSql.append(");"); + + Database* db=theApp->getWalletDB()->getDB(); + ScopedLock sl(theApp->getWalletDB()->getDBLock()); + + db->executeSQL(strSql.c_str()); + + return true; +} + bool Wallet::nodeIdentityLoad() { std::string strSql("SELECT * FROM NodeIdentity;"); - ScopedLock sl(theApp->getWalletDB()->getDBLock()); - Database *db=theApp->getWalletDB()->getDB(); + Database* db=theApp->getWalletDB()->getDB(); + ScopedLock sl(theApp->getWalletDB()->getDBLock()); if(!db->executeSQL(strSql.c_str())) return false; if(!db->startIterRows()) return false; diff --git a/src/Wallet.h b/src/Wallet.h index 79b3f1d02d..0d337d97e0 100644 --- a/src/Wallet.h +++ b/src/Wallet.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -25,6 +26,12 @@ private: bool nodeIdentityLoad(); bool nodeIdentityCreate(); + // Misc persistent information + boost::posix_time::ptime mPtScoresUpdated; + + bool miscLoad(); + bool miscSave(); + protected: boost::recursive_mutex mLock; @@ -43,7 +50,11 @@ protected: // void addFamily(const NewcoinAddress& family, const std::string& pubKey, int seq, const std::string& name, const std::string& comment); public: - Wallet() : mLedger(0) { ; } + Wallet(); + + // Begin processing. + // - Maintain peer connectivity through validation and peer management. + void start(); NewcoinAddress addFamily(const std::string& passPhrase, bool lock); NewcoinAddress addFamily(const NewcoinAddress& familySeed, bool lock); diff --git a/src/main.cpp b/src/main.cpp index 71dba966a8..48ded74a6e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,7 +36,7 @@ void printHelp() cout << " sendto []" << endl; cout << " stop" << endl; cout << " tx" << endl; - cout << " unl_add []" << endl; + cout << " unl_add | []" << endl; cout << " unl_delete " << endl; cout << " unl_list" << endl; cout << " unl_reset" << endl;