diff options
| -rw-r--r-- | .travis.yml | 1 | ||||
| -rw-r--r-- | src/Makefile.test.include | 1 | ||||
| -rw-r--r-- | src/chainparams.h | 6 | ||||
| -rw-r--r-- | src/db.cpp | 77 | ||||
| -rw-r--r-- | src/db.h | 6 | ||||
| -rw-r--r-- | src/main.cpp | 33 | ||||
| -rw-r--r-- | src/net.cpp | 4 | ||||
| -rw-r--r-- | src/net.h | 2 | ||||
| -rw-r--r-- | src/protocol.cpp | 13 | ||||
| -rw-r--r-- | src/protocol.h | 8 | ||||
| -rw-r--r-- | src/qt/bitcoingui.cpp | 10 | ||||
| -rw-r--r-- | src/qt/bitcoingui.h | 4 | ||||
| -rw-r--r-- | src/qt/clientmodel.cpp | 18 | ||||
| -rw-r--r-- | src/qt/clientmodel.h | 5 | ||||
| -rw-r--r-- | src/qt/guiutil.cpp | 46 | ||||
| -rw-r--r-- | src/qt/rpcconsole.cpp | 9 | ||||
| -rw-r--r-- | src/qt/rpcconsole.h | 4 | ||||
| -rw-r--r-- | src/qt/sendcoinsdialog.cpp | 2 | ||||
| -rw-r--r-- | src/test/DoS_tests.cpp | 4 | ||||
| -rw-r--r-- | src/test/accounting_tests.cpp | 4 | ||||
| -rw-r--r-- | src/test/alert_tests.cpp | 4 | ||||
| -rw-r--r-- | src/test/main_tests.cpp | 4 | ||||
| -rw-r--r-- | src/test/miner_tests.cpp | 4 | ||||
| -rw-r--r-- | src/test/rpc_tests.cpp | 4 | ||||
| -rw-r--r-- | src/test/rpc_wallet_tests.cpp | 4 | ||||
| -rw-r--r-- | src/test/test_bitcoin.cpp | 28 | ||||
| -rw-r--r-- | src/test/test_bitcoin.h | 18 | ||||
| -rw-r--r-- | src/test/wallet_tests.cpp | 4 | ||||
| -rw-r--r-- | src/util.h | 1 | ||||
| -rw-r--r-- | src/walletdb.cpp | 6 |
30 files changed, 218 insertions, 116 deletions
diff --git a/.travis.yml b/.travis.yml index e08e78dab..9c18729b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ env: global: - MAKEJOBS=-j3 - RUN_TESTS=false + - BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID - CCACHE_SIZE=100M - CCACHE_TEMPDIR=/tmp/.ccache-temp - CCACHE_COMPRESS=1 diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 6774745de..e9d99323c 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -66,6 +66,7 @@ BITCOIN_TESTS =\ test/sigopcount_tests.cpp \ test/skiplist_tests.cpp \ test/test_bitcoin.cpp \ + test/test_bitcoin.h \ test/timedata_tests.cpp \ test/transaction_tests.cpp \ test/uint256_tests.cpp \ diff --git a/src/chainparams.h b/src/chainparams.h index 86b84df66..78b575d8f 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -14,8 +14,6 @@ #include <vector> -typedef unsigned char MessageStartChars[MESSAGE_START_SIZE]; - struct CDNSSeedData { std::string name, host; CDNSSeedData(const std::string &strName, const std::string &strHost) : name(strName), host(strHost) {} @@ -42,7 +40,7 @@ public: }; const uint256& HashGenesisBlock() const { return hashGenesisBlock; } - const MessageStartChars& MessageStart() const { return pchMessageStart; } + const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; } int GetDefaultPort() const { return nDefaultPort; } const arith_uint256& ProofOfWorkLimit() const { return bnProofOfWorkLimit; } @@ -83,7 +81,7 @@ protected: CChainParams() {} uint256 hashGenesisBlock; - MessageStartChars pchMessageStart; + CMessageHeader::MessageStartChars pchMessageStart; //! Raw pub key bytes for the broadcast alert signing key. std::vector<unsigned char> vAlertPubKey; int nDefaultPort; diff --git a/src/db.cpp b/src/db.cpp index 3246e4b67..36946b7dc 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -39,22 +39,31 @@ void CDBEnv::EnvShutdown() return; fDbEnvInit = false; - int ret = dbenv.close(0); + int ret = dbenv->close(0); if (ret != 0) LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret)); if (!fMockDb) DbEnv(0).remove(path.string().c_str(), 0); } -CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS) +void CDBEnv::Reset() { + delete dbenv; + dbenv = new DbEnv(DB_CXX_NO_EXCEPTIONS); fDbEnvInit = false; fMockDb = false; } +CDBEnv::CDBEnv() : dbenv(NULL) +{ + Reset(); +} + CDBEnv::~CDBEnv() { EnvShutdown(); + delete dbenv; + dbenv = NULL; } void CDBEnv::Close() @@ -79,17 +88,17 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn) if (GetBoolArg("-privdb", true)) nEnvFlags |= DB_PRIVATE; - dbenv.set_lg_dir(pathLogDir.string().c_str()); - dbenv.set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet - dbenv.set_lg_bsize(0x10000); - dbenv.set_lg_max(1048576); - dbenv.set_lk_max_locks(40000); - dbenv.set_lk_max_objects(40000); - dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug - dbenv.set_flags(DB_AUTO_COMMIT, 1); - dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); - dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); - int ret = dbenv.open(path.string().c_str(), + dbenv->set_lg_dir(pathLogDir.string().c_str()); + dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet + dbenv->set_lg_bsize(0x10000); + dbenv->set_lg_max(1048576); + dbenv->set_lk_max_locks(40000); + dbenv->set_lk_max_objects(40000); + dbenv->set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug + dbenv->set_flags(DB_AUTO_COMMIT, 1); + dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1); + dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1); + int ret = dbenv->open(path.string().c_str(), DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -116,14 +125,14 @@ void CDBEnv::MakeMock() LogPrint("db", "CDBEnv::MakeMock\n"); - dbenv.set_cachesize(1, 0, 1); - dbenv.set_lg_bsize(10485760 * 4); - dbenv.set_lg_max(10485760); - dbenv.set_lk_max_locks(10000); - dbenv.set_lk_max_objects(10000); - dbenv.set_flags(DB_AUTO_COMMIT, 1); - dbenv.log_set_config(DB_LOG_IN_MEMORY, 1); - int ret = dbenv.open(NULL, + dbenv->set_cachesize(1, 0, 1); + dbenv->set_lg_bsize(10485760 * 4); + dbenv->set_lg_max(10485760); + dbenv->set_lk_max_locks(10000); + dbenv->set_lk_max_objects(10000); + dbenv->set_flags(DB_AUTO_COMMIT, 1); + dbenv->log_set_config(DB_LOG_IN_MEMORY, 1); + int ret = dbenv->open(NULL, DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -144,7 +153,7 @@ CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDB LOCK(cs_db); assert(mapFileUseCount.count(strFile) == 0); - Db db(&dbenv, 0); + Db db(dbenv, 0); int result = db.verify(strFile.c_str(), NULL, NULL, 0); if (result == 0) return VERIFY_OK; @@ -167,7 +176,7 @@ bool CDBEnv::Salvage(std::string strFile, bool fAggressive, std::vector<CDBEnv:: stringstream strDump; - Db db(&dbenv, 0); + Db db(dbenv, 0); int result = db.verify(strFile.c_str(), NULL, &strDump, flags); if (result == DB_VERIFY_BAD) { LogPrintf("CDBEnv::Salvage: Database salvage found errors, all data may not be recoverable.\n"); @@ -208,10 +217,10 @@ bool CDBEnv::Salvage(std::string strFile, bool fAggressive, std::vector<CDBEnv:: void CDBEnv::CheckpointLSN(const std::string& strFile) { - dbenv.txn_checkpoint(0, 0, 0); + dbenv->txn_checkpoint(0, 0, 0); if (fMockDb) return; - dbenv.lsn_reset(strFile.c_str(), 0); + dbenv->lsn_reset(strFile.c_str(), 0); } @@ -237,7 +246,7 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose ++bitdb.mapFileUseCount[strFile]; pdb = bitdb.mapDb[strFile]; if (pdb == NULL) { - pdb = new Db(&bitdb.dbenv, 0); + pdb = new Db(bitdb.dbenv, 0); bool fMockDb = bitdb.IsMock(); if (fMockDb) { @@ -284,7 +293,7 @@ void CDB::Flush() if (fReadOnly) nMinutes = 1; - bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100) * 1024 : 0, nMinutes, 0); + bitdb.dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100) * 1024 : 0, nMinutes, 0); } void CDB::Close() @@ -324,7 +333,7 @@ bool CDBEnv::RemoveDb(const string& strFile) this->CloseDb(strFile); LOCK(cs_db); - int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT); + int rc = dbenv->dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT); return (rc == 0); } @@ -344,7 +353,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) string strFileRes = strFile + ".rewrite"; { // surround usage of db with extra {} CDB db(strFile.c_str(), "r"); - Db* pdbCopy = new Db(&bitdb.dbenv, 0); + Db* pdbCopy = new Db(bitdb.dbenv, 0); int ret = pdbCopy->open(NULL, // Txn pointer strFileRes.c_str(), // Filename @@ -394,10 +403,10 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) } } if (fSuccess) { - Db dbA(&bitdb.dbenv, 0); + Db dbA(bitdb.dbenv, 0); if (dbA.remove(strFile.c_str(), NULL, 0)) fSuccess = false; - Db dbB(&bitdb.dbenv, 0); + Db dbB(bitdb.dbenv, 0); if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) fSuccess = false; } @@ -430,10 +439,10 @@ void CDBEnv::Flush(bool fShutdown) // Move log data to the dat file CloseDb(strFile); LogPrint("db", "CDBEnv::Flush: %s checkpoint\n", strFile); - dbenv.txn_checkpoint(0, 0, 0); + dbenv->txn_checkpoint(0, 0, 0); LogPrint("db", "CDBEnv::Flush: %s detach\n", strFile); if (!fMockDb) - dbenv.lsn_reset(strFile.c_str(), 0); + dbenv->lsn_reset(strFile.c_str(), 0); LogPrint("db", "CDBEnv::Flush: %s closed\n", strFile); mapFileUseCount.erase(mi++); } else @@ -443,7 +452,7 @@ void CDBEnv::Flush(bool fShutdown) if (fShutdown) { char** listp; if (mapFileUseCount.empty()) { - dbenv.log_archive(&listp, DB_ARCH_REMOVE); + dbenv->log_archive(&listp, DB_ARCH_REMOVE); Close(); if (!fMockDb) boost::filesystem::remove_all(path / "database"); @@ -39,12 +39,14 @@ private: public: mutable CCriticalSection cs_db; - DbEnv dbenv; + DbEnv *dbenv; std::map<std::string, int> mapFileUseCount; std::map<std::string, Db*> mapDb; CDBEnv(); ~CDBEnv(); + void Reset(); + void MakeMock(); bool IsMock() { return fMockDb; } @@ -79,7 +81,7 @@ public: DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) { DbTxn* ptxn = NULL; - int ret = dbenv.txn_begin(NULL, &ptxn, flags); + int ret = dbenv->txn_begin(NULL, &ptxn, flags); if (!ptxn || ret != 0) return NULL; return ptxn; diff --git a/src/main.cpp b/src/main.cpp index 183dff6d3..e16887fa4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,7 +54,6 @@ bool fTxIndex = false; bool fIsBareMultisigStd = true; unsigned int nCoinCacheSize = 5000; - /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ CFeeRate minRelayTxFee = CFeeRate(1000); @@ -3088,10 +3087,31 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth void UnloadBlockIndex() { - mapBlockIndex.clear(); + LOCK(cs_main); setBlockIndexCandidates.clear(); chainActive.SetTip(NULL); pindexBestInvalid = NULL; + pindexBestHeader = NULL; + mempool.clear(); + mapOrphanTransactions.clear(); + mapOrphanTransactionsByPrev.clear(); + nSyncStarted = 0; + mapBlocksUnlinked.clear(); + vinfoBlockFile.clear(); + nLastBlockFile = 0; + nBlockSequenceId = 1; + mapBlockSource.clear(); + mapBlocksInFlight.clear(); + nQueuedValidatedHeaders = 0; + nPreferredDownload = 0; + setDirtyBlockIndex.clear(); + setDirtyFileInfo.clear(); + mapNodeState.clear(); + + BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { + delete entry.second; + } + mapBlockIndex.clear(); } bool LoadBlockIndex() @@ -4024,7 +4044,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "getaddr") + // This asymmetric behavior for inbound and outbound connections was introduced + // to prevent a fingerprinting attack: an attacker can send specific fake addresses + // to users' AddrMan and later request them by sending getaddr messages. + // Making users (which are behind NAT and can only make outgoing connections) ignore + // getaddr message mitigates the attack. + else if ((strCommand == "getaddr") && (pfrom->fInbound)) { pfrom->vAddrToSend.clear(); vector<CAddress> vAddr = addrman.GetAddr(); @@ -4308,7 +4333,7 @@ bool ProcessMessages(CNode* pfrom) // Read header CMessageHeader& hdr = msg.hdr; - if (!hdr.IsValid()) + if (!hdr.IsValid(Params().MessageStart())) { LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); continue; diff --git a/src/net.cpp b/src/net.cpp index 08d1d5740..0723ee218 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -510,7 +510,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) // get current incomplete message, or create a new one if (vRecvMsg.empty() || vRecvMsg.back().complete()) - vRecvMsg.push_back(CNetMessage(SER_NETWORK, nRecvVersion)); + vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, nRecvVersion)); CNetMessage& msg = vRecvMsg.back(); @@ -1976,7 +1976,7 @@ void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSen { ENTER_CRITICAL_SECTION(cs_vSend); assert(ssSend.size() == 0); - ssSend << CMessageHeader(pszCommand, 0); + ssSend << CMessageHeader(Params().MessageStart(), pszCommand, 0); LogPrint("net", "sending: %s ", SanitizeString(pszCommand)); } @@ -185,7 +185,7 @@ public: int64_t nTime; // time (in microseconds) of message receipt. - CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) { + CNetMessage(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) { hdrbuf.resize(24); in_data = false; nHdrPos = 0; diff --git a/src/protocol.cpp b/src/protocol.cpp index 74ac706d6..568580a59 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -5,7 +5,6 @@ #include "protocol.h" -#include "chainparams.h" #include "util.h" #include "utilstrencodings.h" @@ -21,17 +20,17 @@ static const char* ppszTypeName[] = "filtered block" }; -CMessageHeader::CMessageHeader() +CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn) { - memcpy(pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE); + memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE); memset(pchCommand, 0, sizeof(pchCommand)); nMessageSize = -1; nChecksum = 0; } -CMessageHeader::CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) +CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn) { - memcpy(pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE); + memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE); memset(pchCommand, 0, sizeof(pchCommand)); strncpy(pchCommand, pszCommand, COMMAND_SIZE); nMessageSize = nMessageSizeIn; @@ -43,10 +42,10 @@ std::string CMessageHeader::GetCommand() const return std::string(pchCommand, pchCommand + strnlen(pchCommand, COMMAND_SIZE)); } -bool CMessageHeader::IsValid() const +bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const { // Check start string - if (memcmp(pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) + if (memcmp(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE) != 0) return false; // Check the command string for errors diff --git a/src/protocol.h b/src/protocol.h index f8394ce52..e838c0d36 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -29,11 +29,13 @@ class CMessageHeader { public: - CMessageHeader(); - CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn); + typedef unsigned char MessageStartChars[MESSAGE_START_SIZE]; + + CMessageHeader(const MessageStartChars& pchMessageStartIn); + CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn); std::string GetCommand() const; - bool IsValid() const; + bool IsValid(const MessageStartChars& messageStart) const; ADD_SERIALIZE_METHODS; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index a0dcb46e2..1ec968ff2 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -340,6 +340,7 @@ void BitcoinGUI::createActions() openAction->setStatusTip(tr("Open a bitcoin: URI or payment request")); showHelpMessageAction = new QAction(TextColorIcon(":/icons/info"), tr("&Command-line options"), this); + showHelpMessageAction->setMenuRole(QAction::NoRole); showHelpMessageAction->setStatusTip(tr("Show the Bitcoin Core help message to get a list with possible Bitcoin command-line options")); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); @@ -435,8 +436,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) setNumConnections(clientModel->getNumConnections()); connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - setNumBlocks(clientModel->getNumBlocks()); - connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); + setNumBlocks(clientModel->getNumBlocks(), clientModel->getLastBlockDate()); + connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime)), this, SLOT(setNumBlocks(int,QDateTime))); // Receive and report messages from client model connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int))); @@ -652,7 +653,7 @@ void BitcoinGUI::setNumConnections(int count) labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count)); } -void BitcoinGUI::setNumBlocks(int count) +void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate) { if(!clientModel) return; @@ -680,9 +681,8 @@ void BitcoinGUI::setNumBlocks(int count) QString tooltip; - QDateTime lastBlockDate = clientModel->getLastBlockDate(); QDateTime currentDate = QDateTime::currentDateTime(); - qint64 secs = lastBlockDate.secsTo(currentDate); + qint64 secs = blockDate.secsTo(currentDate); tooltip = tr("Processed %n blocks of transaction history.", "", count); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index fd776d63f..5a289a904 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -143,8 +143,8 @@ signals: public slots: /** Set number of connections shown in the UI */ void setNumConnections(int count); - /** Set number of blocks shown in the UI */ - void setNumBlocks(int count); + /** Set number of blocks and last block date shown in the UI */ + void setNumBlocks(int count, const QDateTime& blockDate); /** Notify the user of an event from the core network or transaction handling code. @param[in] title the message box / notification title diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 729eb84a1..dc32f8157 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -18,7 +18,6 @@ #include <stdint.h> -#include <QDateTime> #include <QDebug> #include <QTimer> @@ -29,6 +28,7 @@ ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : optionsModel(optionsModel), peerTableModel(0), cachedNumBlocks(0), + cachedBlockDate(QDateTime()), cachedReindexing(0), cachedImporting(0), pollTimer(0) @@ -79,10 +79,11 @@ quint64 ClientModel::getTotalBytesSent() const QDateTime ClientModel::getLastBlockDate() const { LOCK(cs_main); + if (chainActive.Tip()) return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime()); - else - return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network + + return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network } double ClientModel::getVerificationProgress() const @@ -97,21 +98,26 @@ void ClientModel::updateTimer() // periodical polls if the core is holding the locks for a longer time - // for example, during a wallet rescan. TRY_LOCK(cs_main, lockMain); - if(!lockMain) + if (!lockMain) return; + // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change. // Periodically check and update with a timer. int newNumBlocks = getNumBlocks(); + QDateTime newBlockDate = getLastBlockDate(); // check for changed number of blocks we have, number of blocks peers claim to have, reindexing state and importing state if (cachedNumBlocks != newNumBlocks || - cachedReindexing != fReindex || cachedImporting != fImporting) + cachedBlockDate != newBlockDate || + cachedReindexing != fReindex || + cachedImporting != fImporting) { cachedNumBlocks = newNumBlocks; + cachedBlockDate = newBlockDate; cachedReindexing = fReindex; cachedImporting = fImporting; - emit numBlocksChanged(newNumBlocks); + emit numBlocksChanged(newNumBlocks, newBlockDate); } emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent()); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 7b74728b2..214701810 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_CLIENTMODEL_H #include <QObject> +#include <QDateTime> class AddressTableModel; class OptionsModel; @@ -15,7 +16,6 @@ class TransactionTableModel; class CWallet; QT_BEGIN_NAMESPACE -class QDateTime; class QTimer; QT_END_NAMESPACE @@ -73,6 +73,7 @@ private: PeerTableModel *peerTableModel; int cachedNumBlocks; + QDateTime cachedBlockDate; bool cachedReindexing; bool cachedImporting; @@ -83,7 +84,7 @@ private: signals: void numConnectionsChanged(int count); - void numBlocksChanged(int count); + void numBlocksChanged(int count, const QDateTime& blockDate); void alertsChanged(const QString &warnings); void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 2a13f43ea..9db0a7597 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -40,6 +40,7 @@ #if BOOST_FILESYSTEM_VERSION >= 3 #include <boost/filesystem/detail/utf8_codecvt_facet.hpp> #endif +#include <boost/scoped_array.hpp> #include <QAbstractItemView> #include <QApplication> @@ -567,12 +568,17 @@ TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* t #ifdef WIN32 boost::filesystem::path static StartupShortcutPath() { + if (GetBoolArg("-testnet", false)) + return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (testnet).lnk"; + else if (GetBoolArg("-regtest", false)) + return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (regtest).lnk"; + return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk"; } bool GetStartOnSystemStartup() { - // check for Bitcoin.lnk + // check for Bitcoin*.lnk return boost::filesystem::exists(StartupShortcutPath()); } @@ -588,8 +594,8 @@ bool SetStartOnSystemStartup(bool fAutoStart) // Get a pointer to the IShellLink interface. IShellLink* psl = NULL; HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, - CLSCTX_INPROC_SERVER, IID_IShellLink, - reinterpret_cast<void**>(&psl)); + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast<void**>(&psl)); if (SUCCEEDED(hres)) { @@ -597,20 +603,34 @@ bool SetStartOnSystemStartup(bool fAutoStart) TCHAR pszExePath[MAX_PATH]; GetModuleFileName(NULL, pszExePath, sizeof(pszExePath)); - TCHAR pszArgs[5] = TEXT("-min"); + // Start client minimized + QString strArgs = "-min"; + // Set -testnet /-regtest options + strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", GetBoolArg("-testnet", false), GetBoolArg("-regtest", false))); + +#ifdef UNICODE + boost::scoped_array<TCHAR> args(new TCHAR[strArgs.length() + 1]); + // Convert the QString to TCHAR* + strArgs.toWCharArray(args.get()); + // Add missing '\0'-termination to string + args[strArgs.length()] = '\0'; +#endif // Set the path to the shortcut target psl->SetPath(pszExePath); PathRemoveFileSpec(pszExePath); psl->SetWorkingDirectory(pszExePath); psl->SetShowCmd(SW_SHOWMINNOACTIVE); - psl->SetArguments(pszArgs); +#ifndef UNICODE + psl->SetArguments(strArgs.toStdString().c_str()); +#else + psl->SetArguments(args.get()); +#endif // Query IShellLink for the IPersistFile interface for // saving the shortcut in persistent storage. IPersistFile* ppf = NULL; - hres = psl->QueryInterface(IID_IPersistFile, - reinterpret_cast<void**>(&ppf)); + hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&ppf)); if (SUCCEEDED(hres)) { WCHAR pwsz[MAX_PATH]; @@ -630,11 +650,10 @@ bool SetStartOnSystemStartup(bool fAutoStart) } return true; } - #elif defined(Q_OS_LINUX) // Follow the Desktop Application Autostart Spec: -// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html +// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html boost::filesystem::path static GetAutostartDir() { @@ -690,8 +709,13 @@ bool SetStartOnSystemStartup(bool fAutoStart) // Write a bitcoin.desktop file to the autostart directory: optionFile << "[Desktop Entry]\n"; optionFile << "Type=Application\n"; - optionFile << "Name=Bitcoin\n"; - optionFile << "Exec=" << pszExePath << " -min\n"; + if (GetBoolArg("-testnet", false)) + optionFile << "Name=Bitcoin (testnet)\n"; + else if (GetBoolArg("-regtest", false)) + optionFile << "Name=Bitcoin (regtest)\n"; + else + optionFile << "Name=Bitcoin\n"; + optionFile << "Exec=" << pszExePath << strprintf(" -min -testnet=%d -regtest=%d\n", GetBoolArg("-testnet", false), GetBoolArg("-regtest", false)); optionFile << "Terminal=false\n"; optionFile << "Hidden=false\n"; optionFile.close(); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 9f3991c4c..ccde44fb2 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -293,8 +293,8 @@ void RPCConsole::setClientModel(ClientModel *model) setNumConnections(model->getNumConnections()); connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - setNumBlocks(model->getNumBlocks()); - connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); + setNumBlocks(model->getNumBlocks(), model->getLastBlockDate()); + connect(model, SIGNAL(numBlocksChanged(int,QDateTime)), this, SLOT(setNumBlocks(int,QDateTime))); updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent()); connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64))); @@ -404,11 +404,10 @@ void RPCConsole::setNumConnections(int count) ui->numberOfConnections->setText(connections); } -void RPCConsole::setNumBlocks(int count) +void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate) { ui->numberOfBlocks->setText(QString::number(count)); - if(clientModel) - ui->lastBlockTime->setText(clientModel->getLastBlockDate().toString()); + ui->lastBlockTime->setText(blockDate.toString()); } void RPCConsole::on_lineEdit_returnPressed() diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index fff5cfbf5..8737be35d 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -63,8 +63,8 @@ public slots: void message(int category, const QString &message, bool html = false); /** Set number of connections shown in the UI */ void setNumConnections(int count); - /** Set number of blocks shown in the UI */ - void setNumBlocks(int count); + /** Set number of blocks and last block date shown in the UI */ + void setNumBlocks(int count, const QDateTime& blockDate); /** Go forward or back in history */ void browseHistory(int offset); /** Scroll console view to end */ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 5aef2d753..4f3230a8c 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -121,7 +121,7 @@ void SendCoinsDialog::setClientModel(ClientModel *clientModel) this->clientModel = clientModel; if (clientModel) { - connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(updateSmartFeeLabel())); + connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime)), this, SLOT(updateSmartFeeLabel())); } } diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 9407511ec..bf2554875 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -16,6 +16,8 @@ #include "serialize.h" #include "util.h" +#include "test/test_bitcoin.h" + #include <stdint.h> #include <boost/assign/list_of.hpp> // for 'map_list_of()' @@ -41,7 +43,7 @@ CService ip(uint32_t i) return CService(CNetAddr(s), Params().GetDefaultPort()); } -BOOST_AUTO_TEST_SUITE(DoS_tests) +BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup) BOOST_AUTO_TEST_CASE(DoS_banning) { diff --git a/src/test/accounting_tests.cpp b/src/test/accounting_tests.cpp index da07b8c7a..36499f01a 100644 --- a/src/test/accounting_tests.cpp +++ b/src/test/accounting_tests.cpp @@ -5,6 +5,8 @@ #include "wallet.h" #include "walletdb.h" +#include "test/test_bitcoin.h" + #include <stdint.h> #include <boost/foreach.hpp> @@ -12,7 +14,7 @@ extern CWallet* pwalletMain; -BOOST_AUTO_TEST_SUITE(accounting_tests) +BOOST_FIXTURE_TEST_SUITE(accounting_tests, TestingSetup) static void GetResults(CWalletDB& walletdb, std::map<CAmount, CAccountingEntry>& results) diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index efc921171..5e1f5f029 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -15,6 +15,8 @@ #include "util.h" #include "utilstrencodings.h" +#include "test/test_bitcoin.h" + #include <fstream> #include <boost/filesystem/operations.hpp> @@ -78,7 +80,7 @@ } #endif -struct ReadAlerts +struct ReadAlerts : public TestingSetup { ReadAlerts() { diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp index 2a72a220a..1927f3dea 100644 --- a/src/test/main_tests.cpp +++ b/src/test/main_tests.cpp @@ -5,9 +5,11 @@ #include "primitives/transaction.h" #include "main.h" +#include "test/test_bitcoin.h" + #include <boost/test/unit_test.hpp> -BOOST_AUTO_TEST_SUITE(main_tests) +BOOST_FIXTURE_TEST_SUITE(main_tests, TestingSetup) BOOST_AUTO_TEST_CASE(subsidy_limit_test) { diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 44c57a8ea..6ab9cb8a4 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -8,9 +8,11 @@ #include "uint256.h" #include "util.h" +#include "test/test_bitcoin.h" + #include <boost/test/unit_test.hpp> -BOOST_AUTO_TEST_SUITE(miner_tests) +BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup) static struct { diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 1c6963001..45cb551d0 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -8,6 +8,8 @@ #include "base58.h" #include "netbase.h" +#include "test/test_bitcoin.h" + #include <boost/algorithm/string.hpp> #include <boost/test/unit_test.hpp> @@ -45,7 +47,7 @@ Value CallRPC(string args) } -BOOST_AUTO_TEST_SUITE(rpc_tests) +BOOST_FIXTURE_TEST_SUITE(rpc_tests, TestingSetup) BOOST_AUTO_TEST_CASE(rpc_rawparams) { diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index 57c49c2df..44475076b 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -8,6 +8,8 @@ #include "base58.h" #include "wallet.h" +#include "test/test_bitcoin.h" + #include <boost/algorithm/string.hpp> #include <boost/test/unit_test.hpp> @@ -19,7 +21,7 @@ extern Value CallRPC(string args); extern CWallet* pwalletMain; -BOOST_AUTO_TEST_SUITE(rpc_wallet_tests) +BOOST_FIXTURE_TEST_SUITE(rpc_wallet_tests, TestingSetup) BOOST_AUTO_TEST_CASE(rpc_addmultisig) { diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index f2dae99d6..5df417b8e 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -4,6 +4,8 @@ #define BOOST_TEST_MODULE Bitcoin Test Suite +#include "test_bitcoin.h" + #include "main.h" #include "random.h" #include "txdb.h" @@ -24,18 +26,15 @@ CWallet* pwalletMain; extern bool fPrintToConsole; extern void noui_connect(); -struct TestingSetup { - CCoinsViewDB *pcoinsdbview; - boost::filesystem::path pathTemp; - boost::thread_group threadGroup; - - TestingSetup() { +TestingSetup::TestingSetup() +{ fPrintToDebugLog = false; // don't want to write to debug.log file SelectParams(CBaseChainParams::UNITTEST); noui_connect(); #ifdef ENABLE_WALLET bitdb.MakeMock(); #endif + ClearDatadirCache(); pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); boost::filesystem::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); @@ -53,27 +52,28 @@ struct TestingSetup { for (int i=0; i < nScriptCheckThreads-1; i++) threadGroup.create_thread(&ThreadScriptCheck); RegisterNodeSignals(GetNodeSignals()); - } - ~TestingSetup() - { +} + +TestingSetup::~TestingSetup() +{ + UnregisterNodeSignals(GetNodeSignals()); threadGroup.interrupt_all(); threadGroup.join_all(); - UnregisterNodeSignals(GetNodeSignals()); #ifdef ENABLE_WALLET + UnregisterValidationInterface(pwalletMain); delete pwalletMain; pwalletMain = NULL; #endif + UnloadBlockIndex(); delete pcoinsTip; delete pcoinsdbview; delete pblocktree; #ifdef ENABLE_WALLET bitdb.Flush(true); + bitdb.Reset(); #endif boost::filesystem::remove_all(pathTemp); - } -}; - -BOOST_GLOBAL_FIXTURE(TestingSetup); +} void Shutdown(void* parg) { diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h new file mode 100644 index 000000000..c1448dcde --- /dev/null +++ b/src/test/test_bitcoin.h @@ -0,0 +1,18 @@ +#ifndef BITCOIN_TEST_TEST_BITCOIN_H +#define BITCOIN_TEST_TEST_BITCOIN_H + +#include "txdb.h" + +#include <boost/filesystem.hpp> +#include <boost/thread.hpp> + +struct TestingSetup { + CCoinsViewDB *pcoinsdbview; + boost::filesystem::path pathTemp; + boost::thread_group threadGroup; + + TestingSetup(); + ~TestingSetup(); +}; + +#endif diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp index 289cc8c90..25c8fab33 100644 --- a/src/test/wallet_tests.cpp +++ b/src/test/wallet_tests.cpp @@ -9,6 +9,8 @@ #include <utility> #include <vector> +#include "test/test_bitcoin.h" + #include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> @@ -23,7 +25,7 @@ using namespace std; typedef set<pair<const CWalletTx*,unsigned int> > CoinSet; -BOOST_AUTO_TEST_SUITE(wallet_tests) +BOOST_FIXTURE_TEST_SUITE(wallet_tests, TestingSetup) static CWallet wallet; static vector<COutput> vCoins; diff --git a/src/util.h b/src/util.h index bbb0e8103..4d0cb7136 100644 --- a/src/util.h +++ b/src/util.h @@ -94,6 +94,7 @@ bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest); bool TryCreateDirectory(const boost::filesystem::path& p); boost::filesystem::path GetDefaultDataDir(); const boost::filesystem::path &GetDataDir(bool fNetSpecific = true); +void ClearDatadirCache(); boost::filesystem::path GetConfigFile(); #ifndef WIN32 boost::filesystem::path GetPidFile(); diff --git a/src/walletdb.cpp b/src/walletdb.cpp index b2daf036f..ddec57d9a 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -903,8 +903,8 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) int64_t now = GetTime(); std::string newFilename = strprintf("wallet.%d.bak", now); - int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL, - newFilename.c_str(), DB_AUTO_COMMIT); + int result = dbenv.dbenv->dbrename(NULL, filename.c_str(), NULL, + newFilename.c_str(), DB_AUTO_COMMIT); if (result == 0) LogPrintf("Renamed %s to %s\n", filename, newFilename); else @@ -923,7 +923,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); bool fSuccess = allOK; - boost::scoped_ptr<Db> pdbCopy(new Db(&dbenv.dbenv, 0)); + boost::scoped_ptr<Db> pdbCopy(new Db(dbenv.dbenv, 0)); int ret = pdbCopy->open(NULL, // Txn pointer filename.c_str(), // Filename "main", // Logical db name |