mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-22 00:00:55 +01:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03a7d67387 | ||
|
|
d3381e71a1 | ||
|
|
77f7bcb352 | ||
|
|
5f553f8422 | ||
|
|
1eb11e32e8 | ||
|
|
ec93d0aa43 | ||
|
|
8c3ba8be01 | ||
|
|
a0a5b20156 | ||
|
|
22db46cf11 | ||
|
|
bfb0856986 | ||
|
|
3b8868dca4 | ||
|
|
df0f6d020a | ||
|
|
27e35bf840 | ||
|
|
6f21e7317e | ||
|
|
daf9e4627f | ||
|
|
138705a173 | ||
|
|
96896a0ece | ||
|
|
901ae4bffd | ||
|
|
c9c67b00df | ||
|
|
d3fe7c44f7 | ||
|
|
98289bbe25 | ||
|
|
9f7fc8b332 | ||
|
|
1b350f25a6 | ||
|
|
89c2ea0891 | ||
|
|
df238b1975 | ||
|
|
187f8a5675 | ||
|
|
9612e4c0d9 | ||
|
|
1ca8a75cb4 | ||
|
|
5c029630f9 | ||
|
|
7120d76dc6 | ||
|
|
1c53d0a4fb | ||
|
|
3779de9d89 | ||
|
|
56ce843314 | ||
|
|
e564297156 | ||
|
|
83efc9104f | ||
|
|
e4be09cfae | ||
|
|
068996daa5 | ||
|
|
99f73bd2fd | ||
|
|
f607729b26 | ||
|
|
e88eb41b6c | ||
|
|
0119e0c592 | ||
|
|
f46f128b9a | ||
|
|
05ea79052c | ||
|
|
c4892eb4b3 | ||
|
|
633d95ec22 | ||
|
|
3e89dbb132 | ||
|
|
5c55bf5af0 | ||
|
|
7084756f4f | ||
|
|
ef14a26b12 | ||
|
|
27fefeac71 | ||
|
|
f0a1d87b00 | ||
|
|
839c7d1fa8 | ||
|
|
d7fdc5fac3 | ||
|
|
5e18c6ccbc | ||
|
|
708c75c0ee | ||
|
|
ac7c960067 | ||
|
|
980b1c3571 | ||
|
|
6519339fe8 | ||
|
|
e6552eed63 | ||
|
|
cb1e39f0a3 | ||
|
|
5c739c574f | ||
|
|
20b611770f | ||
|
|
21696c12f3 | ||
|
|
08dd92060b | ||
|
|
cdb3441b5c | ||
|
|
38863afbcc | ||
|
|
6f315b4016 | ||
|
|
6929f2a45f | ||
|
|
40809aed65 | ||
|
|
65c20dbf55 | ||
|
|
7cc960f8f5 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
131
contrib/gitian-downloader/wtogami-key.pgp
Normal file
131
contrib/gitian-downloader/wtogami-key.pgp
Normal file
@@ -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-----
|
||||
@@ -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 \
|
||||
|
||||
@@ -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/"
|
||||
|
||||
118
db/autocompact_test.cc
Normal file
118
db/autocompact_test.cc
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2013 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "leveldb/db.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "leveldb/cache.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class AutoCompactTest {
|
||||
public:
|
||||
std::string dbname_;
|
||||
Cache* tiny_cache_;
|
||||
Options options_;
|
||||
DB* db_;
|
||||
|
||||
AutoCompactTest() {
|
||||
dbname_ = test::TmpDir() + "/autocompact_test";
|
||||
tiny_cache_ = NewLRUCache(100);
|
||||
options_.block_cache = tiny_cache_;
|
||||
DestroyDB(dbname_, options_);
|
||||
options_.create_if_missing = true;
|
||||
options_.compression = kNoCompression;
|
||||
ASSERT_OK(DB::Open(options_, dbname_, &db_));
|
||||
}
|
||||
|
||||
~AutoCompactTest() {
|
||||
delete db_;
|
||||
DestroyDB(dbname_, Options());
|
||||
delete tiny_cache_;
|
||||
}
|
||||
|
||||
std::string Key(int i) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "key%06d", i);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
uint64_t Size(const Slice& start, const Slice& limit) {
|
||||
Range r(start, limit);
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
return size;
|
||||
}
|
||||
|
||||
void DoReads(int n);
|
||||
};
|
||||
|
||||
static const int kValueSize = 200 * 1024;
|
||||
static const int kTotalSize = 100 * 1024 * 1024;
|
||||
static const int kCount = kTotalSize / kValueSize;
|
||||
|
||||
// Read through the first n keys repeatedly and check that they get
|
||||
// compacted (verified by checking the size of the key space).
|
||||
void AutoCompactTest::DoReads(int n) {
|
||||
std::string value(kValueSize, 'x');
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
|
||||
// Fill database
|
||||
for (int i = 0; i < kCount; i++) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(i), value));
|
||||
}
|
||||
ASSERT_OK(dbi->TEST_CompactMemTable());
|
||||
|
||||
// Delete everything
|
||||
for (int i = 0; i < kCount; i++) {
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), Key(i)));
|
||||
}
|
||||
ASSERT_OK(dbi->TEST_CompactMemTable());
|
||||
|
||||
// Get initial measurement of the space we will be reading.
|
||||
const int64_t initial_size = Size(Key(0), Key(n));
|
||||
const int64_t initial_other_size = Size(Key(n), Key(kCount));
|
||||
|
||||
// Read until size drops significantly.
|
||||
std::string limit_key = Key(n);
|
||||
for (int read = 0; true; read++) {
|
||||
ASSERT_LT(read, 100) << "Taking too long to compact";
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
for (iter->SeekToFirst();
|
||||
iter->Valid() && iter->key().ToString() < limit_key;
|
||||
iter->Next()) {
|
||||
// Drop data
|
||||
}
|
||||
delete iter;
|
||||
// Wait a little bit to allow any triggered compactions to complete.
|
||||
Env::Default()->SleepForMicroseconds(1000000);
|
||||
uint64_t size = Size(Key(0), Key(n));
|
||||
fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n",
|
||||
read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0);
|
||||
if (size <= initial_size/10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the size of the key space not touched by the reads
|
||||
// is pretty much unchanged.
|
||||
const int64_t final_other_size = Size(Key(n), Key(kCount));
|
||||
ASSERT_LE(final_other_size, initial_other_size + 1048576);
|
||||
ASSERT_GE(final_other_size, initial_other_size/5 - 1048576);
|
||||
}
|
||||
|
||||
TEST(AutoCompactTest, ReadAll) {
|
||||
DoReads(kCount);
|
||||
}
|
||||
|
||||
TEST(AutoCompactTest, ReadHalf) {
|
||||
DoReads(kCount/2);
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
- [Translation Process](translation_process.md)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
- Celebrate
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@ bool HTTPAuthorized(map<string, string>& mapHeaders)
|
||||
return false;
|
||||
string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
|
||||
string strUserPass = DecodeBase64(strUserPass64);
|
||||
return strUserPass == strRPCUserColonPass;
|
||||
return TimingResistantEqual(strUserPass, strRPCUserColonPass);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -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<unsigned char>& 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<unsigned char>& 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<unsigned char>& 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;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ class CBloomFilter
|
||||
{
|
||||
private:
|
||||
std::vector<unsigned char> vData;
|
||||
bool isFull;
|
||||
bool isEmpty;
|
||||
unsigned int nHashFuncs;
|
||||
unsigned int nTweak;
|
||||
unsigned char nFlags;
|
||||
@@ -57,9 +59,7 @@ public:
|
||||
// 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 @@ public:
|
||||
|
||||
// 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 */
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "db.h"
|
||||
#include "util.h"
|
||||
#include "main.h"
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
@@ -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<unsigned char> vchData;
|
||||
vchData.resize(dataSize);
|
||||
uint256 hashIn;
|
||||
|
||||
@@ -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"))
|
||||
{
|
||||
|
||||
@@ -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, '\\' );
|
||||
|
||||
|
||||
@@ -328,7 +328,10 @@ bool CKey::SignCompact(uint256 hash, std::vector<unsigned char>& 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,6 +370,7 @@ bool CKey::SetCompactSignature(uint256 hash, const std::vector<unsigned char>& v
|
||||
ECDSA_SIG_free(sig);
|
||||
return true;
|
||||
}
|
||||
ECDSA_SIG_free(sig);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
1
src/leveldb/.gitignore
vendored
1
src/leveldb/.gitignore
vendored
@@ -6,6 +6,7 @@ build_config.mk
|
||||
*.so.*
|
||||
*_test
|
||||
db_bench
|
||||
leveldbutil
|
||||
Release
|
||||
Debug
|
||||
Benchmark
|
||||
|
||||
@@ -6,3 +6,6 @@ Google Inc.
|
||||
# Initial version authors:
|
||||
Jeffrey Dean <jeff@google.com>
|
||||
Sanjay Ghemawat <sanjay@google.com>
|
||||
|
||||
# Partial list of contributors:
|
||||
Kevin Regan <kevin.d.regan@gmail.com>
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 <cstdatomic>. Otherwise use port_posix.h.
|
||||
$CXX $CXXFLAGS -std=c++0x -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $CXXFLAGS -std=c++0x -x c++ - -o $CXXOUTPUT 2>/dev/null <<EOF
|
||||
#include <cstdatomic>
|
||||
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 <<EOF
|
||||
$CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -ltcmalloc 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LIBS="$PLATFORM_LIBS -ltcmalloc"
|
||||
fi
|
||||
|
||||
rm -f $CXXOUTPUT 2>/dev/null
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
|
||||
@@ -35,6 +35,7 @@ class CorruptionTest {
|
||||
CorruptionTest() {
|
||||
tiny_cache_ = NewLRUCache(100);
|
||||
options_.env = &env_;
|
||||
options_.block_cache = tiny_cache_;
|
||||
dbname_ = test::TmpDir() + "/db_test";
|
||||
DestroyDB(dbname_, options_);
|
||||
|
||||
@@ -50,17 +51,14 @@ class CorruptionTest {
|
||||
delete tiny_cache_;
|
||||
}
|
||||
|
||||
Status TryReopen(Options* options = NULL) {
|
||||
Status TryReopen() {
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
Options opt = (options ? *options : options_);
|
||||
opt.env = &env_;
|
||||
opt.block_cache = tiny_cache_;
|
||||
return DB::Open(opt, dbname_, &db_);
|
||||
return DB::Open(options_, dbname_, &db_);
|
||||
}
|
||||
|
||||
void Reopen(Options* options = NULL) {
|
||||
ASSERT_OK(TryReopen(options));
|
||||
void Reopen() {
|
||||
ASSERT_OK(TryReopen());
|
||||
}
|
||||
|
||||
void RepairDB() {
|
||||
@@ -92,6 +90,10 @@ class CorruptionTest {
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
uint64_t key;
|
||||
Slice in(iter->key());
|
||||
if (in == "" || in == "~") {
|
||||
// Ignore boundary keys.
|
||||
continue;
|
||||
}
|
||||
if (!ConsumeDecimalNumber(&in, &key) ||
|
||||
!in.empty() ||
|
||||
key < next_expected) {
|
||||
@@ -233,7 +235,7 @@ TEST(CorruptionTest, TableFile) {
|
||||
dbi->TEST_CompactRange(1, NULL, NULL);
|
||||
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
Check(99, 99);
|
||||
Check(90, 99);
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, TableFileIndexData) {
|
||||
@@ -299,7 +301,7 @@ TEST(CorruptionTest, CompactionInputError) {
|
||||
ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last)));
|
||||
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
Check(9, 9);
|
||||
Check(5, 9);
|
||||
|
||||
// Force compactions by writing lots of values
|
||||
Build(10000);
|
||||
@@ -307,32 +309,23 @@ TEST(CorruptionTest, CompactionInputError) {
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
Options options;
|
||||
options.paranoid_checks = true;
|
||||
options.write_buffer_size = 1048576;
|
||||
Reopen(&options);
|
||||
options_.paranoid_checks = true;
|
||||
options_.write_buffer_size = 512 << 10;
|
||||
Reopen();
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
|
||||
// Fill levels >= 1 so memtable compaction outputs to level 1
|
||||
for (int level = 1; level < config::kNumLevels; level++) {
|
||||
dbi->Put(WriteOptions(), "", "begin");
|
||||
dbi->Put(WriteOptions(), "~", "end");
|
||||
// Make multiple inputs so we need to compact.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Build(10);
|
||||
dbi->TEST_CompactMemTable();
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
env_.SleepForMicroseconds(100000);
|
||||
}
|
||||
dbi->CompactRange(NULL, NULL);
|
||||
|
||||
Build(10);
|
||||
dbi->TEST_CompactMemTable();
|
||||
ASSERT_EQ(1, Property("leveldb.num-files-at-level0"));
|
||||
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
Check(9, 9);
|
||||
|
||||
// Write must eventually fail because of corrupted table
|
||||
Status s;
|
||||
// Write must fail because of corrupted table
|
||||
std::string tmp1, tmp2;
|
||||
for (int i = 0; i < 10000 && s.ok(); i++) {
|
||||
s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
|
||||
}
|
||||
Status s = db_->Put(WriteOptions(), Key(5, &tmp1), Value(5, &tmp2));
|
||||
ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
|
||||
}
|
||||
|
||||
|
||||
@@ -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<uint64_t> expected;
|
||||
versions_->AddLiveFiles(&expected);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
std::vector<uint64_t> 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<int>(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<const SnapshotImpl*>(options.snapshot)->number_
|
||||
: latest_snapshot));
|
||||
: latest_snapshot),
|
||||
seed);
|
||||
}
|
||||
|
||||
void DBImpl::RecordReadSample(Slice key) {
|
||||
MutexLock l(&mutex_);
|
||||
if (versions_->current()->RecordReadSample(key)) {
|
||||
MaybeScheduleCompaction();
|
||||
}
|
||||
}
|
||||
|
||||
const Snapshot* DBImpl::GetSnapshot() {
|
||||
@@ -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
|
||||
|
||||
@@ -59,13 +59,19 @@ class DBImpl : public DB {
|
||||
// file at a level >= 1.
|
||||
int64_t TEST_MaxNextLevelOverlappingBytes();
|
||||
|
||||
// Record a sample of bytes read at the specified internal key.
|
||||
// Samples are taken approximately once every config::kReadBytesPeriod
|
||||
// bytes.
|
||||
void RecordReadSample(Slice key);
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
struct CompactionState;
|
||||
struct Writer;
|
||||
|
||||
Iterator* NewInternalIterator(const ReadOptions&,
|
||||
SequenceNumber* latest_snapshot);
|
||||
SequenceNumber* latest_snapshot,
|
||||
uint32_t* seed);
|
||||
|
||||
Status NewDB();
|
||||
|
||||
@@ -135,6 +141,7 @@ class DBImpl : public DB {
|
||||
WritableFile* logfile_;
|
||||
uint64_t logfile_number_;
|
||||
log::Writer* log_;
|
||||
uint32_t seed_; // For sampling.
|
||||
|
||||
// Queue of writers.
|
||||
std::deque<Writer*> writers_;
|
||||
@@ -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".
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<std::string> 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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -289,6 +289,51 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) {
|
||||
return a->number > b->number;
|
||||
}
|
||||
|
||||
void Version::ForEachOverlapping(Slice user_key, Slice internal_key,
|
||||
void* arg,
|
||||
bool (*func)(void*, int, FileMetaData*)) {
|
||||
// TODO(sanjay): Change Version::Get() to use this function.
|
||||
const Comparator* ucmp = vset_->icmp_.user_comparator();
|
||||
|
||||
// Search level-0 in order from newest to oldest.
|
||||
std::vector<FileMetaData*> tmp;
|
||||
tmp.reserve(files_[0].size());
|
||||
for (uint32_t i = 0; i < files_[0].size(); i++) {
|
||||
FileMetaData* f = files_[0][i];
|
||||
if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 &&
|
||||
ucmp->Compare(user_key, f->largest.user_key()) <= 0) {
|
||||
tmp.push_back(f);
|
||||
}
|
||||
}
|
||||
if (!tmp.empty()) {
|
||||
std::sort(tmp.begin(), tmp.end(), NewestFirst);
|
||||
for (uint32_t i = 0; i < tmp.size(); i++) {
|
||||
if (!(*func)(arg, 0, tmp[i])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search other levels.
|
||||
for (int level = 1; level < config::kNumLevels; level++) {
|
||||
size_t num_files = files_[level].size();
|
||||
if (num_files == 0) continue;
|
||||
|
||||
// Binary search to find earliest index whose largest key >= internal_key.
|
||||
uint32_t index = FindFile(vset_->icmp_, files_[level], internal_key);
|
||||
if (index < num_files) {
|
||||
FileMetaData* f = files_[level][index];
|
||||
if (ucmp->Compare(user_key, f->smallest.user_key()) < 0) {
|
||||
// All of "f" is past any data for user_key
|
||||
} else {
|
||||
if (!(*func)(arg, level, f)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status Version::Get(const ReadOptions& options,
|
||||
const LookupKey& k,
|
||||
std::string* value,
|
||||
@@ -401,6 +446,44 @@ bool Version::UpdateStats(const GetStats& stats) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Version::RecordReadSample(Slice internal_key) {
|
||||
ParsedInternalKey ikey;
|
||||
if (!ParseInternalKey(internal_key, &ikey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct State {
|
||||
GetStats stats; // Holds first matching file
|
||||
int matches;
|
||||
|
||||
static bool Match(void* arg, int level, FileMetaData* f) {
|
||||
State* state = reinterpret_cast<State*>(arg);
|
||||
state->matches++;
|
||||
if (state->matches == 1) {
|
||||
// Remember first match.
|
||||
state->stats.seek_file = f;
|
||||
state->stats.seek_file_level = level;
|
||||
}
|
||||
// We can stop iterating once we have a second match.
|
||||
return state->matches < 2;
|
||||
}
|
||||
};
|
||||
|
||||
State state;
|
||||
state.matches = 0;
|
||||
ForEachOverlapping(ikey.user_key, internal_key, &state, &State::Match);
|
||||
|
||||
// Must have at least two matches since we want to merge across
|
||||
// files. But what if we have a single file that contains many
|
||||
// overwrites and deletions? Should we have another mechanism for
|
||||
// finding such files?
|
||||
if (state.matches >= 2) {
|
||||
// 1MB cost is about 1 seek (see comment in Builder::Apply).
|
||||
return UpdateStats(state.stats);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Version::Ref() {
|
||||
++refs_;
|
||||
}
|
||||
@@ -435,10 +518,13 @@ int Version::PickLevelForMemTableOutput(
|
||||
if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) {
|
||||
break;
|
||||
}
|
||||
GetOverlappingInputs(level + 2, &start, &limit, &overlaps);
|
||||
const int64_t sum = TotalFileSize(overlaps);
|
||||
if (sum > kMaxGrandParentOverlapBytes) {
|
||||
break;
|
||||
if (level + 2 < config::kNumLevels) {
|
||||
// Check that file does not overlap too many grandparent bytes.
|
||||
GetOverlappingInputs(level + 2, &start, &limit, &overlaps);
|
||||
const int64_t sum = TotalFileSize(overlaps);
|
||||
if (sum > kMaxGrandParentOverlapBytes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
level++;
|
||||
}
|
||||
@@ -452,6 +538,8 @@ void Version::GetOverlappingInputs(
|
||||
const InternalKey* begin,
|
||||
const InternalKey* end,
|
||||
std::vector<FileMetaData*>* inputs) {
|
||||
assert(level >= 0);
|
||||
assert(level < config::kNumLevels);
|
||||
inputs->clear();
|
||||
Slice user_begin, user_end;
|
||||
if (begin != NULL) {
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
92
src/leveldb/issues/issue178_test.cc
Normal file
92
src/leveldb/issues/issue178_test.cc
Normal file
@@ -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 <iostream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
|
||||
#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();
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
209
src/main.cpp
209
src/main.cpp
@@ -58,8 +58,8 @@ CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes
|
||||
map<uint256, CBlock*> mapOrphanBlocks;
|
||||
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
|
||||
|
||||
map<uint256, CDataStream*> mapOrphanTransactions;
|
||||
map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
|
||||
map<uint256, CTransaction> mapOrphanTransactions;
|
||||
map<uint256, set<uint256> > 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<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
|
||||
map<uint256, CTransaction>::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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
// To limit dust spam, require base fee if any output is less than 0.01
|
||||
if (nMinFee < nBaseFee)
|
||||
// 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();
|
||||
@@ -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<COutPoint, CInPoint>::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<COutPoint, CInPoint>::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");
|
||||
|
||||
@@ -1778,7 +1786,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
|
||||
}
|
||||
|
||||
// Disconnect shorter branch
|
||||
vector<CTransaction> vResurrect;
|
||||
list<CTransaction> vResurrect;
|
||||
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
|
||||
CBlock block;
|
||||
if (!block.ReadFromDisk(pindex))
|
||||
@@ -1792,9 +1800,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 +1868,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 +2133,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)
|
||||
@@ -2192,7 +2201,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 +2724,7 @@ bool LoadBlockIndex()
|
||||
pchMessageStart[1] = 0x11;
|
||||
pchMessageStart[2] = 0x09;
|
||||
pchMessageStart[3] = 0x07;
|
||||
hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
|
||||
hashGenesisBlock = uint256("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
|
||||
}
|
||||
|
||||
//
|
||||
@@ -3141,6 +3151,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 +3213,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 +3284,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 +3527,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<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
|
||||
for (set<uint256>::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 +3575,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 +3605,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 +3708,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 +3738,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 +3777,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<CNetMessage>::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 +3870,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 +4216,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 +4226,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 +4773,6 @@ public:
|
||||
mapOrphanBlocks.clear();
|
||||
|
||||
// orphan transactions
|
||||
std::map<uint256, CDataStream*>::iterator it3 = mapOrphanTransactions.begin();
|
||||
for (; it3 != mapOrphanTransactions.end(); it3++)
|
||||
delete (*it3).second;
|
||||
mapOrphanTransactions.clear();
|
||||
}
|
||||
} instance_of_cmaincleanup;
|
||||
|
||||
26
src/main.h
26
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 @@ public:
|
||||
/** 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
|
||||
@@ -1881,13 +1890,15 @@ private:
|
||||
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 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool CorruptionPossible() {
|
||||
return corruptionPossible;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2067,7 +2081,7 @@ public:
|
||||
std::map<COutPoint, CInPoint> 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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
27
src/net.cpp
27
src/net.cpp
@@ -21,6 +21,9 @@
|
||||
#include <miniupnpc/upnperrors.h>
|
||||
#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<void (*)()>, "msghand", &ThreadMessageHandler));
|
||||
|
||||
// Dump network addresses
|
||||
threadGroup.create_thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 10000));
|
||||
threadGroup.create_thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, DUMP_ADDRESSES_INTERVAL * 1000));
|
||||
}
|
||||
|
||||
bool StopNode()
|
||||
|
||||
10
src/net.h
10
src/net.h
@@ -98,7 +98,7 @@ public:
|
||||
int64 nTimeConnected;
|
||||
std::string addrName;
|
||||
int nVersion;
|
||||
std::string strSubVer;
|
||||
std::string cleanSubVer;
|
||||
bool fInbound;
|
||||
int nStartingHeight;
|
||||
int nMisbehavior;
|
||||
@@ -177,7 +177,11 @@ public:
|
||||
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 @@ public:
|
||||
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)
|
||||
|
||||
@@ -860,7 +860,7 @@ std::vector<unsigned char> 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
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include <QValidator>
|
||||
|
||||
/** 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
|
||||
{
|
||||
|
||||
@@ -52,6 +52,8 @@ MacDockIconHandler::MacDockIconHandler() : QObject()
|
||||
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 @@ MacDockIconHandler *MacDockIconHandler::instance()
|
||||
|
||||
void MacDockIconHandler::handleDockIconClickEvent()
|
||||
{
|
||||
this->mainWindow->activateWindow();
|
||||
this->mainWindow->show();
|
||||
if (this->mainWindow)
|
||||
{
|
||||
this->mainWindow->activateWindow();
|
||||
this->mainWindow->show();
|
||||
}
|
||||
|
||||
emit this->dockIconClicked();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -203,6 +203,7 @@ bool WalletView::handleURI(const QString& strURI)
|
||||
if (sendCoinsPage->handleURI(strURI))
|
||||
{
|
||||
gotoSendCoinsPage();
|
||||
emit showNormalIfMinimized();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// 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<uint256, CDataStream*> mapOrphanTransactions;
|
||||
extern std::map<uint256, std::map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
|
||||
extern std::map<uint256, CTransaction> mapOrphanTransactions;
|
||||
extern std::map<uint256, std::set<uint256> > mapOrphanTransactionsByPrev;
|
||||
|
||||
CService ip(uint32_t i)
|
||||
{
|
||||
@@ -133,14 +133,11 @@ BOOST_AUTO_TEST_CASE(DoS_checknbits)
|
||||
|
||||
CTransaction RandomOrphan()
|
||||
{
|
||||
std::map<uint256, CDataStream*>::iterator it;
|
||||
std::map<uint256, CTransaction>::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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
19
src/util.cpp
19
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
|
||||
|
||||
29
src/util.h
29
src/util.h
@@ -22,6 +22,7 @@ typedef int pid_t; /* define for Windows compatibility */
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
@@ -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<std::string>& 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<unsigned char> ParseHex(const char* psz);
|
||||
std::vector<unsigned char> 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<std::string, std::string>& mapSettingsRet, std::map<std::string, std::vector<std::string> >& 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 <typename T>
|
||||
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<void (*)()>, "dumpaddr", &DumpAddresses, 10000));
|
||||
// new boost::thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 900000));
|
||||
// or maybe:
|
||||
// boost::function<void()> f = boost::bind(&FunctionWithArg, argument);
|
||||
// threadGroup.create_thread(boost::bind(&LoopForever<boost::function<void()> >, "nothing", f, milliseconds));
|
||||
@@ -544,8 +567,8 @@ template <typename Callable> void LoopForever(const char* name, Callable func,
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
func();
|
||||
MilliSleep(msecs);
|
||||
func();
|
||||
}
|
||||
}
|
||||
catch (boost::thread_interrupted)
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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<pair<CScript, int64> >& 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);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "walletdb.h"
|
||||
#include "wallet.h"
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
Reference in New Issue
Block a user