diff --git a/README.md b/README.md index d675d73adf4..5453f998dc2 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ out collectively by the network. Bitcoin is also the name of the open source software which enables the use of this currency. For more information, as well as an immediately useable, binary version of -the Bitcoin client sofware, see http://www.bitcoin.org. +the Bitcoin client software, see http://www.bitcoin.org. License ------- @@ -38,7 +38,7 @@ submitter will be asked to start a discussion (if they haven't already) on the The patch will be accepted if there is broad consensus that it is a good thing. Developers should expect to rework and resubmit patches if the code doesn't -match the project's coding conventions (see `doc/coding.txt`) or are +match the project's coding conventions (see `doc/coding.md`) or are controversial. The `master` branch is regularly built and tested, but is not guaranteed to be diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index bb27170cb63..69c2478184d 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -1,7 +1,7 @@ TEMPLATE = app TARGET = bitcoin-qt macx:TARGET = "Bitcoin-Qt" -VERSION = 0.8.2 +VERSION = 0.8.6 INCLUDEPATH += src src/json src/qt QT += network DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE diff --git a/contrib/debian/bitcoin-qt.desktop b/contrib/debian/bitcoin-qt.desktop index 5d6f781e4e5..b2a2cef622e 100644 --- a/contrib/debian/bitcoin-qt.desktop +++ b/contrib/debian/bitcoin-qt.desktop @@ -4,7 +4,7 @@ Name=Bitcoin Comment=Bitcoin P2P Cryptocurrency Comment[fr]=Bitcoin, monnaie virtuelle cryptographique pair à pair Comment[tr]=Bitcoin, eşten eşe kriptografik sanal para birimi -Exec=/usr/bin/bitcoin-qt +Exec=/usr/bin/bitcoin-qt %u Terminal=false Type=Application Icon=/usr/share/pixmaps/bitcoin128.png diff --git a/contrib/gitian-downloader/linux-download-config b/contrib/gitian-downloader/linux-download-config index aef614d0caa..8340a5dd247 100644 --- a/contrib/gitian-downloader/linux-download-config +++ b/contrib/gitian-downloader/linux-download-config @@ -35,4 +35,8 @@ signers: weight: 40 name: "Wladimir J. van der Laan" key: laanwj + AEC1884398647C47413C1C3FB1179EB7347DC10D: + weight: 40 + name: "Warren Togami" + key: wtogami minimum_weight: 120 diff --git a/contrib/gitian-downloader/win32-download-config b/contrib/gitian-downloader/win32-download-config index 0f7032e6438..49d52851b6b 100644 --- a/contrib/gitian-downloader/win32-download-config +++ b/contrib/gitian-downloader/win32-download-config @@ -35,4 +35,8 @@ signers: weight: 40 name: "Wladimir J. van der Laan" key: laanwj + AEC1884398647C47413C1C3FB1179EB7347DC10D: + weight: 40 + name: "Warren Togami" + key: wtogami minimum_weight: 120 diff --git a/contrib/gitian-downloader/wtogami-key.pgp b/contrib/gitian-downloader/wtogami-key.pgp new file mode 100644 index 00000000000..e0f6c4c5fdf --- /dev/null +++ b/contrib/gitian-downloader/wtogami-key.pgp @@ -0,0 +1,131 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.13 (GNU/Linux) + +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 +=icdI +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/homebrew/makefile.osx.patch b/contrib/homebrew/makefile.osx.patch index 340de0efdfe..287db2fdf22 100644 --- a/contrib/homebrew/makefile.osx.patch +++ b/contrib/homebrew/makefile.osx.patch @@ -1,5 +1,5 @@ diff --git a/src/makefile.osx b/src/makefile.osx -index 8b7c559..8a0560c 100644 +index bef0ef3..07ef8d3 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -7,17 +7,21 @@ @@ -28,7 +28,7 @@ index 8b7c559..8a0560c 100644 USE_UPNP:=1 USE_IPV6:=1 -@@ -31,13 +35,13 @@ ifdef STATIC +@@ -31,14 +35,14 @@ ifdef STATIC TESTLIBS += \ $(DEPSDIR)/lib/libboost_unit_test_framework-mt.a LIBS += \ @@ -38,6 +38,7 @@ index 8b7c559..8a0560c 100644 $(DEPSDIR)/lib/libboost_filesystem-mt.a \ $(DEPSDIR)/lib/libboost_program_options-mt.a \ $(DEPSDIR)/lib/libboost_thread-mt.a \ + $(DEPSDIR)/lib/libboost_chrono-mt.a \ - $(DEPSDIR)/lib/libssl.a \ - $(DEPSDIR)/lib/libcrypto.a \ + $(OPENSSLDIR)/lib/libssl.a \ diff --git a/contrib/verifysfbinaries/verify.sh b/contrib/verifysfbinaries/verify.sh index 768be86bdc5..cd9d2d0d43f 100755 --- a/contrib/verifysfbinaries/verify.sh +++ b/contrib/verifysfbinaries/verify.sh @@ -18,7 +18,7 @@ WORKINGDIR="/tmp/bitcoin" TMPFILE="hashes.tmp" #this URL is used if a version number is not specified as an argument to the script -SIGNATUREFILE="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/bitcoin-0.8.2/SHA256SUMS.asc" +SIGNATUREFILE="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/bitcoin-0.8.6/SHA256SUMS.asc" SIGNATUREFILENAME="SHA256SUMS.asc" RCSUBDIR="test/" diff --git a/db/autocompact_test.cc b/db/autocompact_test.cc new file mode 100644 index 00000000000..d20a2362c30 --- /dev/null +++ b/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(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/doc/Doxyfile b/doc/Doxyfile index c32d0f8959a..7040bc6c5d7 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -34,7 +34,7 @@ PROJECT_NAME = Bitcoin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.5.0 +PROJECT_NUMBER = 0.8.6 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer diff --git a/doc/README.md b/doc/README.md index cdd31057adc..ce094f15b11 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,4 +1,4 @@ -Bitcoin 0.8.2 BETA +Bitcoin 0.8.6 BETA ==================== Copyright (c) 2009-2013 Bitcoin Developers @@ -43,4 +43,4 @@ Other Pages - [Release Notes](release-notes.md) - [Multiwallet Qt Development](multiwallet-qt.md) - [Unit Tests](unit-tests.md) -- [Translation Process](translation_process.md) \ No newline at end of file +- [Translation Process](translation_process.md) diff --git a/doc/README_windows.txt b/doc/README_windows.txt index b26fab3be8c..f0eb2115043 100644 --- a/doc/README_windows.txt +++ b/doc/README_windows.txt @@ -1,4 +1,4 @@ -Bitcoin 0.8.2 BETA +Bitcoin 0.8.5 BETA Copyright (c) 2009-2013 Bitcoin Developers Distributed under the MIT/X11 software license, see the accompanying diff --git a/doc/release-notes.md b/doc/release-notes.md index 0e0b04bcaa2..2a4874b7b2a 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,80 +1,43 @@ (note: this is a temporary file, to be added-to by anybody, and deleted at release time) -Fee Policy changes ------------------- +0.8.6 changes +============= -The default fee for low-priority transactions is lowered from 0.0005 BTC -(for each 1,000 bytes in the transaction; an average transaction is -about 500 bytes) to 0.0001 BTC. +- Default block size increase for miners + (see https://gist.github.com/gavinandresen/7670433#086-accept-into-block) -Payments (transaction outputs) of 0.543 times the minimum relay fee -(0.00005430 BTC) are now considered 'non-standard', because storing them -costs the network more than they are worth and spending them will usually -cost their owner more in transaction fees than they are worth. +- Remove the all-outputs-must-be-greater-than-CENT-to-qualify-as-free rule for relaying + (see https://gist.github.com/gavinandresen/7670433#086-relaying) -Non-standard transactions are not relayed across the network, are not included -in blocks by most miners, and will not show up in your wallet until they are -included in a block. +- Lower maximum size for free transaction creation + (see https://gist.github.com/gavinandresen/7670433#086-wallet) -The default fee policy can be overridden using the -mintxfee and -minrelaytxfee -command-line options, but note that we intend to replace the hard-coded fees -with code that automatically calculates and suggests appropriate fees in the -0.9 release and note that if you set a fee policy significantly different from -the rest of the network your transactions may never confirm. +- OSX block chain database corruption fixes + - Update leveldb to 1.13 + - Use fcntl with `F_FULLSYNC` instead of fsync on OSX + - Use native Darwin memory barriers + - Replace use of mmap in leveldb for improved reliability (only on OSX) -Bitcoin-Qt changes ------------------- +- Fix nodes forwarding transactions with empty vins and getting banned -- New icon and splash screen -- Improve reporting of synchronization process -- Remove hardcoded fee recommendations -- Improve metadata of executable on MacOSX and Windows -- Move export button to individual tabs instead of toolbar -- Add "send coins" command to context menu in address book -- Add "copy txid" command to copy transaction IDs from transaction overview -- Save & restore window size and position when showing & hiding window -- New translations: Arabic (ar), Bosnian (bs), Catalan (ca), Welsh (cy), Esperanto (eo), Interlingua (la), Latvian (lv) and many improvements to current translations +- Network code performance and robustness improvements -MacOSX: +- Additional debug.log logging for diagnosis of network problems, log timestamps by default -- OSX support for click-to-pay (bitcoin:) links -- Fix GUI disappearing problem on MacOSX (issue #1522) +- Fix Bitcoin-Qt startup crash when clicking dock icon on OSX -Linux/Unix: +- Fix memory leaks in CKey::SetCompactSignature() and Key::SignCompact() -- Copy addresses to middle-mouse-button clipboard +- Fix rare GUI crash on send +- Various small GUI, documentation and build fixes -Command-line options --------------------- +Warning +-------- -* `-walletnotify` will call a command on receiving transactions that affect the wallet. -* `-alertnotify` will call a command on receiving an alert from the network. -* `-par` now takes a negative number, to leave a certain amount of cores free. - -JSON-RPC API changes --------------------- - -* `listunspent` now lists account and address infromation. -* `getinfo` now also returns the time adjustment estimated from your peers. -* `getpeerinfo` now returns bytessent, bytesrecv and syncnode. -* `gettxoutsetinfo` returns statistics about the unspent transaction output database. -* `gettxout` returns information about a specific unspent transaction output. - - -Networking changes ------------------- - -* Significant changes to the networking code, reducing latency and memory consumption. -* Avoid initial block download stalling. -* Remove IRC seeding support. -* Performance tweaks. -* Added testnet DNS seeds. - -Wallet compatibility/rescuing ------------------------------ - -* Cases where wallets cannot be opened in another version/installation should be reduced. -* `-salvagewallet` now works for encrypted wallets. +- There have been frequent reports of users running out of virtual memory on 32-bit systems + during the initial sync. + Hence it is recommended to use a 64-bit executable if possible. + A 64-bit executable for Windows is planned for 0.9. diff --git a/doc/release-process.md b/doc/release-process.md index 17d6a089c9f..b8aa7d9a915 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -31,7 +31,7 @@ Release Process export SIGNER=(your gitian key, ie bluematt, sipa, etc) export VERSION=0.8.0 - cd ./gitian-builder + pushd ./gitian-builder Fetch and build inputs: (first time, or when dependency versions change) @@ -58,13 +58,14 @@ Release Process ./bin/gsign --signer $SIGNER --release ${VERSION} --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian.yml pushd build/out zip -r bitcoin-${VERSION}-linux-gitian.zip * - mv bitcoin-${VERSION}-linux-gitian.zip ../../ + mv bitcoin-${VERSION}-linux-gitian.zip ../../../ popd ./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win32.yml ./bin/gsign --signer $SIGNER --release ${VERSION}-win32 --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win32.yml pushd build/out zip -r bitcoin-${VERSION}-win32-gitian.zip * - mv bitcoin-${VERSION}-win32-gitian.zip ../../ + mv bitcoin-${VERSION}-win32-gitian.zip ../../../ + popd popd Build output expected: @@ -161,4 +162,4 @@ From a directory containing bitcoin source, gitian.sigs and gitian zips popd - Upload gitian zips to SourceForge -- Celebrate \ No newline at end of file +- Celebrate diff --git a/share/setup.nsi b/share/setup.nsi index d5f2ffc3f8c..ee92feddcaa 100644 --- a/share/setup.nsi +++ b/share/setup.nsi @@ -5,7 +5,7 @@ SetCompressor /SOLID lzma # General Symbol Definitions !define REGKEY "SOFTWARE\$(^Name)" -!define VERSION 0.8.2 +!define VERSION 0.8.6 !define COMPANY "Bitcoin project" !define URL http://www.bitcoin.org/ @@ -45,13 +45,13 @@ Var StartMenuGroup !insertmacro MUI_LANGUAGE English # Installer attributes -OutFile bitcoin-0.8.2-win32-setup.exe +OutFile bitcoin-0.8.6-win32-setup.exe InstallDir $PROGRAMFILES\Bitcoin CRCCheck on XPStyle on BrandingText " " ShowInstDetails show -VIProductVersion 0.8.2.2 +VIProductVersion 0.8.6.0 VIAddVersionKey ProductName Bitcoin VIAddVersionKey ProductVersion "${VERSION}" VIAddVersionKey CompanyName "${COMPANY}" diff --git a/src/alert.cpp b/src/alert.cpp index 4b029840dd6..8eb9451a7d7 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -243,15 +243,7 @@ bool CAlert::ProcessAlert(bool fThread) // be safe we first strip anything not in safeChars, then add single quotes around // the whole string before passing it to the shell: std::string singleQuote("'"); - // safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything - // even possibly remotely dangerous like & or > - std::string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); - std::string safeStatus; - for (std::string::size_type i = 0; i < strStatusBar.size(); i++) - { - if (safeChars.find(strStatusBar[i]) != std::string::npos) - safeStatus.push_back(strStatusBar[i]); - } + std::string safeStatus = SanitizeString(strStatusBar); safeStatus = singleQuote+safeStatus+singleQuote; boost::replace_all(strCmd, "%s", safeStatus); diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 2c4744a579e..31452fa1e73 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -479,7 +479,7 @@ bool HTTPAuthorized(map& mapHeaders) return false; string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); string strUserPass = DecodeBase64(strUserPass64); - return strUserPass == strRPCUserColonPass; + return TimingResistantEqual(strUserPass, strRPCUserColonPass); } // diff --git a/src/bloom.cpp b/src/bloom.cpp index d9ec2efa811..6bdffdbb38c 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -23,6 +23,8 @@ vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM // The ideal number of hash functions is filter size * ln(2) / number of elements // Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits // See http://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas +isFull(false), +isEmpty(false), nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), nTweak(nTweakIn), nFlags(nFlagsIn) @@ -37,7 +39,7 @@ inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector< void CBloomFilter::insert(const vector& vKey) { - if (vData.size() == 1 && vData[0] == 0xff) + if (isFull) return; for (unsigned int i = 0; i < nHashFuncs; i++) { @@ -45,6 +47,7 @@ void CBloomFilter::insert(const vector& vKey) // Sets bit nIndex of vData vData[nIndex >> 3] |= bit_mask[7 & nIndex]; } + isEmpty = false; } void CBloomFilter::insert(const COutPoint& outpoint) @@ -63,8 +66,10 @@ void CBloomFilter::insert(const uint256& hash) bool CBloomFilter::contains(const vector& vKey) const { - if (vData.size() == 1 && vData[0] == 0xff) + if (isFull) return true; + if (isEmpty) + return false; for (unsigned int i = 0; i < nHashFuncs; i++) { unsigned int nIndex = Hash(i, vKey); @@ -99,6 +104,10 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& ha bool fFound = false; // Match if the filter contains the hash of tx // for finding tx when they appear in a block + if (isFull) + return true; + if (isEmpty) + return false; if (contains(hash)) fFound = true; @@ -158,3 +167,16 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& ha return false; } + +void CBloomFilter::UpdateEmptyFull() +{ + bool full = true; + bool empty = true; + for (unsigned int i = 0; i < vData.size(); i++) + { + full &= vData[i] == 0xff; + empty &= vData[i] == 0; + } + isFull = full; + isEmpty = empty; +} diff --git a/src/bloom.h b/src/bloom.h index 389ae748e62..f482bfcc10d 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -42,6 +42,8 @@ class CBloomFilter { private: std::vector vData; + bool isFull; + bool isEmpty; unsigned int nHashFuncs; unsigned int nTweak; unsigned char nFlags; @@ -57,9 +59,7 @@ class CBloomFilter // It should generally always be a random value (and is largely only exposed for unit testing) // nFlags should be one of the BLOOM_UPDATE_* enums (not _MASK) CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak, unsigned char nFlagsIn); - // Using a filter initialized with this results in undefined behavior - // Should only be used for deserialization - CBloomFilter() {} + CBloomFilter() : isFull(true) {} IMPLEMENT_SERIALIZE ( @@ -83,6 +83,9 @@ class CBloomFilter // Also adds any outputs which match the filter to the filter (to match their spending txes) bool IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash); + + // Checks for empty and full filters to avoid wasting cpu + void UpdateEmptyFull(); }; #endif /* BITCOIN_BLOOM_H */ diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 9e8e0f70242..911b84f8d67 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -45,11 +45,12 @@ namespace Checkpoints (210000, uint256("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")) (216116, uint256("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")) (225430, uint256("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")) + (250000, uint256("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")) ; static const CCheckpointData data = { &mapCheckpoints, - 1363044259, // * UNIX timestamp of last checkpoint block - 14264869, // * total number of transactions between genesis and last checkpoint + 1375533383, // * UNIX timestamp of last checkpoint block + 21491097, // * total number of transactions between genesis and last checkpoint // (the tx=... number in the SetBestChain debug.log lines) 60000.0 // * estimated number of transactions per day after checkpoint }; diff --git a/src/clientversion.h b/src/clientversion.h index e4fd6a69f8b..2387c8fe41e 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -8,8 +8,8 @@ // These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 #define CLIENT_VERSION_MINOR 8 -#define CLIENT_VERSION_REVISION 2 -#define CLIENT_VERSION_BUILD 2 +#define CLIENT_VERSION_REVISION 6 +#define CLIENT_VERSION_BUILD 0 // Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true diff --git a/src/db.cpp b/src/db.cpp index 3133d99bf80..2bda0659ba6 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -6,7 +6,6 @@ #include "db.h" #include "util.h" #include "main.h" -#include #include #include @@ -541,6 +540,8 @@ bool CAddrDB::Read(CAddrMan& addr) // use file size to size memory buffer int fileSize = GetFilesize(filein); int dataSize = fileSize - sizeof(uint256); + //Don't try to resize to a negative number if file is small + if ( dataSize < 0 ) dataSize = 0; vector vchData; vchData.resize(dataSize); uint256 hashIn; diff --git a/src/init.cpp b/src/init.cpp index 59bf32ecac8..68ca62288db 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -89,6 +89,7 @@ static CCoinsViewDB *pcoinsdbview; void Shutdown() { + printf("Shutdown : In progress...\n"); static CCriticalSection cs_Shutdown; TRY_LOCK(cs_Shutdown, lockShutdown); if (!lockShutdown) return; @@ -114,6 +115,7 @@ void Shutdown() boost::filesystem::remove(GetPidFile()); UnregisterWallet(pwalletMain); delete pwalletMain; + printf("Shutdown : done\n"); } // @@ -333,7 +335,7 @@ std::string HelpMessage() " -testnet " + _("Use the test network") + "\n" + " -debug " + _("Output extra debugging information. Implies all other -debug* options") + "\n" + " -debugnet " + _("Output extra network debugging information") + "\n" + - " -logtimestamps " + _("Prepend debug output with timestamp") + "\n" + + " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n" + " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n" + " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n" + #ifdef WIN32 @@ -531,7 +533,7 @@ bool AppInit2(boost::thread_group& threadGroup) // Make sure enough file descriptors are available int nBind = std::max((int)mapArgs.count("-bind"), 1); nMaxConnections = GetArg("-maxconnections", 125); - nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS), 0); + nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); if (nFD < MIN_CORE_FILEDESCRIPTORS) return InitError(_("Not enough file descriptors available.")); @@ -569,7 +571,7 @@ bool AppInit2(boost::thread_group& threadGroup) #endif fPrintToConsole = GetBoolArg("-printtoconsole"); fPrintToDebugger = GetBoolArg("-printtodebugger"); - fLogTimestamps = GetBoolArg("-logtimestamps"); + fLogTimestamps = GetBoolArg("-logtimestamps", true); if (mapArgs.count("-timeout")) { diff --git a/src/json/json_spirit_writer_template.h b/src/json/json_spirit_writer_template.h index 28c49ddc64d..6b4978a1ff2 100644 --- a/src/json/json_spirit_writer_template.h +++ b/src/json/json_spirit_writer_template.h @@ -28,7 +28,8 @@ namespace json_spirit template< class String_type > String_type non_printable_to_string( unsigned int c ) { - typedef typename String_type::value_type Char_type; + // Silence the warning: typedef ‘Char_type’ locally defined but not used [-Wunused-local-typedefs] + // typedef typename String_type::value_type Char_type; String_type result( 6, '\\' ); diff --git a/src/key.cpp b/src/key.cpp index 20114e6bb2f..2b25308fe55 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -328,7 +328,10 @@ bool CKey::SignCompact(uint256 hash, std::vector& vchSig) } if (nRecId == -1) + { + ECDSA_SIG_free(sig); throw key_error("CKey::SignCompact() : unable to construct recoverable key"); + } vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0); BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]); @@ -367,16 +370,40 @@ bool CKey::SetCompactSignature(uint256 hash, const std::vector& v ECDSA_SIG_free(sig); return true; } + ECDSA_SIG_free(sig); return false; } bool CKey::Verify(uint256 hash, const std::vector& vchSig) { - // -1 = error, 0 = bad sig, 1 = good - if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) + if (vchSig.empty()) return false; - return true; + // New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first. + unsigned char *norm_der = NULL; + ECDSA_SIG *norm_sig = ECDSA_SIG_new(); + const unsigned char* sigptr = &vchSig[0]; + assert(norm_sig); + if (d2i_ECDSA_SIG(&norm_sig, &sigptr, vchSig.size()) == NULL) + { + /* As of OpenSSL 1.0.0p d2i_ECDSA_SIG frees and nulls the pointer on + * error. But OpenSSL's own use of this function redundantly frees the + * result. As ECDSA_SIG_free(NULL) is a no-op, and in the absence of a + * clear contract for the function behaving the same way is more + * conservative. + */ + ECDSA_SIG_free(norm_sig); + return false; + } + int derlen = i2d_ECDSA_SIG(norm_sig, &norm_der); + ECDSA_SIG_free(norm_sig); + if (derlen <= 0) + return false; + + // -1 = error, 0 = bad sig, 1 = good + bool ret = ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), norm_der, derlen, pkey) == 1; + OPENSSL_free(norm_der); + return ret; } bool CKey::VerifyCompact(uint256 hash, const std::vector& vchSig) diff --git a/src/leveldb/.gitignore b/src/leveldb/.gitignore index 55ba072e0e9..71d87a4eeb6 100644 --- a/src/leveldb/.gitignore +++ b/src/leveldb/.gitignore @@ -6,6 +6,7 @@ build_config.mk *.so.* *_test db_bench +leveldbutil Release Debug Benchmark diff --git a/src/leveldb/AUTHORS b/src/leveldb/AUTHORS index 27a9407e52f..fc40194ab94 100644 --- a/src/leveldb/AUTHORS +++ b/src/leveldb/AUTHORS @@ -6,3 +6,6 @@ Google Inc. # Initial version authors: Jeffrey Dean Sanjay Ghemawat + +# Partial list of contributors: +Kevin Regan diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile index 42c4952fec0..20c9c4f2875 100644 --- a/src/leveldb/Makefile +++ b/src/leveldb/Makefile @@ -12,7 +12,7 @@ OPT ?= -O2 -DNDEBUG # (A) Production use (optimized mode) #----------------------------------------------- # detect what platform we're building on -$(shell CC=$(CC) CXX=$(CXX) TARGET_OS=$(TARGET_OS) \ +$(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \ ./build_detect_platform build_config.mk ./) # this file is generated by the previous line to set build flags and sources include build_config.mk @@ -31,6 +31,7 @@ TESTHARNESS = ./util/testharness.o $(TESTUTIL) TESTS = \ arena_test \ + autocompact_test \ bloom_test \ c_test \ cache_test \ @@ -42,6 +43,7 @@ TESTS = \ env_test \ filename_test \ filter_block_test \ + issue178_test \ log_test \ memenv_test \ skiplist_test \ @@ -69,7 +71,7 @@ SHARED = $(SHARED1) else # Update db.h if you change these. SHARED_MAJOR = 1 -SHARED_MINOR = 9 +SHARED_MINOR = 13 SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) SHARED2 = $(SHARED1).$(SHARED_MAJOR) SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) @@ -113,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) @@ -146,6 +151,9 @@ filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform index 609cb512248..bdfd64172cd 100755 --- a/src/leveldb/build_detect_platform +++ b/src/leveldb/build_detect_platform @@ -44,6 +44,10 @@ if test -z "$CXX"; then CXX=g++ fi +if test -z "$TMPDIR"; then + TMPDIR=/tmp +fi + # Detect OS if test -z "$TARGET_OS"; then TARGET_OS=`uname -s` @@ -94,6 +98,12 @@ case "$TARGET_OS" in PLATFORM_LIBS="-lpthread" PORT_FILE=port/port_posix.cc ;; + GNU/kFreeBSD) + PLATFORM=OS_KFREEBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_KFREEBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; NetBSD) PLATFORM=OS_NETBSD COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" @@ -163,8 +173,10 @@ if [ "$CROSS_COMPILE" = "true" ]; then # Cross-compiling; do not try any compilation tests. true else + CXXOUTPUT="${TMPDIR}/leveldb_build_detect_platform-cxx.$$" + # If -std=c++0x works, use . Otherwise use port_posix.h. - $CXX $CXXFLAGS -std=c++0x -x c++ - -o /dev/null 2>/dev/null </dev/null < int main() {} EOF @@ -176,12 +188,14 @@ EOF fi # Test whether tcmalloc is available - $CXX $CXXFLAGS -x c++ - -o /dev/null -ltcmalloc 2>/dev/null </dev/null </dev/null fi PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS" diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc index 31b2d5f4166..b37ffdfe645 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(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 c9de169f29f..fa1351038bc 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -35,6 +35,8 @@ namespace leveldb { +const int kNumNonTableCacheFiles = 10; + // Information kept for every waiting writer struct DBImpl::Writer { Status status; @@ -92,9 +94,9 @@ Options SanitizeOptions(const std::string& dbname, Options result = src; result.comparator = icmp; result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; - ClipToRange(&result.max_open_files, 20, 50000); - ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); - ClipToRange(&result.block_size, 1<<10, 4<<20); + ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); + ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); + ClipToRange(&result.block_size, 1<<10, 4<<20); if (result.info_log == NULL) { // Open a log file in the same directory as the db src.env->CreateDir(dbname); // In case it does not exist @@ -111,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), @@ -128,14 +130,16 @@ 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) { + manual_compaction_(NULL), + consecutive_compaction_errors_(0) { mem_->Ref(); 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 - 10; + 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_, @@ -310,16 +314,24 @@ Status DBImpl::Recover(VersionEdit* edit) { if (!s.ok()) { return s; } + std::set expected; + versions_->AddLiveFiles(&expected); uint64_t number; FileType type; std::vector logs; for (size_t i = 0; i < filenames.size(); i++) { - if (ParseFileName(filenames[i], &number, &type) - && type == kLogFile - && ((number >= min_log) || (number == prev_log))) { - logs.push_back(number); + if (ParseFileName(filenames[i], &number, &type)) { + expected.erase(number); + if (type == kLogFile && ((number >= min_log) || (number == prev_log))) + logs.push_back(number); } } + if (!expected.empty()) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d missing files; e.g.", + static_cast(expected.size())); + return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); + } // Recover in the order in which the logs were generated std::sort(logs.begin(), logs.end()); @@ -611,6 +623,7 @@ void DBImpl::BackgroundCall() { Status s = BackgroundCompaction(); if (s.ok()) { // Success + consecutive_compaction_errors_ = 0; } else if (shutting_down_.Acquire_Load()) { // Error most likely due to shutdown; do not wait } else { @@ -622,7 +635,12 @@ void DBImpl::BackgroundCall() { Log(options_.info_log, "Waiting after background compaction error: %s", s.ToString().c_str()); mutex_.Unlock(); - env_->SleepForMicroseconds(1000000); + ++consecutive_compaction_errors_; + int seconds_to_sleep = 1; + for (int i = 0; i < 3 && i < consecutive_compaction_errors_ - 1; ++i) { + seconds_to_sleep *= 2; + } + env_->SleepForMicroseconds(seconds_to_sleep * 1000000); mutex_.Lock(); } } @@ -1010,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(); @@ -1034,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() { @@ -1097,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(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() { @@ -1268,10 +1298,11 @@ Status DBImpl::MakeRoomForWrite(bool force) { } else if (imm_ != NULL) { // We have filled up the current memtable, but the previous // one is still being compacted, so we wait. + Log(options_.info_log, "Current memtable full; waiting...\n"); bg_cv_.Wait(); } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { // There are too many level-0 files. - Log(options_.info_log, "waiting...\n"); + Log(options_.info_log, "Too many L0 files; waiting...\n"); bg_cv_.Wait(); } else { // Attempt to switch to a new memtable and trigger compaction of old diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h index bd29dd80554..75fd30abe9a 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 writers_; @@ -163,6 +170,7 @@ class DBImpl : public DB { // Have we encountered a background error in paranoid mode? Status bg_error_; + int consecutive_compaction_errors_; // Per level compaction stats. stats_[level] stores the stats for // compactions that produced data for the specified "level". diff --git a/src/leveldb/db/db_iter.cc b/src/leveldb/db/db_iter.cc index 87dca2ded46..071a54e3f45 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 d9e1b174ab8..04927e937ba 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/db_test.cc b/src/leveldb/db/db_test.cc index 684ea3bdbc8..49aae04dbd3 100644 --- a/src/leveldb/db/db_test.cc +++ b/src/leveldb/db/db_test.cc @@ -33,8 +33,11 @@ class AtomicCounter { public: AtomicCounter() : count_(0) { } void Increment() { + IncrementBy(1); + } + void IncrementBy(int count) { MutexLock l(&mu_); - count_++; + count_ += count; } int Read() { MutexLock l(&mu_); @@ -45,6 +48,10 @@ class AtomicCounter { count_ = 0; } }; + +void DelayMilliseconds(int millis) { + Env::Default()->SleepForMicroseconds(millis * 1000); +} } // Special Env used to delay background operations @@ -69,6 +76,7 @@ class SpecialEnv : public EnvWrapper { AtomicCounter random_read_counter_; AtomicCounter sleep_counter_; + AtomicCounter sleep_time_counter_; explicit SpecialEnv(Env* base) : EnvWrapper(base) { delay_sstable_sync_.Release_Store(NULL); @@ -103,7 +111,7 @@ class SpecialEnv : public EnvWrapper { Status Flush() { return base_->Flush(); } Status Sync() { while (env_->delay_sstable_sync_.Acquire_Load() != NULL) { - env_->SleepForMicroseconds(100000); + DelayMilliseconds(100); } return base_->Sync(); } @@ -174,8 +182,9 @@ class SpecialEnv : public EnvWrapper { virtual void SleepForMicroseconds(int micros) { sleep_counter_.Increment(); - target()->SleepForMicroseconds(micros); + sleep_time_counter_.IncrementBy(micros); } + }; class DBTest { @@ -461,6 +470,20 @@ class DBTest { } return result; } + + bool DeleteAnSSTFile() { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) { + ASSERT_OK(env_->DeleteFile(TableFileName(dbname_, number))); + return true; + } + } + return false; + } }; TEST(DBTest, Empty) { @@ -611,7 +634,7 @@ TEST(DBTest, GetEncountersEmptyLevel) { } // Step 4: Wait for compaction to finish - env_->SleepForMicroseconds(1000000); + DelayMilliseconds(1000); ASSERT_EQ(NumTableFilesAtLevel(0), 0); } while (ChangeOptions()); @@ -1295,7 +1318,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_a) { Reopen(); Reopen(); ASSERT_EQ("(a->v)", Contents()); - env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + DelayMilliseconds(1000); // Wait for compaction to finish ASSERT_EQ("(a->v)", Contents()); } @@ -1311,7 +1334,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_b) { Put("",""); Reopen(); Put("",""); - env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + DelayMilliseconds(1000); // Wait for compaction to finish Reopen(); Put("d","dv"); Reopen(); @@ -1321,7 +1344,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_b) { Delete("b"); Reopen(); ASSERT_EQ("(->)(c->cv)", Contents()); - env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + DelayMilliseconds(1000); // Wait for compaction to finish ASSERT_EQ("(->)(c->cv)", Contents()); } @@ -1506,6 +1529,30 @@ TEST(DBTest, NoSpace) { ASSERT_GE(env_->sleep_counter_.Read(), 5); } +TEST(DBTest, ExponentialBackoff) { + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + Compact("a", "z"); + env_->non_writable_.Release_Store(env_); // Force errors for new files + env_->sleep_counter_.Reset(); + env_->sleep_time_counter_.Reset(); + for (int i = 0; i < 5; i++) { + dbfull()->TEST_CompactRange(2, NULL, NULL); + } + env_->non_writable_.Release_Store(NULL); + + // Wait for compaction to finish + DelayMilliseconds(1000); + + ASSERT_GE(env_->sleep_counter_.Read(), 5); + ASSERT_LT(env_->sleep_counter_.Read(), 10); + ASSERT_GE(env_->sleep_time_counter_.Read(), 10e6); +} + TEST(DBTest, NonWritableFileSystem) { Options options = CurrentOptions(); options.write_buffer_size = 1000; @@ -1519,7 +1566,7 @@ TEST(DBTest, NonWritableFileSystem) { fprintf(stderr, "iter %d; errors %d\n", i, errors); if (!Put("foo", big).ok()) { errors++; - env_->SleepForMicroseconds(100000); + DelayMilliseconds(100); } } ASSERT_GT(errors, 0); @@ -1567,6 +1614,24 @@ TEST(DBTest, ManifestWriteError) { } } +TEST(DBTest, MissingSSTFile) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Dump the memtable to disk. + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + + Close(); + ASSERT_TRUE(DeleteAnSSTFile()); + Options options = CurrentOptions(); + options.paranoid_checks = true; + Status s = TryReopen(&options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("issing") != std::string::npos) + << s.ToString(); +} + TEST(DBTest, FilesDeletedAfterCompaction) { ASSERT_OK(Put("foo", "v2")); Compact("a", "z"); @@ -1711,13 +1776,13 @@ TEST(DBTest, MultiThreaded) { } // Let them run for a while - env_->SleepForMicroseconds(kTestSeconds * 1000000); + DelayMilliseconds(kTestSeconds * 1000); // Stop the threads and wait for them to finish mt.stop.Release_Store(&mt); for (int id = 0; id < kNumThreads; id++) { while (mt.thread_done[id].Acquire_Load() == NULL) { - env_->SleepForMicroseconds(100000); + DelayMilliseconds(100); } } } while (ChangeOptions()); diff --git a/src/leveldb/db/dbformat.cc b/src/leveldb/db/dbformat.cc index 28e11b398d0..20a7ca44626 100644 --- a/src/leveldb/db/dbformat.cc +++ b/src/leveldb/db/dbformat.cc @@ -26,7 +26,7 @@ std::string ParsedInternalKey::DebugString() const { (unsigned long long) sequence, int(type)); std::string result = "'"; - result += user_key.ToString(); + result += EscapeString(user_key.ToString()); result += buf; return result; } diff --git a/src/leveldb/db/dbformat.h b/src/leveldb/db/dbformat.h index f7f64dafb6d..5d8a032bd31 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/filename_test.cc b/src/leveldb/db/filename_test.cc index 47353d6c9aa..5a26da4728f 100644 --- a/src/leveldb/db/filename_test.cc +++ b/src/leveldb/db/filename_test.cc @@ -70,7 +70,7 @@ TEST(FileNameTest, Parse) { for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { std::string f = errors[i]; ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; - }; + } } TEST(FileNameTest, Construction) { diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc index 7d0a5de2b9f..66d73be71fb 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 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(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* inputs) { + assert(level >= 0); + assert(level < config::kNumLevels); inputs->clear(); Slice user_begin, user_end; if (begin != NULL) { @@ -1331,14 +1419,19 @@ Compaction* VersionSet::CompactRange( } // Avoid compacting too much in one shot in case the range is large. - const uint64_t limit = MaxFileSizeForLevel(level); - uint64_t total = 0; - for (size_t i = 0; i < inputs.size(); i++) { - uint64_t s = inputs[i]->file_size; - total += s; - if (total >= limit) { - inputs.resize(i + 1); - break; + // But we cannot do this for level-0 since level-0 files can overlap + // and we must not pick one file and drop another older file if the + // two files overlap. + if (level > 0) { + const uint64_t limit = MaxFileSizeForLevel(level); + uint64_t total = 0; + for (size_t i = 0; i < inputs.size(); i++) { + uint64_t s = inputs[i]->file_size; + total += s; + if (total >= limit) { + inputs.resize(i + 1); + break; + } } } diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h index 9d084fdb7d0..20de0e26291 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 29d36744790..57c00a5da00 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 = 9; +static const int kMinorVersion = 13; struct Options; struct ReadOptions; diff --git a/src/leveldb/issues/issue178_test.cc b/src/leveldb/issues/issue178_test.cc new file mode 100644 index 00000000000..1b1cf8bb28d --- /dev/null +++ b/src/leveldb/issues/issue178_test.cc @@ -0,0 +1,92 @@ +// 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. + +// Test for issue 178: a manual compaction causes deleted data to reappear. +#include +#include +#include + +#include "leveldb/db.h" +#include "leveldb/write_batch.h" +#include "util/testharness.h" + +namespace { + +const int kNumKeys = 1100000; + +std::string Key1(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "my_key_%d", i); + return buf; +} + +std::string Key2(int i) { + return Key1(i) + "_xxx"; +} + +class Issue178 { }; + +TEST(Issue178, Test) { + // Get rid of any state from an old run. + std::string dbpath = leveldb::test::TmpDir() + "/leveldb_cbug_test"; + DestroyDB(dbpath, leveldb::Options()); + + // Open database. Disable compression since it affects the creation + // of layers and the code below is trying to test against a very + // specific scenario. + leveldb::DB* db; + leveldb::Options db_options; + db_options.create_if_missing = true; + db_options.compression = leveldb::kNoCompression; + ASSERT_OK(leveldb::DB::Open(db_options, dbpath, &db)); + + // create first key range + leveldb::WriteBatch batch; + for (size_t i = 0; i < kNumKeys; i++) { + batch.Put(Key1(i), "value for range 1 key"); + } + ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch)); + + // create second key range + batch.Clear(); + for (size_t i = 0; i < kNumKeys; i++) { + batch.Put(Key2(i), "value for range 2 key"); + } + ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch)); + + // delete second key range + batch.Clear(); + for (size_t i = 0; i < kNumKeys; i++) { + batch.Delete(Key2(i)); + } + ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch)); + + // compact database + std::string start_key = Key1(0); + std::string end_key = Key1(kNumKeys - 1); + leveldb::Slice least(start_key.data(), start_key.size()); + leveldb::Slice greatest(end_key.data(), end_key.size()); + + // commenting out the line below causes the example to work correctly + db->CompactRange(&least, &greatest); + + // count the keys + leveldb::Iterator* iter = db->NewIterator(leveldb::ReadOptions()); + size_t num_keys = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + num_keys++; + } + delete iter; + ASSERT_EQ(kNumKeys, num_keys) << "Bad number of keys"; + + // close database + delete db; + DestroyDB(dbpath, leveldb::Options()); +} + +} // anonymous namespace + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h index e17bf435eab..844fba25fed 100644 --- a/src/leveldb/port/atomic_pointer.h +++ b/src/leveldb/port/atomic_pointer.h @@ -50,6 +50,13 @@ namespace port { // http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx #define LEVELDB_HAVE_MEMORY_BARRIER +// Mac OS +#elif defined(OS_MACOSX) +inline void MemoryBarrier() { + OSMemoryBarrier(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + // Gcc on x86 #elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) inline void MemoryBarrier() { @@ -62,19 +69,12 @@ inline void MemoryBarrier() { // Sun Studio #elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) inline void MemoryBarrier() { - // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. asm volatile("" : : : "memory"); } #define LEVELDB_HAVE_MEMORY_BARRIER -// Mac OS -#elif defined(OS_MACOSX) -inline void MemoryBarrier() { - OSMemoryBarrier(); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - // ARM Linux #elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__) typedef void (*LinuxKernelMemoryBarrierFunc)(void); diff --git a/src/leveldb/port/port_posix.h b/src/leveldb/port/port_posix.h index f2b89bffb99..21c845e211f 100644 --- a/src/leveldb/port/port_posix.h +++ b/src/leveldb/port/port_posix.h @@ -62,12 +62,16 @@ #define fflush_unlocked fflush #endif -#if defined(OS_MACOSX) || defined(OS_FREEBSD) ||\ +#if defined(OS_FREEBSD) ||\ defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) // Use fsync() on platforms without fdatasync() #define fdatasync fsync #endif +#if defined(OS_MACOSX) +#define fdatasync(fd) fcntl(fd, F_FULLFSYNC, 0) +#endif + #if defined(OS_ANDROID) && __ANDROID_API__ < 9 // fdatasync() was only introduced in API level 9 on Android. Use fsync() // when targetting older platforms. diff --git a/src/leveldb/port/port_win.cc b/src/leveldb/port/port_win.cc index 99c1d8e3460..1b0f060a19c 100644 --- a/src/leveldb/port/port_win.cc +++ b/src/leveldb/port/port_win.cc @@ -109,12 +109,10 @@ void CondVar::Signal() { void CondVar::SignalAll() { wait_mtx_.Lock(); - for(long i = 0; i < waiting_; ++i) { - ::ReleaseSemaphore(sem1_, 1, NULL); - while(waiting_ > 0) { - --waiting_; - ::WaitForSingleObject(sem2_, INFINITE); - } + ::ReleaseSemaphore(sem1_, waiting_, NULL); + while(waiting_ > 0) { + --waiting_; + ::WaitForSingleObject(sem2_, INFINITE); } wait_mtx_.Unlock(); } diff --git a/src/leveldb/table/block.cc b/src/leveldb/table/block.cc index ab83c1112cc..79ea9d9ee5f 100644 --- a/src/leveldb/table/block.cc +++ b/src/leveldb/table/block.cc @@ -16,7 +16,7 @@ namespace leveldb { inline uint32_t Block::NumRestarts() const { - assert(size_ >= 2*sizeof(uint32_t)); + assert(size_ >= sizeof(uint32_t)); return DecodeFixed32(data_ + size_ - sizeof(uint32_t)); } @@ -27,11 +27,12 @@ Block::Block(const BlockContents& contents) if (size_ < sizeof(uint32_t)) { size_ = 0; // Error marker } else { - restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t); - if (restart_offset_ > size_ - sizeof(uint32_t)) { - // The size is too small for NumRestarts() and therefore - // restart_offset_ wrapped around. + size_t max_restarts_allowed = (size_-sizeof(uint32_t)) / sizeof(uint32_t); + if (NumRestarts() > max_restarts_allowed) { + // The size is too small for NumRestarts() size_ = 0; + } else { + restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t); } } } @@ -253,7 +254,7 @@ class Block::Iter : public Iterator { }; Iterator* Block::NewIterator(const Comparator* cmp) { - if (size_ < 2*sizeof(uint32_t)) { + if (size_ < sizeof(uint32_t)) { return NewErrorIterator(Status::Corruption("bad block contents")); } const uint32_t num_restarts = NumRestarts(); diff --git a/src/leveldb/table/table.cc b/src/leveldb/table/table.cc index dbd6d3a1bf0..71c1756e5f4 100644 --- a/src/leveldb/table/table.cc +++ b/src/leveldb/table/table.cc @@ -228,7 +228,6 @@ Status Table::InternalGet(const ReadOptions& options, const Slice& k, !filter->KeyMayMatch(handle.offset(), k)) { // Not found } else { - Slice handle = iiter->value(); Iterator* block_iter = BlockReader(this, options, iiter->value()); block_iter->Seek(k); if (block_iter->Valid()) { diff --git a/src/leveldb/table/table_test.cc b/src/leveldb/table/table_test.cc index 57cea25334e..c723bf84cf5 100644 --- a/src/leveldb/table/table_test.cc +++ b/src/leveldb/table/table_test.cc @@ -644,6 +644,36 @@ class Harness { Constructor* constructor_; }; +// Test empty table/block. +TEST(Harness, Empty) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 1); + Test(&rnd); + } +} + +// Special test for a block with no restart entries. The C++ leveldb +// code never generates such blocks, but the Java version of leveldb +// seems to. +TEST(Harness, ZeroRestartPointsInBlock) { + char data[sizeof(uint32_t)]; + memset(data, 0, sizeof(data)); + BlockContents contents; + contents.data = Slice(data, sizeof(data)); + contents.cachable = false; + contents.heap_allocated = false; + Block block(contents); + Iterator* iter = block.NewIterator(BytewiseComparator()); + iter->SeekToFirst(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToLast(); + ASSERT_TRUE(!iter->Valid()); + iter->Seek("foo"); + ASSERT_TRUE(!iter->Valid()); + delete iter; +} + // Test the empty key TEST(Harness, SimpleEmptyKey) { for (int i = 0; i < kNumTestArgs; i++) { diff --git a/src/leveldb/util/cache.cc b/src/leveldb/util/cache.cc index 24f1f63f4f0..8b197bc02a9 100644 --- a/src/leveldb/util/cache.cc +++ b/src/leveldb/util/cache.cc @@ -116,7 +116,6 @@ class HandleTable { LRUHandle* h = list_[i]; while (h != NULL) { LRUHandle* next = h->next_hash; - Slice key = h->key(); uint32_t hash = h->hash; LRUHandle** ptr = &new_list[hash & (new_length - 1)]; h->next_hash = *ptr; @@ -160,7 +159,6 @@ class LRUCache { // mutex_ protects the following state. port::Mutex mutex_; size_t usage_; - uint64_t last_id_; // Dummy head of LRU list. // lru.prev is newest entry, lru.next is oldest entry. @@ -170,8 +168,7 @@ class LRUCache { }; LRUCache::LRUCache() - : usage_(0), - last_id_(0) { + : usage_(0) { // Make empty circular linked list lru_.next = &lru_; lru_.prev = &lru_; diff --git a/src/leveldb/util/coding_test.cc b/src/leveldb/util/coding_test.cc index 2c52b17b602..fb5726e3352 100644 --- a/src/leveldb/util/coding_test.cc +++ b/src/leveldb/util/coding_test.cc @@ -109,7 +109,7 @@ TEST(Coding, Varint64) { values.push_back(power); values.push_back(power-1); values.push_back(power+1); - }; + } std::string s; for (int i = 0; i < values.size(); i++) { diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc index db81f56d11a..aff5df9db81 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 @@ -346,6 +377,93 @@ class PosixMmapFile : public WritableFile { } }; +#if defined(OS_MACOSX) +class PosixWriteableFile : public WritableFile { + private: + std::string filename_; + int fd_; + public: + PosixWriteableFile(const std::string& fname, int fd) + : filename_(fname), + fd_(fd) + { } + + + ~PosixWriteableFile() { + if (fd_ >= 0) { + PosixWriteableFile::Close(); + } + } + + virtual Status Append(const Slice& data) { + Status s; + int ret; + ret = write(fd_, data.data(), data.size()); + if (ret < 0) { + s = IOError(filename_, errno); + } else if (ret < data.size()) { + s = Status::IOError(filename_, "short write"); + } + + return s; + } + + virtual Status Close() { + Status s; + if (close(fd_) < 0) { + s = IOError(filename_, errno); + } + fd_ = -1; + return s; + } + + virtual Status Flush() { + return Status::OK(); + } + + 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 (fdatasync(fd_) < 0) { + s = IOError(filename_, errno); + } + + return s; + } +}; +#endif + static int LockOrUnlock(int fd, bool lock) { errno = 0; struct flock f; @@ -386,7 +504,7 @@ class PosixEnv : public Env { PosixEnv(); virtual ~PosixEnv() { fprintf(stderr, "Destroying Env::Default()\n"); - exit(1); + abort(); } virtual Status NewSequentialFile(const std::string& fname, @@ -408,6 +526,7 @@ class PosixEnv : public Env { int fd = open(fname.c_str(), O_RDONLY); if (fd < 0) { s = IOError(fname, errno); +#if !defined(OS_MACOSX) } else if (mmap_limit_.Acquire()) { uint64_t size; s = GetFileSize(fname, &size); @@ -423,6 +542,7 @@ class PosixEnv : public Env { if (!s.ok()) { mmap_limit_.Release(); } +#endif } else { *result = new PosixRandomAccessFile(fname, fd); } @@ -437,7 +557,11 @@ class PosixEnv : public Env { *result = NULL; s = IOError(fname, errno); } else { +#if defined(OS_MACOSX) + *result = new PosixWriteableFile(fname, fd); +#else *result = new PosixMmapFile(fname, fd, page_size_); +#endif } return s; } @@ -467,7 +591,7 @@ class PosixEnv : public Env { result = IOError(fname, errno); } return result; - }; + } virtual Status CreateDir(const std::string& name) { Status result; @@ -475,7 +599,7 @@ class PosixEnv : public Env { result = IOError(name, errno); } return result; - }; + } virtual Status DeleteDir(const std::string& name) { Status result; @@ -483,7 +607,7 @@ class PosixEnv : public Env { result = IOError(name, errno); } return result; - }; + } virtual Status GetFileSize(const std::string& fname, uint64_t* size) { Status s; @@ -589,7 +713,7 @@ class PosixEnv : public Env { void PthreadCall(const char* label, int result) { if (result != 0) { fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); - exit(1); + abort(); } } diff --git a/src/leveldb/util/hash.cc b/src/leveldb/util/hash.cc index ba1818082df..07cf022060d 100644 --- a/src/leveldb/util/hash.cc +++ b/src/leveldb/util/hash.cc @@ -6,6 +6,13 @@ #include "util/coding.h" #include "util/hash.h" +// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through +// between switch labels. The real definition should be provided externally. +// This one is a fallback version for unsupported compilers. +#ifndef FALLTHROUGH_INTENDED +#define FALLTHROUGH_INTENDED do { } while (0) +#endif + namespace leveldb { uint32_t Hash(const char* data, size_t n, uint32_t seed) { @@ -28,10 +35,10 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) { switch (limit - data) { case 3: h += data[2] << 16; - // fall through + FALLTHROUGH_INTENDED; case 2: h += data[1] << 8; - // fall through + FALLTHROUGH_INTENDED; case 1: h += data[0]; h *= m; diff --git a/src/leveldb/util/random.h b/src/leveldb/util/random.h index 07538242ea5..ddd51b1c7b5 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 b7efac53b1e..41322d6a801 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,8 +58,8 @@ CMedianFilter cPeerBlockCounts(8, 0); // Amount of blocks that other nodes map mapOrphanBlocks; multimap mapOrphanBlocksByPrev; -map mapOrphanTransactions; -map > mapOrphanTransactionsByPrev; +map mapOrphanTransactions; +map > mapOrphanTransactionsByPrev; // Constant stuff for coinbase transactions we create: CScript COINBASE_FLAGS; @@ -283,16 +283,12 @@ CBlockTreeDB *pblocktree = NULL; // mapOrphanTransactions // -bool AddOrphanTx(const CDataStream& vMsg) +bool AddOrphanTx(const CTransaction& tx) { - CTransaction tx; - CDataStream(vMsg) >> tx; uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) return false; - CDataStream* pvMsg = new CDataStream(vMsg); - // Ignore big transactions, to avoid a // send-big-orphans memory exhaustion attack. If a peer has a legitimate // large transaction with a missing parent then we assume @@ -300,16 +296,16 @@ bool AddOrphanTx(const CDataStream& vMsg) // have been mined or received. // 10,000 orphans, each of which is at most 5,000 bytes big is // at most 500 megabytes of orphans: - if (pvMsg->size() > 5000) + unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + if (sz > 5000) { - printf("ignoring large orphan tx (size: %"PRIszu", hash: %s)\n", pvMsg->size(), hash.ToString().c_str()); - delete pvMsg; + printf("ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString().c_str()); return false; } - mapOrphanTransactions[hash] = pvMsg; + mapOrphanTransactions[hash] = tx; BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg)); + mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(), mapOrphanTransactions.size()); @@ -320,16 +316,13 @@ void static EraseOrphanTx(uint256 hash) { if (!mapOrphanTransactions.count(hash)) return; - const CDataStream* pvMsg = mapOrphanTransactions[hash]; - CTransaction tx; - CDataStream(*pvMsg) >> tx; + const CTransaction& tx = mapOrphanTransactions[hash]; BOOST_FOREACH(const CTxIn& txin, tx.vin) { mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) mapOrphanTransactionsByPrev.erase(txin.prevout.hash); } - delete pvMsg; mapOrphanTransactions.erase(hash); } @@ -340,7 +333,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) { // Evict a random orphan: uint256 randomhash = GetRandHash(); - map::iterator it = mapOrphanTransactions.lower_bound(randomhash); + map::iterator it = mapOrphanTransactions.lower_bound(randomhash); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); EraseOrphanTx(it->first); @@ -366,44 +359,58 @@ bool CTxOut::IsDust() const // which has units satoshis-per-kilobyte. // If you'd pay more than 1/3 in fees // to spend something, then we consider it dust. - // A typical txout is 33 bytes big, and will + // A typical txout is 34 bytes big, and will // need a CTxIn of at least 148 bytes to spend, // so dust is a txout less than 54 uBTC - // (5430 satoshis) with default nMinRelayTxFee + // (5460 satoshis) with default nMinRelayTxFee return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < CTransaction::nMinRelayTxFee); } -bool CTransaction::IsStandard() const +bool CTransaction::IsStandard(string& strReason) const { - if (nVersion > CTransaction::CURRENT_VERSION) + if (nVersion > CTransaction::CURRENT_VERSION || nVersion < 1) { + strReason = "version"; return false; + } - if (!IsFinal()) + if (!IsFinal()) { + strReason = "not-final"; return false; + } // Extremely large transactions with lots of inputs can cost the network // almost as much to process as they cost the sender in fees, because // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. unsigned int sz = this->GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) + if (sz >= MAX_STANDARD_TX_SIZE) { + strReason = "tx-size"; return false; + } BOOST_FOREACH(const CTxIn& txin, vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG // pay-to-script-hash, which is 3 ~80-byte signatures, 3 // ~65-byte public keys, plus a few script ops. - if (txin.scriptSig.size() > 500) + if (txin.scriptSig.size() > 500) { + strReason = "scriptsig-size"; return false; - if (!txin.scriptSig.IsPushOnly()) + } + if (!txin.scriptSig.IsPushOnly()) { + strReason = "scriptsig-not-pushonly"; return false; + } } BOOST_FOREACH(const CTxOut& txout, vout) { - if (!::IsStandard(txout.scriptPubKey)) + if (!::IsStandard(txout.scriptPubKey)) { + strReason = "scriptpubkey"; return false; - if (txout.IsDust()) + } + if (txout.IsDust()) { + strReason = "dust"; return false; + } } return true; } @@ -601,23 +608,20 @@ int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, if (fAllowFree) { - if (nBlockSize == 1) - { - // Transactions under 10K are free - // (about 4500 BTC if made of 50 BTC inputs) - if (nBytes < 10000) - nMinFee = 0; - } - else - { - // Free transaction area - if (nNewBlockSize < 27000) - nMinFee = 0; - } - } - - // To limit dust spam, require base fee if any output is less than 0.01 - if (nMinFee < nBaseFee) + // There is a free transaction area in blocks created by most miners, + // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 + // to be considered to fall into this category. We don't want to encourage sending + // multiple transactions instead of one big transaction to avoid fees. + // * If we are creating a transaction we allow transactions up to 1,000 bytes + // to be considered safe and assume they can likely make it into this section. + if (nBytes < (mode == GMF_SEND ? 1000 : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))) + nMinFee = 0; + } + + // This code can be removed after enough miners have upgraded to version 0.9. + // Until then, be safe when sending and require a fee if any output + // is less than CENT: + if (nMinFee < nBaseFee && mode == GMF_SEND) { BOOST_FOREACH(const CTxOut& txout, vout) if (txout.nValue < CENT) @@ -668,8 +672,10 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) - if (!fTestNet && !tx.IsStandard()) - return error("CTxMemPool::accept() : nonstandard transaction type"); + string strNonStd; + if (!fTestNet && !tx.IsStandard(strNonStd)) + return error("CTxMemPool::accept() : nonstandard transaction (%s)", + strNonStd.c_str()); // is it already in the memory pool? uint256 hash = tx.GetHash(); @@ -786,7 +792,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) + if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_DERSIG)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str()); } @@ -809,9 +815,6 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn EraseFromWallets(ptxOld->GetHash()); SyncWithWallets(hash, tx, NULL, true); - printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", - hash.ToString().c_str(), - mapTx.size()); return true; } @@ -824,7 +827,7 @@ bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs } } -bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) +bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx) { // Add to memory pool without checking anything. Don't call this directly, // call CTxMemPool::accept to properly check the transaction first. @@ -844,15 +847,15 @@ bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) { LOCK(cs); uint256 hash = tx.GetHash(); + if (fRecursive) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { + std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); + if (it != mapNextTx.end()) + remove(*it->second.ptx, true); + } + } if (mapTx.count(hash)) { - if (fRecursive) { - for (unsigned int i = 0; i < tx.vout.size(); i++) { - std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); - if (it != mapNextTx.end()) - remove(*it->second.ptx, true); - } - } BOOST_FOREACH(const CTxIn& txin, tx.vin) mapNextTx.erase(txin.prevout); mapTx.erase(hash); @@ -1506,6 +1509,11 @@ bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoin CCoins &outs = view.GetCoins(hash); CCoins outsBlock = CCoins(tx, pindex->nHeight); + // The CCoins serialization does not serialize negative numbers. + // No network rules currently depend on the version here, so an inconsistency is harmless + // but it must be corrected before txout nversion ever influences a network rule. + if (outsBlock.nVersion < 0) + outs.nVersion = outsBlock.nVersion; if (outs != outsBlock) fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); @@ -1637,6 +1645,12 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi unsigned int flags = SCRIPT_VERIFY_NOCACHE | (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); + if (nVersion >= 3 && + ((!fTestNet && CBlockIndex::IsSuperMajority(3, pindex->pprev, 750, 1000)) || + (fTestNet && CBlockIndex::IsSuperMajority(3, pindex->pprev, 51, 100)))) { + flags |= SCRIPT_VERIFY_DERSIG; + } + CBlockUndo blockundo; CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); @@ -1778,7 +1792,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) } // Disconnect shorter branch - vector vResurrect; + list vResurrect; BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { CBlock block; if (!block.ReadFromDisk(pindex)) @@ -1792,9 +1806,9 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // Queue memory transactions to resurrect. // We only do this for blocks after the last checkpoint (reorganisation before that // point should only happen with -reindex/-loadblock, or a misbehaving peer. - BOOST_FOREACH(const CTransaction& tx, block.vtx) + BOOST_REVERSE_FOREACH(const CTransaction& tx, block.vtx) if (!tx.IsCoinBase() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate()) - vResurrect.push_back(tx); + vResurrect.push_front(tx); } // Connect longer branch @@ -1860,7 +1874,8 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) BOOST_FOREACH(CTransaction& tx, vResurrect) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - tx.AcceptToMemoryPool(stateDummy, true, false); + if (!tx.AcceptToMemoryPool(stateDummy, true, false)) + mempool.remove(tx, true); } // Delete redundant memory transactions that are in the connected branch @@ -2124,7 +2139,7 @@ bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerk uniqueTx.insert(GetTxHash(i)); } if (uniqueTx.size() != vtx.size()) - return state.DoS(100, error("CheckBlock() : duplicate transaction")); + return state.DoS(100, error("CheckBlock() : duplicate transaction"), true); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, vtx) @@ -2184,6 +2199,15 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); } } + // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: + if (nVersion < 3) + { + if ((!fTestNet && CBlockIndex::IsSuperMajority(3, pindexPrev, 950, 1000)) || + (fTestNet && CBlockIndex::IsSuperMajority(3, pindexPrev, 75, 100))) + { + return state.Invalid(error("AcceptBlock() : rejected nVersion=2 block")); + } + } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height if (nVersion >= 2) { @@ -2192,7 +2216,8 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) { CScript expect = CScript() << nHeight; - if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) + if (vtx[0].vin[0].scriptSig.size() < expect.size() || + !std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); } } @@ -2714,7 +2739,7 @@ bool LoadBlockIndex() pchMessageStart[1] = 0x11; pchMessageStart[2] = 0x09; pchMessageStart[3] = 0x07; - hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"); + hashGenesisBlock = uint256("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"); } // @@ -3141,6 +3166,9 @@ void static ProcessGetData(CNode* pfrom) // Track requests for our stuff. Inventory(inv.hash); + + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + break; } } @@ -3200,8 +3228,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->nVersion = 300; if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; - if (!vRecv.empty()) + if (!vRecv.empty()) { vRecv >> pfrom->strSubVer; + pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); + } if (!vRecv.empty()) vRecv >> pfrom->nStartingHeight; if (!vRecv.empty()) @@ -3269,7 +3299,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; - printf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); + printf("receive version message: %s: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer.c_str(), pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); cPeerBlockCounts.input(pfrom->nStartingHeight); } @@ -3512,40 +3542,45 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CValidationState state; if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs)) { - RelayTransaction(tx, inv.hash, vMsg); + RelayTransaction(tx, inv.hash); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); + printf("AcceptToMemoryPool: %s %s : accepted %s (poolsz %"PRIszu")\n", + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + tx.GetHash().ToString().c_str(), + mempool.mapTx.size()); + // Recursively process any orphan transactions that depended on this one for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; - for (map::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); + for (set::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); mi != mapOrphanTransactionsByPrev[hashPrev].end(); ++mi) { - const CDataStream& vMsg = *((*mi).second); - CTransaction tx; - CDataStream(vMsg) >> tx; - CInv inv(MSG_TX, tx.GetHash()); + const uint256& orphanHash = *mi; + const CTransaction& orphanTx = mapOrphanTransactions[orphanHash]; bool fMissingInputs2 = false; - // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get anyone relaying LegitTxX banned) + // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan + // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get + // anyone relaying LegitTxX banned) CValidationState stateDummy; if (tx.AcceptToMemoryPool(stateDummy, true, true, &fMissingInputs2)) { - printf(" accepted orphan tx %s\n", inv.hash.ToString().c_str()); - RelayTransaction(tx, inv.hash, vMsg); - mapAlreadyAskedFor.erase(inv); - vWorkQueue.push_back(inv.hash); - vEraseQueue.push_back(inv.hash); + printf(" accepted orphan tx %s\n", orphanHash.ToString().c_str()); + RelayTransaction(orphanTx, orphanHash); + mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash)); + vWorkQueue.push_back(orphanHash); + vEraseQueue.push_back(orphanHash); } else if (!fMissingInputs2) { // invalid or too-little-fee orphan - vEraseQueue.push_back(inv.hash); - printf(" removed orphan tx %s\n", inv.hash.ToString().c_str()); + vEraseQueue.push_back(orphanHash); + printf(" removed orphan tx %s\n", orphanHash.ToString().c_str()); } } } @@ -3555,16 +3590,21 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } else if (fMissingInputs) { - AddOrphanTx(vMsg); + AddOrphanTx(tx); // DoS prevention: do not allow mapOrphanTransactions to grow unbounded unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); if (nEvicted > 0) printf("mapOrphan overflow, removed %u tx\n", nEvicted); } - int nDoS; + int nDoS = 0; if (state.IsInvalid(nDoS)) - pfrom->Misbehaving(nDoS); + { + printf("%s from %s %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(), + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str()); + if (nDoS > 0) + pfrom->Misbehaving(nDoS); + } } @@ -3580,11 +3620,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); CValidationState state; - if (ProcessBlock(state, pfrom, &block)) + if (ProcessBlock(state, pfrom, &block) || state.CorruptionPossible()) mapAlreadyAskedFor.erase(inv); - int nDoS; + int nDoS = 0; if (state.IsInvalid(nDoS)) - pfrom->Misbehaving(nDoS); + if (nDoS > 0) + pfrom->Misbehaving(nDoS); } @@ -3682,6 +3723,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LOCK(pfrom->cs_filter); delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(filter); + pfrom->pfilter->UpdateEmptyFull(); } pfrom->fRelayTxes = true; } @@ -3711,7 +3753,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { LOCK(pfrom->cs_filter); delete pfrom->pfilter; - pfrom->pfilter = NULL; + pfrom->pfilter = new CBloomFilter(); pfrom->fRelayTxes = true; } @@ -3750,6 +3792,9 @@ bool ProcessMessages(CNode* pfrom) if (!pfrom->vRecvGetData.empty()) ProcessGetData(pfrom); + // this maintains the order of responses + if (!pfrom->vRecvGetData.empty()) return fOk; + std::deque::iterator it = pfrom->vRecvMsg.begin(); while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway @@ -3840,6 +3885,8 @@ bool ProcessMessages(CNode* pfrom) if (!fRet) printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + + break; } // In case the connection got shut down, its receive buffer was wiped @@ -4184,7 +4231,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) pblocktemplate->vTxSigOps.push_back(-1); // updated at end // Largest block you're willing to create: - unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); + unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); @@ -4194,7 +4241,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay - unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000); + unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); // Minimum block size you want to create; block will be filled with free transactions @@ -4741,9 +4788,6 @@ class CMainCleanup mapOrphanBlocks.clear(); // orphan transactions - std::map::iterator it3 = mapOrphanTransactions.begin(); - for (; it3 != mapOrphanTransactions.end(); it3++) - delete (*it3).second; mapOrphanTransactions.clear(); } } instance_of_cmaincleanup; diff --git a/src/main.h b/src/main.h index 8e71e66cc83..82d40787a31 100644 --- a/src/main.h +++ b/src/main.h @@ -26,10 +26,14 @@ struct CBlockIndexWorkComparator; /** The maximum allowed size for a serialized block, in bytes (network rule) */ static const unsigned int MAX_BLOCK_SIZE = 1000000; -/** The maximum size for mined blocks */ +/** Obsolete: maximum size for mined blocks */ static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; +/** Default for -blockmaxsize, maximum size for mined blocks **/ +static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 350000; +/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ +static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 30000; /** The maximum size for transactions we're willing to relay/mine */ -static const unsigned int MAX_STANDARD_TX_SIZE = MAX_BLOCK_SIZE_GEN/5; +static const unsigned int MAX_STANDARD_TX_SIZE = 100000; /** The maximum allowed number of signature check operations in a block (network rule) */ static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; /** The maximum number of orphan transactions kept in memory */ @@ -559,7 +563,12 @@ class CTransaction /** Check for standard transaction types @return True if all outputs (scriptPubKeys) use only standard transaction forms */ - bool IsStandard() const; + bool IsStandard(std::string& strReason) const; + bool IsStandard() const + { + std::string strReason; + return IsStandard(strReason); + } /** Check for standard transaction types @param[in] mapInputs Map of previous transactions that have outputs we're spending @@ -1259,7 +1268,7 @@ class CBlockHeader { public: // header - static const int CURRENT_VERSION=2; + static const int CURRENT_VERSION=3; int nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; @@ -1881,13 +1890,15 @@ class CValidationState { MODE_ERROR, // run-time error } mode; int nDoS; + bool corruptionPossible; public: - CValidationState() : mode(MODE_VALID), nDoS(0) {} - bool DoS(int level, bool ret = false) { + CValidationState() : mode(MODE_VALID), nDoS(0), corruptionPossible(false) {} + bool DoS(int level, bool ret = false, bool corruptionIn = false) { if (mode == MODE_ERROR) return ret; nDoS += level; mode = MODE_INVALID; + corruptionPossible = corruptionIn; return ret; } bool Invalid(bool ret = false) { @@ -1917,6 +1928,9 @@ class CValidationState { } return false; } + bool CorruptionPossible() { + return corruptionPossible; + } }; @@ -2067,7 +2081,7 @@ class CTxMemPool std::map mapNextTx; bool accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs); - bool addUnchecked(const uint256& hash, CTransaction &tx); + bool addUnchecked(const uint256& hash, const CTransaction &tx); bool remove(const CTransaction &tx, bool fRecursive = false); bool removeConflicts(const CTransaction &tx); void clear(); diff --git a/src/makefile.mingw b/src/makefile.mingw index 8b5a5dccd90..5904d3dd020 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -43,7 +43,7 @@ LIBS= \ -l ssl \ -l crypto -DEFS=-DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE +DEFS=-D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE DEBUGFLAGS=-g CFLAGS=-mthreads -O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) # enable: ASLR, DEP and large address aware @@ -63,7 +63,7 @@ ifneq (${USE_IPV6}, -) DEFS += -DUSE_IPV6=$(USE_IPV6) endif -LIBS += -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l winmm -l shell32 -l comctl32 -l ole32 -l oleaut32 -l uuid -l rpcrt4 -l advapi32 -l ws2_32 -l mswsock -l shlwapi +LIBS += -l mingwthrd -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l winmm -l shell32 -l comctl32 -l ole32 -l oleaut32 -l uuid -l rpcrt4 -l advapi32 -l ws2_32 -l mswsock -l shlwapi # TODO: make the mingw builds smarter about dependencies, like the linux/osx builds are HEADERS = $(wildcard *.h) diff --git a/src/net.cpp b/src/net.cpp index 54ed1d9b516..156ab6349c3 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -21,6 +21,9 @@ #include #endif +// Dump addresses to peers.dat every 15 minutes (900s) +#define DUMP_ADDRESSES_INTERVAL 900 + using namespace std; using namespace boost; @@ -609,7 +612,7 @@ void CNode::copyStats(CNodeStats &stats) X(nTimeConnected); X(addrName); X(nVersion); - X(strSubVer); + X(cleanSubVer); X(fInbound); X(nStartingHeight); X(nMisbehavior); @@ -1626,6 +1629,9 @@ void ThreadMessageHandler() CNode* pnodeTrickle = NULL; if (!vNodesCopy.empty()) pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; + + bool fSleep = true; + BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (pnode->fDisconnect) @@ -1635,8 +1641,18 @@ void ThreadMessageHandler() { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) + { if (!ProcessMessages(pnode)) pnode->CloseSocketDisconnect(); + + if (pnode->nSendSize < SendBufferSize()) + { + if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) + { + fSleep = false; + } + } + } } boost::this_thread::interruption_point(); @@ -1655,7 +1671,8 @@ void ThreadMessageHandler() pnode->Release(); } - MilliSleep(100); + if (fSleep) + MilliSleep(100); } } @@ -1720,8 +1737,12 @@ bool BindListenPort(const CService &addrBind, string& strError) // and enable it by default or not. Try to enable it, if possible. if (addrBind.IsIPv6()) { #ifdef IPV6_V6ONLY +#ifdef WIN32 + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int)); +#else setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int)); #endif +#endif #ifdef WIN32 int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */; int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */; @@ -1855,7 +1876,7 @@ void StartNode(boost::thread_group& threadGroup) threadGroup.create_thread(boost::bind(&TraceThread, "msghand", &ThreadMessageHandler)); // Dump network addresses - threadGroup.create_thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, 10000)); + threadGroup.create_thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, DUMP_ADDRESSES_INTERVAL * 1000)); } bool StopNode() diff --git a/src/net.h b/src/net.h index af66eed0705..dfdd3c8137b 100644 --- a/src/net.h +++ b/src/net.h @@ -98,7 +98,7 @@ class CNodeStats int64 nTimeConnected; std::string addrName; int nVersion; - std::string strSubVer; + std::string cleanSubVer; bool fInbound; int nStartingHeight; int nMisbehavior; @@ -177,7 +177,11 @@ class CNode std::string addrName; CService addrLocal; int nVersion; - std::string strSubVer; + // strSubVer is whatever byte array we read from the wire. However, this field is intended + // to be printed out, displayed to humans in various forms and so on. So we sanitize it and + // store the sanitized version in cleanSubVer. The original should be used when dealing with + // the network or wire types and the cleaned string used when displayed or logged. + std::string strSubVer, cleanSubVer; bool fOneShot; bool fClient; bool fInbound; @@ -253,7 +257,7 @@ class CNode nMisbehavior = 0; fRelayTxes = false; setInventoryKnown.max_size(SendBufferSize() / 1000); - pfilter = NULL; + pfilter = new CBloomFilter(); // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) diff --git a/src/netbase.cpp b/src/netbase.cpp index 08d133740a9..d9695167502 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -860,7 +860,7 @@ std::vector CNetAddr::GetGroup() const nBits = 4; } // for he.net, use /36 groups - else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && GetByte(13) == 0x04 && GetByte(12) == 0x70) + else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) nBits = 36; // for the rest of the IPv6 network, use /32 groups else diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h index 2b6a59367da..b7f4dfee96a 100644 --- a/src/qt/bitcoinaddressvalidator.h +++ b/src/qt/bitcoinaddressvalidator.h @@ -3,8 +3,8 @@ #include -/** Base48 entry widget validator. - Corrects near-miss characters and refuses characters that are no part of base48. +/** Base58 entry widget validator. + Corrects near-miss characters and refuses characters that are not part of base58. */ class BitcoinAddressValidator : public QValidator { diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index b6ea8e1d03d..02117536cbd 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -52,6 +52,8 @@ - (void)handleDockClickEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAp this->m_dummyWidget = new QWidget(); this->m_dockMenu = new QMenu(this->m_dummyWidget); qt_mac_set_dock_menu(this->m_dockMenu); + this->setMainWindow(NULL); + [pool release]; } @@ -100,8 +102,11 @@ - (void)handleDockClickEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAp void MacDockIconHandler::handleDockIconClickEvent() { - this->mainWindow->activateWindow(); - this->mainWindow->show(); + if (this->mainWindow) + { + this->mainWindow->activateWindow(); + this->mainWindow->show(); + } emit this->dockIconClicked(); } diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index eb3ce48a6e2..d64d70baa50 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -170,7 +170,7 @@ void SendCoinsDialog::clear() // Remove entries until only one left while(ui->entries->count()) { - delete ui->entries->takeAt(0)->widget(); + ui->entries->takeAt(0)->widget()->deleteLater(); } addEntry(); @@ -226,7 +226,7 @@ void SendCoinsDialog::updateRemoveEnabled() void SendCoinsDialog::removeEntry(SendCoinsEntry* entry) { - delete entry; + entry->deleteLater(); updateRemoveEnabled(); } diff --git a/src/qt/walletstack.cpp b/src/qt/walletstack.cpp index 9e3060e86b2..3576d55cea5 100644 --- a/src/qt/walletstack.cpp +++ b/src/qt/walletstack.cpp @@ -13,6 +13,7 @@ WalletStack::WalletStack(QWidget *parent) : QStackedWidget(parent), + gui(0), clientModel(0), bOutOfSync(true) { @@ -35,6 +36,10 @@ bool WalletStack::addWallet(const QString& name, WalletModel *walletModel) walletView->showOutOfSyncWarning(bOutOfSync); addWidget(walletView); mapWalletViews[name] = walletView; + + // Ensure a walletView is able to show the main window + connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); + return true; } diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 6d44c174b74..1de145c32df 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -203,6 +203,7 @@ bool WalletView::handleURI(const QString& strURI) if (sendCoinsPage->handleURI(strURI)) { gotoSendCoinsPage(); + emit showNormalIfMinimized(); return true; } else diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 6bcd70baf95..6ad5180d567 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -99,6 +99,10 @@ public slots: void unlockWallet(); void setEncryptionStatus(); + +signals: + /** Signal that we want to show the main window */ + void showNormalIfMinimized(); }; #endif // WALLETVIEW_H diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 644c2675aee..99b94ffa3ae 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -55,7 +55,10 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("bytesrecv", (boost::int64_t)stats.nRecvBytes)); obj.push_back(Pair("conntime", (boost::int64_t)stats.nTimeConnected)); obj.push_back(Pair("version", stats.nVersion)); - obj.push_back(Pair("subver", stats.strSubVer)); + // Use the sanitized form of subver here, to avoid tricksy remote peers from + // corrupting or modifiying the JSON output by putting special characters in + // their ver message. + obj.push_back(Pair("subver", stats.cleanSubVer)); obj.push_back(Pair("inbound", stats.fInbound)); obj.push_back(Pair("startingheight", stats.nStartingHeight)); obj.push_back(Pair("banscore", stats.nMisbehavior)); diff --git a/src/script.cpp b/src/script.cpp index 90066efd336..8a61716e935 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -289,6 +289,86 @@ bool IsCanonicalSignature(const valtype &vchSig) { return true; } +// BIP 66 defined signature encoding check. This largely overlaps with +// IsCanonicalSignature above, but lacks hashtype constraints, and uses the +// exact implementation code from BIP 66. +bool static IsValidSignatureEncoding(const std::vector &sig) { + // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] + // * total-length: 1-byte length descriptor of everything that follows, + // excluding the sighash byte. + // * R-length: 1-byte length descriptor of the R value that follows. + // * R: arbitrary-length big-endian encoded R value. It must use the shortest + // possible encoding for a positive integers (which means no null bytes at + // the start, except a single one when the next byte has its highest bit set). + // * S-length: 1-byte length descriptor of the S value that follows. + // * S: arbitrary-length big-endian encoded S value. The same rules apply. + // * sighash: 1-byte value indicating what data is hashed (not part of the DER + // signature) + + // Minimum and maximum size constraints. + if (sig.size() < 9) return false; + if (sig.size() > 73) return false; + + // A signature is of type 0x30 (compound). + if (sig[0] != 0x30) return false; + + // Make sure the length covers the entire signature. + if (sig[1] != sig.size() - 3) return false; + + // Extract the length of the R element. + unsigned int lenR = sig[3]; + + // Make sure the length of the S element is still inside the signature. + if (5 + lenR >= sig.size()) return false; + + // Extract the length of the S element. + unsigned int lenS = sig[5 + lenR]; + + // Verify that the length of the signature matches the sum of the length + // of the elements. + if ((size_t)(lenR + lenS + 7) != sig.size()) return false; + + // Check whether the R element is an integer. + if (sig[2] != 0x02) return false; + + // Zero-length integers are not allowed for R. + if (lenR == 0) return false; + + // Negative numbers are not allowed for R. + if (sig[4] & 0x80) return false; + + // Null bytes at the start of R are not allowed, unless R would + // otherwise be interpreted as a negative number. + if (lenR > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80)) return false; + + // Check whether the S element is an integer. + if (sig[lenR + 4] != 0x02) return false; + + // Zero-length integers are not allowed for S. + if (lenS == 0) return false; + + // Negative numbers are not allowed for S. + if (sig[lenR + 6] & 0x80) return false; + + // Null bytes at the start of S are not allowed, unless S would otherwise be + // interpreted as a negative number. + if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) return false; + + return true; +} + +bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags) { + // Empty signature. Not strictly DER encoded, but allowed to provide a + // compact way to provide an invalid signature for use with CHECK(MULTI)SIG + if (vchSig.size() == 0) { + return true; + } + if ((flags & SCRIPT_VERIFY_DERSIG) != 0 && !IsValidSignatureEncoding(vchSig)) { + return false; + } + return true; +} + bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { CAutoBN_CTX pctx; @@ -841,6 +921,10 @@ bool EvalScript(vector >& stack, const CScript& script, co // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(CScript(vchSig)); + if (!CheckSignatureEncoding(vchSig, flags)) { + return false; + } + bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); if (fSuccess) fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); @@ -902,6 +986,10 @@ bool EvalScript(vector >& stack, const CScript& script, co valtype& vchSig = stacktop(-isig); valtype& vchPubKey = stacktop(-ikey); + if (!CheckSignatureEncoding(vchSig, flags)) { + return false; + } + // Check signature bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); if (fOk) diff --git a/src/script.h b/src/script.h index 4b29f6273c9..6a843437521 100644 --- a/src/script.h +++ b/src/script.h @@ -35,6 +35,7 @@ enum SCRIPT_VERIFY_P2SH = (1U << 0), SCRIPT_VERIFY_STRICTENC = (1U << 1), SCRIPT_VERIFY_NOCACHE = (1U << 2), + SCRIPT_VERIFY_DERSIG = (1U << 3), // enforce signature encodings as defined by BIP 66 (which is a softfork, while STRICTENC is not) }; enum txnouttype diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index b1e98f65edd..2ee64755d3a 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -16,10 +16,10 @@ #include // Tests this internal-to-main.cpp method: -extern bool AddOrphanTx(const CDataStream& vMsg); +extern bool AddOrphanTx(const CTransaction& tx); extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans); -extern std::map mapOrphanTransactions; -extern std::map > mapOrphanTransactionsByPrev; +extern std::map mapOrphanTransactions; +extern std::map > mapOrphanTransactionsByPrev; CService ip(uint32_t i) { @@ -133,14 +133,11 @@ BOOST_AUTO_TEST_CASE(DoS_checknbits) CTransaction RandomOrphan() { - std::map::iterator it; + std::map::iterator it; it = mapOrphanTransactions.lower_bound(GetRandHash()); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); - const CDataStream* pvMsg = it->second; - CTransaction tx; - CDataStream(*pvMsg) >> tx; - return tx; + return it->second; } BOOST_AUTO_TEST_CASE(DoS_mapOrphans) @@ -162,9 +159,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) tx.vout[0].nValue = 1*CENT; tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); - CDataStream ds(SER_DISK, CLIENT_VERSION); - ds << tx; - AddOrphanTx(ds); + AddOrphanTx(tx); } // ... and 50 that depend on other orphans: @@ -181,9 +176,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); SignSignature(keystore, txPrev, tx, 0); - CDataStream ds(SER_DISK, CLIENT_VERSION); - ds << tx; - AddOrphanTx(ds); + AddOrphanTx(tx); } // This really-big orphan should be ignored: @@ -207,9 +200,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) for (unsigned int j = 1; j < tx.vin.size(); j++) tx.vin[j].scriptSig = tx.vin[0].scriptSig; - CDataStream ds(SER_DISK, CLIENT_VERSION); - ds << tx; - BOOST_CHECK(!AddOrphanTx(ds)); + BOOST_CHECK(!AddOrphanTx(tx)); } // Test LimitOrphanTxSize() function: @@ -246,9 +237,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) tx.vout[0].nValue = 1*CENT; tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); - CDataStream ds(SER_DISK, CLIENT_VERSION); - ds << tx; - AddOrphanTx(ds); + AddOrphanTx(tx); } // Create a transaction that depends on orphans: diff --git a/src/test/data/script_invalid.json b/src/test/data/script_invalid.json index 95664226c51..f1c68da1783 100644 --- a/src/test/data/script_invalid.json +++ b/src/test/data/script_invalid.json @@ -310,5 +310,45 @@ ["NOP1 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"], ["0 0x01 0x50", "HASH160 0x14 0xece424a6bb6ddf4db592c0faed60685047a361b1 EQUAL", "OP_RESERVED in P2SH should fail"], -["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "OP_VER in P2SH should fail"] +["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "OP_VER in P2SH should fail"], + +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Overly long signature is incorrectly encoded for DERSIG"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Missing S is incorrectly encoded for DERSIG"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "S with invalid S length is incorrectly encoded for DERSIG"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer R is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer S is incorrectly encoded for DERSIG"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Zero-length R is incorrectly encoded for DERSIG"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "DERSIG", "Zero-length S is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Negative S is incorrectly encoded for DERSIG"], + +[ + "0x47 0x30440220003040725f724b0e2142fc44ac71f6e13161f6410aeb6dee477952ede3b6a6ca022041ff4940ee3d88116ad281d7cc556e1f2c9427d82290bd7974a25addbcd5bede01", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "P2PK NOT with bad sig with too much R padding" +], +[ + "0x47 0x30440220003040725f724a0e2142fc44ac71f6e13161f6410aeb6dee477952ede3b6a6ca022041ff4940ee3d88116ad281d7cc556e1f2c9427d82290bd7974a25addbcd5bede01", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "P2PK NOT with too much R padding" +], +[ + "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 2, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 6, with DERSIG" +], +[ + "0 0 0x47 0x30440220afa76a8f60622f813b05711f051c6c3407e32d1b1b70b0576c1f01b54e4c05c702200d58e9df044fd1845cabfbeef6e624ba0401daf7d7e084736f9ff601c3783bf501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 10, with DERSIG" +] ] diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json index 58682d38751..1af06e1287b 100644 --- a/src/test/data/script_valid.json +++ b/src/test/data/script_valid.json @@ -375,5 +375,65 @@ ["NOP", "NOP10 1"], ["0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "Very basic P2SH"], -["0x4c 0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"] +["0x4c 0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"], + +["0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"Basic PUSH signedness check"], + +["0x4c 0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"Basic PUSHDATA1 signedness check"], + +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Overly long signature is correctly encoded"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Missing S is correctly encoded"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "", "S with invalid S length is correctly encoded"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer R is correctly encoded"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer S is correctly encoded"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Zero-length R is correctly encoded"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "", "Zero-length S is correctly encoded"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Negative S is correctly encoded"], + +[ + "0x47 0x30440220003040725f724b0e2142fc44ac71f6e13161f6410aeb6dee477952ede3b6a6ca022041ff4940ee3d88116ad281d7cc556e1f2c9427d82290bd7974a25addbcd5bede01", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "", + "P2PK NOT with bad sig with too much R padding but no DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 4, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 4, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 6, without DERSIG" +], +[ + "0 0 0x47 0x30440220afa76a8f60622f813b05711f051c6c3407e32d1b1b70b0576c1f01b54e4c05c702200d58e9df044fd1845cabfbeef6e624ba0401daf7d7e084736f9ff601c3783bf501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 10, without DERSIG" +], +[ + "0 0x47 0x30440220f00a77260d34ec2f0c59621dc710f58169d0ca06df1a88cd4b1f1b97bd46991b02201ee220c7e04f26aed03f94aa97fb09ca5627163bf4ba07e6979972ec737db22601 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 12, without DERSIG" +], +[ + "0 0x47 0x30440220f00a77260d34ec2f0c59621dc710f58169d0ca06df1a88cd4b1f1b97bd46991b02201ee220c7e04f26aed03f94aa97fb09ca5627163bf4ba07e6979972ec737db22601 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 12, with DERSIG" +] ] diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 5d5a1525f76..ee81dbcdc27 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -142,8 +142,13 @@ BOOST_AUTO_TEST_CASE(script_valid) string scriptPubKeyString = test[1].get_str(); CScript scriptPubKey = ParseScript(scriptPubKeyString); + int flagsNow = flags; + if (test.size() > 3 && ("," + test[2].get_str() + ",").find(",DERSIG,") != string::npos) { + flagsNow |= SCRIPT_VERIFY_DERSIG; + } + CTransaction tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, flagsNow, SIGHASH_NONE), strTest); } } @@ -166,8 +171,13 @@ BOOST_AUTO_TEST_CASE(script_invalid) string scriptPubKeyString = test[1].get_str(); CScript scriptPubKey = ParseScript(scriptPubKeyString); + int flagsNow = flags; + if (test.size() > 3 && ("," + test[2].get_str() + ",").find(",DERSIG,") != string::npos) { + flagsNow |= SCRIPT_VERIFY_DERSIG; + } + CTransaction tx; - BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); + BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, flagsNow, SIGHASH_NONE), strTest); } } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 1b0ccad5118..e2247767db1 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -263,28 +263,10 @@ BOOST_AUTO_TEST_CASE(util_IsHex) BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) { - // Expected results for the determinstic seed. - const uint32_t exp_vals[11] = { 91632771U,1889679809U,3842137544U,3256031132U, - 1761911779U, 489223532U,2692793790U,2737472863U, - 2796262275U,1309899767U,840571781U}; - // Expected 0s in rand()%(idx+2) for the determinstic seed. - const int exp_count[9] = {5013,3346,2415,1972,1644,1386,1176,1096,1009}; int i; int count=0; - seed_insecure_rand(); - - //Does the non-determistic rand give us results that look too like the determinstic one? - for (i=0;i<10;i++) - { - int match = 0; - uint32_t rval = insecure_rand(); - for (int j=0;j<11;j++)match |= rval==exp_vals[j]; - count += match; - } - // sum(binomial(10,i)*(11/(2^32))^i*(1-(11/(2^32)))^(10-i),i,0,4) ~= 1-1/2^134.73 - // So _very_ unlikely to throw a false failure here. - BOOST_CHECK(count<=4); + seed_insecure_rand(true); for (int mod=2;mod<11;mod++) { @@ -307,20 +289,17 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) BOOST_CHECK(count<=10000/mod+err); BOOST_CHECK(count>=10000/mod-err); } +} - seed_insecure_rand(true); - - for (i=0;i<11;i++) - { - BOOST_CHECK_EQUAL(insecure_rand(),exp_vals[i]); - } - - for (int mod=2;mod<11;mod++) - { - count = 0; - for (i=0;i<10000;i++) count += insecure_rand()%mod==0; - BOOST_CHECK_EQUAL(count,exp_count[mod-2]); - } +BOOST_AUTO_TEST_CASE(util_TimingResistantEqual) +{ + BOOST_CHECK(TimingResistantEqual(std::string(""), std::string(""))); + BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string(""))); + BOOST_CHECK(!TimingResistantEqual(std::string(""), std::string("abc"))); + BOOST_CHECK(!TimingResistantEqual(std::string("a"), std::string("aa"))); + BOOST_CHECK(!TimingResistantEqual(std::string("aa"), std::string("a"))); + BOOST_CHECK(TimingResistantEqual(std::string("abc"), std::string("abc"))); + BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("aba"))); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/util.cpp b/src/util.cpp index 4c9b897f575..00cd1c92f40 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -454,6 +454,19 @@ bool ParseMoney(const char* pszIn, int64& nRet) return true; } +// safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything +// even possibly remotely dangerous like & or > +static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); +string SanitizeString(const string& str) +{ + string strResult; + for (std::string::size_type i = 0; i < str.size(); i++) + { + if (safeChars.find(str[i]) != std::string::npos) + strResult.push_back(str[i]); + } + return strResult; +} static const signed char phexdigit[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, @@ -1071,7 +1084,7 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) if (fNetSpecific && GetBoolArg("-testnet", false)) path /= "testnet3"; - fs::create_directory(path); + fs::create_directories(path); fCachedPath[fNetSpecific] = true; return path; @@ -1118,6 +1131,7 @@ boost::filesystem::path GetPidFile() return pathPidFile; } +#ifndef WIN32 void CreatePidFile(const boost::filesystem::path &path, pid_t pid) { FILE* file = fopen(path.string().c_str(), "w"); @@ -1127,6 +1141,7 @@ void CreatePidFile(const boost::filesystem::path &path, pid_t pid) fclose(file); } } +#endif bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest) { @@ -1147,6 +1162,8 @@ void FileCommit(FILE *fileout) #else #if defined(__linux__) || defined(__NetBSD__) fdatasync(fileno(fileout)); + #elif defined(__APPLE__) && defined(F_FULLFSYNC) + fcntl(fileno(fileout), F_FULLFSYNC, 0); #else fsync(fileno(fileout)); #endif diff --git a/src/util.h b/src/util.h index 51a694483ad..a190a3b5da8 100644 --- a/src/util.h +++ b/src/util.h @@ -22,6 +22,7 @@ typedef int pid_t; /* define for Windows compatibility */ #include #include +#include #include #include #include @@ -106,7 +107,11 @@ T* alignup(T* p) inline void MilliSleep(int64 n) { -#if BOOST_VERSION >= 105000 +// Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 +// until fixed in 1.52. Use the deprecated sleep method for the broken case. +// See: https://svn.boost.org/trac/boost/ticket/7238 + +#if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); #else boost::this_thread::sleep(boost::posix_time::milliseconds(n)); @@ -182,6 +187,7 @@ void ParseString(const std::string& str, char c, std::vector& v); std::string FormatMoney(int64 n, bool fPlus=false); bool ParseMoney(const std::string& str, int64& nRet); bool ParseMoney(const char* pszIn, int64& nRet); +std::string SanitizeString(const std::string& str); std::vector ParseHex(const char* psz); std::vector ParseHex(const std::string& str); bool IsHex(const std::string& str); @@ -206,7 +212,9 @@ boost::filesystem::path GetDefaultDataDir(); const boost::filesystem::path &GetDataDir(bool fNetSpecific = true); boost::filesystem::path GetConfigFile(); boost::filesystem::path GetPidFile(); +#ifndef WIN32 void CreatePidFile(const boost::filesystem::path &path, pid_t pid); +#endif void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); #ifdef WIN32 boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); @@ -433,6 +441,21 @@ static inline uint32_t insecure_rand(void) */ void seed_insecure_rand(bool fDeterministic=false); +/** + * Timing-attack-resistant comparison. + * Takes time proportional to length + * of first argument. + */ +template +bool TimingResistantEqual(const T& a, const T& b) +{ + if (b.size() == 0) return a.size() == 0; + size_t accumulator = a.size() ^ b.size(); + for (size_t i = 0; i < a.size(); i++) + accumulator |= a[i] ^ b[i%b.size()]; + return accumulator == 0; +} + /** Median filter over a stream of values. * Returns the median of the last N numbers */ @@ -531,7 +554,7 @@ inline uint32_t ByteReverse(uint32_t value) // Standard wrapper for do-something-forever thread functions. // "Forever" really means until the thread is interrupted. // Use it like: -// new boost::thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, 10000)); +// new boost::thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, 900000)); // or maybe: // boost::function f = boost::bind(&FunctionWithArg, argument); // threadGroup.create_thread(boost::bind(&LoopForever >, "nothing", f, milliseconds)); @@ -544,8 +567,8 @@ template void LoopForever(const char* name, Callable func, { while (1) { - func(); MilliSleep(msecs); + func(); } } catch (boost::thread_interrupted) diff --git a/src/version.cpp b/src/version.cpp index 8af406feabd..d9d6724a029 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -36,7 +36,7 @@ const std::string CLIENT_NAME("Satoshi"); // git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ #ifdef GIT_ARCHIVE # define GIT_COMMIT_ID "$Format:%h$" -# define GIT_COMMIT_DATE "$Format:%cD" +# define GIT_COMMIT_DATE "$Format:%cD$" #endif #define BUILD_DESC_FROM_COMMIT(maj,min,rev,build,commit) \ diff --git a/src/wallet.cpp b/src/wallet.cpp index c70ea20e889..1c5fb528ba1 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -740,6 +740,10 @@ void CWalletTx::AddSupportingTransactions() { tx = *mapWalletPrev[hash]; } + else + { + continue; + } int nDepth = tx.SetMerkleBranch(); vtxPrev.push_back(tx); @@ -844,7 +848,10 @@ void CWalletTx::RelayWalletTransaction() { BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) { - if (!tx.IsCoinBase()) + // Important: versions of bitcoin before 0.8.6 had a bug that inserted + // empty transactions into the vtxPrev, which will cause the node to be + // banned when retransmitted, hence the check for !tx.vin.empty() + if (!tx.IsCoinBase() && !tx.vin.empty()) if (tx.GetDepthInMainChain() == 0) RelayTransaction((CTransaction)tx, tx.GetHash()); } @@ -1204,9 +1211,10 @@ bool CWallet::CreateTransaction(const vector >& vecSend, } int64 nChange = nValueIn - nValue - nFeeRet; - // if sub-cent change is required, the fee must be raised to at least nMinTxFee - // or until nChange becomes zero - // NOTE: this depends on the exact behaviour of GetMinFee + // The following if statement should be removed once enough miners + // have upgraded to the 0.9 GetMinFee() rules. Until then, this avoids + // creating free transactions that have change outputs less than + // CENT bitcoins. if (nFeeRet < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT) { int64 nMoveToFee = min(nChange, CTransaction::nMinTxFee - nFeeRet); diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 81a21443a12..42f6bbde1e4 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -5,6 +5,7 @@ #include "walletdb.h" #include "wallet.h" +#include #include using namespace std;