diff options
52 files changed, 1440 insertions, 533 deletions
diff --git a/configure.ac b/configure.ac index d9ed86f6fc..905acd573c 100644 --- a/configure.ac +++ b/configure.ac @@ -154,7 +154,7 @@ use_pkgconfig=yes case $host in *mingw*) - #pkgconfig does more harm than good with mingw + #pkgconfig does more harm than good with MinGW use_pkgconfig=no TARGET_OS=windows @@ -298,7 +298,7 @@ if test x$use_hardening != xno; then AX_CHECK_COMPILE_FLAG([-fno-stack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fno-stack-protector"]) AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"]) - # -pie will link successfully with mingw, but it's unsupported and leads to undeterministic binaries + # -pie will link successfully with MinGW, but it's unsupported and leads to undeterministic binaries AX_CHECK_LINK_FLAG([[-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"]) fi diff --git a/doc/build-unix.md b/doc/build-unix.md index 579341d2d9..422b46cc3a 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -21,6 +21,9 @@ Dependencies libdb4.8 Berkeley DB Blockchain & wallet storage libboost Boost C++ Library miniupnpc UPnP Support Optional firewall-jumping support + qt GUI GUI toolkit + protobuf Payments in GUI Data interchange format used for payment protocol + libqrencode QR codes in GUI Optional for generating QR codes [miniupnpc](http://miniupnp.free.fr/) may be used for UPnP port mapping. It can be downloaded from [here]( http://miniupnp.tuxfamily.org/files/). UPnP support is compiled in and @@ -46,21 +49,25 @@ Licenses of statically linked libraries: - Berkeley DB 4.8.30.NC - Boost 1.37 - miniupnpc 1.6 +- qt 4.8.3 +- protobuf 2.5.0 +- libqrencode 3.2.0 Dependency Build Instructions: Ubuntu & Debian ---------------------------------------------- Build requirements: sudo apt-get install build-essential + sudo apt-get install libtool autotools-dev sudo apt-get install libssl-dev -for Ubuntu 12.04: +for Ubuntu 12.04 and later: sudo apt-get install libboost-all-dev db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin). - Ubuntu precise has packages for libdb5.1-dev and libdb5.1++-dev, + Ubuntu 12.04 and later have packages for libdb5.1-dev and libdb5.1++-dev, but using these will break binary wallet compatibility, and is not recommended. for other Ubuntu & Debian: @@ -74,6 +81,22 @@ Optional: sudo apt-get install libminiupnpc-dev (see --with-miniupnpc and --enable-upnp-default) +Dependencies for the GUI: Ubuntu & Debian +----------------------------------------- + +If you want to build Bitcoin-Qt, make sure that the required packages for Qt development +are installed. Qt 4 is currently necessary to build the GUI. + +To build with Qt 4 you need the following: + + apt-get install libqt4-dev libprotobuf-dev + +libqrencode (optional) can be installed with: + + apt-get install libqrencode-dev + +Once these are installed, they will be found by configure and a bitcoin-qt executable will be +built by default. Notes ----- diff --git a/doc/readme-qt.md b/doc/readme-qt.md deleted file mode 100644 index 5aa849af10..0000000000 --- a/doc/readme-qt.md +++ /dev/null @@ -1,101 +0,0 @@ -Bitcoin-Qt Readme -=============================== -Contains build and configuration instructions for Bitcoin-Qt (Qt4 GUI for Bitcoin). - -Build Instructions ---------------------- - -### Debian - - -First, make sure that the required packages for Qt4 development of your -distribution are installed, these are - - - -for Debian and Ubuntu <= 11.10 : - - - apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ - libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ - libssl-dev libdb4.8++-dev libprotobuf-dev protobuf-compiler - -for Ubuntu >= 12.04 (please read the 'Berkely DB version warning' below): - - apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ - libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ - libssl-dev libdb++-dev libminiupnpc-dev libprotobuf-dev protobuf-compiler - -For Qt 5 you need the following, otherwise you get an error with lrelease when running qmake: - - - apt-get install qt5-qmake libqt5gui5 libqt5core5 libqt5dbus5 qttools5-dev-tools - -Once these are installed, they will be found by configure and bitcoin-qt will be -built by default. - - -### Mac OS X - -* Download and install the [Qt Mac OS X SDK](https://qt-project.org/downloads). It is recommended to also install Apple's Xcode with UNIX tools. -* Download and install either [MacPorts](https://www.macports.org/) or [HomeBrew](http://mxcl.github.io/homebrew/). -* Execute the following commands in a terminal to get the dependencies using MacPorts - - sudo port selfupdate - sudo port install boost db48 miniupnpc protobuf-cpp - -* Execute the following commands in a terminal to get the dependencies using HomeBrew: - - brew update - brew install boost miniupnpc openssl berkeley-db4 protobuf - -Build Configuration Options ---------------------- - -### UPnP port forwarding - -UPnP support is compiled in when possible and turned off by default. See the -configure options for upnp behavior desired: - - --with-miniupnpc No UPnP support miniupnp not required - --disable-upnp-default (the default) UPnP support turned off by default at runtime - --enable-upnp-default UPnP support turned on by default at runtime - -### Notification support for recent (k)ubuntu versions - -DBUS support is enabled by default if dependencies are met. - -See the --with-qtdbus configure option. - -### Generation of QR codes - -[libqrencode](http://fukuchi.org/works/qrencode/) may be used to generate QRCode images for payment requests. - -QR code support is enabled by default if dependencies are met. - -See the --with-qrencode configure option. - -Warnings ---------------------- - -### Berkely DB Version Warning - - -A warning for people using the *static binary* version of Bitcoin on a Linux/UNIX-ish system (tl;dr: **Berkely DB databases are not forward compatible**). - -The static binary version of Bitcoin is linked against libdb4.8 (see also [this Debian issue](http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=621425)). - -Now the nasty thing is that databases from 5.X are not compatible with 4.X. - -If the globally installed development package of Berkely DB installed on your system is 5.X, any source you build yourself will be linked against that. The first time you run with a 5.X version the database will be upgraded, and 4.X cannot open the new format. This means that you cannot go back to the old statically linked version without significant hassle! - -### Ubuntu 11.10 Warning - - -Ubuntu 11.10 has a package called 'qt-at-spi' installed by default. At the time of writing, having that package installed causes bitcoin-qt to crash intermittently. The issue has been reported as [launchpad bug 857790](https://bugs.launchpad.net/ubuntu/+source/qt-at-spi/+bug/857790), but -isn't yet fixed. - -Until the bug is fixed, you can remove the qt-at-spi package to work around the problem, though this will presumably disable screen reader functionality for Qt apps: - - sudo apt-get remove qt-at-spi - diff --git a/doc/tor.md b/doc/tor.md index 86d56cffd5..6057801d34 100644 --- a/doc/tor.md +++ b/doc/tor.md @@ -48,6 +48,7 @@ config file): HiddenServiceDir /var/lib/tor/bitcoin-service/ HiddenServicePort 8333 127.0.0.1:8333 + HiddenServicePort 18333 127.0.0.1:18333 The directory can be different of course, but (both) port numbers should be equal to your bitcoind's P2P listen port (8333 by default). diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index ef50ccd07f..798660dff3 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -229,8 +229,10 @@ static const CRPCCommand vRPCCommands[] = { "getbestblockhash", &getbestblockhash, true, false, false }, { "getconnectioncount", &getconnectioncount, true, false, false }, { "getpeerinfo", &getpeerinfo, true, false, false }, + { "ping", &ping, true, false, false }, { "addnode", &addnode, true, true, false }, { "getaddednodeinfo", &getaddednodeinfo, true, true, false }, + { "getnettotals", &getnettotals, true, true, false }, { "getdifficulty", &getdifficulty, true, false, false }, { "getnetworkhashps", &getnetworkhashps, true, false, false }, { "getgenerate", &getgenerate, true, false, false }, @@ -880,7 +882,8 @@ void StopRPCThreads() deadlineTimers.clear(); rpc_io_service->stop(); - rpc_worker_group->join_all(); + if (rpc_worker_group != NULL) + rpc_worker_group->join_all(); delete rpc_worker_group; rpc_worker_group = NULL; delete rpc_ssl_context; rpc_ssl_context = NULL; delete rpc_io_service; rpc_io_service = NULL; diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index ce1c0e68b7..1cad12f14a 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -153,8 +153,10 @@ extern void EnsureWalletIsUnlocked(); extern json_spirit::Value getconnectioncount(const json_spirit::Array& params, bool fHelp); // in rpcnet.cpp extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value ping(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value addnode(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getaddednodeinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp); diff --git a/src/chainparams.h b/src/chainparams.h index ce3c14306d..3f99b7eb06 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -68,7 +68,7 @@ public: virtual const vector<CAddress>& FixedSeeds() const = 0; int RPCPort() const { return nRPCPort; } protected: - CChainParams() {}; + CChainParams() {} uint256 hashGenesisBlock; MessageStartChars pchMessageStart; diff --git a/src/core.h b/src/core.h index ce21acd59e..9ee8b2edce 100644 --- a/src/core.h +++ b/src/core.h @@ -661,4 +661,38 @@ public: void print() const; }; + +/** Describes a place in the block chain to another node such that if the + * other node doesn't have the same branch, it can find a recent common trunk. + * The further back it is, the further before the fork it may be. + */ +struct CBlockLocator +{ + std::vector<uint256> vHave; + + CBlockLocator() {} + + CBlockLocator(const std::vector<uint256>& vHaveIn) + { + vHave = vHaveIn; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vHave); + ) + + void SetNull() + { + vHave.clear(); + } + + bool IsNull() + { + return vHave.empty(); + } +}; + #endif @@ -16,7 +16,7 @@ #include <db_cxx.h> class CAddrMan; -class CBlockLocator; +struct CBlockLocator; class CDiskBlockIndex; class CMasterKey; class COutPoint; diff --git a/src/init.cpp b/src/init.cpp index e75e981a57..fce5992255 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -116,7 +116,7 @@ void Shutdown() { LOCK(cs_main); if (pwalletMain) - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + pwalletMain->SetBestChain(chainActive.GetLocator()); if (pblocktree) pblocktree->Flush(); if (pcoinsTip) @@ -766,7 +766,7 @@ bool AppInit2(boost::thread_group& threadGroup) // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way around). - if (!mapBlockIndex.empty() && pindexGenesisBlock == NULL) + if (!mapBlockIndex.empty() && chainActive.Genesis() == NULL) return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); // Initialize the block index (no-op if non-empty database was already loaded) @@ -912,7 +912,7 @@ bool AppInit2(boost::thread_group& threadGroup) strErrors << _("Cannot write default address") << "\n"; } - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + pwalletMain->SetBestChain(chainActive.GetLocator()); } LogPrintf("%s", strErrors.str().c_str()); @@ -920,26 +920,26 @@ bool AppInit2(boost::thread_group& threadGroup) RegisterWallet(pwalletMain); - CBlockIndex *pindexRescan = pindexBest; + CBlockIndex *pindexRescan = chainActive.Tip(); if (GetBoolArg("-rescan", false)) - pindexRescan = pindexGenesisBlock; + pindexRescan = chainActive.Genesis(); else { CWalletDB walletdb(strWalletFile); CBlockLocator locator; if (walletdb.ReadBestBlock(locator)) - pindexRescan = locator.GetBlockIndex(); + pindexRescan = chainActive.FindFork(locator); else - pindexRescan = pindexGenesisBlock; + pindexRescan = chainActive.Genesis(); } - if (pindexBest && pindexBest != pindexRescan) + if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { uiInterface.InitMessage(_("Rescanning...")); - LogPrintf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); + LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); pwalletMain->ScanForWalletTransactions(pindexRescan, true); LogPrintf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + pwalletMain->SetBestChain(chainActive.GetLocator()); nWalletDBUpdated++; } @@ -985,7 +985,7 @@ bool AppInit2(boost::thread_group& threadGroup) //// debug print LogPrintf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); - LogPrintf("nBestHeight = %d\n", nBestHeight); + LogPrintf("nBestHeight = %d\n", chainActive.Height()); LogPrintf("setKeyPool.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0); LogPrintf("mapWallet.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapWallet.size() : 0); LogPrintf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile index 96af7765be..20c9c4f287 100644 --- a/src/leveldb/Makefile +++ b/src/leveldb/Makefile @@ -31,6 +31,7 @@ TESTHARNESS = ./util/testharness.o $(TESTUTIL) TESTS = \ arena_test \ + autocompact_test \ bloom_test \ c_test \ cache_test \ @@ -70,7 +71,7 @@ SHARED = $(SHARED1) else # Update db.h if you change these. SHARED_MAJOR = 1 -SHARED_MINOR = 12 +SHARED_MINOR = 13 SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) SHARED2 = $(SHARED1).$(SHARED_MAJOR) SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) @@ -114,6 +115,9 @@ leveldbutil: db/leveldb_main.o $(LIBOBJECTS) arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +autocompact_test: db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) diff --git a/src/leveldb/db/autocompact_test.cc b/src/leveldb/db/autocompact_test.cc new file mode 100644 index 0000000000..d20a2362c3 --- /dev/null +++ b/src/leveldb/db/autocompact_test.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "db/db_impl.h" +#include "leveldb/cache.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class AutoCompactTest { + public: + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + AutoCompactTest() { + dbname_ = test::TmpDir() + "/autocompact_test"; + tiny_cache_ = NewLRUCache(100); + options_.block_cache = tiny_cache_; + DestroyDB(dbname_, options_); + options_.create_if_missing = true; + options_.compression = kNoCompression; + ASSERT_OK(DB::Open(options_, dbname_, &db_)); + } + + ~AutoCompactTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void DoReads(int n); +}; + +static const int kValueSize = 200 * 1024; +static const int kTotalSize = 100 * 1024 * 1024; +static const int kCount = kTotalSize / kValueSize; + +// Read through the first n keys repeatedly and check that they get +// compacted (verified by checking the size of the key space). +void AutoCompactTest::DoReads(int n) { + std::string value(kValueSize, 'x'); + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + + // Fill database + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Put(WriteOptions(), Key(i), value)); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Delete everything + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Delete(WriteOptions(), Key(i))); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Get initial measurement of the space we will be reading. + const int64_t initial_size = Size(Key(0), Key(n)); + const int64_t initial_other_size = Size(Key(n), Key(kCount)); + + // Read until size drops significantly. + std::string limit_key = Key(n); + for (int read = 0; true; read++) { + ASSERT_LT(read, 100) << "Taking too long to compact"; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); + iter->Valid() && iter->key().ToString() < limit_key; + iter->Next()) { + // Drop data + } + delete iter; + // Wait a little bit to allow any triggered compactions to complete. + Env::Default()->SleepForMicroseconds(1000000); + uint64_t size = Size(Key(0), Key(n)); + fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", + read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0); + if (size <= initial_size/10) { + break; + } + } + + // Verify that the size of the key space not touched by the reads + // is pretty much unchanged. + const int64_t final_other_size = Size(Key(n), Key(kCount)); + ASSERT_LE(final_other_size, initial_other_size + 1048576); + ASSERT_GE(final_other_size, initial_other_size/5 - 1048576); +} + +TEST(AutoCompactTest, ReadAll) { + DoReads(kCount); +} + +TEST(AutoCompactTest, ReadHalf) { + DoReads(kCount/2); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc index 31b2d5f416..b37ffdfe64 100644 --- a/src/leveldb/db/corruption_test.cc +++ b/src/leveldb/db/corruption_test.cc @@ -35,6 +35,7 @@ class CorruptionTest { CorruptionTest() { tiny_cache_ = NewLRUCache(100); options_.env = &env_; + options_.block_cache = tiny_cache_; dbname_ = test::TmpDir() + "/db_test"; DestroyDB(dbname_, options_); @@ -50,17 +51,14 @@ class CorruptionTest { delete tiny_cache_; } - Status TryReopen(Options* options = NULL) { + Status TryReopen() { delete db_; db_ = NULL; - Options opt = (options ? *options : options_); - opt.env = &env_; - opt.block_cache = tiny_cache_; - return DB::Open(opt, dbname_, &db_); + return DB::Open(options_, dbname_, &db_); } - void Reopen(Options* options = NULL) { - ASSERT_OK(TryReopen(options)); + void Reopen() { + ASSERT_OK(TryReopen()); } void RepairDB() { @@ -92,6 +90,10 @@ class CorruptionTest { for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { uint64_t key; Slice in(iter->key()); + if (in == "" || in == "~") { + // Ignore boundary keys. + continue; + } if (!ConsumeDecimalNumber(&in, &key) || !in.empty() || key < next_expected) { @@ -233,7 +235,7 @@ TEST(CorruptionTest, TableFile) { dbi->TEST_CompactRange(1, NULL, NULL); Corrupt(kTableFile, 100, 1); - Check(99, 99); + Check(90, 99); } TEST(CorruptionTest, TableFileIndexData) { @@ -299,7 +301,7 @@ TEST(CorruptionTest, CompactionInputError) { ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last))); Corrupt(kTableFile, 100, 1); - Check(9, 9); + Check(5, 9); // Force compactions by writing lots of values Build(10000); @@ -307,32 +309,23 @@ TEST(CorruptionTest, CompactionInputError) { } TEST(CorruptionTest, CompactionInputErrorParanoid) { - Options options; - options.paranoid_checks = true; - options.write_buffer_size = 1048576; - Reopen(&options); + options_.paranoid_checks = true; + options_.write_buffer_size = 512 << 10; + Reopen(); DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); - // Fill levels >= 1 so memtable compaction outputs to level 1 - for (int level = 1; level < config::kNumLevels; level++) { - dbi->Put(WriteOptions(), "", "begin"); - dbi->Put(WriteOptions(), "~", "end"); + // Make multiple inputs so we need to compact. + for (int i = 0; i < 2; i++) { + Build(10); dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + env_.SleepForMicroseconds(100000); } + dbi->CompactRange(NULL, NULL); - Build(10); - dbi->TEST_CompactMemTable(); - ASSERT_EQ(1, Property("leveldb.num-files-at-level0")); - - Corrupt(kTableFile, 100, 1); - Check(9, 9); - - // Write must eventually fail because of corrupted table - Status s; + // Write must fail because of corrupted table std::string tmp1, tmp2; - for (int i = 0; i < 10000 && s.ok(); i++) { - s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2)); - } + Status s = db_->Put(WriteOptions(), Key(5, &tmp1), Value(5, &tmp2)); ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db"; } diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc index 395d3172ad..fa1351038b 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -113,14 +113,14 @@ Options SanitizeOptions(const std::string& dbname, return result; } -DBImpl::DBImpl(const Options& options, const std::string& dbname) - : env_(options.env), - internal_comparator_(options.comparator), - internal_filter_policy_(options.filter_policy), - options_(SanitizeOptions( - dbname, &internal_comparator_, &internal_filter_policy_, options)), - owns_info_log_(options_.info_log != options.info_log), - owns_cache_(options_.block_cache != options.block_cache), +DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) + : env_(raw_options.env), + internal_comparator_(raw_options.comparator), + internal_filter_policy_(raw_options.filter_policy), + options_(SanitizeOptions(dbname, &internal_comparator_, + &internal_filter_policy_, raw_options)), + owns_info_log_(options_.info_log != raw_options.info_log), + owns_cache_(options_.block_cache != raw_options.block_cache), dbname_(dbname), db_lock_(NULL), shutting_down_(NULL), @@ -130,6 +130,7 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname) logfile_(NULL), logfile_number_(0), log_(NULL), + seed_(0), tmp_batch_(new WriteBatch), bg_compaction_scheduled_(false), manual_compaction_(NULL), @@ -138,7 +139,7 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname) has_imm_.Release_Store(NULL); // Reserve ten files or so for other uses and give the rest to TableCache. - const int table_cache_size = options.max_open_files - kNumNonTableCacheFiles; + const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles; table_cache_ = new TableCache(dbname_, &options_, table_cache_size); versions_ = new VersionSet(dbname_, &options_, table_cache_, @@ -1027,7 +1028,8 @@ static void CleanupIteratorState(void* arg1, void* arg2) { } // namespace Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, - SequenceNumber* latest_snapshot) { + SequenceNumber* latest_snapshot, + uint32_t* seed) { IterState* cleanup = new IterState; mutex_.Lock(); *latest_snapshot = versions_->LastSequence(); @@ -1051,13 +1053,15 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, cleanup->version = versions_->current(); internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + *seed = ++seed_; mutex_.Unlock(); return internal_iter; } Iterator* DBImpl::TEST_NewInternalIterator() { SequenceNumber ignored; - return NewInternalIterator(ReadOptions(), &ignored); + uint32_t ignored_seed; + return NewInternalIterator(ReadOptions(), &ignored, &ignored_seed); } int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { @@ -1114,12 +1118,21 @@ Status DBImpl::Get(const ReadOptions& options, Iterator* DBImpl::NewIterator(const ReadOptions& options) { SequenceNumber latest_snapshot; - Iterator* internal_iter = NewInternalIterator(options, &latest_snapshot); + uint32_t seed; + Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); return NewDBIterator( - &dbname_, env_, user_comparator(), internal_iter, + this, user_comparator(), iter, (options.snapshot != NULL ? reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_ - : latest_snapshot)); + : latest_snapshot), + seed); +} + +void DBImpl::RecordReadSample(Slice key) { + MutexLock l(&mutex_); + if (versions_->current()->RecordReadSample(key)) { + MaybeScheduleCompaction(); + } } const Snapshot* DBImpl::GetSnapshot() { diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h index 3c8d711ae0..75fd30abe9 100644 --- a/src/leveldb/db/db_impl.h +++ b/src/leveldb/db/db_impl.h @@ -59,13 +59,19 @@ class DBImpl : public DB { // file at a level >= 1. int64_t TEST_MaxNextLevelOverlappingBytes(); + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. + void RecordReadSample(Slice key); + private: friend class DB; struct CompactionState; struct Writer; Iterator* NewInternalIterator(const ReadOptions&, - SequenceNumber* latest_snapshot); + SequenceNumber* latest_snapshot, + uint32_t* seed); Status NewDB(); @@ -135,6 +141,7 @@ class DBImpl : public DB { WritableFile* logfile_; uint64_t logfile_number_; log::Writer* log_; + uint32_t seed_; // For sampling. // Queue of writers. std::deque<Writer*> writers_; diff --git a/src/leveldb/db/db_iter.cc b/src/leveldb/db/db_iter.cc index 87dca2ded4..071a54e3f4 100644 --- a/src/leveldb/db/db_iter.cc +++ b/src/leveldb/db/db_iter.cc @@ -5,12 +5,14 @@ #include "db/db_iter.h" #include "db/filename.h" +#include "db/db_impl.h" #include "db/dbformat.h" #include "leveldb/env.h" #include "leveldb/iterator.h" #include "port/port.h" #include "util/logging.h" #include "util/mutexlock.h" +#include "util/random.h" namespace leveldb { @@ -46,15 +48,16 @@ class DBIter: public Iterator { kReverse }; - DBIter(const std::string* dbname, Env* env, - const Comparator* cmp, Iterator* iter, SequenceNumber s) - : dbname_(dbname), - env_(env), + DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s, + uint32_t seed) + : db_(db), user_comparator_(cmp), iter_(iter), sequence_(s), direction_(kForward), - valid_(false) { + valid_(false), + rnd_(seed), + bytes_counter_(RandomPeriod()) { } virtual ~DBIter() { delete iter_; @@ -100,8 +103,12 @@ class DBIter: public Iterator { } } - const std::string* const dbname_; - Env* const env_; + // Pick next gap with average value of config::kReadBytesPeriod. + ssize_t RandomPeriod() { + return rnd_.Uniform(2*config::kReadBytesPeriod); + } + + DBImpl* db_; const Comparator* const user_comparator_; Iterator* const iter_; SequenceNumber const sequence_; @@ -112,13 +119,23 @@ class DBIter: public Iterator { Direction direction_; bool valid_; + Random rnd_; + ssize_t bytes_counter_; + // No copying allowed DBIter(const DBIter&); void operator=(const DBIter&); }; inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { - if (!ParseInternalKey(iter_->key(), ikey)) { + Slice k = iter_->key(); + ssize_t n = k.size() + iter_->value().size(); + bytes_counter_ -= n; + while (bytes_counter_ < 0) { + bytes_counter_ += RandomPeriod(); + db_->RecordReadSample(k); + } + if (!ParseInternalKey(k, ikey)) { status_ = Status::Corruption("corrupted internal key in DBIter"); return false; } else { @@ -288,12 +305,12 @@ void DBIter::SeekToLast() { } // anonymous namespace Iterator* NewDBIterator( - const std::string* dbname, - Env* env, + DBImpl* db, const Comparator* user_key_comparator, Iterator* internal_iter, - const SequenceNumber& sequence) { - return new DBIter(dbname, env, user_key_comparator, internal_iter, sequence); + SequenceNumber sequence, + uint32_t seed) { + return new DBIter(db, user_key_comparator, internal_iter, sequence, seed); } } // namespace leveldb diff --git a/src/leveldb/db/db_iter.h b/src/leveldb/db/db_iter.h index d9e1b174ab..04927e937b 100644 --- a/src/leveldb/db/db_iter.h +++ b/src/leveldb/db/db_iter.h @@ -11,15 +11,17 @@ namespace leveldb { +class DBImpl; + // Return a new iterator that converts internal keys (yielded by // "*internal_iter") that were live at the specified "sequence" number // into appropriate user keys. extern Iterator* NewDBIterator( - const std::string* dbname, - Env* env, + DBImpl* db, const Comparator* user_key_comparator, Iterator* internal_iter, - const SequenceNumber& sequence); + SequenceNumber sequence, + uint32_t seed); } // namespace leveldb diff --git a/src/leveldb/db/dbformat.h b/src/leveldb/db/dbformat.h index f7f64dafb6..5d8a032bd3 100644 --- a/src/leveldb/db/dbformat.h +++ b/src/leveldb/db/dbformat.h @@ -38,6 +38,9 @@ static const int kL0_StopWritesTrigger = 12; // space if the same key space is being repeatedly overwritten. static const int kMaxMemCompactLevel = 2; +// Approximate gap in bytes between samples of data read during iteration. +static const int kReadBytesPeriod = 1048576; + } // namespace config class InternalKey; diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc index 4fd1ddef21..66d73be71f 100644 --- a/src/leveldb/db/version_set.cc +++ b/src/leveldb/db/version_set.cc @@ -289,6 +289,51 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) { return a->number > b->number; } +void Version::ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)) { + // TODO(sanjay): Change Version::Get() to use this function. + const Comparator* ucmp = vset_->icmp_.user_comparator(); + + // Search level-0 in order from newest to oldest. + std::vector<FileMetaData*> tmp; + tmp.reserve(files_[0].size()); + for (uint32_t i = 0; i < files_[0].size(); i++) { + FileMetaData* f = files_[0][i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (!tmp.empty()) { + std::sort(tmp.begin(), tmp.end(), NewestFirst); + for (uint32_t i = 0; i < tmp.size(); i++) { + if (!(*func)(arg, 0, tmp[i])) { + return; + } + } + } + + // Search other levels. + for (int level = 1; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Binary search to find earliest index whose largest key >= internal_key. + uint32_t index = FindFile(vset_->icmp_, files_[level], internal_key); + if (index < num_files) { + FileMetaData* f = files_[level][index]; + if (ucmp->Compare(user_key, f->smallest.user_key()) < 0) { + // All of "f" is past any data for user_key + } else { + if (!(*func)(arg, level, f)) { + return; + } + } + } + } +} + Status Version::Get(const ReadOptions& options, const LookupKey& k, std::string* value, @@ -401,6 +446,44 @@ bool Version::UpdateStats(const GetStats& stats) { return false; } +bool Version::RecordReadSample(Slice internal_key) { + ParsedInternalKey ikey; + if (!ParseInternalKey(internal_key, &ikey)) { + return false; + } + + struct State { + GetStats stats; // Holds first matching file + int matches; + + static bool Match(void* arg, int level, FileMetaData* f) { + State* state = reinterpret_cast<State*>(arg); + state->matches++; + if (state->matches == 1) { + // Remember first match. + state->stats.seek_file = f; + state->stats.seek_file_level = level; + } + // We can stop iterating once we have a second match. + return state->matches < 2; + } + }; + + State state; + state.matches = 0; + ForEachOverlapping(ikey.user_key, internal_key, &state, &State::Match); + + // Must have at least two matches since we want to merge across + // files. But what if we have a single file that contains many + // overwrites and deletions? Should we have another mechanism for + // finding such files? + if (state.matches >= 2) { + // 1MB cost is about 1 seek (see comment in Builder::Apply). + return UpdateStats(state.stats); + } + return false; +} + void Version::Ref() { ++refs_; } @@ -435,10 +518,13 @@ int Version::PickLevelForMemTableOutput( if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) { break; } - GetOverlappingInputs(level + 2, &start, &limit, &overlaps); - const int64_t sum = TotalFileSize(overlaps); - if (sum > kMaxGrandParentOverlapBytes) { - break; + if (level + 2 < config::kNumLevels) { + // Check that file does not overlap too many grandparent bytes. + GetOverlappingInputs(level + 2, &start, &limit, &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > kMaxGrandParentOverlapBytes) { + break; + } } level++; } @@ -452,6 +538,8 @@ void Version::GetOverlappingInputs( const InternalKey* begin, const InternalKey* end, std::vector<FileMetaData*>* inputs) { + assert(level >= 0); + assert(level < config::kNumLevels); inputs->clear(); Slice user_begin, user_end; if (begin != NULL) { diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h index 9d084fdb7d..20de0e2629 100644 --- a/src/leveldb/db/version_set.h +++ b/src/leveldb/db/version_set.h @@ -78,6 +78,12 @@ class Version { // REQUIRES: lock is held bool UpdateStats(const GetStats& stats); + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. Returns true if a new compaction may need to be triggered. + // REQUIRES: lock is held + bool RecordReadSample(Slice key); + // Reference count management (so Versions do not disappear out from // under live iterators) void Ref(); @@ -114,6 +120,15 @@ class Version { class LevelFileNumIterator; Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; + // Call func(arg, level, f) for every file that overlaps user_key in + // order from newest to oldest. If an invocation of func returns + // false, makes no more calls. + // + // REQUIRES: user portion of internal_key == user_key. + void ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)); + VersionSet* vset_; // VersionSet to which this Version belongs Version* next_; // Next version in linked list Version* prev_; // Previous version in linked list diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h index da8b11a8c0..57c00a5da0 100644 --- a/src/leveldb/include/leveldb/db.h +++ b/src/leveldb/include/leveldb/db.h @@ -14,7 +14,7 @@ namespace leveldb { // Update Makefile if you change these static const int kMajorVersion = 1; -static const int kMinorVersion = 12; +static const int kMinorVersion = 13; struct Options; struct ReadOptions; diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc index 6badfdc230..0f5dcfac5a 100644 --- a/src/leveldb/util/env_posix.cc +++ b/src/leveldb/util/env_posix.cc @@ -320,8 +320,39 @@ class PosixMmapFile : public WritableFile { return Status::OK(); } - virtual Status Sync() { + Status SyncDirIfManifest() { + const char* f = filename_.c_str(); + const char* sep = strrchr(f, '/'); + Slice basename; + std::string dir; + if (sep == NULL) { + dir = "."; + basename = f; + } else { + dir = std::string(f, sep - f); + basename = sep + 1; + } Status s; + if (basename.starts_with("MANIFEST")) { + int fd = open(dir.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(dir, errno); + } else { + if (fsync(fd) < 0) { + s = IOError(dir, errno); + } + close(fd); + } + } + return s; + } + + virtual Status Sync() { + // Ensure new files referred to by the manifest are in the filesystem. + Status s = SyncDirIfManifest(); + if (!s.ok()) { + return s; + } if (pending_sync_) { // Some unmapped data was not synced diff --git a/src/leveldb/util/random.h b/src/leveldb/util/random.h index 07538242ea..ddd51b1c7b 100644 --- a/src/leveldb/util/random.h +++ b/src/leveldb/util/random.h @@ -16,7 +16,12 @@ class Random { private: uint32_t seed_; public: - explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { } + explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { + // Avoid bad seeds. + if (seed_ == 0 || seed_ == 2147483647L) { + seed_ = 1; + } + } uint32_t Next() { static const uint32_t M = 2147483647L; // 2^31-1 static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 diff --git a/src/main.cpp b/src/main.cpp index ed40a3550a..215a7ba620 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,13 +32,8 @@ CTxMemPool mempool; unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; -std::vector<CBlockIndex*> vBlockIndexByHeight; -CBlockIndex* pindexGenesisBlock = NULL; -int nBestHeight = -1; -uint256 nBestChainWork = 0; +CChain chainActive; uint256 nBestInvalidWork = 0; -uint256 hashBestChain = 0; -CBlockIndex* pindexBest = NULL; set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed int64 nTimeBestReceived = 0; int nScriptCheckThreads = 0; @@ -173,106 +168,83 @@ void static ResendWalletTransactions() // Registration of network node signals. // +int static GetHeight() +{ + LOCK(cs_main); + return chainActive.Height(); +} + void RegisterNodeSignals(CNodeSignals& nodeSignals) { + nodeSignals.GetHeight.connect(&GetHeight); nodeSignals.ProcessMessages.connect(&ProcessMessages); nodeSignals.SendMessages.connect(&SendMessages); } void UnregisterNodeSignals(CNodeSignals& nodeSignals) { + nodeSignals.GetHeight.disconnect(&GetHeight); nodeSignals.ProcessMessages.disconnect(&ProcessMessages); nodeSignals.SendMessages.disconnect(&SendMessages); } ////////////////////////////////////////////////////////////////////////////// // -// CBlockLocator implementation +// CChain implementation // -CBlockLocator::CBlockLocator(uint256 hashBlock) -{ - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end()) - Set((*mi).second); +CBlockIndex *CChain::SetTip(CBlockIndex *pindex) { + if (pindex == NULL) { + vChain.clear(); + return NULL; + } + vChain.resize(pindex->nHeight + 1); + while (pindex && vChain[pindex->nHeight] != pindex) { + vChain[pindex->nHeight] = pindex; + pindex = pindex->pprev; + } + return pindex; } -void CBlockLocator::Set(const CBlockIndex* pindex) -{ - vHave.clear(); +CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const { int nStep = 1; - while (pindex) - { - vHave.push_back(pindex->GetBlockHash()); + std::vector<uint256> vHave; + vHave.reserve(32); - // Exponentially larger steps back - for (int i = 0; pindex && i < nStep; i++) + if (!pindex) + pindex = Tip(); + while (pindex) { + vHave.push_back(pindex->GetBlockHash()); + // Stop when we have added the genesis block. + if (pindex->nHeight == 0) + break; + // Exponentially larger steps back, plus the genesis block. + int nHeight = std::max(pindex->nHeight - nStep, 0); + // In case pindex is not in this chain, iterate pindex->pprev to find blocks. + while (pindex->nHeight > nHeight && !Contains(pindex)) pindex = pindex->pprev; + // If pindex is in this chain, use direct height-based access. + if (pindex->nHeight > nHeight) + pindex = (*this)[nHeight]; if (vHave.size() > 10) nStep *= 2; } - vHave.push_back(Params().HashGenesisBlock()); -} -int CBlockLocator::GetDistanceBack() -{ - // Retrace how far back it was in the sender's branch - int nDistance = 0; - int nStep = 1; - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return nDistance; - } - nDistance += nStep; - if (nDistance > 10) - nStep *= 2; - } - return nDistance; + return CBlockLocator(vHave); } -CBlockIndex *CBlockLocator::GetBlockIndex() -{ +CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { + BOOST_FOREACH(const uint256& hash, locator.vHave) { std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) + if (Contains(pindex)) return pindex; } } - return pindexGenesisBlock; -} - -uint256 CBlockLocator::GetBlockHash() -{ - // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return hash; - } - } - return Params().HashGenesisBlock(); -} - -int CBlockLocator::GetHeight() -{ - CBlockIndex* pindex = GetBlockIndex(); - if (!pindex) - return 0; - return pindex->nHeight; + return Genesis(); } ////////////////////////////////////////////////////////////////////////////// @@ -517,7 +489,7 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64 nBlockTime) if (tx.nLockTime == 0) return true; if (nBlockHeight == 0) - nBlockHeight = nBestHeight; + nBlockHeight = chainActive.Height(); if (nBlockTime == 0) nBlockTime = GetAdjustedTime(); if ((int64)tx.nLockTime < ((int64)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) @@ -644,7 +616,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) if (pblock == NULL) { CCoins coins; if (pcoinsTip->GetCoins(GetHash(), coins)) { - CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); + CBlockIndex *pindex = chainActive[coins.nHeight]; if (pindex) { if (!ReadBlockFromDisk(blockTmp, pindex)) return 0; @@ -678,10 +650,10 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) + if (!pindex || !chainActive.Contains(pindex)) return 0; - return pindexBest->nHeight - pindex->nHeight + 1; + return chainActive.Height() - pindex->nHeight + 1; } @@ -1078,7 +1050,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) + if (!pindex || !chainActive.Contains(pindex)) return 0; // Make sure the merkle branch connects to this block @@ -1090,7 +1062,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const } pindexRet = pindex; - return pindexBest->nHeight - pindex->nHeight + 1; + return chainActive.Height() - pindex->nHeight + 1; } @@ -1173,7 +1145,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock nHeight = coins.nHeight; } if (nHeight > 0) - pindexSlow = FindBlockByHeight(nHeight); + pindexSlow = chainActive[nHeight]; } } @@ -1203,14 +1175,6 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -static CBlockIndex* pblockindexFBBHLast; -CBlockIndex* FindBlockByHeight(int nHeight) -{ - if (nHeight >= (int)vBlockIndexByHeight.size()) - return NULL; - return vBlockIndexByHeight[nHeight]; -} - bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) { // Open history file to append @@ -1404,17 +1368,17 @@ int GetNumBlocksOfPeers() bool IsInitialBlockDownload() { - if (pindexBest == NULL || fImporting || fReindex || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) + if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; - if (pindexBest != pindexLastBest) + if (chainActive.Tip() != pindexLastBest) { - pindexLastBest = pindexBest; + pindexLastBest = chainActive.Tip(); nLastUpdate = GetTime(); } return (GetTime() - nLastUpdate < 10 && - pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); + chainActive.Tip()->GetBlockTime() < GetTime() - 24 * 60 * 60); } bool fLargeWorkForkFound = false; @@ -1430,10 +1394,10 @@ void CheckForkWarningConditions() // If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it) // of our head, drop it - if (pindexBestForkTip && nBestHeight - pindexBestForkTip->nHeight >= 72) + if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) pindexBestForkTip = NULL; - if (pindexBestForkTip || nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) + if (pindexBestForkTip || nBestInvalidWork > chainActive.Tip()->nChainWork + (chainActive.Tip()->GetBlockWork() * 6).getuint256()) { if (!fLargeWorkForkFound) { @@ -1470,7 +1434,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) { // If we are on a fork that is sufficiently large, set a warning flag CBlockIndex* pfork = pindexNewForkTip; - CBlockIndex* plonger = pindexBest; + CBlockIndex* plonger = chainActive.Tip(); while (pfork && pfork != plonger) { while (plonger && plonger->nHeight > pfork->nHeight) @@ -1489,7 +1453,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) // the 7-block condition and from this always have the most-likely-to-cause-warning fork if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && pindexNewForkTip->nChainWork - pfork->nChainWork > (pfork->GetBlockWork() * 7).getuint256() && - nBestHeight - pindexNewForkTip->nHeight < 72) + chainActive.Height() - pindexNewForkTip->nHeight < 72) { pindexBestForkTip = pindexNewForkTip; pindexBestForkBase = pfork; @@ -1511,8 +1475,8 @@ void static InvalidChainFound(CBlockIndex* pindexNew) log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime()).c_str()); LogPrintf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", - hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str()); CheckForkWarningConditions(); } @@ -1521,7 +1485,7 @@ void static InvalidBlockFound(CBlockIndex *pindex) { pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); setBlockIndexValid.erase(pindex); InvalidChainFound(pindex); - if (pindex->GetNextInMainChain()) { + if (chainActive.Next(pindex)) { CValidationState stateDummy; ConnectBestBlock(stateDummy); // reorganise away from the failed block } @@ -1538,7 +1502,7 @@ bool ConnectBestBlock(CValidationState &state) { pindexNewBest = *it; } - if (pindexNewBest == pindexBest || (pindexBest && pindexNewBest->nChainWork == pindexBest->nChainWork)) + if (pindexNewBest == chainActive.Tip() || (chainActive.Tip() && pindexNewBest->nChainWork == chainActive.Tip()->nChainWork)) return true; // nothing to do // check ancestry @@ -1558,10 +1522,10 @@ bool ConnectBestBlock(CValidationState &state) { break; } - if (pindexBest == NULL || pindexTest->nChainWork > pindexBest->nChainWork) + if (chainActive.Tip() == NULL || pindexTest->nChainWork > chainActive.Tip()->nChainWork) vAttach.push_back(pindexTest); - if (pindexTest->pprev == NULL || pindexTest->GetNextInMainChain()) { + if (pindexTest->pprev == NULL || chainActive.Next(pindexTest)) { reverse(vAttach.begin(), vAttach.end()); BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { boost::this_thread::interruption_point(); @@ -1881,7 +1845,6 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C // (its coinbase is unspendable) if (block.GetHash() == Params().HashGenesisBlock()) { view.SetBestBlock(pindex); - pindexGenesisBlock = pindex; return true; } @@ -2129,9 +2092,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // Proceed by updating the memory structures. // Register new best chain - vBlockIndexByHeight.resize(pindexNew->nHeight + 1); - BOOST_FOREACH(CBlockIndex* pindex, vConnect) - vBlockIndexByHeight[pindex->nHeight] = pindex; + chainActive.SetTip(pindexNew); // Resurrect memory transactions that were in the disconnected branch BOOST_FOREACH(CTransaction& tx, vResurrect) { @@ -2151,29 +2112,21 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // Update best block in wallet (so we can detect restored wallets) if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) - { - const CBlockLocator locator(pindexNew); - ::SetBestChain(locator); - } + ::SetBestChain(chainActive.GetLocator(pindexNew)); // New best block - hashBestChain = pindexNew->GetBlockHash(); - pindexBest = pindexNew; - pblockindexFBBHLast = NULL; - nBestHeight = pindexBest->nHeight; - nBestChainWork = pindexNew->nChainWork; nTimeBestReceived = GetTime(); nTransactionsUpdated++; LogPrintf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", - hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str(), - Checkpoints::GuessVerificationProgress(pindexBest)); + chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str(), + Checkpoints::GuessVerificationProgress(chainActive.Tip())); // Check the version of the last 100 blocks to see if we need to upgrade: if (!fIsInitialDownload) { int nUpgraded = 0; - const CBlockIndex* pindex = pindexBest; + const CBlockIndex* pindex = chainActive.Tip(); for (int i = 0; i < 100 && pindex != NULL; i++) { if (pindex->nVersion > CBlock::CURRENT_VERSION) @@ -2191,7 +2144,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) if (!fIsInitialDownload && !strCmd.empty()) { - boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); + boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } @@ -2233,7 +2186,7 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos if (!ConnectBestBlock(state)) return false; - if (pindexNew == pindexBest) + if (pindexNew == chainActive.Tip()) { // Clear fork warning if its no longer applicable CheckForkWarningConditions(); @@ -2482,11 +2435,11 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) // Relay inventory, but don't relay old inventory during initial block download int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); - if (hashBestChain == hash) + if (chainActive.Tip()->GetBlockHash() == hash) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) - if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) pnode->PushInventory(CInv(MSG_BLOCK, hash)); } @@ -2505,6 +2458,18 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } +int64 CBlockIndex::GetMedianTime() const +{ + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan/2; i++) + { + if (!chainActive.Next(pindex)) + return GetBlockTime(); + pindex = chainActive.Next(pindex); + } + return pindex->GetMedianTimePast(); +} + void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) { // Filter out duplicate requests @@ -2513,7 +2478,7 @@ void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) pnode->pindexLastGetBlocksBegin = pindexBegin; pnode->hashLastGetBlocksEnd = hashEnd; - pnode->PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); + pnode->PushMessage("getblocks", chainActive.GetLocator(pindexBegin), hashEnd); } bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) @@ -2530,7 +2495,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl return error("ProcessBlock() : CheckBlock FAILED"); CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); - if (pcheckpoint && pblock->hashPrevBlock != hashBestChain) + if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) { // Extra checks to prevent "fill up memory by spamming with bogus blocks" int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; @@ -2561,7 +2526,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); // Ask this guy to fill in what we're missing - PushGetBlocks(pfrom, pindexBest, GetOrphanRoot(pblock2)); + PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(pblock2)); } return true; } @@ -2875,48 +2840,39 @@ bool static LoadBlockIndexDB() LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); // Load hashBestChain pointer to end of best chain - pindexBest = pcoinsTip->GetBestBlock(); - if (pindexBest == NULL) + chainActive.SetTip(pcoinsTip->GetBestBlock()); + if (chainActive.Tip() == NULL) return true; - hashBestChain = pindexBest->GetBlockHash(); - nBestHeight = pindexBest->nHeight; - nBestChainWork = pindexBest->nChainWork; // register best chain - CBlockIndex *pindex = pindexBest; - vBlockIndexByHeight.resize(pindexBest->nHeight + 1); - while(pindex != NULL) { - vBlockIndexByHeight[pindex->nHeight] = pindex; - pindex = pindex->pprev; - } LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", - hashBestChain.ToString().c_str(), nBestHeight, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str()); return true; } bool VerifyDB(int nCheckLevel, int nCheckDepth) { - if (pindexBest == NULL || pindexBest->pprev == NULL) + if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) return true; // Verify blocks in the best chain if (nCheckDepth <= 0) nCheckDepth = 1000000000; // suffices until the year 19000 - if (nCheckDepth > nBestHeight) - nCheckDepth = nBestHeight; + if (nCheckDepth > chainActive.Height()) + nCheckDepth = chainActive.Height(); nCheckLevel = std::max(0, std::min(4, nCheckLevel)); LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CCoinsViewCache coins(*pcoinsTip, true); - CBlockIndex* pindexState = pindexBest; + CBlockIndex* pindexState = chainActive.Tip(); CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; CValidationState state; - for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); - if (pindex->nHeight < nBestHeight-nCheckDepth) + if (pindex->nHeight < chainActive.Height()-nCheckDepth) break; CBlock block; // check level 0: read from disk @@ -2948,14 +2904,14 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) } } if (pindexFailure) - return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", pindexBest->nHeight - pindexFailure->nHeight + 1, nGoodTransactions); + return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { CBlockIndex *pindex = pindexState; - while (pindex != pindexBest) { + while (pindex != chainActive.Tip()) { boost::this_thread::interruption_point(); - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex)) return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); @@ -2964,7 +2920,7 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) } } - LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", pindexBest->nHeight - pindexState->nHeight, nGoodTransactions); + LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); return true; } @@ -2973,12 +2929,8 @@ void UnloadBlockIndex() { mapBlockIndex.clear(); setBlockIndexValid.clear(); - pindexGenesisBlock = NULL; - nBestHeight = 0; - nBestChainWork = 0; + chainActive.SetTip(NULL); nBestInvalidWork = 0; - hashBestChain = 0; - pindexBest = NULL; } bool LoadBlockIndex() @@ -2992,7 +2944,7 @@ bool LoadBlockIndex() bool InitBlockIndex() { // Check whether we're already initialized - if (pindexGenesisBlock != NULL) + if (chainActive.Genesis() != NULL) return true; // Use the provided setting for -txindex in the new database @@ -3038,7 +2990,7 @@ void PrintBlockTree() } vector<pair<int, CBlockIndex*> > vStack; - vStack.push_back(make_pair(0, pindexGenesisBlock)); + vStack.push_back(make_pair(0, chainActive.Genesis())); int nPrevCol = 0; while (!vStack.empty()) @@ -3081,7 +3033,7 @@ void PrintBlockTree() vector<CBlockIndex*>& vNext = mapNext[pindex]; for (unsigned int i = 0; i < vNext.size(); i++) { - if (vNext[i]->GetNextInMainChain()) + if (chainActive.Next(vNext[i])) { swap(vNext[0], vNext[i]); break; @@ -3328,7 +3280,7 @@ void static ProcessGetData(CNode* pfrom) // and we want it right after the last block so they don't // wait for other stuff first. vector<CInv> vInv; - vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); + vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); pfrom->PushMessage("inv", vInv); pfrom->hashContinue = 0; } @@ -3610,7 +3562,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (!fImporting && !fReindex) pfrom->AskFor(inv); } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { - PushGetBlocks(pfrom, pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); + PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(mapOrphanBlocks[inv.hash])); } else if (nInv == nLastBlock) { // In case we are on a very long side-chain, it is possible that we already have // the last block in an inv bundle sent in response to getblocks. Try to detect @@ -3654,14 +3606,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> locator >> hashStop; // Find the last block the caller has in the main chain - CBlockIndex* pindex = locator.GetBlockIndex(); + CBlockIndex* pindex = chainActive.FindFork(locator); // Send the rest of the chain if (pindex) - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); int nLimit = 500; LogPrint("net", "getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit); - for (; pindex; pindex = pindex->GetNextInMainChain()) + for (; pindex; pindex = chainActive.Next(pindex)) { if (pindex->GetBlockHash() == hashStop) { @@ -3699,16 +3651,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else { // Find the last block the caller has in the main chain - pindex = locator.GetBlockIndex(); + pindex = chainActive.FindFork(locator); if (pindex) - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); } // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end vector<CBlock> vHeaders; int nLimit = 2000; LogPrint("net", "getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str()); - for (; pindex; pindex = pindex->GetNextInMainChain()) + for (; pindex; pindex = chainActive.Next(pindex)) { vHeaders.push_back(pindex->GetBlockHeader()); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) @@ -3863,6 +3815,63 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + else if (strCommand == "pong") + { + int64 pingUsecEnd = GetTimeMicros(); + uint64 nonce = 0; + size_t nAvail = vRecv.in_avail(); + bool bPingFinished = false; + std::string sProblem; + + if (nAvail >= sizeof(nonce)) { + vRecv >> nonce; + + // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) + if (pfrom->nPingNonceSent != 0) { + if (nonce == pfrom->nPingNonceSent) { + // Matching pong received, this ping is no longer outstanding + bPingFinished = true; + int64 pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; + if (pingUsecTime > 0) { + // Successful ping time measurement, replace previous + pfrom->nPingUsecTime = pingUsecTime; + } else { + // This should never happen + sProblem = "Timing mishap"; + } + } else { + // Nonce mismatches are normal when pings are overlapping + sProblem = "Nonce mismatch"; + if (nonce == 0) { + // This is most likely a bug in another implementation somewhere, cancel this ping + bPingFinished = true; + sProblem = "Nonce zero"; + } + } + } else { + sProblem = "Unsolicited pong without ping"; + } + } else { + // This is most likely a bug in another implementation somewhere, cancel this ping + bPingFinished = true; + sProblem = "Short payload"; + } + + if (!(sProblem.empty())) { + LogPrint("net", "pong %s %s: %s, %"PRI64x" expected, %"PRI64x" received, %"PRIszu" bytes\n", + pfrom->addr.ToString().c_str(), + pfrom->strSubVer.c_str(), + sProblem.c_str(), + pfrom->nPingNonceSent, + nonce, + nAvail); + } + if (bPingFinished) { + pfrom->nPingNonceSent = 0; + } + } + + else if (strCommand == "alert") { CAlert alert; @@ -3961,7 +3970,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) bool ProcessMessages(CNode* pfrom) { //if (fDebug) - // LogPrintf("ProcessMessages(%zu messages)\n", pfrom->vRecvMsg.size()); + // LogPrintf("ProcessMessages(%"PRIszu" messages)\n", pfrom->vRecvMsg.size()); // // Message format @@ -3986,7 +3995,7 @@ bool ProcessMessages(CNode* pfrom) CNetMessage& msg = *it; //if (fDebug) - // LogPrintf("ProcessMessages(message %u msgsz, %zu bytes, complete:%s)\n", + // LogPrintf("ProcessMessages(message %u msgsz, %"PRIszu" bytes, complete:%s)\n", // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); @@ -4084,20 +4093,40 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (pto->nVersion == 0) return true; - // Keep-alive ping. We send a nonce of zero because we don't use it anywhere - // right now. + // + // Message: ping + // + bool pingSend = false; + if (pto->fPingQueued) { + // RPC ping request by user + pingSend = true; + } if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSendMsg.empty()) { + // Ping automatically sent as a keepalive + pingSend = true; + } + if (pingSend) { uint64 nonce = 0; - if (pto->nVersion > BIP0031_VERSION) + while (nonce == 0) { + RAND_bytes((unsigned char*)&nonce, sizeof(nonce)); + } + pto->nPingNonceSent = nonce; + pto->fPingQueued = false; + if (pto->nVersion > BIP0031_VERSION) { + // Take timestamp as close as possible before transmitting ping + pto->nPingUsecStart = GetTimeMicros(); pto->PushMessage("ping", nonce); - else + } else { + // Peer is too old to support ping command with nonce, pong will never arrive, disable timing + pto->nPingUsecStart = 0; pto->PushMessage("ping"); + } } // Start block sync if (pto->fStartSync && !fImporting && !fReindex) { pto->fStartSync = false; - PushGetBlocks(pto, pindexBest, uint256(0)); + PushGetBlocks(pto, chainActive.Tip(), uint256(0)); } // Resend wallet transactions that haven't gotten in a block yet diff --git a/src/main.h b/src/main.h index 83b0d07f63..fc60ccc0b5 100644 --- a/src/main.h +++ b/src/main.h @@ -74,14 +74,8 @@ extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern std::map<uint256, CBlockIndex*> mapBlockIndex; -extern std::vector<CBlockIndex*> vBlockIndexByHeight; extern std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; -extern CBlockIndex* pindexGenesisBlock; -extern int nBestHeight; -extern uint256 nBestChainWork; extern uint256 nBestInvalidWork; -extern uint256 hashBestChain; -extern CBlockIndex* pindexBest; extern unsigned int nTransactionsUpdated; extern uint64 nLastBlockTx; extern uint64 nLastBlockSize; @@ -153,8 +147,6 @@ void UnloadBlockIndex(); bool VerifyDB(int nCheckLevel, int nCheckDepth); /** Print the loaded block tree */ void PrintBlockTree(); -/** Find a block by height in the currently-connected chain */ -CBlockIndex* FindBlockByHeight(int nHeight); /** Process protocol messages received from a given node */ bool ProcessMessages(CNode* pfrom); /** Send queued protocol messages to be sent to a give node */ @@ -819,15 +811,6 @@ public: return (CBigNum(1)<<256) / (bnTarget+1); } - bool IsInMainChain() const - { - return nHeight < (int)vBlockIndexByHeight.size() && vBlockIndexByHeight[nHeight] == this; - } - - CBlockIndex *GetNextInMainChain() const { - return nHeight+1 >= (int)vBlockIndexByHeight.size() ? NULL : vBlockIndexByHeight[nHeight+1]; - } - bool CheckIndex() const { return CheckProofOfWork(GetBlockHash(), nBits); @@ -849,17 +832,7 @@ public: return pbegin[(pend - pbegin)/2]; } - int64 GetMedianTime() const - { - const CBlockIndex* pindex = this; - for (int i = 0; i < nMedianTimeSpan/2; i++) - { - if (!pindex->GetNextInMainChain()) - return GetBlockTime(); - pindex = pindex->GetNextInMainChain(); - } - return pindex->GetMedianTimePast(); - } + int64 GetMedianTime() const; /** * Returns true if there are nRequired or more blocks of minVersion or above @@ -870,8 +843,8 @@ public: std::string ToString() const { - return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", - pprev, GetNextInMainChain(), nHeight, + return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, nHeight, hashMerkleRoot.ToString().c_str(), GetBlockHash().ToString().c_str()); } @@ -1011,64 +984,67 @@ public: } }; +/** An in-memory indexed chain of blocks. */ +class CChain { +private: + std::vector<CBlockIndex*> vChain; - - - - - -/** Describes a place in the block chain to another node such that if the - * other node doesn't have the same branch, it can find a recent common trunk. - * The further back it is, the further before the fork it may be. - */ -class CBlockLocator -{ -protected: - std::vector<uint256> vHave; public: - CBlockLocator() {} + /** Returns the index entry for the genesis block of this chain, or NULL if none. */ + CBlockIndex *Genesis() const { + return vChain.size() > 0 ? vChain[0] : NULL; + } - explicit CBlockLocator(const CBlockIndex* pindex) - { - Set(pindex); + /** Returns the index entry for the tip of this chain, or NULL if none. */ + CBlockIndex *Tip() const { + return vChain.size() > 0 ? vChain[vChain.size() - 1] : NULL; } - explicit CBlockLocator(uint256 hashBlock); + /** Returns the index entry at a particular height in this chain, or NULL if no such height exists. */ + CBlockIndex *operator[](int nHeight) const { + if (nHeight < 0 || nHeight >= (int)vChain.size()) + return NULL; + return vChain[nHeight]; + } - CBlockLocator(const std::vector<uint256>& vHaveIn) - { - vHave = vHaveIn; + /** Compare two chains efficiently. */ + friend bool operator==(const CChain &a, const CChain &b) { + return a.vChain.size() == b.vChain.size() && + a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1]; } - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vHave); - ) + /** Efficiently check whether a block is present in this chain. */ + bool Contains(const CBlockIndex *pindex) const { + return (*this)[pindex->nHeight] == pindex; + } - void SetNull() - { - vHave.clear(); + /** Find the successor of a block in this chain, or NULL if the given index is not found or is the tip. */ + CBlockIndex *Next(const CBlockIndex *pindex) const { + if (Contains(pindex)) + return (*this)[pindex->nHeight + 1]; + else + return NULL; } - bool IsNull() - { - return vHave.empty(); + /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */ + int Height() const { + return vChain.size() - 1; } - /** Given a block initialises the locator to that point in the chain. */ - void Set(const CBlockIndex* pindex); - /** Returns the distance in blocks this locator is from our chain head. */ - int GetDistanceBack(); - /** Returns the first best-chain block the locator contains. */ - CBlockIndex* GetBlockIndex(); - /** Returns the hash of the first best chain block the locator contains. */ - uint256 GetBlockHash(); - /** Returns the height of the first best chain block the locator has. */ - int GetHeight(); + /** Set/initialize a chain with a given tip. Returns the forking point. */ + CBlockIndex *SetTip(CBlockIndex *pindex); + + /** Return a CBlockLocator that refers to a block in this chain (by default the tip). */ + CBlockLocator GetLocator(const CBlockIndex *pindex = NULL) const; + + /** Find the last common block between this chain and a locator. */ + CBlockIndex *FindFork(const CBlockLocator &locator) const; }; +/** The currently-connected chain of blocks. */ +extern CChain chainActive; + + diff --git a/src/miner.cpp b/src/miner.cpp index 30c600071f..e9c1d9aff9 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -176,7 +176,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) int64 nFees = 0; { LOCK2(cs_main, mempool.cs); - CBlockIndex* pindexPrev = pindexBest; + CBlockIndex* pindexPrev = chainActive.Tip(); CCoinsViewCache view(*pcoinsTip, true); // Priority order to process transactions @@ -467,7 +467,7 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) // Found a solution { LOCK(cs_main); - if (pblock->hashPrevBlock != hashBestChain) + if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash()) return error("BitcoinMiner : generated block is stale"); // Remove key from key pool @@ -510,7 +510,7 @@ void static BitcoinMiner(CWallet *pwallet) // Create new block // unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrev = pindexBest; + CBlockIndex* pindexPrev = chainActive.Tip(); auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); if (!pblocktemplate.get()) @@ -613,7 +613,7 @@ void static BitcoinMiner(CWallet *pwallet) break; if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) break; - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) break; // Update nTime every few seconds diff --git a/src/net.cpp b/src/net.cpp index 781dbfadca..99457be0f5 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -426,8 +426,10 @@ void AddressCurrentlyConnected(const CService& addr) - - +uint64 CNode::nTotalBytesRecv = 0; +uint64 CNode::nTotalBytesSent = 0; +CCriticalSection CNode::cs_totalBytesRecv; +CCriticalSection CNode::cs_totalBytesSent; CNode* FindNode(const CNetAddr& ip) { @@ -540,6 +542,8 @@ void CNode::Cleanup() void CNode::PushVersion() { + int nBestHeight = g_signals.GetHeight().get_value_or(0); + /// when NTP implemented, change to just nTime = GetAdjustedTime() int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); @@ -620,6 +624,21 @@ void CNode::copyStats(CNodeStats &stats) X(nSendBytes); X(nRecvBytes); stats.fSyncNode = (this == pnodeSync); + + // It is common for nodes with good ping times to suddenly become lagged, + // due to a new block arriving or other large transfer. + // Merely reporting pingtime might fool the caller into thinking the node was still responsive, + // since pingtime does not update until the ping is complete, which might take a while. + // So, if a ping is taking an unusually long time in flight, + // the caller can immediately detect that this is happening. + int64 nPingUsecWait = 0; + if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) { + nPingUsecWait = GetTimeMicros() - nPingUsecStart; + } + + // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :) + stats.dPingTime = (((double)nPingUsecTime) / 1e6); + stats.dPingWait = (((double)nPingUsecWait) / 1e6); } #undef X @@ -716,6 +735,7 @@ void SocketSendData(CNode *pnode) pnode->nLastSend = GetTime(); pnode->nSendBytes += nBytes; pnode->nSendOffset += nBytes; + pnode->RecordBytesSent(nBytes); if (pnode->nSendOffset == data.size()) { pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); @@ -811,10 +831,9 @@ void ThreadSocketHandler() } } } - if (vNodes.size() != nPrevNodeCount) - { + if(vNodes.size() != nPrevNodeCount) { nPrevNodeCount = vNodes.size(); - uiInterface.NotifyNumConnectionsChanged(vNodes.size()); + uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); } @@ -993,6 +1012,7 @@ void ThreadSocketHandler() pnode->CloseSocketDisconnect(); pnode->nLastRecv = GetTime(); pnode->nRecvBytes += nBytes; + pnode->RecordBytesRecv(nBytes); } else if (nBytes == 0) { @@ -1467,6 +1487,8 @@ void static StartSync(const vector<CNode*> &vNodes) { CNode *pnodeNewSync = NULL; double dBestScore = 0; + int nBestHeight = g_signals.GetHeight().get_value_or(0); + // Iterate over all nodes BOOST_FOREACH(CNode* pnode, vNodes) { // check preconditions for allowing a sync @@ -1844,3 +1866,27 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt pnode->PushInventory(inv); } } + +void CNode::RecordBytesRecv(uint64 bytes) +{ + LOCK(cs_totalBytesRecv); + nTotalBytesRecv += bytes; +} + +void CNode::RecordBytesSent(uint64 bytes) +{ + LOCK(cs_totalBytesSent); + nTotalBytesSent += bytes; +} + +uint64 CNode::GetTotalBytesRecv() +{ + LOCK(cs_totalBytesRecv); + return nTotalBytesRecv; +} + +uint64 CNode::GetTotalBytesSent() +{ + LOCK(cs_totalBytesSent); + return nTotalBytesSent; +} @@ -28,7 +28,6 @@ static const unsigned int MAX_INV_SZ = 50000; class CNode; class CBlockIndex; -extern int nBestHeight; @@ -52,6 +51,7 @@ void SocketSendData(CNode *pnode); // Signals for message handling struct CNodeSignals { + boost::signals2::signal<int ()> GetHeight; boost::signals2::signal<bool (CNode*)> ProcessMessages; boost::signals2::signal<bool (CNode*, bool)> SendMessages; }; @@ -119,6 +119,8 @@ public: uint64 nSendBytes; uint64 nRecvBytes; bool fSyncNode; + double dPingTime; + double dPingWait; }; @@ -234,6 +236,12 @@ public: CCriticalSection cs_inventory; std::multimap<int64, CInv> mapAskFor; + // Ping time measurement + uint64 nPingNonceSent; + int64 nPingUsecStart; + int64 nPingUsecTime; + bool fPingQueued; + CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, MIN_PROTO_VERSION) { nServices = 0; @@ -268,6 +276,10 @@ public: fRelayTxes = false; setInventoryKnown.max_size(SendBufferSize() / 1000); pfilter = new CBloomFilter(); + nPingNonceSent = 0; + nPingUsecStart = 0; + nPingUsecTime = 0; + fPingQueued = false; // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) @@ -286,8 +298,15 @@ public: } private: + // Network usage totals + static CCriticalSection cs_totalBytesRecv; + static CCriticalSection cs_totalBytesSent; + static uint64 nTotalBytesRecv; + static uint64 nTotalBytesSent; + CNode(const CNode&); void operator=(const CNode&); + public: @@ -301,7 +320,7 @@ public: unsigned int GetTotalRecvSize() { unsigned int total = 0; - BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) + BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) total += msg.vRecv.size() + 24; return total; } @@ -634,6 +653,13 @@ public: static bool IsBanned(CNetAddr ip); bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot void copyStats(CNodeStats &stats); + + // Network stats + static void RecordBytesRecv(uint64 bytes); + static void RecordBytesSent(uint64 bytes); + + static uint64 GetTotalBytesRecv(); + static uint64 GetTotalBytesSent(); }; diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index 248dcba0ec..e7ae0a905f 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -48,7 +48,7 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \ moc_optionsmodel.cpp moc_overviewpage.cpp moc_paymentserver.cpp \ moc_qrcodedialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ moc_rpcconsole.cpp moc_sendcoinsdialog.cpp moc_sendcoinsentry.cpp \ - moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_transactiondesc.cpp \ + moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_trafficgraphwidget.cpp moc_transactiondesc.cpp \ moc_transactiondescdialog.cpp moc_transactionfilterproxy.cpp \ moc_transactiontablemodel.cpp moc_transactionview.cpp moc_walletframe.cpp \ moc_walletmodel.cpp moc_walletstack.cpp moc_walletview.cpp @@ -73,7 +73,7 @@ BITCOIN_QT_H = aboutdialog.h addressbookpage.h addresstablemodel.h \ optionsmodel.h overviewpage.h paymentrequestplus.h paymentserver.h \ qrcodedialog.h qvalidatedlineedit.h qvaluecombobox.h rpcconsole.h \ sendcoinsdialog.h sendcoinsentry.h signverifymessagedialog.h splashscreen.h \ - transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \ + trafficgraphwidget.h transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \ transactionrecord.h transactiontablemodel.h transactionview.h walletframe.h \ walletmodel.h walletmodeltransaction.h walletstack.h walletview.h @@ -102,7 +102,7 @@ BITCOIN_QT_CPP = aboutdialog.cpp addressbookpage.cpp \ optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \ paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \ rpcconsole.cpp sendcoinsdialog.cpp sendcoinsentry.cpp \ - signverifymessagedialog.cpp splashscreen.cpp transactiondesc.cpp \ + signverifymessagedialog.cpp splashscreen.cpp trafficgraphwidget.cpp transactiondesc.cpp \ transactiondescdialog.cpp transactionfilterproxy.cpp transactionrecord.cpp \ transactiontablemodel.cpp transactionview.cpp walletframe.cpp \ walletmodel.cpp walletmodeltransaction.cpp walletstack.cpp walletview.cpp diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index b33f534f7c..2bab488135 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -42,7 +42,7 @@ int ClientModel::getNumConnections() const int ClientModel::getNumBlocks() const { - return nBestHeight; + return chainActive.Height(); } int ClientModel::getNumBlocksAtStartup() @@ -51,10 +51,20 @@ int ClientModel::getNumBlocksAtStartup() return numBlocksAtStartup; } +quint64 ClientModel::getTotalBytesRecv() const +{ + return CNode::GetTotalBytesRecv(); +} + +quint64 ClientModel::getTotalBytesSent() const +{ + return CNode::GetTotalBytesSent(); +} + QDateTime ClientModel::getLastBlockDate() const { - if (pindexBest) - return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + if (chainActive.Tip()) + return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime()); else if(!isTestNet()) return QDateTime::fromTime_t(1231006505); // Genesis block's time else @@ -63,7 +73,7 @@ QDateTime ClientModel::getLastBlockDate() const double ClientModel::getVerificationProgress() const { - return Checkpoints::GuessVerificationProgress(pindexBest); + return Checkpoints::GuessVerificationProgress(chainActive.Tip()); } void ClientModel::updateTimer() @@ -85,6 +95,8 @@ void ClientModel::updateTimer() // ensure we return the maximum of newNumBlocksOfPeers and newNumBlocks to not create weird displays in the GUI emit numBlocksChanged(newNumBlocks, std::max(newNumBlocksOfPeers, newNumBlocks)); } + + emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent()); } void ClientModel::updateNumConnections(int numConnections) diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 15074300b4..925f20acd9 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -35,6 +35,9 @@ public: int getNumBlocks() const; int getNumBlocksAtStartup(); + quint64 getTotalBytesRecv() const; + quint64 getTotalBytesSent() const; + double getVerificationProgress() const; QDateTime getLastBlockDate() const; @@ -74,6 +77,7 @@ signals: void numConnectionsChanged(int count); void numBlocksChanged(int count, int countOfPeers); void alertsChanged(const QString &warnings); + void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); //! Asynchronous message notification void message(const QString &title, const QString &message, unsigned int style); diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index d1d8ab42a0..54c41ffb67 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -445,10 +445,271 @@ </item> </layout> </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>&Network Traffic</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="TrafficGraphWidget" name="trafficGraph" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QSlider" name="sldGraphRange"> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>288</number> + </property> + <property name="pageStep"> + <number>12</number> + </property> + <property name="value"> + <number>6</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblGraphRange"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnClearTrafficGraph"> + <property name="text"> + <string>&Clear</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Totals</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="Line" name="line"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>10</width> + <height>0</height> + </size> + </property> + <property name="palette"> + <palette> + <active> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>255</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>255</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>255</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_16"> + <property name="text"> + <string>In:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblBytesIn"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="Line" name="line_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>10</width> + <height>0</height> + </size> + </property> + <property name="palette"> + <palette> + <active> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_17"> + <property name="text"> + <string>Out:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblBytesOut"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>407</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> </widget> </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>TrafficGraphWidget</class> + <extends>QWidget</extends> + <header>trafficgraphwidget.h</header> + <container>1</container> + <slots> + <slot>clear()</slot> + </slots> + </customwidget> + </customwidgets> <resources> <include location="../bitcoin.qrc"/> </resources> diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 72f75b42e6..96ba997943 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -48,6 +48,7 @@ const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds const QString BITCOIN_IPC_PREFIX("bitcoin:"); const char* BITCOIN_REQUEST_MIMETYPE = "application/bitcoin-paymentrequest"; const char* BITCOIN_PAYMENTACK_MIMETYPE = "application/bitcoin-paymentack"; +const char* BITCOIN_PAYMENTACK_CONTENTTYPE = "application/bitcoin-payment"; X509_STORE* PaymentServer::certStore = NULL; void PaymentServer::freeCertStore() @@ -471,11 +472,7 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, QList<Sen recipients.append(SendCoinsRecipient()); recipients[i].amount = sendingTo.second; QString memo = QString::fromStdString(request.getDetails().memo()); -#if QT_VERSION < 0x050000 - recipients[i].label = Qt::escape(memo); -#else - recipients[i].label = memo.toHtmlEscaped(); -#endif + recipients[i].label = GUIUtil::HtmlEscape(memo); CTxDestination dest; if (ExtractDestination(sendingTo.first, dest)) { if (i == 0) // Tie request to first pay-to, we don't want multiple ACKs @@ -518,7 +515,7 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien QNetworkRequest netRequest; netRequest.setAttribute(QNetworkRequest::User, "PaymentACK"); netRequest.setUrl(QString::fromStdString(details.payment_url())); - netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/bitcoin-payment"); + netRequest.setHeader(QNetworkRequest::ContentTypeHeader, BITCOIN_PAYMENTACK_CONTENTTYPE); netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); netRequest.setRawHeader("Accept", BITCOIN_PAYMENTACK_MIMETYPE); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 8953c36579..e7dcdf62a1 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -22,6 +22,8 @@ const int CONSOLE_HISTORY = 50; const QSize ICON_SIZE(24, 24); +const int INITIAL_TRAFFIC_GRAPH_MINS = 30; + const struct { const char *url; const char *source; @@ -204,6 +206,7 @@ RPCConsole::RPCConsole(QWidget *parent) : ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION)); startExecutor(); + setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); clear(); } @@ -253,7 +256,8 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event) void RPCConsole::setClientModel(ClientModel *model) { - this->clientModel = model; + clientModel = model; + ui->trafficGraph->setClientModel(model); if(model) { // Keep up to date with client @@ -263,6 +267,9 @@ void RPCConsole::setClientModel(ClientModel *model) setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers()); connect(model, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int))); + updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent()); + connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64))); + // Provide initial values ui->clientVersion->setText(model->formatFullVersion()); ui->clientName->setText(model->clientName()); @@ -431,3 +438,49 @@ void RPCConsole::on_showCLOptionsButton_clicked() GUIUtil::HelpMessageBox help; help.exec(); } + +void RPCConsole::on_sldGraphRange_valueChanged(int value) +{ + const int multiplier = 5; // each position on the slider represents 5 min + int mins = value * multiplier; + setTrafficGraphRange(mins); +} + +QString RPCConsole::FormatBytes(quint64 bytes) +{ + if(bytes < 1024) + return QString(tr("%1 B")).arg(bytes); + if(bytes < 1024 * 1024) + return QString(tr("%1 KB")).arg(bytes / 1024); + if(bytes < 1024 * 1024 * 1024) + return QString(tr("%1 MB")).arg(bytes / 1024 / 1024); + + return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); +} + +void RPCConsole::setTrafficGraphRange(int mins) +{ + ui->trafficGraph->setGraphRangeMins(mins); + if(mins < 60) { + ui->lblGraphRange->setText(QString(tr("%1 m")).arg(mins)); + } else { + int hours = mins / 60; + int minsLeft = mins % 60; + if(minsLeft == 0) { + ui->lblGraphRange->setText(QString(tr("%1 h")).arg(hours)); + } else { + ui->lblGraphRange->setText(QString(tr("%1 h %2 m")).arg(hours).arg(minsLeft)); + } + } +} + +void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) +{ + ui->lblBytesIn->setText(FormatBytes(totalBytesIn)); + ui->lblBytesOut->setText(FormatBytes(totalBytesOut)); +} + +void RPCConsole::on_btnClearTrafficGraph_clicked() +{ + ui->trafficGraph->clear(); +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 3c38b4b8de..af92b55770 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -37,6 +37,12 @@ private slots: void on_openDebugLogfileButton_clicked(); /** display messagebox with program parameters (same as bitcoin-qt --help) */ void on_showCLOptionsButton_clicked(); + /** change the time range of the network traffic graph */ + void on_sldGraphRange_valueChanged(int value); + /** update traffic statistics */ + void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut); + /** clear traffic graph */ + void on_btnClearTrafficGraph_clicked(); public slots: void clear(); @@ -55,6 +61,9 @@ signals: void cmdRequest(const QString &command); private: + static QString FormatBytes(quint64 bytes); + void setTrafficGraphRange(int mins); + Ui::RPCConsole *ui; ClientModel *clientModel; QStringList history; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 00cea463ef..3fd4a26e76 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -365,9 +365,8 @@ bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) else { CBitcoinAddress address(rv.address.toStdString()); if (!address.IsValid()) { - QString strAddress(address.ToString().c_str()); QMessageBox::warning(this, strSendCoins, - tr("Invalid payment address %1").arg(strAddress)); + tr("Invalid payment address %1").arg(rv.address)); return false; } } diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index ee84f7bc11..59724c94f4 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -85,10 +85,17 @@ void SendCoinsEntry::setRemoveEnabled(bool enabled) void SendCoinsEntry::clear() { + // clear UI elements for insecure payments ui->payTo->clear(); ui->addAsLabel->clear(); ui->payAmount->clear(); + // and the ones for secure payments just to be sure + ui->payTo_s->clear(); + ui->memoTextLabel_s->clear(); + ui->payAmount_s->clear(); + ui->payTo->setFocus(); + // update the display unit, to not use the default ("BTC") updateDisplayUnit(); } @@ -154,17 +161,19 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value) { recipient = value; - ui->payTo->setText(value.address); - ui->addAsLabel->setText(value.label); - ui->payAmount->setValue(value.amount); - - if (!recipient.authenticatedMerchant.isEmpty()) + if (recipient.authenticatedMerchant.isEmpty()) + { + ui->payTo->setText(recipient.address); + ui->addAsLabel->setText(recipient.label); + ui->payAmount->setValue(recipient.amount); + } + else { - const payments::PaymentDetails& details = value.paymentRequest.getDetails(); + const payments::PaymentDetails& details = recipient.paymentRequest.getDetails(); - ui->payTo_s->setText(value.authenticatedMerchant); + ui->payTo_s->setText(recipient.authenticatedMerchant); ui->memoTextLabel_s->setText(QString::fromStdString(details.memo())); - ui->payAmount_s->setValue(value.amount); + ui->payAmount_s->setValue(recipient.amount); setCurrentWidget(ui->SendCoinsSecure); } } diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp new file mode 100644 index 0000000000..d49bc31f3e --- /dev/null +++ b/src/qt/trafficgraphwidget.cpp @@ -0,0 +1,169 @@ +#include "trafficgraphwidget.h" +#include "clientmodel.h" + +#include <QPainter> +#include <QColor> +#include <QTimer> + +#include <cmath> + +#define DESIRED_SAMPLES 800 + +#define XMARGIN 10 +#define YMARGIN 10 + +TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) : + QWidget(parent), + timer(0), + fMax(0.0f), + nMins(0), + vSamplesIn(), + vSamplesOut(), + nLastBytesIn(0), + nLastBytesOut(0), + clientModel(0) +{ + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(updateRates())); +} + +void TrafficGraphWidget::setClientModel(ClientModel *model) +{ + clientModel = model; + if(model) { + nLastBytesIn = model->getTotalBytesRecv(); + nLastBytesOut = model->getTotalBytesSent(); + } +} + +int TrafficGraphWidget::getGraphRangeMins() const +{ + return nMins; +} + +void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue<float> &samples) +{ + int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2; + int sampleCount = samples.size(), x = XMARGIN + w, y; + if(sampleCount > 0) { + path.moveTo(x, YMARGIN + h); + for(int i = 0; i < sampleCount; ++i) { + x = XMARGIN + w - w * i / DESIRED_SAMPLES; + y = YMARGIN + h - (int)(h * samples.at(i) / fMax); + path.lineTo(x, y); + } + path.lineTo(x, YMARGIN + h); + } +} + +void TrafficGraphWidget::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.fillRect(rect(), Qt::black); + + if(fMax <= 0.0f) return; + + QColor axisCol(Qt::gray); + int h = height() - YMARGIN * 2; + painter.setPen(axisCol); + painter.drawLine(XMARGIN, YMARGIN + h, width() - XMARGIN, YMARGIN + h); + + // decide what order of magnitude we are + int base = floor(log10(fMax)); + float val = pow(10.0f, base); + + const QString units = tr("KB/s"); + // draw lines + painter.setPen(axisCol); + painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units)); + for(float y = val; y < fMax; y += val) { + int yy = YMARGIN + h - h * y / fMax; + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); + } + // if we drew 3 or fewer lines, break them up at the next lower order of magnitude + if(fMax / val <= 3.0f) { + axisCol = axisCol.darker(); + val = pow(10.0f, base - 1); + painter.setPen(axisCol); + painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units)); + int count = 1; + for(float y = val; y < fMax; y += val, count++) { + // don't overwrite lines drawn above + if(count % 10 == 0) + continue; + int yy = YMARGIN + h - h * y / fMax; + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); + } + } + + if(!vSamplesIn.empty()) { + QPainterPath p; + paintPath(p, vSamplesIn); + painter.fillPath(p, QColor(0, 255, 0, 128)); + painter.setPen(Qt::green); + painter.drawPath(p); + } + if(!vSamplesOut.empty()) { + QPainterPath p; + paintPath(p, vSamplesOut); + painter.fillPath(p, QColor(255, 0, 0, 128)); + painter.setPen(Qt::red); + painter.drawPath(p); + } +} + +void TrafficGraphWidget::updateRates() +{ + if(!clientModel) return; + + quint64 bytesIn = clientModel->getTotalBytesRecv(), + bytesOut = clientModel->getTotalBytesSent(); + float inRate = (bytesIn - nLastBytesIn) / 1024.0f * 1000 / timer->interval(); + float outRate = (bytesOut - nLastBytesOut) / 1024.0f * 1000 / timer->interval(); + vSamplesIn.push_front(inRate); + vSamplesOut.push_front(outRate); + nLastBytesIn = bytesIn; + nLastBytesOut = bytesOut; + + while(vSamplesIn.size() > DESIRED_SAMPLES) { + vSamplesIn.pop_back(); + } + while(vSamplesOut.size() > DESIRED_SAMPLES) { + vSamplesOut.pop_back(); + } + + float tmax = 0.0f; + foreach(float f, vSamplesIn) { + if(f > tmax) tmax = f; + } + foreach(float f, vSamplesOut) { + if(f > tmax) tmax = f; + } + fMax = tmax; + update(); +} + +void TrafficGraphWidget::setGraphRangeMins(int mins) +{ + nMins = mins; + int msecsPerSample = nMins * 60 * 1000 / DESIRED_SAMPLES; + timer->stop(); + timer->setInterval(msecsPerSample); + + clear(); +} + +void TrafficGraphWidget::clear() +{ + timer->stop(); + + vSamplesOut.clear(); + vSamplesIn.clear(); + fMax = 0.0f; + + if(clientModel) { + nLastBytesIn = clientModel->getTotalBytesRecv(); + nLastBytesOut = clientModel->getTotalBytesSent(); + } + timer->start(); +} diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h new file mode 100644 index 0000000000..b31d1d5b0a --- /dev/null +++ b/src/qt/trafficgraphwidget.h @@ -0,0 +1,44 @@ +#ifndef TRAFFICGRAPHWIDGET_H +#define TRAFFICGRAPHWIDGET_H + +#include <QWidget> +#include <QQueue> + +class ClientModel; + +QT_BEGIN_NAMESPACE +class QPaintEvent; +class QTimer; +QT_END_NAMESPACE + +class TrafficGraphWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TrafficGraphWidget(QWidget *parent = 0); + void setClientModel(ClientModel *model); + int getGraphRangeMins() const; + +protected: + void paintEvent(QPaintEvent *); + +public slots: + void updateRates(); + void setGraphRangeMins(int mins); + void clear(); + +private: + void paintPath(QPainterPath &path, QQueue<float> &samples); + + QTimer *timer; + float fMax; + int nMins; + QQueue<float> vSamplesIn; + QQueue<float> vSamplesOut; + quint64 nLastBytesIn; + quint64 nLastBytesOut; + ClientModel *clientModel; +}; + +#endif // TRAFFICGRAPHWIDGET_H diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index e27aa93a4a..93fc8cab22 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -17,7 +17,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) - return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight + 1); + return tr("Open for %n more block(s)", "", wtx.nLockTime - chainActive.Height() + 1); else return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime)); } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index ea2c1f0a5c..162908a9a4 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -160,14 +160,14 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) idx); status.confirmed = wtx.IsConfirmed(); status.depth = wtx.GetDepthInMainChain(); - status.cur_num_blocks = nBestHeight; + status.cur_num_blocks = chainActive.Height(); if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) { status.status = TransactionStatus::OpenUntilBlock; - status.open_for = wtx.nLockTime - nBestHeight + 1; + status.open_for = wtx.nLockTime - chainActive.Height() + 1; } else { @@ -221,7 +221,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) bool TransactionRecord::statusUpdateNeeded() { - return status.cur_num_blocks != nBestHeight; + return status.cur_num_blocks != chainActive.Height(); } QString TransactionRecord::getTxID() const diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 07f6a62150..6f7a5933ab 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -250,9 +250,9 @@ void TransactionTableModel::updateTransaction(const QString &hash, int status) void TransactionTableModel::updateConfirmations() { - if(nBestHeight != cachedNumBlocks) + if(chainActive.Height() != cachedNumBlocks) { - cachedNumBlocks = nBestHeight; + cachedNumBlocks = chainActive.Height(); // Blocks came in since last poll. // Invalidate status (number of confirmations) and (possibly) description // for all rows. Qt is smart enough to only actually request the data for the diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index bda39b675f..099fbe8dc3 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -73,10 +73,10 @@ void WalletModel::updateStatus() void WalletModel::pollBalanceChanged() { - if(nBestHeight != cachedNumBlocks) + if(chainActive.Height() != cachedNumBlocks) { // Balance and number of transactions might have changed - cachedNumBlocks = nBestHeight; + cachedNumBlocks = chainActive.Height(); checkBalanceChanged(); } } diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 398f33605a..957241d6a0 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -17,10 +17,10 @@ double GetDifficulty(const CBlockIndex* blockindex) // minimum difficulty = 1.0. if (blockindex == NULL) { - if (pindexBest == NULL) + if (chainActive.Tip() == NULL) return 1.0; else - blockindex = pindexBest; + blockindex = chainActive.Tip(); } int nShift = (blockindex->nBits >> 24) & 0xff; @@ -66,7 +66,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = blockindex->GetNextInMainChain(); + CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); return result; @@ -80,7 +80,7 @@ Value getblockcount(const Array& params, bool fHelp) "getblockcount\n" "Returns the number of blocks in the longest block chain."); - return nBestHeight; + return chainActive.Height(); } Value getbestblockhash(const Array& params, bool fHelp) @@ -90,7 +90,7 @@ Value getbestblockhash(const Array& params, bool fHelp) "getbestblockhash\n" "Returns the hash of the best (tip) block in the longest block chain."); - return hashBestChain.GetHex(); + return chainActive.Tip()->GetBlockHash().GetHex(); } Value getdifficulty(const Array& params, bool fHelp) @@ -145,11 +145,11 @@ Value getblockhash(const Array& params, bool fHelp) "Returns hash of block in best-block-chain at <index>."); int nHeight = params[0].get_int(); - if (nHeight < 0 || nHeight > nBestHeight) + if (nHeight < 0 || nHeight > chainActive.Height()) throw runtime_error("Block number out of range."); - CBlockIndex* pblockindex = FindBlockByHeight(nHeight); - return pblockindex->phashBlock->GetHex(); + CBlockIndex* pblockindex = chainActive[nHeight]; + return pblockindex->GetBlockHash().GetHex(); } Value getblock(const Array& params, bool fHelp) diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 842910c7e0..61cd07d507 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -102,7 +102,7 @@ Value importprivkey(const Array& params, bool fHelp) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); if (fRescan) { - pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); pwalletMain->ReacceptWalletTransactions(); } } @@ -124,7 +124,7 @@ Value importwallet(const Array& params, bool fHelp) if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); - int64 nTimeBegin = pindexBest->nTime; + int64 nTimeBegin = chainActive.Tip()->nTime; bool fGood = true; @@ -175,11 +175,11 @@ Value importwallet(const Array& params, bool fHelp) } file.close(); - CBlockIndex *pindex = pindexBest; + CBlockIndex *pindex = chainActive.Tip(); while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200) pindex = pindex->pprev; - LogPrintf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1); + LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); pwalletMain->ScanForWalletTransactions(pindex); pwalletMain->ReacceptWalletTransactions(); pwalletMain->MarkDirty(); @@ -243,8 +243,8 @@ Value dumpwallet(const Array& params, bool fHelp) // produce output file << strprintf("# Wallet dump created by Bitcoin %s (%s)\n", CLIENT_BUILD.c_str(), CLIENT_DATE.c_str()); file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()).c_str()); - file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString().c_str()); - file << strprintf("# mined on %s\n", EncodeDumpTime(pindexBest->nTime).c_str()); + file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString().c_str()); + file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->nTime).c_str()); file << "\n"; for (std::vector<std::pair<int64, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index b013b4b200..f123c3a9e0 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -37,10 +37,7 @@ void ShutdownRPCMining() // or from the last difficulty change if 'lookup' is nonpositive. // If 'height' is nonnegative, compute the estimate at the time when a given block was found. Value GetNetworkHashPS(int lookup, int height) { - CBlockIndex *pb = pindexBest; - - if (height >= 0 && height < nBestHeight) - pb = FindBlockByHeight(height); + CBlockIndex *pb = chainActive[height]; if (pb == NULL || !pb->nHeight) return 0; @@ -148,7 +145,7 @@ Value getmininginfo(const Array& params, bool fHelp) "Returns an object containing mining-related information."); Object obj; - obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); @@ -192,10 +189,10 @@ Value getwork(const Array& params, bool fHelp) static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || + if (pindexPrev != chainActive.Tip() || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); @@ -209,7 +206,7 @@ Value getwork(const Array& params, bool fHelp) // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block @@ -328,7 +325,7 @@ Value getblocktemplate(const Array& params, bool fHelp) static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || + if (pindexPrev != chainActive.Tip() || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on @@ -336,7 +333,7 @@ Value getblocktemplate(const Array& params, bool fHelp) // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index bd7bc0ba10..f78f034e32 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -19,6 +19,24 @@ Value getconnectioncount(const Array& params, bool fHelp) return (int)vNodes.size(); } +Value ping(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "ping\n" + "Requests that a ping be sent to all other nodes, to measure ping time.\n" + "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" + "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping."); + + // Request that each node send a ping during next message processing pass + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pNode, vNodes) { + pNode->fPingQueued = true; + } + + return Value::null; +} + static void CopyNodeStats(std::vector<CNodeStats>& vstats) { vstats.clear(); @@ -54,6 +72,9 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("bytessent", (boost::int64_t)stats.nSendBytes)); obj.push_back(Pair("bytesrecv", (boost::int64_t)stats.nRecvBytes)); obj.push_back(Pair("conntime", (boost::int64_t)stats.nTimeConnected)); + obj.push_back(Pair("pingtime", stats.dPingTime)); + if (stats.dPingWait > 0.0) + obj.push_back(Pair("pingwait", stats.dPingWait)); obj.push_back(Pair("version", stats.nVersion)); obj.push_back(Pair("subver", stats.strSubVer)); obj.push_back(Pair("inbound", stats.fInbound)); @@ -202,3 +223,17 @@ Value getaddednodeinfo(const Array& params, bool fHelp) return ret; } +Value getnettotals(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "getnettotals\n" + "Returns information about network traffic, including bytes in, bytes out,\n" + "and current time."); + + Object obj; + obj.push_back(Pair("totalbytesrecv", static_cast< boost::uint64_t>(CNode::GetTotalBytesRecv()))); + obj.push_back(Pair("totalbytessent", static_cast<boost::uint64_t>(CNode::GetTotalBytesSent()))); + obj.push_back(Pair("timemillis", static_cast<boost::int64_t>(GetTimeMillis()))); + return obj; +} diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index fcc5359dd6..5384b65906 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -87,9 +87,9 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) + if (chainActive.Contains(pindex)) { - entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); + entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); entry.push_back(Pair("time", (boost::int64_t)pindex->nTime)); entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime)); } diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index cafb6db9b1..f7341f7b69 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -76,7 +76,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); } - obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); @@ -1169,7 +1169,9 @@ Value listsinceblock(const Array& params, bool fHelp) uint256 blockId = 0; blockId.SetHex(params[0].get_str()); - pindex = CBlockLocator(blockId).GetBlockIndex(); + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(blockId); + if (it != mapBlockIndex.end()) + pindex = it->second; } if (params.size() > 1) @@ -1180,7 +1182,7 @@ Value listsinceblock(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); } - int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; + int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; Array transactions; @@ -1192,23 +1194,8 @@ Value listsinceblock(const Array& params, bool fHelp) ListTransactions(tx, "*", 0, true, transactions); } - uint256 lastblock; - - if (target_confirms == 1) - { - lastblock = hashBestChain; - } - else - { - int target_height = pindexBest->nHeight + 1 - target_confirms; - - CBlockIndex *block; - for (block = pindexBest; - block && block->nHeight > target_height; - block = block->pprev) { } - - lastblock = block ? block->GetBlockHash() : 0; - } + CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; + uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : 0; Object ret; ret.push_back(Pair("transactions", transactions)); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index eeeacb0ad4..67165760b2 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -65,10 +65,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 1; - pblock->nTime = pindexBest->GetMedianTimePast()+1; + pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1; pblock->vtx[0].vin[0].scriptSig = CScript(); pblock->vtx[0].vin[0].scriptSig.push_back(blockinfo[i].extranonce); - pblock->vtx[0].vin[0].scriptSig.push_back(pindexBest->nHeight); + pblock->vtx[0].vin[0].scriptSig.push_back(chainActive.Height()); pblock->vtx[0].vout[0].scriptPubKey = CScript(); if (txFirst.size() < 2) txFirst.push_back(new CTransaction(pblock->vtx[0])); @@ -193,14 +193,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.clear(); // subsidy changing - int nHeight = pindexBest->nHeight; - pindexBest->nHeight = 209999; + int nHeight = chainActive.Height(); + chainActive.Tip()->nHeight = 209999; BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; - pindexBest->nHeight = 210000; + chainActive.Tip()->nHeight = 210000; BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; - pindexBest->nHeight = nHeight; + chainActive.Tip()->nHeight = nHeight; BOOST_FOREACH(CTransaction *tx, txFirst) delete tx; diff --git a/src/txdb.cpp b/src/txdb.cpp index 0d2fdc2887..24ee8ec3e8 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -223,10 +223,6 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - // Watch for genesis block - if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == Params().HashGenesisBlock()) - pindexGenesisBlock = pindexNew; - if (!pindexNew->CheckIndex()) return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str()); diff --git a/src/wallet.cpp b/src/wallet.cpp index 26ffc71e19..0f0ce7e631 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -799,7 +799,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) // no need to read and scan block, if block was created before // our wallet birthday (as adjusted for block time variability) if (nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200))) { - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); continue; } @@ -810,7 +810,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) ret++; } - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); } } return ret; @@ -864,7 +864,7 @@ void CWallet::ReacceptWalletTransactions() if (fMissing) { // TODO: optimize this to scan just part of the block chain? - if (ScanForWalletTransactions(pindexGenesisBlock)) + if (ScanForWalletTransactions(chainActive.Genesis())) fRepeat = true; // Found missing transactions: re-do re-accept. } } @@ -1933,7 +1933,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const { mapKeyBirth[it->first] = it->second.nCreateTime; // map in which we'll infer heights of other keys - CBlockIndex *pindexMax = FindBlockByHeight(std::max(0, nBestHeight - 144)); // the tip can be reorganised; use a 144-block safety margin + CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganised; use a 144-block safety margin std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock; std::set<CKeyID> setKeys; GetKeys(setKeys); @@ -1953,7 +1953,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const { // iterate over all wallet transactions... const CWalletTx &wtx = (*it).second; std::map<uint256, CBlockIndex*>::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); - if (blit != mapBlockIndex.end() && blit->second->IsInMainChain()) { + if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { // ... which are already in a block int nHeight = blit->second->nHeight; BOOST_FOREACH(const CTxOut &txout, wtx.vout) { |