aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/db.cpp1
-rw-r--r--src/init.cpp23
-rw-r--r--src/init.h1
-rw-r--r--src/leveldb/AUTHORS3
-rw-r--r--src/leveldb/Makefile6
-rw-r--r--src/leveldb/db/db_impl.cc41
-rw-r--r--src/leveldb/db/db_impl.h1
-rw-r--r--src/leveldb/db/db_test.cc85
-rw-r--r--src/leveldb/db/dbformat.cc2
-rw-r--r--src/leveldb/db/filename_test.cc2
-rw-r--r--src/leveldb/db/version_set.cc21
-rw-r--r--src/leveldb/include/leveldb/db.h2
-rw-r--r--src/leveldb/port/port_win.cc10
-rw-r--r--src/leveldb/table/block.cc13
-rw-r--r--src/leveldb/table/table.cc1
-rw-r--r--src/leveldb/table/table_test.cc30
-rw-r--r--src/leveldb/util/cache.cc5
-rw-r--r--src/leveldb/util/coding_test.cc2
-rw-r--r--src/leveldb/util/comparator.cc4
-rw-r--r--src/leveldb/util/env_posix.cc10
-rw-r--r--src/leveldb/util/hash.cc11
-rw-r--r--src/makefile.mingw2
-rw-r--r--src/net.cpp4
-rw-r--r--src/qt/bitcoin.cpp104
-rw-r--r--src/qt/bitcoinamountfield.cpp3
-rw-r--r--src/qt/bitcoingui.cpp4
-rw-r--r--src/qt/forms/intro.ui266
-rw-r--r--src/qt/guiutil.cpp7
-rw-r--r--src/qt/intro.cpp270
-rw-r--r--src/qt/intro.h67
-rw-r--r--src/qt/locale/bitcoin_en.ts102
-rw-r--r--src/qt/notificator.cpp2
-rw-r--r--src/qt/optionsmodel.cpp2
-rw-r--r--src/qt/paymentserver.cpp4
-rw-r--r--src/qt/splashscreen.cpp4
-rw-r--r--src/walletdb.cpp1
36 files changed, 990 insertions, 126 deletions
diff --git a/src/db.cpp b/src/db.cpp
index 93f3f5d8c4..03f46f3c26 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -8,7 +8,6 @@
#include "util.h"
#include "hash.h"
#include "addrman.h"
-#include <boost/version.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <openssl/rand.h>
diff --git a/src/init.cpp b/src/init.cpp
index f6b2c91b40..90dbad61b7 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -29,6 +29,7 @@
using namespace std;
using namespace boost;
+std::string strWalletFile;
CWallet* pwalletMain;
CClientUIInterface uiInterface;
@@ -169,6 +170,7 @@ std::string HelpMessage()
strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n";
strUsage += " -gen " + _("Generate coins (default: 0)") + "\n";
strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n";
+ strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n";
strUsage += " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n";
strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n";
strUsage += " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n";
@@ -237,12 +239,12 @@ std::string HelpMessage()
strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n";
strUsage += " -par=<n> " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n";
- strUsage += "\n"; _("Block creation options:") + "\n";
+ strUsage += "\n" + _("Block creation options:") + "\n";
strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n";
strUsage += " -blockmaxsize=<n> " + _("Set maximum block size in bytes (default: 250000)") + "\n";
strUsage += " -blockprioritysize=<n> " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n";
- strUsage += "\n"; _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n";
+ strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n";
strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n";
strUsage += " -rpcsslcertificatechainfile=<file.cert> " + _("Server certificate file (default: server.cert)") + "\n";
strUsage += " -rpcsslprivatekeyfile=<file.pem> " + _("Server private key (default: server.pem)") + "\n";
@@ -493,10 +495,16 @@ bool AppInit2(boost::thread_group& threadGroup)
InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction."));
}
+ strWalletFile = GetArg("-wallet", "wallet.dat");
+
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
std::string strDataDir = GetDataDir().string();
+ // Wallet file must be a plain filename without a directory
+ if (strWalletFile != boost::filesystem::basename(strWalletFile) + boost::filesystem::extension(strWalletFile))
+ return InitError(strprintf(_("Wallet %s resides outside data directory %s\n"), strWalletFile.c_str(), strDataDir.c_str()));
+
// Make sure only a single Bitcoin process is using the data directory.
boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist.
@@ -555,13 +563,13 @@ bool AppInit2(boost::thread_group& threadGroup)
if (GetBoolArg("-salvagewallet", false))
{
// Recover readable keypairs:
- if (!CWalletDB::Recover(bitdb, "wallet.dat", true))
+ if (!CWalletDB::Recover(bitdb, strWalletFile, true))
return false;
}
- if (filesystem::exists(GetDataDir() / "wallet.dat"))
+ if (filesystem::exists(GetDataDir() / strWalletFile))
{
- CDBEnv::VerifyResult r = bitdb.Verify("wallet.dat", CWalletDB::Recover);
+ CDBEnv::VerifyResult r = bitdb.Verify(strWalletFile, CWalletDB::Recover);
if (r == CDBEnv::RECOVER_OK)
{
string msg = strprintf(_("Warning: wallet.dat corrupt, data salvaged!"
@@ -785,6 +793,7 @@ bool AppInit2(boost::thread_group& threadGroup)
fReindex = true;
fRequestShutdown = false;
} else {
+ printf("Aborted block database rebuild. Exiting.\n");
return false;
}
} else {
@@ -838,7 +847,7 @@ bool AppInit2(boost::thread_group& threadGroup)
nStart = GetTimeMillis();
bool fFirstRun = true;
- pwalletMain = new CWallet("wallet.dat");
+ pwalletMain = new CWallet(strWalletFile);
DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
@@ -903,7 +912,7 @@ bool AppInit2(boost::thread_group& threadGroup)
pindexRescan = pindexGenesisBlock;
else
{
- CWalletDB walletdb("wallet.dat");
+ CWalletDB walletdb(strWalletFile);
CBlockLocator locator;
if (walletdb.ReadBestBlock(locator))
pindexRescan = locator.GetBlockIndex();
diff --git a/src/init.h b/src/init.h
index 5927670c83..a4d5a67252 100644
--- a/src/init.h
+++ b/src/init.h
@@ -7,6 +7,7 @@
#include "wallet.h"
+extern std::string strWalletFile;
extern CWallet* pwalletMain;
void StartShutdown();
diff --git a/src/leveldb/AUTHORS b/src/leveldb/AUTHORS
index 27a9407e52..fc40194ab9 100644
--- a/src/leveldb/AUTHORS
+++ b/src/leveldb/AUTHORS
@@ -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>
diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile
index 42c4952fec..38b9bf7729 100644
--- a/src/leveldb/Makefile
+++ b/src/leveldb/Makefile
@@ -42,6 +42,7 @@ TESTS = \
env_test \
filename_test \
filter_block_test \
+ issue178_test \
log_test \
memenv_test \
skiplist_test \
@@ -69,7 +70,7 @@ SHARED = $(SHARED1)
else
# Update db.h if you change these.
SHARED_MAJOR = 1
-SHARED_MINOR = 9
+SHARED_MINOR = 12
SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
SHARED2 = $(SHARED1).$(SHARED_MAJOR)
SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
@@ -146,6 +147,9 @@ filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
$(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
$(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc
index c9de169f29..af02467b33 100644
--- a/src/leveldb/db/db_impl.cc
+++ b/src/leveldb/db/db_impl.cc
@@ -35,6 +35,8 @@
namespace leveldb {
+const int kNumNonTableCacheFiles = 10;
+
// Information kept for every waiting writer
struct DBImpl::Writer {
Status status;
@@ -92,9 +94,9 @@ Options SanitizeOptions(const std::string& dbname,
Options result = src;
result.comparator = icmp;
result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL;
- ClipToRange(&result.max_open_files, 20, 50000);
- ClipToRange(&result.write_buffer_size, 64<<10, 1<<30);
- ClipToRange(&result.block_size, 1<<10, 4<<20);
+ ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000);
+ ClipToRange(&result.write_buffer_size, 64<<10, 1<<30);
+ ClipToRange(&result.block_size, 1<<10, 4<<20);
if (result.info_log == NULL) {
// Open a log file in the same directory as the db
src.env->CreateDir(dbname); // In case it does not exist
@@ -130,12 +132,13 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname)
log_(NULL),
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 +313,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))) {
+ 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 +622,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 +634,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();
}
}
@@ -805,6 +822,9 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact,
(unsigned long long) output_number,
(unsigned long long) current_entries,
(unsigned long long) current_bytes);
+
+ // rate-limit compaction file creation with a 100ms pause
+ env_->SleepForMicroseconds(100000);
}
}
return s;
@@ -1268,10 +1288,11 @@ Status DBImpl::MakeRoomForWrite(bool force) {
} else if (imm_ != NULL) {
// We have filled up the current memtable, but the previous
// one is still being compacted, so we wait.
+ Log(options_.info_log, "Current memtable full; waiting...\n");
bg_cv_.Wait();
} else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) {
// There are too many level-0 files.
- Log(options_.info_log, "waiting...\n");
+ Log(options_.info_log, "Too many L0 files; waiting...\n");
bg_cv_.Wait();
} else {
// Attempt to switch to a new memtable and trigger compaction of old
diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h
index bd29dd8055..3c8d711ae0 100644
--- a/src/leveldb/db/db_impl.h
+++ b/src/leveldb/db/db_impl.h
@@ -163,6 +163,7 @@ class DBImpl : public DB {
// Have we encountered a background error in paranoid mode?
Status bg_error_;
+ int consecutive_compaction_errors_;
// Per level compaction stats. stats_[level] stores the stats for
// compactions that produced data for the specified "level".
diff --git a/src/leveldb/db/db_test.cc b/src/leveldb/db/db_test.cc
index 684ea3bdbc..49aae04dbd 100644
--- a/src/leveldb/db/db_test.cc
+++ b/src/leveldb/db/db_test.cc
@@ -33,8 +33,11 @@ class AtomicCounter {
public:
AtomicCounter() : count_(0) { }
void Increment() {
+ IncrementBy(1);
+ }
+ void IncrementBy(int count) {
MutexLock l(&mu_);
- count_++;
+ count_ += count;
}
int Read() {
MutexLock l(&mu_);
@@ -45,6 +48,10 @@ class AtomicCounter {
count_ = 0;
}
};
+
+void DelayMilliseconds(int millis) {
+ Env::Default()->SleepForMicroseconds(millis * 1000);
+}
}
// Special Env used to delay background operations
@@ -69,6 +76,7 @@ class SpecialEnv : public EnvWrapper {
AtomicCounter random_read_counter_;
AtomicCounter sleep_counter_;
+ AtomicCounter sleep_time_counter_;
explicit SpecialEnv(Env* base) : EnvWrapper(base) {
delay_sstable_sync_.Release_Store(NULL);
@@ -103,7 +111,7 @@ class SpecialEnv : public EnvWrapper {
Status Flush() { return base_->Flush(); }
Status Sync() {
while (env_->delay_sstable_sync_.Acquire_Load() != NULL) {
- env_->SleepForMicroseconds(100000);
+ DelayMilliseconds(100);
}
return base_->Sync();
}
@@ -174,8 +182,9 @@ class SpecialEnv : public EnvWrapper {
virtual void SleepForMicroseconds(int micros) {
sleep_counter_.Increment();
- target()->SleepForMicroseconds(micros);
+ sleep_time_counter_.IncrementBy(micros);
}
+
};
class DBTest {
@@ -461,6 +470,20 @@ class DBTest {
}
return result;
}
+
+ bool DeleteAnSSTFile() {
+ std::vector<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());
diff --git a/src/leveldb/db/dbformat.cc b/src/leveldb/db/dbformat.cc
index 28e11b398d..20a7ca4462 100644
--- a/src/leveldb/db/dbformat.cc
+++ b/src/leveldb/db/dbformat.cc
@@ -26,7 +26,7 @@ std::string ParsedInternalKey::DebugString() const {
(unsigned long long) sequence,
int(type));
std::string result = "'";
- result += user_key.ToString();
+ result += EscapeString(user_key.ToString());
result += buf;
return result;
}
diff --git a/src/leveldb/db/filename_test.cc b/src/leveldb/db/filename_test.cc
index 47353d6c9a..5a26da4728 100644
--- a/src/leveldb/db/filename_test.cc
+++ b/src/leveldb/db/filename_test.cc
@@ -70,7 +70,7 @@ TEST(FileNameTest, Parse) {
for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) {
std::string f = errors[i];
ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f;
- };
+ }
}
TEST(FileNameTest, Construction) {
diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc
index 7d0a5de2b9..4fd1ddef21 100644
--- a/src/leveldb/db/version_set.cc
+++ b/src/leveldb/db/version_set.cc
@@ -1331,14 +1331,19 @@ Compaction* VersionSet::CompactRange(
}
// Avoid compacting too much in one shot in case the range is large.
- const uint64_t limit = MaxFileSizeForLevel(level);
- uint64_t total = 0;
- for (size_t i = 0; i < inputs.size(); i++) {
- uint64_t s = inputs[i]->file_size;
- total += s;
- if (total >= limit) {
- inputs.resize(i + 1);
- break;
+ // But we cannot do this for level-0 since level-0 files can overlap
+ // and we must not pick one file and drop another older file if the
+ // two files overlap.
+ if (level > 0) {
+ const uint64_t limit = MaxFileSizeForLevel(level);
+ uint64_t total = 0;
+ for (size_t i = 0; i < inputs.size(); i++) {
+ uint64_t s = inputs[i]->file_size;
+ total += s;
+ if (total >= limit) {
+ inputs.resize(i + 1);
+ break;
+ }
}
}
diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h
index 29d3674479..da8b11a8c0 100644
--- a/src/leveldb/include/leveldb/db.h
+++ b/src/leveldb/include/leveldb/db.h
@@ -14,7 +14,7 @@ namespace leveldb {
// Update Makefile if you change these
static const int kMajorVersion = 1;
-static const int kMinorVersion = 9;
+static const int kMinorVersion = 12;
struct Options;
struct ReadOptions;
diff --git a/src/leveldb/port/port_win.cc b/src/leveldb/port/port_win.cc
index 99c1d8e346..1b0f060a19 100644
--- a/src/leveldb/port/port_win.cc
+++ b/src/leveldb/port/port_win.cc
@@ -109,12 +109,10 @@ void CondVar::Signal() {
void CondVar::SignalAll() {
wait_mtx_.Lock();
- for(long i = 0; i < waiting_; ++i) {
- ::ReleaseSemaphore(sem1_, 1, NULL);
- while(waiting_ > 0) {
- --waiting_;
- ::WaitForSingleObject(sem2_, INFINITE);
- }
+ ::ReleaseSemaphore(sem1_, waiting_, NULL);
+ while(waiting_ > 0) {
+ --waiting_;
+ ::WaitForSingleObject(sem2_, INFINITE);
}
wait_mtx_.Unlock();
}
diff --git a/src/leveldb/table/block.cc b/src/leveldb/table/block.cc
index ab83c1112c..79ea9d9ee5 100644
--- a/src/leveldb/table/block.cc
+++ b/src/leveldb/table/block.cc
@@ -16,7 +16,7 @@
namespace leveldb {
inline uint32_t Block::NumRestarts() const {
- assert(size_ >= 2*sizeof(uint32_t));
+ assert(size_ >= sizeof(uint32_t));
return DecodeFixed32(data_ + size_ - sizeof(uint32_t));
}
@@ -27,11 +27,12 @@ Block::Block(const BlockContents& contents)
if (size_ < sizeof(uint32_t)) {
size_ = 0; // Error marker
} else {
- restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t);
- if (restart_offset_ > size_ - sizeof(uint32_t)) {
- // The size is too small for NumRestarts() and therefore
- // restart_offset_ wrapped around.
+ size_t max_restarts_allowed = (size_-sizeof(uint32_t)) / sizeof(uint32_t);
+ if (NumRestarts() > max_restarts_allowed) {
+ // The size is too small for NumRestarts()
size_ = 0;
+ } else {
+ restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t);
}
}
}
@@ -253,7 +254,7 @@ class Block::Iter : public Iterator {
};
Iterator* Block::NewIterator(const Comparator* cmp) {
- if (size_ < 2*sizeof(uint32_t)) {
+ if (size_ < sizeof(uint32_t)) {
return NewErrorIterator(Status::Corruption("bad block contents"));
}
const uint32_t num_restarts = NumRestarts();
diff --git a/src/leveldb/table/table.cc b/src/leveldb/table/table.cc
index dbd6d3a1bf..71c1756e5f 100644
--- a/src/leveldb/table/table.cc
+++ b/src/leveldb/table/table.cc
@@ -228,7 +228,6 @@ Status Table::InternalGet(const ReadOptions& options, const Slice& k,
!filter->KeyMayMatch(handle.offset(), k)) {
// Not found
} else {
- Slice handle = iiter->value();
Iterator* block_iter = BlockReader(this, options, iiter->value());
block_iter->Seek(k);
if (block_iter->Valid()) {
diff --git a/src/leveldb/table/table_test.cc b/src/leveldb/table/table_test.cc
index 57cea25334..c723bf84cf 100644
--- a/src/leveldb/table/table_test.cc
+++ b/src/leveldb/table/table_test.cc
@@ -644,6 +644,36 @@ class Harness {
Constructor* constructor_;
};
+// Test empty table/block.
+TEST(Harness, Empty) {
+ for (int i = 0; i < kNumTestArgs; i++) {
+ Init(kTestArgList[i]);
+ Random rnd(test::RandomSeed() + 1);
+ Test(&rnd);
+ }
+}
+
+// Special test for a block with no restart entries. The C++ leveldb
+// code never generates such blocks, but the Java version of leveldb
+// seems to.
+TEST(Harness, ZeroRestartPointsInBlock) {
+ char data[sizeof(uint32_t)];
+ memset(data, 0, sizeof(data));
+ BlockContents contents;
+ contents.data = Slice(data, sizeof(data));
+ contents.cachable = false;
+ contents.heap_allocated = false;
+ Block block(contents);
+ Iterator* iter = block.NewIterator(BytewiseComparator());
+ iter->SeekToFirst();
+ ASSERT_TRUE(!iter->Valid());
+ iter->SeekToLast();
+ ASSERT_TRUE(!iter->Valid());
+ iter->Seek("foo");
+ ASSERT_TRUE(!iter->Valid());
+ delete iter;
+}
+
// Test the empty key
TEST(Harness, SimpleEmptyKey) {
for (int i = 0; i < kNumTestArgs; i++) {
diff --git a/src/leveldb/util/cache.cc b/src/leveldb/util/cache.cc
index 24f1f63f4f..8b197bc02a 100644
--- a/src/leveldb/util/cache.cc
+++ b/src/leveldb/util/cache.cc
@@ -116,7 +116,6 @@ class HandleTable {
LRUHandle* h = list_[i];
while (h != NULL) {
LRUHandle* next = h->next_hash;
- Slice key = h->key();
uint32_t hash = h->hash;
LRUHandle** ptr = &new_list[hash & (new_length - 1)];
h->next_hash = *ptr;
@@ -160,7 +159,6 @@ class LRUCache {
// mutex_ protects the following state.
port::Mutex mutex_;
size_t usage_;
- uint64_t last_id_;
// Dummy head of LRU list.
// lru.prev is newest entry, lru.next is oldest entry.
@@ -170,8 +168,7 @@ class LRUCache {
};
LRUCache::LRUCache()
- : usage_(0),
- last_id_(0) {
+ : usage_(0) {
// Make empty circular linked list
lru_.next = &lru_;
lru_.prev = &lru_;
diff --git a/src/leveldb/util/coding_test.cc b/src/leveldb/util/coding_test.cc
index 2c52b17b60..fb5726e335 100644
--- a/src/leveldb/util/coding_test.cc
+++ b/src/leveldb/util/coding_test.cc
@@ -109,7 +109,7 @@ TEST(Coding, Varint64) {
values.push_back(power);
values.push_back(power-1);
values.push_back(power+1);
- };
+ }
std::string s;
for (int i = 0; i < values.size(); i++) {
diff --git a/src/leveldb/util/comparator.cc b/src/leveldb/util/comparator.cc
index 4b7b5724ef..6cc319242e 100644
--- a/src/leveldb/util/comparator.cc
+++ b/src/leveldb/util/comparator.cc
@@ -66,7 +66,7 @@ class BytewiseComparatorImpl : public Comparator {
};
} // namespace
-static port::OnceType once = LEVELDB_ONCE_INIT;
+static port::OnceType once_comparator = LEVELDB_ONCE_INIT;
static const Comparator* bytewise;
static void InitModule() {
@@ -74,7 +74,7 @@ static void InitModule() {
}
const Comparator* BytewiseComparator() {
- port::InitOnce(&once, InitModule);
+ port::InitOnce(&once_comparator, InitModule);
return bytewise;
}
diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc
index db81f56d11..6badfdc230 100644
--- a/src/leveldb/util/env_posix.cc
+++ b/src/leveldb/util/env_posix.cc
@@ -386,7 +386,7 @@ class PosixEnv : public Env {
PosixEnv();
virtual ~PosixEnv() {
fprintf(stderr, "Destroying Env::Default()\n");
- exit(1);
+ abort();
}
virtual Status NewSequentialFile(const std::string& fname,
@@ -467,7 +467,7 @@ class PosixEnv : public Env {
result = IOError(fname, errno);
}
return result;
- };
+ }
virtual Status CreateDir(const std::string& name) {
Status result;
@@ -475,7 +475,7 @@ class PosixEnv : public Env {
result = IOError(name, errno);
}
return result;
- };
+ }
virtual Status DeleteDir(const std::string& name) {
Status result;
@@ -483,7 +483,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 +589,7 @@ class PosixEnv : public Env {
void PthreadCall(const char* label, int result) {
if (result != 0) {
fprintf(stderr, "pthread %s: %s\n", label, strerror(result));
- exit(1);
+ abort();
}
}
diff --git a/src/leveldb/util/hash.cc b/src/leveldb/util/hash.cc
index ba1818082d..07cf022060 100644
--- a/src/leveldb/util/hash.cc
+++ b/src/leveldb/util/hash.cc
@@ -6,6 +6,13 @@
#include "util/coding.h"
#include "util/hash.h"
+// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
+// between switch labels. The real definition should be provided externally.
+// This one is a fallback version for unsupported compilers.
+#ifndef FALLTHROUGH_INTENDED
+#define FALLTHROUGH_INTENDED do { } while (0)
+#endif
+
namespace leveldb {
uint32_t Hash(const char* data, size_t n, uint32_t seed) {
@@ -28,10 +35,10 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) {
switch (limit - data) {
case 3:
h += data[2] << 16;
- // fall through
+ FALLTHROUGH_INTENDED;
case 2:
h += data[1] << 8;
- // fall through
+ FALLTHROUGH_INTENDED;
case 1:
h += data[0];
h *= m;
diff --git a/src/makefile.mingw b/src/makefile.mingw
index 3659f52040..002e36d3e2 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -21,7 +21,7 @@ USE_UPNP:=-
USE_IPV6:=1
DEPSDIR?=/usr/local
-BOOST_SUFFIX?=-mgw46-mt-sd-1_52
+BOOST_SUFFIX?=-mgw46-mt-s-1_52
INCLUDEPATHS= \
-I"$(CURDIR)" \
diff --git a/src/net.cpp b/src/net.cpp
index 5418c3de40..bd9aa1f50f 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1598,8 +1598,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 */;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 76e88b36a7..4e9180b88c 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -2,8 +2,6 @@
* W.J. van der Laan 2011-2012
*/
-#include <QApplication>
-
#include "bitcoingui.h"
#include "clientmodel.h"
#include "walletmodel.h"
@@ -15,7 +13,10 @@
#include "ui_interface.h"
#include "paymentserver.h"
#include "splashscreen.h"
+#include "intro.h"
+#undef loop /* Todo: ugh, remove this when the #define loop is gone from util.h */
+#include <QApplication>
#include <QMessageBox>
#if QT_VERSION < 0x050000
#include <QTextCodec>
@@ -24,6 +25,7 @@
#include <QTimer>
#include <QTranslator>
#include <QLibraryInfo>
+#include <QSettings>
#if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED)
#define _BITCOIN_QT_PLUGINS_INCLUDED
@@ -110,6 +112,46 @@ static void handleRunawayException(std::exception *e)
exit(1);
}
+/** Set up translations */
+static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator)
+{
+ QSettings settings;
+
+ // Get desired locale (e.g. "de_DE")
+ // 1) System default language
+ QString lang_territory = QLocale::system().name();
+ // 2) Language from QSettings
+ QString lang_territory_qsettings = settings.value("language", "").toString();
+ if(!lang_territory_qsettings.isEmpty())
+ lang_territory = lang_territory_qsettings;
+ // 3) -lang command line argument
+ lang_territory = QString::fromStdString(GetArg("-lang", lang_territory.toStdString()));
+
+ // Convert to "de" only by truncating "_DE"
+ QString lang = lang_territory;
+ lang.truncate(lang_territory.lastIndexOf('_'));
+
+ // Load language files for configured locale:
+ // - First load the translator for the base language, without territory
+ // - Then load the more specific locale translator
+
+ // Load e.g. qt_de.qm
+ if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
+ QApplication::installTranslator(&qtTranslatorBase);
+
+ // Load e.g. qt_de_DE.qm
+ if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
+ QApplication::installTranslator(&qtTranslator);
+
+ // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc)
+ if (translatorBase.load(lang, ":/translations/"))
+ QApplication::installTranslator(&translatorBase);
+
+ // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc)
+ if (translator.load(lang_territory, ":/translations/"))
+ QApplication::installTranslator(&translator);
+}
+
#ifndef BITCOIN_QT_TEST
int main(int argc, char *argv[])
{
@@ -130,6 +172,22 @@ int main(int argc, char *argv[])
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType< bool* >();
+ // Application identification (must be set before OptionsModel is initialized,
+ // as it is used to locate QSettings)
+ QApplication::setOrganizationName("Bitcoin");
+ QApplication::setOrganizationDomain("bitcoin.org");
+ if (GetBoolArg("-testnet", false)) // Separate UI settings for testnet
+ QApplication::setApplicationName("Bitcoin-Qt-testnet");
+ else
+ QApplication::setApplicationName("Bitcoin-Qt");
+
+ // Now that QSettings are accessible, initialize translations
+ QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
+ initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
+
+ // User language is set up: pick a data directory
+ Intro::pickDataDirectory();
+
// Do this early as we don't want to bother initializing if we are just calling IPC
// ... but do it after creating app, so QCoreApplication::arguments is initialized:
if (PaymentServer::ipcSendCommandLine())
@@ -142,53 +200,15 @@ int main(int argc, char *argv[])
// ... then bitcoin.conf:
if (!boost::filesystem::is_directory(GetDataDir(false)))
{
- // This message can not be translated, as translation is not initialized yet
- // (which not yet possible because lang=XX can be overridden in bitcoin.conf in the data directory)
- QMessageBox::critical(0, "Bitcoin",
- QString("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"])));
+ QMessageBox::critical(0, QObject::tr("Bitcoin"),
+ QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"])));
return 1;
}
ReadConfigFile(mapArgs, mapMultiArgs);
- // Application identification (must be set before OptionsModel is initialized,
- // as it is used to locate QSettings)
- QApplication::setOrganizationName("Bitcoin");
- QApplication::setOrganizationDomain("bitcoin.org");
- if (GetBoolArg("-testnet", false)) // Separate UI settings for testnet
- QApplication::setApplicationName("Bitcoin-Qt-testnet");
- else
- QApplication::setApplicationName("Bitcoin-Qt");
-
// ... then GUI settings:
OptionsModel optionsModel;
- // Get desired locale (e.g. "de_DE") from command line or use system locale
- QString lang_territory = QString::fromStdString(GetArg("-lang", QLocale::system().name().toStdString()));
- QString lang = lang_territory;
- // Convert to "de" only by truncating "_DE"
- lang.truncate(lang_territory.lastIndexOf('_'));
-
- QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
- // Load language files for configured locale:
- // - First load the translator for the base language, without territory
- // - Then load the more specific locale translator
-
- // Load e.g. qt_de.qm
- if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
- app.installTranslator(&qtTranslatorBase);
-
- // Load e.g. qt_de_DE.qm
- if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
- app.installTranslator(&qtTranslator);
-
- // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc)
- if (translatorBase.load(lang, ":/translations/"))
- app.installTranslator(&translatorBase);
-
- // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc)
- if (translator.load(lang_territory, ":/translations/"))
- app.installTranslator(&translator);
-
// Subscribe to global signals from core
uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox);
uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee);
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index b502505f31..eeb6fe89bb 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -4,10 +4,11 @@
#include "bitcoinunits.h"
#include "guiconstants.h"
+#include <QApplication>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QDoubleSpinBox>
-#include <QApplication>
+
#include <qmath.h> // for qPow()
BitcoinAmountField::BitcoinAmountField(QWidget *parent):
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 190da6caf8..11767cf312 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -5,8 +5,6 @@
* The Bitcoin Developers 2011-2012
*/
-#include <QApplication>
-
#include "bitcoingui.h"
#include "transactiontablemodel.h"
@@ -30,6 +28,8 @@
#include "macdockiconhandler.h"
#endif
+#undef loop /* Todo: ugh, remove this when the #define loop is gone from util.h */
+#include <QApplication>
#include <QMenuBar>
#include <QMenu>
#include <QIcon>
diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui
new file mode 100644
index 0000000000..0f6ae5a7d0
--- /dev/null
+++ b/src/qt/forms/intro.ui
@@ -0,0 +1,266 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Intro</class>
+ <widget class="QDialog" name="Intro">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>674</width>
+ <height>363</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Welcome</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="styleSheet">
+ <string notr="true">QLabel { font-style:italic; }</string>
+ </property>
+ <property name="text">
+ <string>Welcome to Bitcoin-Qt.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Minimum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>15</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="sizeWarningLabel">
+ <property name="text">
+ <string>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="dataDirDefault">
+ <property name="text">
+ <string>Use the default data directory</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="dataDirCustom">
+ <property name="text">
+ <string>Use a custom data directory:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>60</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="dataDirectory"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="ellipsisButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>30</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string notr="true">…</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="freeSpace">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="errorMessage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>Intro</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>Intro</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 88a6e7226e..521d9bddd8 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -1,5 +1,3 @@
-#include <QApplication>
-
#include "guiutil.h"
#include "bitcoinaddressvalidator.h"
@@ -9,6 +7,8 @@
#include "util.h"
#include "init.h"
+#undef loop /* Todo: ugh, remove this when the #define loop is gone from util.h */
+#include <QApplication>
#include <QDateTime>
#include <QDoubleValidator>
#include <QFont>
@@ -500,7 +500,8 @@ HelpMessageBox::HelpMessageBox(QWidget *parent) :
uiOptions = tr("UI options") + ":\n" +
" -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
" -min " + tr("Start minimized") + "\n" +
- " -splash " + tr("Show splash screen on startup (default: 1)") + "\n";
+ " -splash " + tr("Show splash screen on startup (default: 1)") + "\n" +
+ " -choosedatadir " + tr("Choose data directory on startup (default: 0)") + "\n";
setWindowTitle(tr("Bitcoin-Qt"));
setTextFormat(Qt::PlainText);
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
new file mode 100644
index 0000000000..51f3c812e4
--- /dev/null
+++ b/src/qt/intro.cpp
@@ -0,0 +1,270 @@
+#include "intro.h"
+#include "ui_intro.h"
+#include "util.h"
+
+#include <QFileDialog>
+#include <QSettings>
+#include <QMessageBox>
+
+#include <boost/filesystem.hpp>
+
+/* Minimum free space (in bytes) needed for data directory */
+static const uint64 GB_BYTES = 1000000000LL;
+static const uint64 BLOCK_CHAIN_SIZE = 10LL * GB_BYTES;
+
+/* Check free space asynchronously to prevent hanging the UI thread.
+
+ Up to one request to check a path is in flight to this thread; when the check()
+ function runs, the current path is requested from the associated Intro object.
+ The reply is sent back through a signal.
+
+ This ensures that no queue of checking requests is built up while the user is
+ still entering the path, and that always the most recently entered path is checked as
+ soon as the thread becomes available.
+*/
+class FreespaceChecker : public QObject
+{
+ Q_OBJECT
+public:
+ FreespaceChecker(Intro *intro);
+
+ enum Status {
+ ST_OK,
+ ST_ERROR
+ };
+
+public slots:
+ void check();
+
+signals:
+ void reply(int status, const QString &message, quint64 available);
+
+private:
+ Intro *intro;
+};
+
+#include "intro.moc"
+
+FreespaceChecker::FreespaceChecker(Intro *intro)
+{
+ this->intro = intro;
+}
+
+void FreespaceChecker::check()
+{
+ namespace fs = boost::filesystem;
+ QString dataDirStr = intro->getPathToCheck();
+ fs::path dataDir = fs::path(dataDirStr.toStdString());
+ uint64 freeBytesAvailable = 0;
+ int replyStatus = ST_OK;
+ QString replyMessage = tr("A new data directory will be created.");
+
+ /* Find first parent that exists, so that fs::space does not fail */
+ fs::path parentDir = dataDir;
+ while(parentDir.has_parent_path() && !fs::exists(parentDir))
+ {
+ parentDir = parentDir.parent_path();
+ }
+
+ try {
+ freeBytesAvailable = fs::space(parentDir).available;
+ if(fs::exists(dataDir))
+ {
+ if(fs::is_directory(dataDir))
+ {
+ QString separator = QDir::toNativeSeparators("/");
+ replyStatus = ST_OK;
+ replyMessage = tr("Directory already exists. Add <code>%1name</code> if you intend to create a new directory here.").arg(separator);
+ } else {
+ replyStatus = ST_ERROR;
+ replyMessage = tr("Path already exists, and is not a directory.");
+ }
+ }
+ } catch(fs::filesystem_error &e)
+ {
+ /* Parent directory does not exist or is not accessible */
+ replyStatus = ST_ERROR;
+ replyMessage = tr("Cannot create data directory here.");
+ }
+ emit reply(replyStatus, replyMessage, freeBytesAvailable);
+}
+
+
+Intro::Intro(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::Intro),
+ thread(0),
+ signalled(false)
+{
+ ui->setupUi(this);
+ ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(BLOCK_CHAIN_SIZE/GB_BYTES));
+ startThread();
+}
+
+Intro::~Intro()
+{
+ delete ui;
+ /* Ensure thread is finished before it is deleted */
+ emit stopThread();
+ thread->wait();
+}
+
+QString Intro::getDataDirectory()
+{
+ return ui->dataDirectory->text();
+}
+
+void Intro::setDataDirectory(const QString &dataDir)
+{
+ ui->dataDirectory->setText(dataDir);
+ if(dataDir == getDefaultDataDirectory())
+ {
+ ui->dataDirDefault->setChecked(true);
+ ui->dataDirectory->setEnabled(false);
+ ui->ellipsisButton->setEnabled(false);
+ } else {
+ ui->dataDirCustom->setChecked(true);
+ ui->dataDirectory->setEnabled(true);
+ ui->ellipsisButton->setEnabled(true);
+ }
+}
+
+QString Intro::getDefaultDataDirectory()
+{
+ return QString::fromStdString(GetDefaultDataDir().string());
+}
+
+void Intro::pickDataDirectory()
+{
+ namespace fs = boost::filesystem;;
+ QSettings settings;
+ /* If data directory provided on command line, no need to look at settings
+ or show a picking dialog */
+ if(!GetArg("-datadir", "").empty())
+ return;
+ /* 1) Default data directory for operating system */
+ QString dataDir = getDefaultDataDirectory();
+ /* 2) Allow QSettings to override default dir */
+ dataDir = settings.value("strDataDir", dataDir).toString();
+
+ if(!fs::exists(dataDir.toStdString()) || GetBoolArg("-choosedatadir", false))
+ {
+ /* If current default data directory does not exist, let the user choose one */
+ Intro intro;
+ intro.setDataDirectory(dataDir);
+ while(true)
+ {
+ if(!intro.exec())
+ {
+ /* Cancel clicked */
+ exit(0);
+ }
+ dataDir = intro.getDataDirectory();
+ try {
+ fs::create_directory(dataDir.toStdString());
+ break;
+ } catch(fs::filesystem_error &e) {
+ QMessageBox::critical(0, QObject::tr("Bitcoin"),
+ QObject::tr("Error: Specified data directory \"%1\" can not be created.").arg(dataDir));
+ /* fall through, back to choosing screen */
+ }
+ }
+
+ settings.setValue("strDataDir", dataDir);
+ }
+ SoftSetArg("-datadir", dataDir.toStdString());
+}
+
+void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
+{
+ switch(status)
+ {
+ case FreespaceChecker::ST_OK:
+ ui->errorMessage->setText(message);
+ ui->errorMessage->setStyleSheet("");
+ break;
+ case FreespaceChecker::ST_ERROR:
+ ui->errorMessage->setText(tr("Error") + ": " + message);
+ ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
+ break;
+ }
+ /* Indicate number of bytes available */
+ if(status == FreespaceChecker::ST_ERROR)
+ {
+ ui->freeSpace->setText("");
+ } else {
+ QString freeString = QString::number(bytesAvailable/GB_BYTES) + tr("GB of free space available");
+ if(bytesAvailable < BLOCK_CHAIN_SIZE)
+ {
+ freeString += " " + tr("(of %1GB needed)").arg(BLOCK_CHAIN_SIZE/GB_BYTES);
+ ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
+ } else {
+ ui->freeSpace->setStyleSheet("");
+ }
+ ui->freeSpace->setText(freeString+".");
+ }
+ /* Don't allow confirm in ERROR state */
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
+}
+
+void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
+{
+ /* Disable OK button until check result comes in */
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+ checkPath(dataDirStr);
+}
+
+void Intro::on_ellipsisButton_clicked()
+{
+ QString dir = QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text());
+ if(!dir.isEmpty())
+ ui->dataDirectory->setText(dir);
+}
+
+void Intro::on_dataDirDefault_clicked()
+{
+ setDataDirectory(getDefaultDataDirectory());
+}
+
+void Intro::on_dataDirCustom_clicked()
+{
+ ui->dataDirectory->setEnabled(true);
+ ui->ellipsisButton->setEnabled(true);
+}
+
+void Intro::startThread()
+{
+ thread = new QThread(this);
+ FreespaceChecker *executor = new FreespaceChecker(this);
+ executor->moveToThread(thread);
+
+ connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64)));
+ connect(this, SIGNAL(requestCheck()), executor, SLOT(check()));
+ /* make sure executor object is deleted in its own thread */
+ connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
+ connect(this, SIGNAL(stopThread()), thread, SLOT(quit()));
+
+ thread->start();
+}
+
+void Intro::checkPath(const QString &dataDir)
+{
+ mutex.lock();
+ pathToCheck = dataDir;
+ if(!signalled)
+ {
+ signalled = true;
+ emit requestCheck();
+ }
+ mutex.unlock();
+}
+
+QString Intro::getPathToCheck()
+{
+ QString retval;
+ mutex.lock();
+ retval = pathToCheck;
+ signalled = false; /* new request can be queued now */
+ mutex.unlock();
+ return retval;
+}
diff --git a/src/qt/intro.h b/src/qt/intro.h
new file mode 100644
index 0000000000..b246c65a82
--- /dev/null
+++ b/src/qt/intro.h
@@ -0,0 +1,67 @@
+#ifndef INTRO_H
+#define INTRO_H
+
+#include <QDialog>
+#include <QThread>
+#include <QMutex>
+
+namespace Ui {
+class Intro;
+}
+class FreespaceChecker;
+
+/** Introduction screen (pre-GUI startup).
+ Allows the user to choose a data directory,
+ in which the wallet and block chain will be stored.
+ */
+class Intro : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit Intro(QWidget *parent = 0);
+ ~Intro();
+
+ QString getDataDirectory();
+ void setDataDirectory(const QString &dataDir);
+
+ /**
+ * Determine data directory. Let the user choose if the current one doesn't exist.
+ *
+ * @note do NOT call global GetDataDir() before calling this function, this
+ * will cause the wrong path to be cached.
+ */
+ static void pickDataDirectory();
+
+ /**
+ * Determine default data directory for operating system.
+ */
+ static QString getDefaultDataDirectory();
+signals:
+ void requestCheck();
+ void stopThread();
+
+public slots:
+ void setStatus(int status, const QString &message, quint64 bytesAvailable);
+
+private slots:
+ void on_dataDirectory_textChanged(const QString &arg1);
+ void on_ellipsisButton_clicked();
+ void on_dataDirDefault_clicked();
+ void on_dataDirCustom_clicked();
+
+private:
+ Ui::Intro *ui;
+ QThread *thread;
+ QMutex mutex;
+ bool signalled;
+ QString pathToCheck;
+
+ void startThread();
+ void checkPath(const QString &dataDir);
+ QString getPathToCheck();
+
+ friend class FreespaceChecker;
+};
+
+#endif // INTRO_H
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index 7628b39bd3..61af88d056 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="en">
-<defaultcodec>UTF-8</defaultcodec>
<context>
<name>AboutDialog</name>
<message>
@@ -689,7 +688,7 @@ Address: %4
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+109"/>
+ <location filename="../bitcoin.cpp" line="+111"/>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
<translation>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</translation>
</message>
@@ -771,15 +770,38 @@ Address: %4
</message>
</context>
<context>
+ <name>FreespaceChecker</name>
+ <message>
+ <location filename="../intro.cpp" line="+60"/>
+ <source>A new data directory will be created.</source>
+ <translation>A new data directory will be created.</translation>
+ </message>
+ <message>
+ <location line="+17"/>
+ <source>Directory already exists. Add &lt;code&gt;%1name&lt;/code&gt; if you intend to create a new directory here.</source>
+ <translation>Directory already exists. Add &lt;code&gt;%1name&lt;/code&gt; if you intend to create a new directory here.</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Path already exists, and is not a directory.</source>
+ <translation>Path already exists, and is not a directory.</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Cannot create data directory here.</source>
+ <translation>Cannot create data directory here.</translation>
+ </message>
+</context>
+<context>
<name>GUIUtil::HelpMessageBox</name>
<message>
<location filename="../guiutil.cpp" line="+493"/>
- <location line="+12"/>
+ <location line="+13"/>
<source>Bitcoin-Qt</source>
<translation>Bitcoin-Qt</translation>
</message>
<message>
- <location line="-12"/>
+ <location line="-13"/>
<source>version</source>
<translation>version</translation>
</message>
@@ -813,6 +835,59 @@ Address: %4
<source>Show splash screen on startup (default: 1)</source>
<translation>Show splash screen on startup (default: 1)</translation>
</message>
+ <message>
+ <location line="+1"/>
+ <source>Choose data directory on startup (default: 0)</source>
+ <translation>Choose data directory on startup (default: 0)</translation>
+ </message>
+</context>
+<context>
+ <name>Intro</name>
+ <message>
+ <location filename="../forms/intro.ui" line="+14"/>
+ <source>Welcome</source>
+ <translation>Welcome</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Welcome to Bitcoin-Qt.</source>
+ <translation>Welcome to Bitcoin-Qt.</translation>
+ </message>
+ <message>
+ <location line="+26"/>
+ <source>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</source>
+ <translation>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</source>
+ <translation>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Use the default data directory</source>
+ <translation>Use the default data directory</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Use a custom data directory:</source>
+ <translation>Use a custom data directory:</translation>
+ </message>
+ <message>
+ <location filename="../intro.cpp" line="+100"/>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>GB of free space available</source>
+ <translation>GB of free space available</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>(of %1GB needed)</source>
+ <translation>(of %1GB needed)</translation>
+ </message>
</context>
<context>
<name>OptionsDialog</name>
@@ -1103,6 +1178,25 @@ Address: %4
</message>
</context>
<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../bitcoin.cpp" line="+92"/>
+ <location filename="../intro.cpp" line="-32"/>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Error: Specified data directory &quot;%1&quot; does not exist.</source>
+ <translation>Error: Specified data directory &quot;%1&quot; does not exist.</translation>
+ </message>
+ <message>
+ <location filename="../intro.cpp" line="+1"/>
+ <source>Error: Specified data directory &quot;%1&quot; can not be created.</source>
+ <translation>Error: Specified data directory &quot;%1&quot; can not be created.</translation>
+ </message>
+</context>
+<context>
<name>QRCodeDialog</name>
<message>
<location filename="../forms/qrcodedialog.ui" line="+14"/>
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index 7cfaef6079..903c54b39d 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -1,9 +1,9 @@
#include "notificator.h"
+#include <QApplication>
#include <QMetaType>
#include <QVariant>
#include <QIcon>
-#include <QApplication>
#include <QStyle>
#include <QByteArray>
#include <QSystemTrayIcon>
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 6b1b4e3d8e..7ebe5b4755 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -89,7 +89,7 @@ bool OptionsModel::Upgrade()
settings.setValue("bImportFinished", true);
// Move settings from old wallet.dat (if any):
- CWalletDB walletdb("wallet.dat");
+ CWalletDB walletdb(strWalletFile);
QList<QString> intOptions;
intOptions << "nDisplayUnit" << "nTransactionFee";
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 0b0bce55bb..8178065bcc 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -2,14 +2,14 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <QApplication>
-
#include "paymentserver.h"
#include "guiconstants.h"
#include "ui_interface.h"
#include "util.h"
+#undef loop /* Todo: ugh, remove this when the #define loop is gone from util.h */
+#include <QApplication>
#include <QByteArray>
#include <QDataStream>
#include <QDebug>
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index e400278123..43430a858e 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -2,9 +2,9 @@
#include "clientversion.h"
#include "util.h"
-#include <QPainter>
-#undef loop /* ugh, remove this when the #define loop is gone from util.h */
+#undef loop /* Todo: ugh, remove this when the #define loop is gone from util.h */
#include <QApplication>
+#include <QPainter>
SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) :
QSplashScreen(pixmap, f)
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
index 702e219a5b..7aad779767 100644
--- a/src/walletdb.cpp
+++ b/src/walletdb.cpp
@@ -5,6 +5,7 @@
#include "walletdb.h"
#include "wallet.h"
+#include <boost/version.hpp>
#include <boost/filesystem.hpp>
using namespace std;