diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bitcoinrpc.cpp | 1 | ||||
-rw-r--r-- | src/bitcoinrpc.h | 1 | ||||
-rw-r--r-- | src/db.cpp | 1 | ||||
-rw-r--r-- | src/init.cpp | 5 | ||||
-rw-r--r-- | src/key.cpp | 5 | ||||
-rw-r--r-- | src/main.cpp | 37 | ||||
-rw-r--r-- | src/main.h | 2 | ||||
-rw-r--r-- | src/makefile.mingw | 2 | ||||
-rw-r--r-- | src/net.cpp | 4 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 100 | ||||
-rw-r--r-- | src/qt/forms/intro.ui | 266 | ||||
-rw-r--r-- | src/qt/guiutil.cpp | 3 | ||||
-rw-r--r-- | src/qt/intro.cpp | 270 | ||||
-rw-r--r-- | src/qt/intro.h | 67 | ||||
-rw-r--r-- | src/rpcblockchain.cpp | 9 | ||||
-rw-r--r-- | src/test/script_P2SH_tests.cpp | 8 | ||||
-rw-r--r-- | src/test/transaction_tests.cpp | 9 | ||||
-rw-r--r-- | src/test/util_tests.cpp | 34 | ||||
-rw-r--r-- | src/version.cpp | 2 | ||||
-rw-r--r-- | src/walletdb.cpp | 1 |
20 files changed, 728 insertions, 99 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 5908126200..11fac42213 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -195,6 +195,7 @@ static const CRPCCommand vRPCCommands[] = { "help", &help, true, true }, { "stop", &stop, true, true }, { "getblockcount", &getblockcount, true, false }, + { "getbestblockhash", &getbestblockhash, true, false }, { "getconnectioncount", &getconnectioncount, true, false }, { "getpeerinfo", &getpeerinfo, true, false }, { "addnode", &addnode, true, true }, diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 247c47adf9..4d5599be84 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -201,6 +201,7 @@ extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, b extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getblockcount(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp +extern json_spirit::Value getbestblockhash(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getdifficulty(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value settxfee(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawmempool(const json_spirit::Array& params, bool fHelp); 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 535f224902..90dbad61b7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -239,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"; @@ -793,6 +793,7 @@ bool AppInit2(boost::thread_group& threadGroup) fReindex = true; fRequestShutdown = false; } else { + printf("Aborted block database rebuild. Exiting.\n"); return false; } } else { diff --git a/src/key.cpp b/src/key.cpp index f73708199a..1ab4c62ebf 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -155,7 +155,8 @@ public: BN_clear_free(&bn); } - void GetPrivKey(CPrivKey &privkey) { + void GetPrivKey(CPrivKey &privkey, bool fCompressed) { + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); int nSize = i2d_ECPrivateKey(pkey, NULL); assert(nSize); privkey.resize(nSize); @@ -304,7 +305,7 @@ CPrivKey CKey::GetPrivKey() const { CECKey key; key.SetSecretBytes(vch); CPrivKey privkey; - key.GetPrivKey(privkey); + key.GetPrivKey(privkey, fCompressed); return privkey; } diff --git a/src/main.cpp b/src/main.cpp index b8b9bc3567..d358914406 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -471,38 +471,53 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) -bool IsStandardTx(const CTransaction& tx) +bool IsStandardTx(const CTransaction& tx, string& reason) { - if (tx.nVersion > CTransaction::CURRENT_VERSION) + if (tx.nVersion > CTransaction::CURRENT_VERSION) { + reason = "version"; return false; + } - if (!IsFinalTx(tx)) + if (!IsFinalTx(tx)) { + reason = "non-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 = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) + if (sz >= MAX_STANDARD_TX_SIZE) { + reason = "tx-size"; return false; + } BOOST_FOREACH(const CTxIn& txin, tx.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) { + reason = "scriptsig-size"; return false; - if (!txin.scriptSig.IsPushOnly()) + } + if (!txin.scriptSig.IsPushOnly()) { + reason = "scriptsig-not-pushonly"; return false; + } } BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey)) + if (!::IsStandard(txout.scriptPubKey)) { + reason = "scriptpubkey"; return false; - if (txout.IsDust(CTransaction::nMinRelayTxFee)) + } + if (txout.IsDust(CTransaction::nMinRelayTxFee)) { + reason = "dust"; return false; + } } + return true; } @@ -796,8 +811,10 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) - if (!TestNet() && !IsStandardTx(tx)) - return error("CTxMemPool::accept() : nonstandard transaction type"); + string reason; + if (!TestNet() && !IsStandardTx(tx, reason)) + return error("CTxMemPool::accept() : nonstandard transaction: %s", + reason.c_str()); // is it already in the memory pool? uint256 hash = tx.GetHash(); diff --git a/src/main.h b/src/main.h index 7b77b0e3ab..8ad2437c63 100644 --- a/src/main.h +++ b/src/main.h @@ -326,7 +326,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state); /** Check for standard transaction types @return True if all outputs (scriptPubKeys) use only standard transaction forms */ -bool IsStandardTx(const CTransaction& tx); +bool IsStandardTx(const CTransaction& tx, std::string& reason); bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64 nBlockTime = 0); 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..b0c45d6863 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -15,6 +15,7 @@ #include "ui_interface.h" #include "paymentserver.h" #include "splashscreen.h" +#include "intro.h" #include <QMessageBox> #if QT_VERSION < 0x050000 @@ -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/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..3d1e91efdc 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -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/rpcblockchain.cpp b/src/rpcblockchain.cpp index 3c24016fe3..edaa732225 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -82,6 +82,15 @@ Value getblockcount(const Array& params, bool fHelp) return nBestHeight; } +Value getbestblockhash(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getbestblockhash\n" + "Returns the hash of the best (tip) block in the longest block chain."); + + return hashBestChain.GetHex(); +} Value getdifficulty(const Array& params, bool fHelp) { diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 23cb3a8e0a..3c666d2842 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -74,6 +74,7 @@ BOOST_AUTO_TEST_CASE(sign) } CTransaction txFrom; // Funding transaction: + string reason; txFrom.vout.resize(8); for (int i = 0; i < 4; i++) { @@ -82,7 +83,7 @@ BOOST_AUTO_TEST_CASE(sign) txFrom.vout[i+4].scriptPubKey = standardScripts[i]; txFrom.vout[i+4].nValue = COIN; } - BOOST_CHECK(IsStandardTx(txFrom)); + BOOST_CHECK(IsStandardTx(txFrom, reason)); CTransaction txTo[8]; // Spending transactions for (int i = 0; i < 8; i++) @@ -167,13 +168,14 @@ BOOST_AUTO_TEST_CASE(set) } CTransaction txFrom; // Funding transaction: + string reason; txFrom.vout.resize(4); for (int i = 0; i < 4; i++) { txFrom.vout[i].scriptPubKey = outer[i]; txFrom.vout[i].nValue = CENT; } - BOOST_CHECK(IsStandardTx(txFrom)); + BOOST_CHECK(IsStandardTx(txFrom, reason)); CTransaction txTo[4]; // Spending transactions for (int i = 0; i < 4; i++) @@ -189,7 +191,7 @@ BOOST_AUTO_TEST_CASE(set) for (int i = 0; i < 4; i++) { BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); - BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i]), strprintf("txTo[%d].IsStandard", i)); + BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i)); } } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 53d1307b69..0c7475b4f2 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -260,16 +260,17 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) key.MakeNewKey(true); t.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); - BOOST_CHECK(IsStandardTx(t)); + string reason; + BOOST_CHECK(IsStandardTx(t, reason)); t.vout[0].nValue = 5011; // dust - BOOST_CHECK(!IsStandardTx(t)); + BOOST_CHECK(!IsStandardTx(t, reason)); t.vout[0].nValue = 6011; // not dust - BOOST_CHECK(IsStandardTx(t)); + BOOST_CHECK(IsStandardTx(t, reason)); t.vout[0].scriptPubKey = CScript() << OP_1; - BOOST_CHECK(!IsStandardTx(t)); + BOOST_CHECK(!IsStandardTx(t, reason)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 64bd3a1b28..fd936517fd 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -263,28 +263,10 @@ BOOST_AUTO_TEST_CASE(util_IsHex) BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) { - // Expected results for the determinstic seed. - const uint32_t exp_vals[11] = { 91632771U,1889679809U,3842137544U,3256031132U, - 1761911779U, 489223532U,2692793790U,2737472863U, - 2796262275U,1309899767U,840571781U}; - // Expected 0s in rand()%(idx+2) for the determinstic seed. - const int exp_count[9] = {5013,3346,2415,1972,1644,1386,1176,1096,1009}; int i; int count=0; - seed_insecure_rand(); - - //Does the non-determistic rand give us results that look too like the determinstic one? - for (i=0;i<10;i++) - { - int match = 0; - uint32_t rval = insecure_rand(); - for (int j=0;j<11;j++)match |= rval==exp_vals[j]; - count += match; - } - // sum(binomial(10,i)*(11/(2^32))^i*(1-(11/(2^32)))^(10-i),i,0,4) ~= 1-1/2^134.73 - // So _very_ unlikely to throw a false failure here. - BOOST_CHECK(count<=4); + seed_insecure_rand(true); for (int mod=2;mod<11;mod++) { @@ -307,20 +289,6 @@ 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_SUITE_END() diff --git a/src/version.cpp b/src/version.cpp index 8af406feab..d9d6724a02 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -36,7 +36,7 @@ const std::string CLIENT_NAME("Satoshi"); // git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ #ifdef GIT_ARCHIVE # define GIT_COMMIT_ID "$Format:%h$" -# define GIT_COMMIT_DATE "$Format:%cD" +# define GIT_COMMIT_DATE "$Format:%cD$" #endif #define BUILD_DESC_FROM_COMMIT(maj,min,rev,build,commit) \ diff --git a/src/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; |