diff options
154 files changed, 3163 insertions, 1385 deletions
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index 100b8653a8..121e10bd37 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -110,13 +110,8 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ if test x$bitcoin_qt_got_major_vers = x5; then _BITCOIN_QT_IS_STATIC if test x$bitcoin_cv_static_qt = xyes; then + _BITCOIN_QT_FIND_STATIC_PLUGINS AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) - if test x$qt_plugin_path != x; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" - fi - if test x$use_pkgconfig = xyes; then - PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"]) - fi AC_CACHE_CHECK(for Qt < 5.4, bitcoin_cv_need_acc_widget,[AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#include <QtCore>]],[[ #if QT_VERSION >= 0x050400 @@ -127,25 +122,15 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ [bitcoin_cv_need_acc_widget=no]) ]) if test "x$bitcoin_cv_need_acc_widget" = "xyes"; then - if test x$qt_plugin_path != x; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" - fi _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets]) fi if test x$TARGET_OS = xwindows; then _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows]) AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows]) elif test x$TARGET_OS = xlinux; then - PKG_CHECK_MODULES([X11XCB], [x11-xcb], [QT_LIBS="$X11XCB_LIBS $QT_LIBS"]) - if ${PKG_CONFIG} --exists "Qt5Core >= 5.5" 2>/dev/null; then - PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) - fi _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)],[-lqxcb -lxcb-static]) AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb]) elif test x$TARGET_OS = xdarwin; then - if test x$use_pkgconfig = xyes; then - PKG_CHECK_MODULES([QTPRINT], [Qt5PrintSupport], [QT_LIBS="$QTPRINT_LIBS $QT_LIBS"]) - fi AX_CHECK_LINK_FLAG([[-framework IOKit]],[QT_LIBS="$QT_LIBS -framework IOKit"],[AC_MSG_ERROR(could not iokit framework)]) _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)],[-lqcocoa]) AC_DEFINE(QT_QPA_PLATFORM_COCOA, 1, [Define this symbol if the qt platform is cocoa]) @@ -154,10 +139,6 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ else if test x$TARGET_OS = xwindows; then AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) - if test x$qt_plugin_path != x; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" - QT_LIBS="$QT_LIBS -L$qt_plugin_path/codecs" - fi _BITCOIN_QT_CHECK_STATIC_PLUGINS([ Q_IMPORT_PLUGIN(qcncodecs) Q_IMPORT_PLUGIN(qjpcodecs) @@ -297,6 +278,39 @@ AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_PLUGINS],[ LIBS="$CHECK_STATIC_PLUGINS_TEMP_LIBS" ]) +dnl Internal. Find paths necessary for linking qt static plugins +dnl Inputs: bitcoin_qt_got_major_vers. 4 or 5. +dnl Inputs: qt_plugin_path. optional. +dnl Outputs: QT_LIBS is appended +AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[ + if test x$bitcoin_qt_got_major_vers = x5; then + if test x$qt_plugin_path != x; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" + if test -d "$qt_plugin_path/accessible"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" + fi + fi + m4_ifdef([PKG_CHECK_MODULES],[ + if test x$use_pkgconfig = xyes; then + PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"]) + if test x$TARGET_OS = xlinux; then + PKG_CHECK_MODULES([X11XCB], [x11-xcb], [QT_LIBS="$X11XCB_LIBS $QT_LIBS"]) + if ${PKG_CONFIG} --exists "Qt5Core >= 5.5" 2>/dev/null; then + PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) + fi + elif test x$TARGET_OS = xdarwin; then + PKG_CHECK_MODULES([QTPRINT], [Qt5PrintSupport], [QT_LIBS="$QTPRINT_LIBS $QT_LIBS"]) + fi + fi + ]) + else + if test x$qt_plugin_path != x; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" + QT_LIBS="$QT_LIBS -L$qt_plugin_path/codecs" + fi + fi +]) + dnl Internal. Find Qt libraries using pkg-config. dnl Inputs: bitcoin_qt_want_version (from --with-gui=). The version to check dnl first. diff --git a/configure.ac b/configure.ac index 7f90b5f395..a524bded6f 100644 --- a/configure.ac +++ b/configure.ac @@ -680,19 +680,26 @@ else fi fi -AC_CHECK_LIB([crypto],[RAND_egd],[],[ - AC_ARG_WITH([libressl], - [AS_HELP_STRING([--with-libressl],[Build with system LibreSSL (default is no; DANGEROUS; NOT SUPPORTED)])], - [AC_MSG_WARN([Detected LibreSSL: This is NOT supported, and may break consensus compatibility!])], - [AC_MSG_ERROR([Detected LibreSSL: This is NOT supported, and may break consensus compatibility!])] - ) -]) - CFLAGS_TEMP="$CFLAGS" LIBS_TEMP="$LIBS" CFLAGS="$CFLAGS $SSL_CFLAGS $CRYPTO_CFLAGS" LIBS="$LIBS $SSL_LIBS $CRYPTO_LIBS" AC_CHECK_HEADER([openssl/ec.h],, AC_MSG_ERROR(OpenSSL ec header missing),) + +AC_MSG_CHECKING(for a supported OpenSSL version) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include <openssl/rand.h> + ]], + [[RAND_egd(NULL);]])], + [AC_MSG_RESULT(yes)], + [ + AC_ARG_WITH([libressl], + [AS_HELP_STRING([--with-libressl],[Build with system LibreSSL (default is no; DANGEROUS; NOT SUPPORTED)])], + [AC_MSG_WARN([Detected LibreSSL: This is NOT supported, and may break consensus compatibility!])], + [AC_MSG_ERROR([Detected LibreSSL: This is NOT supported, and may break consensus compatibility!])] + )] +) + CFLAGS="$CFLAGS_TEMP" LIBS="$LIBS_TEMP" diff --git a/contrib/README.md b/contrib/README.md index 7d4b91e887..125594312b 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -1,9 +1,6 @@ Wallet Tools --------------------- -### [BitRPC](/contrib/bitrpc) ### -Allows for sending of all standard Bitcoin commands via RPC rather than as command line args. - ### [SpendFrom](/contrib/spendfrom) ### Use the raw transactions API to send coins received on a particular diff --git a/contrib/bitrpc/README.md b/contrib/bitrpc/README.md deleted file mode 100644 index f5ef2f0405..0000000000 --- a/contrib/bitrpc/README.md +++ /dev/null @@ -1,8 +0,0 @@ -### BitRPC -Allows for sending of all standard Bitcoin commands via RPC rather than as command line args. - -### Looking for Wallet Tools? -BitRPC.py is able to do the exact same thing as `walletchangepass.py` and `walletunlock.py`. Their respective commands in BitRPC.py are: - - bitrpc.py walletpassphrasechange - bitrpc.py walletpassphrase
\ No newline at end of file diff --git a/contrib/bitrpc/bitrpc.py b/contrib/bitrpc/bitrpc.py deleted file mode 100644 index c3ce9d7936..0000000000 --- a/contrib/bitrpc/bitrpc.py +++ /dev/null @@ -1,335 +0,0 @@ -from jsonrpc import ServiceProxy -import sys -import string -import getpass - -# ===== BEGIN USER SETTINGS ===== -# if you do not set these you will be prompted for a password for every command -rpcuser = "" -rpcpass = "" -# ====== END USER SETTINGS ====== - - -if rpcpass == "": - access = ServiceProxy("http://127.0.0.1:8332") -else: - access = ServiceProxy("http://"+rpcuser+":"+rpcpass+"@127.0.0.1:8332") -cmd = sys.argv[1].lower() - -if cmd == "backupwallet": - try: - path = raw_input("Enter destination path/filename: ") - print access.backupwallet(path) - except Exception as inst: - print inst - -elif cmd == "encryptwallet": - try: - pwd = getpass.getpass(prompt="Enter passphrase: ") - pwd2 = getpass.getpass(prompt="Repeat passphrase: ") - if pwd == pwd2: - access.encryptwallet(pwd) - print "\n---Wallet encrypted. Server stopping, restart to run with encrypted wallet---\n" - else: - print "\n---Passphrases do not match---\n" - except Exception as inst: - print inst - -elif cmd == "getaccount": - try: - addr = raw_input("Enter a Bitcoin address: ") - print access.getaccount(addr) - except Exception as inst: - print inst - -elif cmd == "getaccountaddress": - try: - acct = raw_input("Enter an account name: ") - print access.getaccountaddress(acct) - except Exception as inst: - print inst - -elif cmd == "getaddressesbyaccount": - try: - acct = raw_input("Enter an account name: ") - print access.getaddressesbyaccount(acct) - except Exception as inst: - print inst - -elif cmd == "getbalance": - try: - acct = raw_input("Enter an account (optional): ") - mc = raw_input("Minimum confirmations (optional): ") - try: - print access.getbalance(acct, mc) - except: - print access.getbalance() - except Exception as inst: - print inst - -elif cmd == "getblockbycount": - try: - height = raw_input("Height: ") - print access.getblockbycount(height) - except Exception as inst: - print inst - -elif cmd == "getblockcount": - try: - print access.getblockcount() - except Exception as inst: - print inst - -elif cmd == "getblocknumber": - try: - print access.getblocknumber() - except Exception as inst: - print inst - -elif cmd == "getconnectioncount": - try: - print access.getconnectioncount() - except Exception as inst: - print inst - -elif cmd == "getdifficulty": - try: - print access.getdifficulty() - except Exception as inst: - print inst - -elif cmd == "getgenerate": - try: - print access.getgenerate() - except Exception as inst: - print inst - -elif cmd == "gethashespersec": - try: - print access.gethashespersec() - except Exception as inst: - print inst - -elif cmd == "getinfo": - try: - print access.getinfo() - except Exception as inst: - print inst - -elif cmd == "getnewaddress": - try: - acct = raw_input("Enter an account name: ") - try: - print access.getnewaddress(acct) - except: - print access.getnewaddress() - except Exception as inst: - print inst - -elif cmd == "getreceivedbyaccount": - try: - acct = raw_input("Enter an account (optional): ") - mc = raw_input("Minimum confirmations (optional): ") - try: - print access.getreceivedbyaccount(acct, mc) - except: - print access.getreceivedbyaccount() - except Exception as inst: - print inst - -elif cmd == "getreceivedbyaddress": - try: - addr = raw_input("Enter a Bitcoin address (optional): ") - mc = raw_input("Minimum confirmations (optional): ") - try: - print access.getreceivedbyaddress(addr, mc) - except: - print access.getreceivedbyaddress() - except Exception as inst: - print inst - -elif cmd == "gettransaction": - try: - txid = raw_input("Enter a transaction ID: ") - print access.gettransaction(txid) - except Exception as inst: - print inst - -elif cmd == "getwork": - try: - data = raw_input("Data (optional): ") - try: - print access.gettransaction(data) - except: - print access.gettransaction() - except Exception as inst: - print inst - -elif cmd == "help": - try: - cmd = raw_input("Command (optional): ") - try: - print access.help(cmd) - except: - print access.help() - except Exception as inst: - print inst - -elif cmd == "listaccounts": - try: - mc = raw_input("Minimum confirmations (optional): ") - try: - print access.listaccounts(mc) - except: - print access.listaccounts() - except Exception as inst: - print inst - -elif cmd == "listreceivedbyaccount": - try: - mc = raw_input("Minimum confirmations (optional): ") - incemp = raw_input("Include empty? (true/false, optional): ") - try: - print access.listreceivedbyaccount(mc, incemp) - except: - print access.listreceivedbyaccount() - except Exception as inst: - print inst - -elif cmd == "listreceivedbyaddress": - try: - mc = raw_input("Minimum confirmations (optional): ") - incemp = raw_input("Include empty? (true/false, optional): ") - try: - print access.listreceivedbyaddress(mc, incemp) - except: - print access.listreceivedbyaddress() - except Exception as inst: - print inst - -elif cmd == "listtransactions": - try: - acct = raw_input("Account (optional): ") - count = raw_input("Number of transactions (optional): ") - frm = raw_input("Skip (optional):") - try: - print access.listtransactions(acct, count, frm) - except: - print access.listtransactions() - except Exception as inst: - print inst - -elif cmd == "move": - try: - frm = raw_input("From: ") - to = raw_input("To: ") - amt = raw_input("Amount:") - mc = raw_input("Minimum confirmations (optional): ") - comment = raw_input("Comment (optional): ") - try: - print access.move(frm, to, amt, mc, comment) - except: - print access.move(frm, to, amt) - except Exception as inst: - print inst - -elif cmd == "sendfrom": - try: - frm = raw_input("From: ") - to = raw_input("To: ") - amt = raw_input("Amount:") - mc = raw_input("Minimum confirmations (optional): ") - comment = raw_input("Comment (optional): ") - commentto = raw_input("Comment-to (optional): ") - try: - print access.sendfrom(frm, to, amt, mc, comment, commentto) - except: - print access.sendfrom(frm, to, amt) - except Exception as inst: - print inst - -elif cmd == "sendmany": - try: - frm = raw_input("From: ") - to = raw_input("To (in format address1:amount1,address2:amount2,...): ") - mc = raw_input("Minimum confirmations (optional): ") - comment = raw_input("Comment (optional): ") - try: - print access.sendmany(frm,to,mc,comment) - except: - print access.sendmany(frm,to) - except Exception as inst: - print inst - -elif cmd == "sendtoaddress": - try: - to = raw_input("To (in format address1:amount1,address2:amount2,...): ") - amt = raw_input("Amount:") - comment = raw_input("Comment (optional): ") - commentto = raw_input("Comment-to (optional): ") - try: - print access.sendtoaddress(to,amt,comment,commentto) - except: - print access.sendtoaddress(to,amt) - except Exception as inst: - print inst - -elif cmd == "setaccount": - try: - addr = raw_input("Address: ") - acct = raw_input("Account:") - print access.setaccount(addr,acct) - except Exception as inst: - print inst - -elif cmd == "setgenerate": - try: - gen= raw_input("Generate? (true/false): ") - cpus = raw_input("Max processors/cores (-1 for unlimited, optional):") - try: - print access.setgenerate(gen, cpus) - except: - print access.setgenerate(gen) - except Exception as inst: - print inst - -elif cmd == "settxfee": - try: - amt = raw_input("Amount:") - print access.settxfee(amt) - except Exception as inst: - print inst - -elif cmd == "stop": - try: - print access.stop() - except Exception as inst: - print inst - -elif cmd == "validateaddress": - try: - addr = raw_input("Address: ") - print access.validateaddress(addr) - except Exception as inst: - print inst - -elif cmd == "walletpassphrase": - try: - pwd = getpass.getpass(prompt="Enter wallet passphrase: ") - access.walletpassphrase(pwd, 60) - print "\n---Wallet unlocked---\n" - except Exception as inst: - print inst - -elif cmd == "walletpassphrasechange": - try: - pwd = getpass.getpass(prompt="Enter old wallet passphrase: ") - pwd2 = getpass.getpass(prompt="Enter new wallet passphrase: ") - access.walletpassphrasechange(pwd, pwd2) - print - print "\n---Passphrase changed---\n" - except Exception as inst: - print inst - -else: - print "Command not found or not supported" diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 7ce3babc1b..bd7ab3524c 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -149,7 +149,7 @@ bitcoin (0.5.3-natty0) natty; urgency=low bitcoin (0.5.2-natty1) natty; urgency=low * Remove mentions on anonymity in package descriptions and manpage. - These should never have been there, bitcoin isnt anonymous without + These should never have been there, bitcoin isn't anonymous without a ton of work that virtually no users will ever be willing and capable of doing @@ -190,7 +190,7 @@ bitcoin (0.5.0~rc1-natty1) natty; urgency=low * Add test_bitcoin to build test * Fix clean - * Remove uneccessary build-dependancies + * Remove unnecessary build-dependancies -- Matt Corallo <matt@bluematt.me> Wed, 26 Oct 2011 14:37:18 -0400 @@ -350,7 +350,7 @@ bitcoin (0.3.20.01~dfsg-1) unstable; urgency=low bitcoin (0.3.19~dfsg-6) unstable; urgency=low - * Fix override agressive optimizations. + * Fix override aggressive optimizations. * Fix tighten build-dependencies to really fit backporting to Lenny: + Add fallback build-dependency on libdb4.6++-dev. + Tighten unversioned Boost build-dependencies to recent versions, diff --git a/contrib/devtools/optimize-pngs.py b/contrib/devtools/optimize-pngs.py index 38aaa00f31..b6d6a097d6 100755 --- a/contrib/devtools/optimize-pngs.py +++ b/contrib/devtools/optimize-pngs.py @@ -1,5 +1,8 @@ #!/usr/bin/env python - +''' +Run this scrip every time you change one of the png files. Using pngcrush, it will optimize the png files, remove various color profiles, remove ancillary chunks (alla) and text chunks (text). +#pngcrush -brute -ow -rem gAMA -rem cHRM -rem iCCP -rem sRGB -rem alla -rem text +''' import os import sys import subprocess @@ -18,14 +21,12 @@ def content_hash(filename): data = i.tostring() return hashlib.sha256(data).hexdigest() -#optimize png, remove various color profiles, remove ancillary chunks (alla) and text chunks (text) -#pngcrush -brute -ow -rem gAMA -rem cHRM -rem iCCP -rem sRGB -rem alla -rem text - pngcrush = 'pngcrush' git = 'git' -folders = ["src/qt/res/movies", "src/qt/res/icons", "src/qt/res/images"] +folders = ["src/qt/res/movies", "src/qt/res/icons"] basePath = subprocess.check_output([git, 'rev-parse', '--show-toplevel']).rstrip('\n') totalSaveBytes = 0 +noHashChange = True outputArray = [] for folder in folders: @@ -68,6 +69,7 @@ for fileDict in outputArray: oldHash = fileDict['sha256Old'] newHash = fileDict['sha256New'] totalSaveBytes += fileDict['osize'] - fileDict['psize'] + noHashChange = noHashChange and (oldHash == newHash) print fileDict['file']+"\n size diff from: "+str(fileDict['osize'])+" to: "+str(fileDict['psize'])+"\n old sha256: "+oldHash+"\n new sha256: "+newHash+"\n" -print "completed. Total reduction: "+str(totalSaveBytes)+" bytes" +print "completed. Checksum stable: "+str(noHashChange)+". Total reduction: "+str(totalSaveBytes)+" bytes" diff --git a/depends/config.guess b/depends/config.guess index f7eb141e75..f357ec6c8f 100755 --- a/depends/config.guess +++ b/depends/config.guess @@ -1117,7 +1117,7 @@ EOF # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; diff --git a/doc/REST-interface.md b/doc/REST-interface.md index ac7cd45f70..bf669235e3 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -77,6 +77,20 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76 } ``` +####Memory pool +`GET /rest/mempool/info.json` + +Returns various information about the TX mempool. +Only supports JSON as output format. +* size : (numeric) the number of transactions in the TX mempool +* bytes : (numeric) size of the TX mempool in bytes +* usage : (numeric) total TX mempool memory usage + +`GET /rest/mempool/contents.json` + +Returns transactions in the TX mempool. +Only supports JSON as output format. + Risks ------------- Running a web browser on the same node with a REST enabled bitcoind can be a risk. Accessing prepared XSS websites could read out tx/block data of your node by placing links like `<script src="http://127.0.0.1:8332/rest/tx/1234567890.json">` which might break the nodes privacy. diff --git a/doc/assets-attribution.md b/doc/assets-attribution.md index c6da1a4586..b5a033dd79 100644 --- a/doc/assets-attribution.md +++ b/doc/assets-attribution.md @@ -10,37 +10,58 @@ The following is a list of assets used in the bitcoin source and their proper at * Site: [https://github.com/stephenhutchings/typicons.font](https://github.com/stephenhutchings/typicons.font) ### Assets Used - src/qt/res/icons/add.png, src/qt/res/icons/address-book.png, - src/qt/res/icons/configure.png, src/qt/res/icons/connect4.png, - src/qt/res/icons/debugwindow.png, src/qt/res/icons/edit.png, - src/qt/res/icons/exitcopy.png, src/qt/res/icons/editpaste.png, - src/qt/res/icons/export.png, src/qt/res/icons/eye.png, - src/qt/res/icons/filesave.png, src/qt/res/icons/history.png, - src/qt/res/icons/info.png, src/qt/res/icons/key.png, - src/qt/res/icons/lock_*.png, src/qt/res/icons/open.png, - src/qt/res/icons/overview.png, src/qt/res/icons/quit.png, - src/qt/res/icons/receive.png, src/qt/res/icons/remove.png, - src/qt/res/icons/send.png, src/qt/res/icons/synced.png, - src/qt/res/icons/transaction*.png, src/qt/res/icons/tx_output.png, + src/qt/res/icons/add.png + src/qt/res/icons/address-book.png, + src/qt/res/icons/configure.png + src/qt/res/icons/debugwindow.png + src/qt/res/icons/edit.png, + src/qt/res/icons/exitcopy.png + src/qt/res/icons/editpaste.png, + src/qt/res/icons/export.png + src/qt/res/icons/eye.png, + src/qt/res/icons/filesave.png + src/qt/res/icons/history.png, + src/qt/res/icons/info.png + src/qt/res/icons/key.png, + src/qt/res/icons/lock_*.png + src/qt/res/icons/open.png, + src/qt/res/icons/overview.png + src/qt/res/icons/quit.png, + src/qt/res/icons/receive.png + src/qt/res/icons/remove.png, + src/qt/res/icons/send.png + src/qt/res/icons/synced.png, + src/qt/res/icons/transaction*.png + src/qt/res/icons/tx_output.png, src/qt/res/icons/warning.png -Jonas Schnelli +Other ----------------------- ### Info -* Designer: Jonas Schnelli -* Bitcoin Icon: (based on the original bitcoin logo from Bitboy) +* Designer: Jonas Schnelli, Bitboy, Stephen Hutchings, Marco Falke +* Bitcoin Icon: Based on the original bitcoin logo from Bitboy +* Network connection icon: Inspired by flow-merge.svg from Stephen Hutchings * Some icons are based on Stephan Hutchings Typicons * License: MIT ### Assets Used - src/qt/res/icons/about.png, src/qt/res/icons/about_qt.png, - src/qt/res/icons/bitcoin.icns, src/qt/res/icons/bitcoin.ico, - src/qt/res/icons/bitcoin.png, src/qt/res/icons/clock*.png, - src/qt/res/icons/connect[0-3].png, src/qt/res/icons/eye_minus.png, - src/qt/res/icons/eye_plus.png, src/qt/res/icons/verify.png, - src/qt/res/icons/tx_inout.png, src/qt/res/icons/tx_input.png, - src/qt/res/src/verify.svg, src/qt/res/src/bitcoin.svg, - src/qt/res/src/clock*.svg, src/qt/res/src/connect*.svg, - src/qt/res/src/mine.svg, src/qt/res/src/qt.svg, src/qt/res/src/tx*.svg, - src/qt/res/src/verify.svg, + src/qt/res/icons/about.png + src/qt/res/icons/about_qt.png, + src/qt/res/icons/bitcoin.icns + src/qt/res/icons/bitcoin.ico, + src/qt/res/icons/bitcoin.png + src/qt/res/icons/clock*.png, + src/qt/res/icons/connect*.png + src/qt/res/icons/eye_minus.png, + src/qt/res/icons/eye_plus.png + src/qt/res/icons/verify.png, + src/qt/res/icons/tx_inout.png + src/qt/res/icons/tx_input.png, + src/qt/res/src/bitcoin.svg, + src/qt/res/src/clock*.svg + src/qt/res/src/connect*.svg, + src/qt/res/src/mine.svg + src/qt/res/src/qt.svg + src/qt/res/src/tx*.svg, + src/qt/res/src/verify.svg diff --git a/doc/files.md b/doc/files.md index 80195535bb..c083bcb038 100644 --- a/doc/files.md +++ b/doc/files.md @@ -1,12 +1,17 @@ -Used in 0.8.0 ---------------------- -* wallet.dat: personal wallet (BDB) with keys and transactions -* peers.dat: peer IP address database (custom format); since 0.7.0 + +* banlist.dat: stores the IPs/Subnets of banned nodes +* bitcoin.conf: contains configuration settings for bitcoind or bitcoin-qt +* bitcoind.pid: stores the process id of bitcoind while running * blocks/blk000??.dat: block data (custom, 128 MiB per file); since 0.8.0 * blocks/rev000??.dat; block undo data (custom); since 0.8.0 (format changed since pre-0.8) * blocks/index/*; block index (LevelDB); since 0.8.0 * chainstate/*; block chain state database (LevelDB); since 0.8.0 * database/*: BDB database environment; only used for wallet since 0.8.0 +* db.log: wallet database log file +* debug.log: contains debug information and general logging generated by bitcoind or bitcoin-qt +* fee_estimates.dat: stores statistics used to estimate minimum transaction fees and priorities required for confirmation; since 0.10.0 +* peers.dat: peer IP address database (custom format); since 0.7.0 +* wallet.dat: personal wallet (BDB) with keys and transactions Only used in pre-0.8.0 --------------------- diff --git a/doc/init.md b/doc/init.md index 1f206a6c02..ed9ce72154 100644 --- a/doc/init.md +++ b/doc/init.md @@ -29,28 +29,32 @@ file, however it is recommended that a strong and secure password be used as this password is security critical to securing the wallet should the wallet be enabled. -If bitcoind is run with "-daemon" flag, and no rpcpassword is set, it will -print a randomly generated suitable password to stderr. You can also -generate one from the shell yourself like this: +If bitcoind is run with the "-server" flag (set by default), and no rpcpassword is set, +it will use a special cookie file for authentication. The cookie is generated with random +content when the daemon starts, and deleted when it exits. Read access to this file +controls who can access it through RPC. -bash -c 'tr -dc a-zA-Z0-9 < /dev/urandom | head -c32 && echo' +By default the cookie is stored in the data directory, but it's location can be overridden +with the option '-rpccookiefile'. -Once you have a password in hand, set rpcpassword= in /etc/bitcoin/bitcoin.conf +This allows for running bitcoind without having to do any manual configuration. + +`conf`, `pid`, and `wallet` accept relative paths which are interpreted as +relative to the data directory. `wallet` *only* supports relative paths. For an example configuration file that describes the configuration settings, -see contrib/debian/examples/bitcoin.conf. +see `contrib/debian/examples/bitcoin.conf`. 3. Paths --------------------------------- All three configurations assume several paths that might need to be adjusted. -Binary: /usr/bin/bitcoind -Configuration file: /etc/bitcoin/bitcoin.conf -Data directory: /var/lib/bitcoind -PID file: /var/run/bitcoind/bitcoind.pid (OpenRC and Upstart) - /var/lib/bitcoind/bitcoind.pid (systemd) -Lock file: /var/lock/subsys/bitcoind (CentOS) +Binary: `/usr/bin/bitcoind` +Configuration file: `/etc/bitcoin/bitcoin.conf` +Data directory: `/var/lib/bitcoind` +PID file: `/var/run/bitcoind/bitcoind.pid` (OpenRC and Upstart) or `/var/lib/bitcoind/bitcoind.pid` (systemd) +Lock file: `/var/lock/subsys/bitcoind` (CentOS) The configuration file, PID directory (if applicable) and data directory should all be owned by the bitcoin user and group. It is advised for security @@ -65,21 +69,21 @@ can then be controlled by group membership. Installing this .service file consists of just copying it to /usr/lib/systemd/system directory, followed by the command -"systemctl daemon-reload" in order to update running systemd configuration. +`systemctl daemon-reload` in order to update running systemd configuration. -To test, run "systemctl start bitcoind" and to enable for system startup run -"systemctl enable bitcoind" +To test, run `systemctl start bitcoind` and to enable for system startup run +`systemctl enable bitcoind` 4b) OpenRC Rename bitcoind.openrc to bitcoind and drop it in /etc/init.d. Double check ownership and permissions and make it executable. Test it with -"/etc/init.d/bitcoind start" and configure it to run on startup with -"rc-update add bitcoind" +`/etc/init.d/bitcoind start` and configure it to run on startup with +`rc-update add bitcoind` 4c) Upstart (for Debian/Ubuntu based distributions) -Drop bitcoind.conf in /etc/init. Test by running "service bitcoind start" +Drop bitcoind.conf in /etc/init. Test by running `service bitcoind start` it will automatically start on reboot. NOTE: This script is incompatible with CentOS 5 and Amazon Linux 2014 as they @@ -87,7 +91,7 @@ use old versions of Upstart and do not supply the start-stop-daemon utility. 4d) CentOS -Copy bitcoind.init to /etc/init.d/bitcoind. Test by running "service bitcoind start". +Copy bitcoind.init to /etc/init.d/bitcoind. Test by running `service bitcoind start`. Using this script, you can adjust the path and flags to the bitcoind program by setting the BITCOIND and FLAGS environment variables in the file @@ -99,4 +103,3 @@ setting the BITCOIND and FLAGS environment variables in the file Auto respawning is currently only configured for Upstart and systemd. Reasonable defaults have been chosen but YMMV. - diff --git a/doc/release-notes.md b/doc/release-notes.md index 7480a7cd21..9caac4f7ae 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -27,6 +27,14 @@ Low-level RPC API changes advantage if a JSON library insists on using a lossy floating point type for numbers, which would be dangerous for monetary amounts. +Option parsing behavior +----------------------- + +Command line options are now parsed strictly in the order in which they are +specified. It used to be the case that `-X -noX` ends up, unintuitively, with X +set, as `-X` had precedence over `-noX`. This is no longer the case. Like for +other software, the last specified value for an option will hold. + 0.12.0 Change log ================= @@ -55,3 +63,5 @@ git merge commit are mentioned. ### Miscellaneous +- Removed bitrpc.py from contrib + diff --git a/doc/release-notes/release-notes-0.11.0.md b/doc/release-notes/release-notes-0.11.0.md new file mode 100644 index 0000000000..28e49fb7ea --- /dev/null +++ b/doc/release-notes/release-notes-0.11.0.md @@ -0,0 +1,505 @@ +Bitcoin Core version 0.11.0 is now available from: + + <https://bitcoin.org/bin/bitcoin-core-0.11.0/> + +This is a new major version release, bringing both new features and +bug fixes. + +Please report bugs using the issue tracker at github: + + <https://github.com/bitcoin/bitcoin/issues> + +Upgrading and downgrading +========================= + +How to Upgrade +-------------- + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or +bitcoind/bitcoin-qt (on Linux). + +Downgrade warning +------------------ + +Because release 0.10.0 and later makes use of headers-first synchronization and +parallel block download (see further), the block files and databases are not +backwards-compatible with pre-0.10 versions of Bitcoin Core or other software: + +* Blocks will be stored on disk out of order (in the order they are +received, really), which makes it incompatible with some tools or +other programs. Reindexing using earlier versions will also not work +anymore as a result of this. + +* The block index database will now hold headers for which no block is +stored on disk, which earlier versions won't support. + +If you want to be able to downgrade smoothly, make a backup of your entire data +directory. Without this your node will need start syncing (or importing from +bootstrap.dat) anew afterwards. It is possible that the data from a completely +synchronised 0.10 node may be usable in older versions as-is, but this is not +supported and may break as soon as the older version attempts to reindex. + +This does not affect wallet forward or backward compatibility. There are no +known problems when downgrading from 0.11.x to 0.10.x. + +Important information +====================== + +Transaction flooding +--------------------- + +At the time of this release, the P2P network is being flooded with low-fee +transactions. This causes a ballooning of the mempool size. + +If this growth of the mempool causes problematic memory use on your node, it is +possible to change a few configuration options to work around this. The growth +of the mempool can be monitored with the RPC command `getmempoolinfo`. + +One is to increase the minimum transaction relay fee `minrelaytxfee`, which +defaults to 0.00001. This will cause transactions with fewer BTC/kB fee to be +rejected, and thus fewer transactions entering the mempool. + +The other is to restrict the relaying of free transactions with +`limitfreerelay`. This option sets the number of kB/minute at which +free transactions (with enough priority) will be accepted. It defaults to 15. +Reducing this number reduces the speed at which the mempool can grow due +to free transactions. + +For example, add the following to `bitcoin.conf`: + + minrelaytxfee=0.00005 + limitfreerelay=5 + +More robust solutions are being worked on for a follow-up release. + +Notable changes +=============== + +Block file pruning +---------------------- + +This release supports running a fully validating node without maintaining a copy +of the raw block and undo data on disk. To recap, there are four types of data +related to the blockchain in the bitcoin system: the raw blocks as received over +the network (blk???.dat), the undo data (rev???.dat), the block index and the +UTXO set (both LevelDB databases). The databases are built from the raw data. + +Block pruning allows Bitcoin Core to delete the raw block and undo data once +it's been validated and used to build the databases. At that point, the raw data +is used only to relay blocks to other nodes, to handle reorganizations, to look +up old transactions (if -txindex is enabled or via the RPC/REST interfaces), or +for rescanning the wallet. The block index continues to hold the metadata about +all blocks in the blockchain. + +The user specifies how much space to allot for block & undo files. The minimum +allowed is 550MB. Note that this is in addition to whatever is required for the +block index and UTXO databases. The minimum was chosen so that Bitcoin Core will +be able to maintain at least 288 blocks on disk (two days worth of blocks at 10 +minutes per block). In rare instances it is possible that the amount of space +used will exceed the pruning target in order to keep the required last 288 +blocks on disk. + +Block pruning works during initial sync in the same way as during steady state, +by deleting block files "as you go" whenever disk space is allocated. Thus, if +the user specifies 550MB, once that level is reached the program will begin +deleting the oldest block and undo files, while continuing to download the +blockchain. + +For now, block pruning disables block relay. In the future, nodes with block +pruning will at a minimum relay "new" blocks, meaning blocks that extend their +active chain. + +Block pruning is currently incompatible with running a wallet due to the fact +that block data is used for rescanning the wallet and importing keys or +addresses (which require a rescan.) However, running the wallet with block +pruning will be supported in the near future, subject to those limitations. + +Block pruning is also incompatible with -txindex and will automatically disable +it. + +Once you have pruned blocks, going back to unpruned state requires +re-downloading the entire blockchain. To do this, re-start the node with +-reindex. Note also that any problem that would cause a user to reindex (e.g., +disk corruption) will cause a pruned node to redownload the entire blockchain. +Finally, note that when a pruned node reindexes, it will delete any blk???.dat +and rev???.dat files in the data directory prior to restarting the download. + +To enable block pruning on the command line: + +- `-prune=N`: where N is the number of MB to allot for raw block & undo data. + +Modified RPC calls: + +- `getblockchaininfo` now includes whether we are in pruned mode or not. +- `getblock` will check if the block's data has been pruned and if so, return an +error. +- `getrawtransaction` will no longer be able to locate a transaction that has a +UTXO but where its block file has been pruned. + +Pruning is disabled by default. + +Big endian support +-------------------- + +Experimental support for big-endian CPU architectures was added in this +release. All little-endian specific code was replaced with endian-neutral +constructs. This has been tested on at least MIPS and PPC hosts. The build +system will automatically detect the endianness of the target. + +Memory usage optimization +-------------------------- + +There have been many changes in this release to reduce the default memory usage +of a node, among which: + +- Accurate UTXO cache size accounting (#6102); this makes the option `-dbcache` + precise where this grossly underestimated memory usage before +- Reduce size of per-peer data structure (#6064 and others); this increases the + number of connections that can be supported with the same amount of memory +- Reduce the number of threads (#5964, #5679); lowers the amount of (esp. + virtual) memory needed + +Fee estimation changes +---------------------- + +This release improves the algorithm used for fee estimation. Previously, -1 +was returned when there was insufficient data to give an estimate. Now, -1 +will also be returned when there is no fee or priority high enough for the +desired confirmation target. In those cases, it can help to ask for an estimate +for a higher target number of blocks. It is not uncommon for there to be no +fee or priority high enough to be reliably (85%) included in the next block and +for this reason, the default for `-txconfirmtarget=n` has changed from 1 to 2. + +Privacy: Disable wallet transaction broadcast +---------------------------------------------- + +This release adds an option `-walletbroadcast=0` to prevent automatic +transaction broadcast and rebroadcast (#5951). This option allows separating +transaction submission from the node functionality. + +Making use of this, third-party scripts can be written to take care of +transaction (re)broadcast: + +- Send the transaction as normal, either through RPC or the GUI +- Retrieve the transaction data through RPC using `gettransaction` (NOT + `getrawtransaction`). The `hex` field of the result will contain the raw + hexadecimal representation of the transaction +- The transaction can then be broadcasted through arbitrary mechanisms + supported by the script + +One such application is selective Tor usage, where the node runs on the normal +internet but transactions are broadcasted over Tor. + +For an example script see [bitcoin-submittx](https://github.com/laanwj/bitcoin-submittx). + +Privacy: Stream isolation for Tor +---------------------------------- + +This release adds functionality to create a new circuit for every peer +connection, when the software is used with Tor. The new option, +`-proxyrandomize`, is on by default. + +When enabled, every outgoing connection will (potentially) go through a +different exit node. That significantly reduces the chance to get unlucky and +pick a single exit node that is either malicious, or widely banned from the P2P +network. This improves connection reliability as well as privacy, especially +for the initial connections. + +**Important note:** If a non-Tor SOCKS5 proxy is configured that supports +authentication, but doesn't require it, this change may cause that proxy to reject +connections. A user and password is sent where they weren't before. This setup +is exceedingly rare, but in this case `-proxyrandomize=0` can be passed to +disable the behavior. + +0.11.0 Change log +================= + +Detailed release notes follow. This overview includes changes that affect +behavior, not code moves, refactors and string updates. For convenience in locating +the code changes and accompanying discussion, both the pull request and +git merge commit are mentioned. + +### RPC and REST +- #5461 `5f7279a` signrawtransaction: validate private key +- #5444 `103f66b` Add /rest/headers/<count>/<hash>.<ext> +- #4964 `95ecc0a` Add scriptPubKey field to validateaddress RPC call +- #5476 `c986972` Add time offset into getpeerinfo output +- #5540 `84eba47` Add unconfirmed and immature balances to getwalletinfo +- #5599 `40e96a3` Get rid of the internal miner's hashmeter +- #5711 `87ecfb0` Push down RPC locks +- #5754 `1c4e3f9` fix getblocktemplate lock issue +- #5756 `5d901d8` Fix getblocktemplate_proposals test by mining one block +- #5548 `d48ce48` Add /rest/chaininfos +- #5992 `4c4f1b4` Push down RPC reqWallet flag +- #6036 `585b5db` Show zero value txouts in listunspent +- #5199 `6364408` Add RPC call `gettxoutproof` to generate and verify merkle blocks +- #5418 `16341cc` Report missing inputs in sendrawtransaction +- #5937 `40f5e8d` show script verification errors in signrawtransaction result +- #5420 `1fd2d39` getutxos REST command (based on Bip64) +- #6193 `42746b0` [REST] remove json input for getutxos, limit to query max. 15 outpoints +- #6226 `5901596` json: fail read_string if string contains trailing garbage + +### Configuration and command-line options +- #5636 `a353ad4` Add option `-allowselfsignedrootcertificate` to allow self signed root certs (for testing payment requests) +- #5900 `3e8a1f2` Add a consistency check `-checkblockindex` for the block chain data structures +- #5951 `7efc9cf` Make it possible to disable wallet transaction broadcast (using `-walletbroadcast=0`) +- #5911 `b6ea3bc` privacy: Stream isolation for Tor (on by default, use `-proxyrandomize=0` to disable) +- #5863 `c271304` Add autoprune functionality (`-prune=<size>`) +- #6153 `0bcf04f` Parameter interaction: disable upnp if -proxy set +- #6274 `4d9c7fe` Add option `-alerts` to opt out of alert system + +### Block and transaction handling +- #5367 `dcc1304` Do all block index writes in a batch +- #5253 `203632d` Check against MANDATORY flags prior to accepting to mempool +- #5459 `4406c3e` Reject headers that build on an invalid parent +- #5481 `055f3ae` Apply AreSane() checks to the fees from the network +- #5580 `40d65eb` Preemptively catch a few potential bugs +- #5349 `f55c5e9` Implement test for merkle tree malleability in CPartialMerkleTree +- #5564 `a89b837` clarify obscure uses of EvalScript() +- #5521 `8e4578a` Reject non-final txs even in testnet/regtest +- #5707 `6af674e` Change hardcoded character constants to descriptive named constants for db keys +- #5286 `fcf646c` Change the default maximum OP_RETURN size to 80 bytes +- #5710 `175d86e` Add more information to errors in ReadBlockFromDisk +- #5948 `b36f1ce` Use GetAncestor to compute new target +- #5959 `a0bfc69` Add additional block index consistency checks +- #6058 `7e0e7f8` autoprune minor post-merge improvements +- #5159 `2cc1372` New fee estimation code +- #6102 `6fb90d8` Implement accurate UTXO cache size accounting +- #6129 `2a82298` Bug fix for clearing fCheckForPruning +- #5947 `e9af4e6` Alert if it is very likely we are getting a bad chain +- #6203 `c00ae64` Remove P2SH coinbase flag, no longer interesting +- #5985 `37b4e42` Fix removing of orphan transactions +- #6221 `6cb70ca` Prune: Support noncontiguous block files +- #6256 `fce474c` Use best header chain timestamps to detect partitioning +- #6233 `a587606` Advance pindexLastCommonBlock for blocks in chainActive + +### P2P protocol and network code +- #5507 `844ace9` Prevent DOS attacks on in-flight data structures +- #5770 `32a8b6a` Sanitize command strings before logging them +- #5859 `dd4ffce` Add correct bool combiner for net signals +- #5876 `8e4fd0c` Add a NODE_GETUTXO service bit and document NODE_NETWORK +- #6028 `b9311fb` Move nLastTry from CAddress to CAddrInfo +- #5662 `5048465` Change download logic to allow calling getdata on inbound peers +- #5971 `18d2832` replace absolute sleep with conditional wait +- #5918 `7bf5d5e` Use equivalent PoW for non-main-chain requests +- #6059 `f026ab6` chainparams: use SeedSpec6's rather than CAddress's for fixed seeds +- #6080 `31c0bf1` Add jonasschnellis dns seeder +- #5976 `9f7809f` Reduce download timeouts as blocks arrive +- #6172 `b4bbad1` Ignore getheaders requests when not synced +- #5875 `304892f` Be stricter in processing unrequested blocks +- #6333 `41bbc85` Hardcoded seeds update June 2015 + +### Validation +- #5143 `48e1765` Implement BIP62 rule 6 +- #5713 `41e6e4c` Implement BIP66 + +### Build system +- #5501 `c76c9d2` Add mips, mipsel and aarch64 to depends platforms +- #5334 `cf87536` libbitcoinconsensus: Add pkg-config support +- #5514 `ed11d53` Fix 'make distcheck' +- #5505 `a99ef7d` Build winshutdownmonitor.cpp on Windows only +- #5582 `e8a6639` Osx toolchain update +- #5684 `ab64022` osx: bump build sdk to 10.9 +- #5695 `23ef5b7` depends: latest config.guess and config.sub +- #5509 `31dedb4` Fixes when compiling in c++11 mode +- #5819 `f8e68f7` release: use static libstdc++ and disable reduced exports by default +- #5510 `7c3fbc3` Big endian support +- #5149 `c7abfa5` Add script to verify all merge commits are signed +- #6082 `7abbb7e` qt: disable qt tests when one of the checks for the gui fails +- #6244 `0401aa2` configure: Detect (and reject) LibreSSL +- #6269 `95aca44` gitian: Use the new bitcoin-detached-sigs git repo for OSX signatures +- #6285 `ef1d506` Fix scheduler build with some boost versions. +- #6280 `25c2216` depends: fix Boost 1.55 build on GCC 5 +- #6303 `b711599` gitian: add a gitian-win-signer descriptor +- #6246 `8ea6d37` Fix build on FreeBSD +- #6282 `daf956b` fix crash on shutdown when e.g. changing -txindex and abort action +- #6354 `bdf0d94` Gitian windows signing normalization + +### Wallet +- #2340 `811c71d` Discourage fee sniping with nLockTime +- #5485 `d01bcc4` Enforce minRelayTxFee on wallet created tx and add a maxtxfee option +- #5508 `9a5cabf` Add RandAddSeedPerfmon to MakeNewKey +- #4805 `8204e19` Do not flush the wallet in AddToWalletIfInvolvingMe(..) +- #5319 `93b7544` Clean up wallet encryption code +- #5831 `df5c246` Subtract fee from amount +- #6076 `6c97fd1` wallet: fix boost::get usage with boost 1.58 +- #5511 `23c998d` Sort pending wallet transactions before reaccepting +- #6126 `26e08a1` Change default nTxConfirmTarget to 2 +- #6183 `75a4d51` Fix off-by-one error w/ nLockTime in the wallet +- #6276 `c9fd907` Fix getbalance * 0 + +### GUI +- #5219 `f3af0c8` New icons +- #5228 `bb3c75b` HiDPI (retina) support for splash screen +- #5258 `73cbf0a` The RPC Console should be a QWidget to make window more independent +- #5488 `851dfc7` Light blue icon color for regtest +- #5547 `a39aa74` New icon for the debug window +- #5493 `e515309` Adopt style colour for button icons +- #5557 `70477a0` On close of splashscreen interrupt verifyDB +- #5559 `83be8fd` Make the command-line-args dialog better +- #5144 `c5380a9` Elaborate on signverify message dialog warning +- #5489 `d1aa3c6` Optimize PNG files +- #5649 `e0cd2f5` Use text-color icons for system tray Send/Receive menu entries +- #5651 `848f55d` Coin Control: Use U+2248 "ALMOST EQUAL TO" rather than a simple tilde +- #5626 `ab0d798` Fix icon sizes and column width +- #5683 `c7b22aa` add new osx dmg background picture +- #5620 `7823598` Payment request expiration bug fix +- #5729 `9c4a5a5` Allow unit changes for read-only BitcoinAmountField +- #5753 `0f44672` Add bitcoin logo to about screen +- #5629 `a956586` Prevent amount overflow problem with payment requests +- #5830 `215475a` Don't save geometry for options and about/help window +- #5793 `d26f0b2` Honor current network when creating autostart link +- #5847 `f238add` Startup script for centos, with documentation +- #5915 `5bd3a92` Fix a static qt5 crash when using certain versions of libxcb +- #5898 `bb56781` Fix rpc console font size to flexible metrics +- #5467 `bc8535b` Payment request / server work - part 2 +- #6161 `180c164` Remove movable option for toolbar +- #6160 `0d862c2` Overviewpage: make sure warning icons gets colored + +### Tests +- #5453 `2f2d337` Add ability to run single test manually to RPC tests +- #5421 `886eb57` Test unexecuted OP_CODESEPARATOR +- #5530 `565b300` Additional rpc tests +- #5611 `37b185c` Fix spurious windows test failures after 012598880c +- #5613 `2eda47b` Fix smartfees test for change to relay policy +- #5612 `e3f5727` Fix zapwallettxes test +- #5642 `30a5b5f` Prepare paymentservertests for new unit tests +- #5784 `e3a3cd7` Fix usage of NegateSignatureS in script_tests +- #5813 `ee9f2bf` Add unit tests for next difficulty calculations +- #5855 `d7989c0` Travis: run unit tests in different orders +- #5852 `cdae53e` Reinitialize state in between individual unit tests. +- #5883 `164d7b6` tests: add a BasicTestingSetup and apply to all tests +- #5940 `446bb70` Regression test for ResendWalletTransactions +- #6052 `cf7adad` fix and enable bip32 unit test +- #6039 `734f80a` tests: Error when setgenerate is used on regtest +- #6074 `948beaf` Correct the PUSHDATA4 minimal encoding test in script_invalid.json +- #6032 `e08886d` Stop nodes after RPC tests, even with --nocleanup +- #6075 `df1609f` Add additional script edge condition tests +- #5981 `da38dc6` Python P2P testing +- #5958 `9ef00c3` Add multisig rpc tests +- #6112 `fec5c0e` Add more script edge condition tests + +### Miscellaneous +- #5457, #5506, #5952, #6047 Update libsecp256k1 +- #5437 `84857e8` Add missing CAutoFile::IsNull() check in main +- #5490 `ec20fd7` Replace uint256/uint160 with opaque blobs where possible +- #5654, #5764 Adding jonasschnelli's GPG key +- #5477 `5f04d1d` OS X 10.10: LSSharedFileListItemResolve() is deprecated +- #5679 `beff11a` Get rid of DetectShutdownThread +- #5787 `9bd8c9b` Add fanquake PGP key +- #5366 `47a79bb` No longer check osx compatibility in RenameThread +- #5689 `07f4386` openssl: abstract out OPENSSL_cleanse +- #5708 `8b298ca` Add list of implemented BIPs +- #5809 `46bfbe7` Add bitcoin-cli man page +- #5839 `86eb461` keys: remove libsecp256k1 verification until it's actually supported +- #5749 `d734d87` Help messages correctly formatted (79 chars) +- #5884 `7077fe6` BUGFIX: Stack around the variable 'rv' was corrupted +- #5849 `41259ca` contrib/init/bitcoind.openrc: Compatibility with previous OpenRC init script variables +- #5950 `41113e3` Fix locale fallback and guard tests against invalid locale settings +- #5965 `7c6bfb1` Add git-subtree-check.sh script +- #6033 `1623f6e` FreeBSD, OpenBSD thread renaming +- #6064 `b46e7c2` Several changes to mruset +- #6104 `3e2559c` Show an init message while activating best chain +- #6125 `351f73e` Clean up parsing of bool command line args +- #5964 `b4c219b` Lightweight task scheduler +- #6116 `30dc3c1` [OSX] rename Bitcoin-Qt.app to Bitcoin-Core.app +- #6168 `b3024f0` contrib/linearize: Support linearization of testnet blocks +- #6098 `7708fcd` Update Windows resource files (and add one for bitcoin-tx) +- #6159 `e1412d3` Catch errors on datadir lock and pidfile delete +- #6186 `182686c` Fix two problems in CSubnet parsing +- #6174 `df992b9` doc: add translation strings policy +- #6210 `dfdb6dd` build: disable optional use of gmp in internal secp256k1 build +- #6264 `94cd705` Remove translation for -help-debug options +- #6286 `3902c15` Remove berkeley-db4 workaround in MacOSX build docs +- #6319 `3f8fcc9` doc: update mailing list address + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- 21E14 +- Adam Weiss +- Alex Morcos +- ayeowch +- azeteki +- Ben Holden-Crowther +- bikinibabe +- BitcoinPRReadingGroup +- Blake Jakopovic +- BtcDrak +- charlescharles +- Chris Arnesen +- Ciemon +- CohibAA +- Corinne Dashjr +- Cory Fields +- Cozz Lovan +- Daira Hopwood +- Daniel Kraft +- Dave Collins +- David A. Harding +- dexX7 +- Earlz +- Eric Lombrozo +- Eric R. Schulz +- Everett Forth +- Flavien Charlon +- fsb4000 +- Gavin Andresen +- Gregory Maxwell +- Heath +- Ivan Pustogarov +- Jacob Welsh +- Jameson Lopp +- Jason Lewicki +- Jeff Garzik +- Jonas Schnelli +- Jonathan Brown +- Jorge Timón +- joshr +- jtimon +- Julian Yap +- Luca Venturini +- Luke Dashjr +- Manuel Araoz +- MarcoFalke +- Matt Bogosian +- Matt Corallo +- Micha +- Michael Ford +- Mike Hearn +- mrbandrews +- Nicolas Benoit +- paveljanik +- Pavel JanÃk +- Pavel Vasin +- Peter Todd +- Philip Kaufmann +- Pieter Wuille +- pstratem +- randy-waterhouse +- rion +- Rob Van Mieghem +- Ross Nicoll +- Ruben de Vries +- sandakersmann +- Shaul Kfir +- Shawn Wilkinson +- sinetek +- Suhas Daftuar +- svost +- Thomas Zander +- Tom Harding +- UdjinM6 +- Vitalii Demianets +- Wladimir J. van der Laan + +And all those who contributed additional code review and/or security research: + +- Sergio Demian Lerner + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). + diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index ce52247b2e..fc29789218 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -13,14 +13,15 @@ class RawTransactionsTest(BitcoinTestFramework): def setup_chain(self): print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) + initialize_chain_clean(self.options.tmpdir, 4) def setup_network(self, split=False): - self.nodes = start_nodes(3, self.options.tmpdir) + self.nodes = start_nodes(4, self.options.tmpdir) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes,0,3) self.is_network_split=False self.sync_all() @@ -31,11 +32,20 @@ class RawTransactionsTest(BitcoinTestFramework): self.nodes[2].generate(1) self.sync_all() - self.nodes[0].generate(101) + self.nodes[0].generate(121) self.sync_all() + + watchonly_address = self.nodes[0].getnewaddress() + watchonly_pubkey = self.nodes[0].validateaddress(watchonly_address)["pubkey"] + watchonly_amount = 200 + self.nodes[3].importpubkey(watchonly_pubkey, "", True) + watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount) + self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), watchonly_amount / 10); + self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.5); self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.0); self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),5.0); + self.sync_all() self.nodes[0].generate(1) self.sync_all() @@ -428,11 +438,12 @@ class RawTransactionsTest(BitcoinTestFramework): stop_nodes(self.nodes) wait_bitcoinds() - self.nodes = start_nodes(3, self.options.tmpdir) + self.nodes = start_nodes(4, self.options.tmpdir) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes,0,3) self.is_network_split=False self.sync_all() @@ -541,5 +552,45 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(len(dec_tx['vout']), 2) # one change output added + ################################################## + # test a fundrawtransaction using only watchonly # + ################################################## + + inputs = [] + outputs = {self.nodes[2].getnewaddress() : watchonly_amount / 2} + rawtx = self.nodes[3].createrawtransaction(inputs, outputs) + + result = self.nodes[3].fundrawtransaction(rawtx, True) + res_dec = self.nodes[0].decoderawtransaction(result["hex"]) + assert_equal(len(res_dec["vin"]), 1) + assert_equal(res_dec["vin"][0]["txid"], watchonly_txid) + + assert_equal("fee" in result.keys(), True) + assert_greater_than(result["changepos"], -1) + + ############################################################### + # test fundrawtransaction using the entirety of watched funds # + ############################################################### + + inputs = [] + outputs = {self.nodes[2].getnewaddress() : watchonly_amount} + rawtx = self.nodes[3].createrawtransaction(inputs, outputs) + + result = self.nodes[3].fundrawtransaction(rawtx, True) + res_dec = self.nodes[0].decoderawtransaction(result["hex"]) + assert_equal(len(res_dec["vin"]), 2) + assert(res_dec["vin"][0]["txid"] == watchonly_txid or res_dec["vin"][1]["txid"] == watchonly_txid) + + assert_greater_than(result["fee"], 0) + assert_greater_than(result["changepos"], -1) + assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], watchonly_amount / 10) + + signedtx = self.nodes[3].signrawtransaction(result["hex"]) + assert(not signedtx["complete"]) + signedtx = self.nodes[0].signrawtransaction(signedtx["hex"]) + assert(signedtx["complete"]) + self.nodes[0].sendrawtransaction(signedtx["hex"]) + + if __name__ == '__main__': RawTransactionsTest().main() diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index aee29a596a..5a67220021 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -73,6 +73,21 @@ def run_test(nodes, tmpdir): except JSONRPCException,e: assert(e.error['code']==-12) + # refill keypool with three new addresses + nodes[0].walletpassphrase('test', 12000) + nodes[0].keypoolrefill(3) + nodes[0].walletlock() + + # drain them by mining + nodes[0].generate(1) + nodes[0].generate(1) + nodes[0].generate(1) + nodes[0].generate(1) + try: + nodes[0].generate(1) + raise AssertionError('Keypool should be exhausted after three addesses') + except JSONRPCException,e: + assert(e.error['code']==-12) def main(): import optparse diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py index eeae2d2fa2..b30a6bc9d1 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -93,6 +93,16 @@ class ListTransactionsTest(BitcoinTestFramework): {"category":"receive","amount":Decimal("0.44")}, {"txid":txid, "account" : "toself"} ) + multisig = self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()]) + self.nodes[0].importaddress(multisig["redeemScript"], "watchonly", False, True) + txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1) + self.nodes[1].generate(1) + self.sync_all() + assert(len(self.nodes[0].listtransactions("watchonly", 100, 0, False)) == 0) + check_array_result(self.nodes[0].listtransactions("watchonly", 100, 0, True), + {"category":"receive","amount":Decimal("0.1")}, + {"txid":txid, "account" : "watchonly"} ) + if __name__ == '__main__': ListTransactionsTest().main() diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py index fcdd1e1b99..83c03eeb78 100755 --- a/qa/rpc-tests/p2p-acceptblock.py +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -40,6 +40,11 @@ The test: it's missing an intermediate block. Node1 should reorg to this longer chain. +4b.Send 288 more blocks on the longer chain. + Node0 should process all but the last block (too far ahead in height). + Send all headers to Node1, and then send the last block in that chain. + Node1 should accept the block because it's coming from a whitelisted peer. + 5. Send a duplicate of the block in #3 to Node0. Node0 should not process the block because it is unrequested, and stay on the shorter chain. @@ -59,6 +64,8 @@ class TestNode(NodeConnCB): NodeConnCB.__init__(self) self.create_callback_map() self.connection = None + self.ping_counter = 1 + self.last_pong = msg_pong() def add_connection(self, conn): self.connection = conn @@ -82,6 +89,24 @@ class TestNode(NodeConnCB): def send_message(self, message): self.connection.send_message(message) + def on_pong(self, conn, message): + self.last_pong = message + + # Sync up with the node after delivery of a block + def sync_with_ping(self, timeout=30): + self.connection.send_message(msg_ping(nonce=self.ping_counter)) + received_pong = False + sleep_time = 0.05 + while not received_pong and timeout > 0: + time.sleep(sleep_time) + timeout -= sleep_time + with mininode_lock: + if self.last_pong.nonce == self.ping_counter: + received_pong = True + self.ping_counter += 1 + return received_pong + + class AcceptBlockTest(BitcoinTestFramework): def add_options(self, parser): parser.add_option("--testbinary", dest="testbinary", @@ -126,13 +151,15 @@ class AcceptBlockTest(BitcoinTestFramework): # 2. Send one block that builds on each tip. # This should be accepted. blocks_h2 = [] # the height 2 blocks on each node's chain + block_time = time.time() + 1 for i in xrange(2): - blocks_h2.append(create_block(tips[i], create_coinbase(), time.time()+1)) + blocks_h2.append(create_block(tips[i], create_coinbase(), block_time)) blocks_h2[i].solve() + block_time += 1 test_node.send_message(msg_block(blocks_h2[0])) white_node.send_message(msg_block(blocks_h2[1])) - time.sleep(1) + [ x.sync_with_ping() for x in [test_node, white_node] ] assert_equal(self.nodes[0].getblockcount(), 2) assert_equal(self.nodes[1].getblockcount(), 2) print "First height 2 block accepted by both nodes" @@ -145,7 +172,7 @@ class AcceptBlockTest(BitcoinTestFramework): test_node.send_message(msg_block(blocks_h2f[0])) white_node.send_message(msg_block(blocks_h2f[1])) - time.sleep(1) # Give time to process the block + [ x.sync_with_ping() for x in [test_node, white_node] ] for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h2f[0].hash: assert_equal(x['status'], "headers-only") @@ -164,7 +191,7 @@ class AcceptBlockTest(BitcoinTestFramework): test_node.send_message(msg_block(blocks_h3[0])) white_node.send_message(msg_block(blocks_h3[1])) - time.sleep(1) + [ x.sync_with_ping() for x in [test_node, white_node] ] # Since the earlier block was not processed by node0, the new block # can't be fully validated. for x in self.nodes[0].getchaintips(): @@ -182,6 +209,45 @@ class AcceptBlockTest(BitcoinTestFramework): assert_equal(self.nodes[1].getblockcount(), 3) print "Successfully reorged to length 3 chain from whitelisted peer" + # 4b. Now mine 288 more blocks and deliver; all should be processed but + # the last (height-too-high) on node0. Node1 should process the tip if + # we give it the headers chain leading to the tip. + tips = blocks_h3 + headers_message = msg_headers() + all_blocks = [] # node0's blocks + for j in xrange(2): + for i in xrange(288): + next_block = create_block(tips[j].sha256, create_coinbase(), tips[j].nTime+1) + next_block.solve() + if j==0: + test_node.send_message(msg_block(next_block)) + all_blocks.append(next_block) + else: + headers_message.headers.append(CBlockHeader(next_block)) + tips[j] = next_block + + time.sleep(2) + for x in all_blocks: + try: + self.nodes[0].getblock(x.hash) + if x == all_blocks[287]: + raise AssertionError("Unrequested block too far-ahead should have been ignored") + except: + if x == all_blocks[287]: + print "Unrequested block too far-ahead not processed" + else: + raise AssertionError("Unrequested block with more work should have been accepted") + + headers_message.headers.pop() # Ensure the last block is unrequested + white_node.send_message(headers_message) # Send headers leading to tip + white_node.send_message(msg_block(tips[1])) # Now deliver the tip + try: + white_node.sync_with_ping() + self.nodes[1].getblock(tips[1].hash) + print "Unrequested block far ahead of tip accepted from whitelisted peer" + except: + raise AssertionError("Unrequested block from whitelisted peer not accepted") + # 5. Test handling of unrequested block on the node that didn't process # Should still not be processed (even though it has a child that has more # work). @@ -192,7 +258,7 @@ class AcceptBlockTest(BitcoinTestFramework): # the node processes it and incorrectly advances the tip). # But this would be caught later on, when we verify that an inv triggers # a getdata request for this block. - time.sleep(1) + test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) print "Unrequested block that would complete more-work chain was ignored" @@ -204,21 +270,20 @@ class AcceptBlockTest(BitcoinTestFramework): test_node.last_getdata = None test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)])) - time.sleep(1) + test_node.sync_with_ping() with mininode_lock: getdata = test_node.last_getdata - # Check that the getdata is for the right block - assert_equal(len(getdata.inv), 1) + # Check that the getdata includes the right block assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256) print "Inv at tip triggered getdata for unprocessed block" # 7. Send the missing block for the third time (now it is requested) test_node.send_message(msg_block(blocks_h2f[0])) - time.sleep(1) - assert_equal(self.nodes[0].getblockcount(), 3) - print "Successfully reorged to length 3 chain from non-whitelisted peer" + test_node.sync_with_ping() + assert_equal(self.nodes[0].getblockcount(), 290) + print "Successfully reorged to longer chain from non-whitelisted peer" [ c.disconnect_node() for c in connections ] diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py index b0cde7268e..3a035f996c 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -200,7 +200,7 @@ class RESTTest (BitcoinTestFramework): response = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True) assert_equal(response.status, 200) #must be a 500 because we exceeding the limits - self.nodes[0].generate(1) #generate block to not affect upcomming tests + self.nodes[0].generate(1) #generate block to not affect upcoming tests self.sync_all() ################ @@ -292,6 +292,19 @@ class RESTTest (BitcoinTestFramework): txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)) self.sync_all() + # check that there are exactly 3 transactions in the TX memory pool before generating the block + json_string = http_get_call(url.hostname, url.port, '/rest/mempool/info'+self.FORMAT_SEPARATOR+'json') + json_obj = json.loads(json_string) + assert_equal(json_obj['size'], 3) + # the size of the memory pool should be greater than 3x ~100 bytes + assert_greater_than(json_obj['bytes'], 300) + + # check that there are our submitted transactions in the TX memory pool + json_string = http_get_call(url.hostname, url.port, '/rest/mempool/contents'+self.FORMAT_SEPARATOR+'json') + json_obj = json.loads(json_string) + for tx in txs: + assert_equal(tx in json_obj, True) + # now mine the transactions newblockhash = self.nodes[1].generate(1) self.sync_all() diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index 7fb31d4a06..b945f1bf29 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -27,6 +27,20 @@ generator that returns TestInstance objects. See below for definition. global mininode_lock +def wait_until(predicate, attempts=float('inf'), timeout=float('inf')): + attempt = 0 + elapsed = 0 + + while attempt < attempts and elapsed < timeout: + with mininode_lock: + if predicate(): + return True + attempt += 1 + elapsed += 0.05 + time.sleep(0.05) + + return False + class TestNode(NodeConnCB): def __init__(self, block_store, tx_store): @@ -43,6 +57,10 @@ class TestNode(NodeConnCB): # a response self.pingMap = {} self.lastInv = [] + self.closed = False + + def on_close(self, conn): + self.closed = True def add_connection(self, conn): self.conn = conn @@ -116,7 +134,7 @@ class TestNode(NodeConnCB): # is reached) and then sent out in one inv message. Then the final block # will be synced across all connections, and the outcome of the final # block will be tested. -# sync_every_tx: analagous to behavior for sync_every_block, except if outcome +# sync_every_tx: analogous to behavior for sync_every_block, except if outcome # on the final tx is None, then contents of entire mempool are compared # across all connections. (If outcome of final tx is specified as true # or false, then only the last tx is tested against outcome.) @@ -132,6 +150,7 @@ class TestManager(object): def __init__(self, testgen, datadir): self.test_generator = testgen self.connections = [] + self.test_nodes = [] self.block_store = BlockStore(datadir) self.tx_store = TxStore(datadir) self.ping_counter = 1 @@ -139,54 +158,40 @@ class TestManager(object): def add_all_connections(self, nodes): for i in range(len(nodes)): # Create a p2p connection to each node - self.connections.append(NodeConn('127.0.0.1', p2p_port(i), - nodes[i], TestNode(self.block_store, self.tx_store))) + test_node = TestNode(self.block_store, self.tx_store) + self.test_nodes.append(test_node) + self.connections.append(NodeConn('127.0.0.1', p2p_port(i), nodes[i], test_node)) # Make sure the TestNode (callback class) has a reference to its # associated NodeConn - self.connections[-1].cb.add_connection(self.connections[-1]) + test_node.add_connection(self.connections[-1]) + + def wait_for_disconnections(self): + def disconnected(): + return all(node.closed for node in self.test_nodes) + return wait_until(disconnected, timeout=10) def wait_for_verack(self): - sleep_time = 0.05 - max_tries = 10 / sleep_time # Wait at most 10 seconds - while max_tries > 0: - done = True - with mininode_lock: - for c in self.connections: - if c.cb.verack_received is False: - done = False - break - if done: - break - time.sleep(sleep_time) + def veracked(): + return all(node.verack_received for node in self.test_nodes) + return wait_until(veracked, timeout=10) def wait_for_pings(self, counter): - received_pongs = False - while received_pongs is not True: - time.sleep(0.05) - received_pongs = True - with mininode_lock: - for c in self.connections: - if c.cb.received_ping_response(counter) is not True: - received_pongs = False - break + def received_pongs(): + return all(node.received_ping_response(counter) for node in self.test_nodes) + return wait_until(received_pongs) # sync_blocks: Wait for all connections to request the blockhash given # then send get_headers to find out the tip of each node, and synchronize # the response by using a ping (and waiting for pong with same nonce). def sync_blocks(self, blockhash, num_blocks): - # Wait for nodes to request block (50ms sleep * 20 tries * num_blocks) - max_tries = 20*num_blocks - while max_tries > 0: - with mininode_lock: - results = [ blockhash in c.cb.block_request_map and - c.cb.block_request_map[blockhash] for c in self.connections ] - if False not in results: - break - time.sleep(0.05) - max_tries -= 1 + def blocks_requested(): + return all( + blockhash in node.block_request_map and node.block_request_map[blockhash] + for node in self.test_nodes + ) # --> error if not requested - if max_tries == 0: + if not wait_until(blocks_requested, attempts=20*num_blocks): # print [ c.cb.block_request_map for c in self.connections ] raise AssertionError("Not all nodes requested block") # --> Answer request (we did this inline!) @@ -202,18 +207,14 @@ class TestManager(object): # Analogous to sync_block (see above) def sync_transaction(self, txhash, num_events): # Wait for nodes to request transaction (50ms sleep * 20 tries * num_events) - max_tries = 20*num_events - while max_tries > 0: - with mininode_lock: - results = [ txhash in c.cb.tx_request_map and - c.cb.tx_request_map[txhash] for c in self.connections ] - if False not in results: - break - time.sleep(0.05) - max_tries -= 1 + def transaction_requested(): + return all( + txhash in node.tx_request_map and node.tx_request_map[txhash] + for node in self.test_nodes + ) # --> error if not requested - if max_tries == 0: + if not wait_until(transaction_requested, attempts=20*num_events): # print [ c.cb.tx_request_map for c in self.connections ] raise AssertionError("Not all nodes requested transaction") # --> Answer request (we did this inline!) @@ -336,6 +337,7 @@ class TestManager(object): print "Test %d: PASS" % test_number, [ c.rpc.getblockcount() for c in self.connections ] test_number += 1 + [ c.disconnect_node() for c in self.connections ] + self.wait_for_disconnections() self.block_store.close() self.tx_store.close() - [ c.disconnect_node() for c in self.connections ] diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 2ec3468e06..748f2b14d5 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -185,13 +185,13 @@ BITCOIN_QT_H = \ qt/paymentrequestplus.h \ qt/paymentserver.h \ qt/peertablemodel.h \ + qt/platformstyle.h \ qt/qvalidatedlineedit.h \ qt/qvaluecombobox.h \ qt/receivecoinsdialog.h \ qt/receiverequestdialog.h \ qt/recentrequeststablemodel.h \ qt/rpcconsole.h \ - qt/scicon.h \ qt/sendcoinsdialog.h \ qt/sendcoinsentry.h \ qt/signverifymessagedialog.h \ @@ -273,10 +273,10 @@ BITCOIN_QT_CPP = \ qt/optionsdialog.cpp \ qt/optionsmodel.cpp \ qt/peertablemodel.cpp \ + qt/platformstyle.cpp \ qt/qvalidatedlineedit.cpp \ qt/qvaluecombobox.cpp \ qt/rpcconsole.cpp \ - qt/scicon.cpp \ qt/splashscreen.cpp \ qt/trafficgraphwidget.cpp \ qt/utilitydialog.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index f9384a09a4..fc4e047c35 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -15,6 +15,8 @@ EXTRA_DIST += \ test/data/tx394b54bb.hex \ test/data/txcreate1.hex \ test/data/txcreate2.hex \ + test/data/txcreatedata1.hex \ + test/data/txcreatedata2.hex \ test/data/txcreatesign.hex JSON_TEST_FILES = \ @@ -50,6 +52,7 @@ BITCOIN_TESTS =\ test/getarg_tests.cpp \ test/hash_tests.cpp \ test/key_tests.cpp \ + test/limitedmap_tests.cpp \ test/main_tests.cpp \ test/mempool_tests.cpp \ test/miner_tests.cpp \ diff --git a/src/addrman.h b/src/addrman.h index 2623d89809..384b6cfdb9 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -265,7 +265,7 @@ public: * Notice that vvTried, mapAddr and vVector are never encoded explicitly; * they are instead reconstructed from the other information. * - * vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change, + * vvNew is serialized, but only used if ADDRMAN_UNKNOWN_BUCKET_COUNT didn't change, * otherwise it is reconstructed as well. * * This format is more complex, but significantly smaller (at most 1.5 MiB), and supports diff --git a/src/amount.cpp b/src/amount.cpp index 0a394c96fc..b469181984 100644 --- a/src/amount.cpp +++ b/src/amount.cpp @@ -7,6 +7,8 @@ #include "tinyformat.h" +const std::string CURRENCY_UNIT = "BTC"; + CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nSize) { if (nSize > 0) @@ -27,5 +29,5 @@ CAmount CFeeRate::GetFee(size_t nSize) const std::string CFeeRate::ToString() const { - return strprintf("%d.%08d BTC/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN); + return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT); } diff --git a/src/amount.h b/src/amount.h index 7dc62edac4..90e6b5aa8e 100644 --- a/src/amount.h +++ b/src/amount.h @@ -16,6 +16,8 @@ typedef int64_t CAmount; static const CAmount COIN = 100000000; static const CAmount CENT = 1000000; +extern const std::string CURRENCY_UNIT; + /** No amount larger than this (in satoshi) is valid. * * Note that this constant is *not* the total money supply, which in Bitcoin diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 9ad57d5c6f..97a073174d 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -70,6 +70,7 @@ static bool AppInitRawTx(int argc, char* argv[]) strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N")); strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX")); + strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX")); strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX")); strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " + _("This command requires JSON registers:") + @@ -143,13 +144,14 @@ static void RegisterLoad(const string& strInput) valStr.insert(valStr.size(), buf, bread); } - if (ferror(f)) { + int error = ferror(f); + fclose(f); + + if (error) { string strErr = "Error reading file " + filename; throw runtime_error(strErr); } - fclose(f); - // evaluate as JSON buffer register RegisterSetJson(key, valStr); } @@ -230,6 +232,35 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput) tx.vout.push_back(txout); } +static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput) +{ + CAmount value = 0; + + // separate [VALUE:]DATA in string + size_t pos = strInput.find(':'); + + if (pos==0) + throw runtime_error("TX output value not specified"); + + if (pos != string::npos) { + // extract and validate VALUE + string strValue = strInput.substr(0, pos); + if (!ParseMoney(strValue, value)) + throw runtime_error("invalid TX output value"); + } + + // extract and validate DATA + string strData = strInput.substr(pos + 1, string::npos); + + if (!IsHex(strData)) + throw runtime_error("invalid TX output data"); + + std::vector<unsigned char> data = ParseHex(strData); + + CTxOut txout(value, CScript() << OP_RETURN << data); + tx.vout.push_back(txout); +} + static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput) { // separate VALUE:SCRIPT in string @@ -469,6 +500,8 @@ static void MutateTx(CMutableTransaction& tx, const string& command, MutateTxDelOutput(tx, commandVal); else if (command == "outaddr") MutateTxAddOutAddr(tx, commandVal); + else if (command == "outdata") + MutateTxAddOutData(tx, commandVal); else if (command == "outscript") MutateTxAddOutScript(tx, commandVal); diff --git a/src/bloom.cpp b/src/bloom.cpp index 36cba491c4..de87206592 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -8,6 +8,7 @@ #include "hash.h" #include "script/script.h" #include "script/standard.h" +#include "random.h" #include "streams.h" #include <math.h> @@ -121,6 +122,12 @@ void CBloomFilter::clear() isEmpty = true; } +void CBloomFilter::reset(unsigned int nNewTweak) +{ + clear(); + nTweak = nNewTweak; +} + bool CBloomFilter::IsWithinSizeConstraints() const { return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; @@ -209,15 +216,17 @@ void CBloomFilter::UpdateEmptyFull() isEmpty = empty; } -CRollingBloomFilter::CRollingBloomFilter(unsigned int nElements, double fpRate, unsigned int nTweak) : - b1(nElements * 2, fpRate, nTweak), b2(nElements * 2, fpRate, nTweak) +CRollingBloomFilter::CRollingBloomFilter(unsigned int nElements, double fpRate) : + b1(nElements * 2, fpRate, 0), b2(nElements * 2, fpRate, 0) { // Implemented using two bloom filters of 2 * nElements each. // We fill them up, and clear them, staggered, every nElements // inserted, so at least one always contains the last nElements // inserted. - nBloomSize = nElements * 2; nInsertions = 0; + nBloomSize = nElements * 2; + + reset(); } void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey) @@ -234,6 +243,12 @@ void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey) } } +void CRollingBloomFilter::insert(const uint256& hash) +{ + vector<unsigned char> data(hash.begin(), hash.end()); + insert(data); +} + bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const { if (nInsertions < nBloomSize / 2) { @@ -242,9 +257,16 @@ bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const return b1.contains(vKey); } -void CRollingBloomFilter::clear() +bool CRollingBloomFilter::contains(const uint256& hash) const +{ + vector<unsigned char> data(hash.begin(), hash.end()); + return contains(data); +} + +void CRollingBloomFilter::reset() { - b1.clear(); - b2.clear(); + unsigned int nNewTweak = GetRand(std::numeric_limits<unsigned int>::max()); + b1.reset(nNewTweak); + b2.reset(nNewTweak); nInsertions = 0; } diff --git a/src/bloom.h b/src/bloom.h index bb17f59c86..a4dba8cb4f 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -89,6 +89,7 @@ public: bool contains(const uint256& hash) const; void clear(); + void reset(unsigned int nNewTweak); //! True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS //! (catch a filter which was just deserialized which was too big) @@ -103,7 +104,11 @@ public: /** * RollingBloomFilter is a probabilistic "keep track of most recently inserted" set. - * Construct it with the number of items to keep track of, and a false-positive rate. + * Construct it with the number of items to keep track of, and a false-positive + * rate. Unlike CBloomFilter, by default nTweak is set to a cryptographically + * secure random value for you. Similarly rather than clear() the method + * reset() is provided, which also changes nTweak to decrease the impact of + * false-positives. * * contains(item) will always return true if item was one of the last N things * insert()'ed ... but may also return true for items that were not inserted. @@ -111,12 +116,17 @@ public: class CRollingBloomFilter { public: - CRollingBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak); + // A random bloom filter calls GetRand() at creation time. + // Don't create global CRollingBloomFilter objects, as they may be + // constructed before the randomizer is properly initialized. + CRollingBloomFilter(unsigned int nElements, double nFPRate); void insert(const std::vector<unsigned char>& vKey); + void insert(const uint256& hash); bool contains(const std::vector<unsigned char>& vKey) const; + bool contains(const uint256& hash) const; - void clear(); + void reset(); private: unsigned int nBloomSize; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c9dee25c57..9c843f6e7e 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -14,7 +14,7 @@ #include "chainparamsseeds.h" -static CBlock CreateGenesisBlock(const char* pszTimestamp, CScript genesisOutputScript, uint32_t nTime=1231006505, uint32_t nNonce=2083236893, uint32_t nBits=0x1d00ffff, int32_t nVersion=1, const CAmount& genesisReward=50 * COIN) +static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) { CMutableTransaction txNew; txNew.nVersion = 1; @@ -46,10 +46,10 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, CScript genesisOutput * CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) * vMerkleTree: 4a5e1e */ -static CBlock CreateGenesisBlock(uint32_t nTime=1231006505, uint32_t nNonce=2083236893, uint32_t nBits=0x1d00ffff, int32_t nVersion=1, const CAmount& genesisReward=50 * COIN) +static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) { const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; - CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; + const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); } @@ -89,7 +89,7 @@ public: nDefaultPort = 8333; nPruneAfterHeight = 100000; - genesis = CreateGenesisBlock(); + genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); assert(consensus.hashGenesisBlock == uint256S("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")); assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); @@ -162,9 +162,10 @@ public: nDefaultPort = 18333; nPruneAfterHeight = 1000; - genesis = CreateGenesisBlock(1296688602, 414098458); + genesis = CreateGenesisBlock(1296688602, 414098458, 0x1d00ffff, 1, 50 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); assert(consensus.hashGenesisBlock == uint256S("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943")); + assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); vFixedSeeds.clear(); vSeeds.clear(); @@ -214,16 +215,19 @@ public: consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; + pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0xbf; pchMessageStart[2] = 0xb5; pchMessageStart[3] = 0xda; - genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff); - consensus.hashGenesisBlock = genesis.GetHash(); nDefaultPort = 18444; - assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); nPruneAfterHeight = 1000; + genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); + assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + vFixedSeeds.clear(); //! Regtest mode doesn't have any fixed seeds. vSeeds.clear(); //! Regtest mode doesn't have any DNS seeds. diff --git a/src/coincontrol.h b/src/coincontrol.h index 3e8de83c39..bc965f9e19 100644 --- a/src/coincontrol.h +++ b/src/coincontrol.h @@ -14,6 +14,8 @@ public: CTxDestination destChange; //! If false, allows unselected inputs, but requires all selected inputs be used bool fAllowOtherInputs; + //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria + bool fAllowWatchOnly; CCoinControl() { @@ -24,6 +26,7 @@ public: { destChange = CNoDestination(); fAllowOtherInputs = false; + fAllowWatchOnly = false; setSelected.clear(); } diff --git a/src/consensus/validation.h b/src/consensus/validation.h index a97d983a31..d6051edc38 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -28,16 +28,19 @@ private: } mode; int nDoS; std::string strRejectReason; - unsigned char chRejectCode; + unsigned int chRejectCode; bool corruptionPossible; + std::string strDebugMessage; public: CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {} bool DoS(int level, bool ret = false, - unsigned char chRejectCodeIn=0, std::string strRejectReasonIn="", - bool corruptionIn=false) { + unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="", + bool corruptionIn=false, + const std::string &strDebugMessageIn="") { chRejectCode = chRejectCodeIn; strRejectReason = strRejectReasonIn; corruptionPossible = corruptionIn; + strDebugMessage = strDebugMessageIn; if (mode == MODE_ERROR) return ret; nDoS += level; @@ -45,8 +48,9 @@ public: return ret; } bool Invalid(bool ret = false, - unsigned char _chRejectCode=0, std::string _strRejectReason="") { - return DoS(0, ret, _chRejectCode, _strRejectReason); + unsigned int _chRejectCode=0, const std::string &_strRejectReason="", + const std::string &_strDebugMessage="") { + return DoS(0, ret, _chRejectCode, _strRejectReason, false, _strDebugMessage); } bool Error(const std::string& strRejectReasonIn) { if (mode == MODE_VALID) @@ -73,8 +77,9 @@ public: bool CorruptionPossible() const { return corruptionPossible; } - unsigned char GetRejectCode() const { return chRejectCode; } + unsigned int GetRejectCode() const { return chRejectCode; } std::string GetRejectReason() const { return strRejectReason; } + std::string GetDebugMessage() const { return strDebugMessage; } }; #endif // BITCOIN_CONSENSUS_VALIDATION_H diff --git a/src/ecwrapper.cpp b/src/ecwrapper.cpp index 5e3aec25ba..f94bc954fd 100644 --- a/src/ecwrapper.cpp +++ b/src/ecwrapper.cpp @@ -13,6 +13,29 @@ namespace { +class ecgroup_order +{ +public: + static const EC_GROUP* get() + { + static const ecgroup_order wrapper; + return wrapper.pgroup; + } + +private: + ecgroup_order() + : pgroup(EC_GROUP_new_by_curve_name(NID_secp256k1)) + { + } + + ~ecgroup_order() + { + EC_GROUP_free(pgroup); + } + + EC_GROUP* pgroup; +}; + /** * Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields * recid selects which key is recovered @@ -92,8 +115,10 @@ err: } // anon namespace CECKey::CECKey() { - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + pkey = EC_KEY_new(); assert(pkey != NULL); + int result = EC_KEY_set_group(pkey, ecgroup_order::get()); + assert(result); } CECKey::~CECKey() { @@ -185,11 +210,9 @@ bool CECKey::TweakPublic(const unsigned char vchTweak[32]) { bool CECKey::SanityCheck() { - EC_KEY *pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if(pkey == NULL) + const EC_GROUP *pgroup = ecgroup_order::get(); + if(pgroup == NULL) return false; - EC_KEY_free(pkey); - // TODO Is there more EC functionality that could be missing? return true; } diff --git a/src/init.cpp b/src/init.cpp index 365bd30714..085e04fdfd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -314,7 +314,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-externalip=<ip>", _("Specify your own public address")); strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), 0)); strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)")); - strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), 125)); + strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS)); strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), 5000)); strUsage += HelpMessageOpt("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), 1000)); strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy")); @@ -342,16 +342,17 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), 100)); if (showDebug) - strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)", - FormatMoney(CWallet::minTxFee.GetFeePerK()))); - strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)", + CURRENCY_UNIT, FormatMoney(CWallet::minTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), + CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions") + " " + _("on startup")); strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet.dat") + " " + _("on startup")); strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), 0)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), 1)); strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); - strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)"), - FormatMoney(maxTxFee))); + strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)"), + CURRENCY_UNIT, FormatMoney(maxTxFee))); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format") + " " + _("on startup")); strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), "wallet.dat")); strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), true)); @@ -372,7 +373,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", 1)); strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0)); } - string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, net, proxy, prune"; // Don't translate these and qt below + string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, mempoolrej, net, proxy, prune"; // Don't translate these and qt below if (mode == HMM_BITCOIN_QT) debugCategories += ", qt"; strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + @@ -388,7 +389,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", 1)); strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> entries (default: %u)", 50000)); } - strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for relaying (default: %s)"), FormatMoney(::minRelayTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying (default: %s)"), + CURRENCY_UNIT, FormatMoney(::minRelayTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); if (showDebug) { @@ -441,6 +443,9 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-min", _("Start minimized")); strUsage += HelpMessageOpt("-rootcertificates=<file>", _("Set SSL root certificates for payment request (default: -system-)")); strUsage += HelpMessageOpt("-splash", _("Show splash screen on startup (default: 1)")); + if (showDebug) { + strUsage += HelpMessageOpt("-uiplatform", "Select platform to customize UI for (one of windows, macosx, other; default: platform compiled on)"); + } } return strUsage; @@ -747,7 +752,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Make sure enough file descriptors are available int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1); - int nUserMaxConnections = GetArg("-maxconnections", 125); + int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); nMaxConnections = std::max(nUserMaxConnections, 0); int nUserWhiteConnections = GetArg("-whiteconnections", 0); nWhiteConnections = std::max(nUserWhiteConnections, 0); @@ -1015,6 +1020,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) RegisterNodeSignals(GetNodeSignals()); + // format user agent, check total size + strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, mapMultiArgs.count("-uacomment") ? mapMultiArgs["-uacomment"] : std::vector<string>()); + if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { + return InitError(strprintf("Total length of network version string %i exceeds maximum of %i characters. Reduce the number and/or size of uacomments.", + strSubVersion.size(), MAX_SUBVERSION_LENGTH)); + } + if (mapArgs.count("-onlynet")) { std::set<enum Network> nets; BOOST_FOREACH(const std::string& snet, mapMultiArgs["-onlynet"]) { @@ -1225,6 +1237,18 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("Prune: pruned datadir may not have more than %d blocks; -checkblocks=%d may fail\n", MIN_BLOCKS_TO_KEEP, GetArg("-checkblocks", 288)); } + + { + LOCK(cs_main); + CBlockIndex* tip = chainActive.Tip(); + if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { + strLoadError = _("The block database contains a block which appears to be from the future. " + "This may be due to your computer's date and time being set incorrectly. " + "Only rebuild the block database if you are sure that your computer's date and time are correct"); + break; + } + } + if (!CVerifyDB().VerifyDB(pcoinsdbview, GetArg("-checklevel", 3), GetArg("-checkblocks", 288))) { strLoadError = _("Corrupted block database detected"); @@ -1275,15 +1299,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) mempool.ReadFeeEstimates(est_filein); fFeeEstimatesInitialized = true; - // if prune mode, unset NODE_NETWORK and prune block files - if (fPruneMode) { - LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); - nLocalServices &= ~NODE_NETWORK; - if (!fReindex) { - PruneAndFlush(); - } - } - // ********************************************************* Step 8: load wallet #ifdef ENABLE_WALLET if (fDisableWallet) { @@ -1437,7 +1452,21 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #else // ENABLE_WALLET LogPrintf("No wallet support compiled in!\n"); #endif // !ENABLE_WALLET - // ********************************************************* Step 9: import blocks + + // ********************************************************* Step 9: data directory maintenance + + // if pruning, unset the service bit and perform the initial blockstore prune + // after any wallet rescanning has taken place. + if (fPruneMode) { + uiInterface.InitMessage(_("Pruning blockstore...")); + LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); + nLocalServices &= ~NODE_NETWORK; + if (!fReindex) { + PruneAndFlush(); + } + } + + // ********************************************************* Step 10: import blocks if (mapArgs.count("-blocknotify")) uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); @@ -1461,7 +1490,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) MilliSleep(10); } - // ********************************************************* Step 10: start node + // ********************************************************* Step 11: start node if (!CheckDiskSpace()) return false; diff --git a/src/keystore.cpp b/src/keystore.cpp index 3bae24b7b9..cf49ba83ad 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -6,23 +6,30 @@ #include "keystore.h" #include "key.h" +#include "pubkey.h" #include "util.h" #include <boost/foreach.hpp> -bool CKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const +bool CKeyStore::AddKey(const CKey &key) { + return AddKeyPubKey(key, key.GetPubKey()); +} + +bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const { CKey key; - if (!GetKey(address, key)) + if (!GetKey(address, key)) { + WatchKeyMap::const_iterator it = mapWatchKeys.find(address); + if (it != mapWatchKeys.end()) { + vchPubKeyOut = it->second; + return true; + } return false; + } vchPubKeyOut = key.GetPubKey(); return true; } -bool CKeyStore::AddKey(const CKey &key) { - return AddKeyPubKey(key, key.GetPubKey()); -} - bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) { LOCK(cs_KeyStore); @@ -58,10 +65,29 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) return false; } +static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut) +{ + //TODO: Use Solver to extract this? + CScript::const_iterator pc = dest.begin(); + opcodetype opcode; + std::vector<unsigned char> vch; + if (!dest.GetOp(pc, opcode, vch) || vch.size() < 33 || vch.size() > 65) + return false; + pubKeyOut = CPubKey(vch); + if (!pubKeyOut.IsFullyValid()) + return false; + if (!dest.GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG || dest.GetOp(pc, opcode, vch)) + return false; + return true; +} + bool CBasicKeyStore::AddWatchOnly(const CScript &dest) { LOCK(cs_KeyStore); setWatchOnly.insert(dest); + CPubKey pubKey; + if (ExtractPubKey(dest, pubKey)) + mapWatchKeys[pubKey.GetID()] = pubKey; return true; } @@ -69,6 +95,9 @@ bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest) { LOCK(cs_KeyStore); setWatchOnly.erase(dest); + CPubKey pubKey; + if (ExtractPubKey(dest, pubKey)) + mapWatchKeys.erase(pubKey.GetID()); return true; } diff --git a/src/keystore.h b/src/keystore.h index 4a4b6d20af..b917bf20b4 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -32,7 +32,7 @@ public: virtual bool HaveKey(const CKeyID &address) const =0; virtual bool GetKey(const CKeyID &address, CKey& keyOut) const =0; virtual void GetKeys(std::set<CKeyID> &setAddress) const =0; - virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const =0; //! Support for BIP 0013 : see https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki virtual bool AddCScript(const CScript& redeemScript) =0; @@ -47,6 +47,7 @@ public: }; typedef std::map<CKeyID, CKey> KeyMap; +typedef std::map<CKeyID, CPubKey> WatchKeyMap; typedef std::map<CScriptID, CScript > ScriptMap; typedef std::set<CScript> WatchOnlySet; @@ -55,11 +56,13 @@ class CBasicKeyStore : public CKeyStore { protected: KeyMap mapKeys; + WatchKeyMap mapWatchKeys; ScriptMap mapScripts; WatchOnlySet setWatchOnly; public: bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); + bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; bool HaveKey(const CKeyID &address) const { bool result; diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp index c353dfa6d9..26cacf95ae 100644 --- a/src/leveldbwrapper.cpp +++ b/src/leveldbwrapper.cpp @@ -58,7 +58,8 @@ CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCa } else { if (fWipe) { LogPrintf("Wiping LevelDB in %s\n", path.string()); - leveldb::DestroyDB(path.string(), options); + leveldb::Status result = leveldb::DestroyDB(path.string(), options); + HandleError(result); } TryCreateDirectory(path); LogPrintf("Opening LevelDB in %s\n", path.string()); diff --git a/src/limitedmap.h b/src/limitedmap.h index e8ea549653..5456dfc7c4 100644 --- a/src/limitedmap.h +++ b/src/limitedmap.h @@ -27,7 +27,11 @@ protected: size_type nMaxSize; public: - limitedmap(size_type nMaxSizeIn = 0) { nMaxSize = nMaxSizeIn; } + limitedmap(size_type nMaxSizeIn) + { + assert(nMaxSizeIn > 0); + nMaxSize = nMaxSizeIn; + } const_iterator begin() const { return map.begin(); } const_iterator end() const { return map.end(); } size_type size() const { return map.size(); } @@ -38,13 +42,12 @@ public: { std::pair<iterator, bool> ret = map.insert(x); if (ret.second) { - if (nMaxSize && map.size() == nMaxSize) { + if (map.size() > nMaxSize) { map.erase(rmap.begin()->second); rmap.erase(rmap.begin()); } rmap.insert(make_pair(x.second, ret.first)); } - return; } void erase(const key_type& k) { @@ -81,11 +84,11 @@ public: size_type max_size() const { return nMaxSize; } size_type max_size(size_type s) { - if (s) - while (map.size() > s) { - map.erase(rmap.begin()->second); - rmap.erase(rmap.begin()); - } + assert(s > 0); + while (map.size() > s) { + map.erase(rmap.begin()->second); + rmap.erase(rmap.begin()); + } nMaxSize = s; return nMaxSize; } diff --git a/src/main.cpp b/src/main.cpp index fefeabeb64..33b57a5285 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -162,6 +162,29 @@ namespace { */ map<uint256, NodeId> mapBlockSource; + /** + * Filter for transactions that were recently rejected by + * AcceptToMemoryPool. These are not rerequested until the chain tip + * changes, at which point the entire filter is reset. Protected by + * cs_main. + * + * Without this filter we'd be re-requesting txs from each of our peers, + * increasing bandwidth consumption considerably. For instance, with 100 + * peers, half of which relay a tx we don't accept, that might be a 50x + * bandwidth increase. A flooding attacker attempting to roll-over the + * filter using minimum-sized, 60byte, transactions might manage to send + * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a + * two minute window to send invs to us. + * + * Decreasing the false positive rate is fairly cheap, so we pick one in a + * million to make it highly unlikely for users to have issues with this + * filter. + * + * Memory used: 1.7MB + */ + boost::scoped_ptr<CRollingBloomFilter> recentRejects; + uint256 hashRecentRejectsChainTip; + /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; @@ -673,30 +696,24 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context if (tx.vin.empty()) - return state.DoS(10, error("CheckTransaction(): vin empty"), - REJECT_INVALID, "bad-txns-vin-empty"); + return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); if (tx.vout.empty()) - return state.DoS(10, error("CheckTransaction(): vout empty"), - REJECT_INVALID, "bad-txns-vout-empty"); + return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckTransaction(): size limits failed"), - REJECT_INVALID, "bad-txns-oversize"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values CAmount nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (txout.nValue < 0) - return state.DoS(100, error("CheckTransaction(): txout.nValue negative"), - REJECT_INVALID, "bad-txns-vout-negative"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CheckTransaction(): txout.nValue too high"), - REJECT_INVALID, "bad-txns-vout-toolarge"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CheckTransaction(): txout total out of range"), - REJECT_INVALID, "bad-txns-txouttotal-toolarge"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } // Check for duplicate inputs @@ -704,23 +721,20 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CheckTransaction(): duplicate inputs"), - REJECT_INVALID, "bad-txns-inputs-duplicate"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate"); vInOutPoints.insert(txin.prevout); } if (tx.IsCoinBase()) { if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CheckTransaction(): coinbase script size"), - REJECT_INVALID, "bad-cb-length"); + return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); } else { BOOST_FOREACH(const CTxIn& txin, tx.vin) if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction(): prevout is null"), - REJECT_INVALID, "bad-txns-prevout-null"); + return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); } return true; @@ -755,6 +769,14 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF return nMinFee; } +/** Convert CValidationState to a human-readable message for logging */ +static std::string FormatStateMessage(const CValidationState &state) +{ + return strprintf("%s%s (code %i)", + state.GetRejectReason(), + state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(), + state.GetRejectCode()); +} bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectAbsurdFee) @@ -764,31 +786,27 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa *pfMissingInputs = false; if (!CheckTransaction(tx, state)) - return error("AcceptToMemoryPool: CheckTransaction failed"); + return false; // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"), - REJECT_INVALID, "coinbase"); + return state.DoS(100, false, REJECT_INVALID, "coinbase"); // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (fRequireStandard && !IsStandardTx(tx, reason)) - return state.DoS(0, - error("AcceptToMemoryPool: nonstandard transaction: %s", reason), - REJECT_NONSTANDARD, reason); + return state.DoS(0, false, REJECT_NONSTANDARD, reason); // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. if (!CheckFinalTx(tx)) - return state.DoS(0, error("AcceptToMemoryPool: non-final"), - REJECT_NONSTANDARD, "non-final"); + return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) - return false; + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); // Check for conflicts with in-memory transactions { @@ -799,7 +817,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now - return false; + return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); } } } @@ -816,7 +834,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // do we already have it? if (view.HaveCoins(hash)) - return false; + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known"); // do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), @@ -825,14 +843,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) *pfMissingInputs = true; - return false; + return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() } } // are the actual inputs available? if (!view.HaveInputs(tx)) - return state.Invalid(error("AcceptToMemoryPool: inputs already spent"), - REJECT_DUPLICATE, "bad-txns-inputs-spent"); + return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent"); // Bring the best block into scope view.GetBestBlock(); @@ -845,7 +862,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Check for non-standard pay-to-script-hash in inputs if (fRequireStandard && !AreInputsStandard(tx, view)) - return error("AcceptToMemoryPool: nonstandard transaction input"); + return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction @@ -855,10 +872,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa unsigned int nSigOps = GetLegacySigOpCount(tx); nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_STANDARD_TX_SIGOPS) - return state.DoS(0, - error("AcceptToMemoryPool: too many sigops %s, %d > %d", - hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS), - REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); + return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, + strprintf("%d > %d", nSigOps, MAX_STANDARD_TX_SIGOPS)); CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; @@ -870,9 +885,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Don't accept it if it can't get into a block CAmount txMinFee = GetMinRelayFee(tx, nSize, true); if (fLimitFree && nFees < txMinFee) - return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d", - hash.ToString(), nFees, txMinFee), - REJECT_INSUFFICIENTFEE, "insufficient fee"); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, + strprintf("%d < %d", nFees, txMinFee)); // Require that free transactions have sufficient priority to be mined in the next block. if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { @@ -897,23 +911,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "rate limited free transaction"); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction"); LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", - hash.ToString(), - nFees, ::minRelayTxFee.GetFee(nSize) * 10000); + return state.Invalid(false, + REJECT_HIGHFEE, "absurdly-high-fee", + strprintf("%d > %d", nFees, ::minRelayTxFee.GetFee(nSize) * 10000)); // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) - { - return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); - } + return false; // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause @@ -926,7 +937,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // can be exploited as a DoS attack. if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) { - return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); + return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s", + __func__, hash.ToString(), FormatStateMessage(state)); } // Store transaction in memory @@ -1011,7 +1023,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); @@ -1204,9 +1216,11 @@ void static InvalidChainFound(CBlockIndex* pindexNew) pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime())); + CBlockIndex *tip = chainActive.Tip(); + assert (tip); LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime())); + tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); CheckForkWarningConditions(); } @@ -1215,7 +1229,8 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state if (state.IsInvalid(nDoS)) { std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash()); if (it != mapBlockSource.end() && State(it->second)) { - CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; + assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes + CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; State(it->second)->rejects.push_back(reject); if (nDoS > 0) Misbehaving(it->second, nDoS); @@ -1265,7 +1280,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { - return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); + return false; } return true; } @@ -1283,7 +1298,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) - return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); + return state.Invalid(false, 0, "", "Inputs unavailable"); CAmount nValueIn = 0; CAmount nFees = 0; @@ -1296,33 +1311,29 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins // If prev is coinbase, check that it's matured if (coins->IsCoinBase()) { if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) - return state.Invalid( - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + return state.Invalid(false, + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", + strprintf("tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight)); } // Check for negative or overflow input values nValueIn += coins->vout[prevout.n].nValue; if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, error("CheckInputs(): txin values out of range"), - REJECT_INVALID, "bad-txns-inputvalues-outofrange"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } if (nValueIn < tx.GetValueOut()) - return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s)", - tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), - REJECT_INVALID, "bad-txns-in-belowout"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); // Tally transaction fees CAmount nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) - return state.DoS(100, error("CheckInputs(): %s nTxFee < 0", tx.GetHash().ToString()), - REJECT_INVALID, "bad-txns-fee-negative"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); nFees += nTxFee; if (!MoneyRange(nFees)) - return state.DoS(100, error("CheckInputs(): nFees out of range"), - REJECT_INVALID, "bad-txns-fee-outofrange"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); return true; } }// namespace Consensus @@ -1446,7 +1457,7 @@ bool AbortNode(const std::string& strMessage, const std::string& userMessage="") strMiscWarning = strMessage; LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox( - userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, + userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; @@ -1493,7 +1504,7 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const CO return fClean; } -bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) +bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex->GetBlockHash() == view.GetBestBlock()); @@ -1768,7 +1779,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector<CScriptCheck> vChecks; if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL)) - return false; + return error("ConnectBlock(): CheckInputs on %s failed with %s", + tx.GetHash().ToString(), FormatStateMessage(state)); control.Add(vChecks); } @@ -2050,7 +2062,7 @@ static int64_t nTimePostConnect = 0; * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. */ -bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) { +bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, const CBlock *pblock) { assert(pindexNew->pprev == chainActive.Tip()); mempool.check(pcoinsTip); // Read block from disk. @@ -2182,7 +2194,7 @@ static void PruneBlockIndexCandidates() { * Try to make some progress towards making pindexMostWork the active block. * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. */ -static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock) { +static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, const CBlock *pblock) { AssertLockHeld(cs_main); bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -2251,7 +2263,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo * or an activated best chain. pblock is either NULL or a pointer to a block * that is already loaded (to avoid loading it again from disk). */ -bool ActivateBestChain(CValidationState &state, CBlock *pblock) { +bool ActivateBestChain(CValidationState &state, const CBlock *pblock) { CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexMostWork = NULL; const CChainParams& chainParams = Params(); @@ -2595,7 +2607,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!CheckTransaction(tx, state)) - return error("CheckBlock(): CheckTransaction failed"); + return error("CheckBlock(): CheckTransaction of %s failed with %s", + tx.GetHash().ToString(), + FormatStateMessage(state)); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2722,7 +2736,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return true; } -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) +bool AcceptBlock(const CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); @@ -2734,9 +2748,15 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // Try to process all requested blocks that we don't have, but only // process an unrequested block if it's new and has enough work to - // advance our tip. + // advance our tip, and isn't too many blocks ahead. bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true); + // Blocks that are too out-of-order needlessly limit the effectiveness of + // pruning, because pruning will not delete block files that contain any + // blocks which are too close in height to the tip. Apply this test + // regardless of whether pruning is enabled; it should generally be safe to + // not process unrequested blocks. + bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP)); // TODO: deal better with return value and error conditions for duplicate // and unrequested blocks. @@ -2744,6 +2764,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!fRequested) { // If we didn't ask for it: if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned if (!fHasMoreWork) return true; // Don't process less-work chains + if (fTooFarAhead) return true; // Block height is too high } if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { @@ -2792,7 +2813,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned } -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) +bool ProcessNewBlock(CValidationState &state, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) { // Preliminary checks bool checked = CheckBlock(*pblock, state); @@ -3241,6 +3262,7 @@ void UnloadBlockIndex() setDirtyBlockIndex.clear(); setDirtyFileInfo.clear(); mapNodeState.clear(); + recentRejects.reset(NULL); BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; @@ -3261,6 +3283,10 @@ bool LoadBlockIndex() bool InitBlockIndex() { const CChainParams& chainparams = Params(); LOCK(cs_main); + + // Initialize global variables that cannot be constructed at startup. + recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); + // Check whether we're already initialized if (chainActive.Genesis() != NULL) return true; @@ -3391,7 +3417,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } } } catch (const std::exception& e) { - LogPrintf("%s: Deserialize or I/O error - %s", __func__, e.what()); + LogPrintf("%s: Deserialize or I/O error - %s\n", __func__, e.what()); } } } catch (const std::runtime_error& e) { @@ -3663,10 +3689,21 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { case MSG_TX: { - bool txInMap = false; - txInMap = mempool.exists(inv.hash); - return txInMap || mapOrphanTransactions.count(inv.hash) || - pcoinsTip->HaveCoins(inv.hash); + assert(recentRejects); + if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) + { + // If the chain tip has changed previously rejected transactions + // might be now valid, e.g. due to a nLockTime'd tx becoming valid, + // or a double-spend. Reset the rejects filter and give those + // txs a second chance. + hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); + recentRejects->reset(); + } + + return recentRejects->contains(inv.hash) || + mempool.exists(inv.hash) || + mapOrphanTransactions.count(inv.hash) || + pcoinsTip->HaveCoins(inv.hash); } case MSG_BLOCK: return mapBlockIndex.count(inv.hash); @@ -3852,7 +3889,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) { - vRecv >> LIMITED_STRING(pfrom->strSubVer, 256); + vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH); pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); } if (!vRecv.empty()) @@ -4266,6 +4303,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Probably non-standard or insufficient fee/priority LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); vEraseQueue.push_back(orphanHash); + assert(recentRejects); + recentRejects->insert(orphanHash); } mempool.check(pcoinsTip); } @@ -4283,20 +4322,34 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); if (nEvicted > 0) LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); - } else if (pfrom->fWhitelisted) { - // Always relay transactions received from whitelisted peers, even - // if they are already in the mempool (allowing the node to function - // as a gateway for nodes hidden behind it). - RelayTransaction(tx); + } else { + // AcceptToMemoryPool() returned false, possibly because the tx is + // already in the mempool; if the tx isn't in the mempool that + // means it was rejected and we shouldn't ask for it again. + if (!mempool.exists(tx.GetHash())) { + assert(recentRejects); + recentRejects->insert(tx.GetHash()); + } + if (pfrom->fWhitelisted) { + // Always relay transactions received from whitelisted peers, even + // if they were rejected from the mempool, allowing the node to + // function as a gateway for nodes hidden behind it. + // + // FIXME: This includes invalid transactions, which means a + // whitelisted peer could get us banned! We may want to change + // that. + RelayTransaction(tx); + } } int nDoS = 0; if (state.IsInvalid(nDoS)) { - LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), + LogPrint("mempoolrej", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), pfrom->id, pfrom->cleanSubVer, - state.GetRejectReason()); - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + FormatStateMessage(state)); + if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P + pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } @@ -4368,10 +4421,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->AddInventoryKnown(inv); CValidationState state; - // Process all blocks from whitelisted peers, even if not requested. - ProcessNewBlock(state, pfrom, &block, pfrom->fWhitelisted, NULL); + // Process all blocks from whitelisted peers, even if not requested, + // unless we're still syncing with the network. + // Such an unrequested block may still be processed, subject to the + // conditions in AcceptBlock(). + bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(); + ProcessNewBlock(state, pfrom, &block, forceProcessing, NULL); int nDoS; if (state.IsInvalid(nDoS)) { + assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) { @@ -4786,7 +4844,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { // Periodically clear addrKnown to allow refresh broadcasts if (nLastRebroadcast) - pnode->addrKnown.clear(); + pnode->addrKnown.reset(); // Rebroadcast our address AdvertizeLocal(pnode); diff --git a/src/main.h b/src/main.h index 96ad54cda6..e3479b4b3b 100644 --- a/src/main.h +++ b/src/main.h @@ -142,7 +142,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals); * @param[out] dbp If pblock is stored to disk (or already there), this will be set to its location. * @return True if state.IsValid() */ -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp); +bool ProcessNewBlock(CValidationState &state, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); /** Open a block file (blk?????.dat) */ @@ -179,7 +179,7 @@ std::string GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); /** Find the best known block, and make it the tip of the block chain */ -bool ActivateBestChain(CValidationState &state, CBlock *pblock = NULL); +bool ActivateBestChain(CValidationState &state, const CBlock *pblock = NULL); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** @@ -335,7 +335,7 @@ public: /** Functions for disk access for blocks */ -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); @@ -346,7 +346,7 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean * will be true if no problems were found. Otherwise, the return value will be false in case * of problems. Note that in any case, coins may be modified. */ -bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); +bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); /** Apply the effects of this block (with given index) on the UTXO set represented by coins */ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); @@ -363,7 +363,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex *pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); /** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */ -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp); +bool AcceptBlock(const CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp); bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL); @@ -455,4 +455,16 @@ extern CBlockTreeDB *pblocktree; */ int GetSpendHeight(const CCoinsViewCache& inputs); +/** Reject codes greater or equal to this can be returned by AcceptToMemPool + * for transactions, to signal internal conditions. They cannot and should not + * be sent over the P2P network. + */ +static const unsigned int REJECT_INTERNAL = 0x100; +/** Too high fee. Can not be triggered by P2P transactions */ +static const unsigned int REJECT_HIGHFEE = 0x100; +/** Transaction is already known (either in mempool or blockchain) */ +static const unsigned int REJECT_ALREADY_KNOWN = 0x101; +/** Transaction conflicts with a transaction already known */ +static const unsigned int REJECT_CONFLICT = 0x102; + #endif // BITCOIN_MAIN_H diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index 4d90fd8cd7..f8e877df25 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -168,7 +168,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) { // traverse the partial tree unsigned int nBitsUsed = 0, nHashUsed = 0; uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch); - // verify that no problems occured during the tree traversal + // verify that no problems occurred during the tree traversal if (fBad) return uint256(); // verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence) diff --git a/src/miner.cpp b/src/miner.cpp index e44f3392c8..9dd1d459b5 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -84,13 +84,19 @@ public: } }; -void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) +int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { - pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + int64_t nOldTime = pblock->nTime; + int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + + if (nOldTime < nNewTime) + pblock->nTime = nNewTime; // Updating time can change work required on testnet: if (consensusParams.fPowAllowMinDifficultyBlocks) pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams); + + return nNewTime - nOldTime; } CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) @@ -346,7 +352,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) return pblocktemplate.release(); } -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce) { // Update nExtraNonce static uint256 hashPrevBlock; @@ -403,7 +409,7 @@ bool static ScanHash(const CBlockHeader *pblock, uint32_t& nNonce, uint256 *phas } } -static bool ProcessBlockFound(CBlock* pblock, const CChainParams& chainparams) +static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainparams) { LogPrintf("%s\n", pblock->ToString()); LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue)); @@ -438,8 +444,10 @@ void static BitcoinMiner(const CChainParams& chainparams) GetMainSignals().ScriptForMining(coinbaseScript); try { - //throw an error if no script was provided - if (!coinbaseScript->reserveScript.size()) + // Throw an error if no script was provided. This can happen + // due to some internal error but also if the keypool is empty. + // In the latter case, already the pointer is NULL. + if (!coinbaseScript || coinbaseScript->reserveScript.empty()) throw std::runtime_error("No coinbase script available (mining requires a wallet)"); while (true) { @@ -521,7 +529,9 @@ void static BitcoinMiner(const CChainParams& chainparams) break; // Update nTime every few seconds - UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + if (UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev) < 0) + break; // Recreate the block if the clock has run backwards, + // so that we can use the correct time. if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) { // Changing pblock->nTime can change work required on testnet: diff --git a/src/miner.h b/src/miner.h index 777a091967..7e0e58d540 100644 --- a/src/miner.h +++ b/src/miner.h @@ -29,7 +29,7 @@ void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainpar /** Generate a new block, without valid proof-of-work */ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); /** Modify the extranonce in a block */ -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); -void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); #endif // BITCOIN_MINER_H diff --git a/src/net.cpp b/src/net.cpp index 3cece520de..fb5726a2b9 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -80,9 +80,10 @@ static CNode* pnodeLocalHost = NULL; uint64_t nLocalHostNonce = 0; static std::vector<ListenSocket> vhListenSocket; CAddrMan addrman; -int nMaxConnections = 125; +int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS; int nWhiteConnections = 0; bool fAddressesInitialized = false; +std::string strSubVersion; vector<CNode*> vNodes; CCriticalSection cs_vNodes; @@ -445,7 +446,7 @@ void CNode::PushVersion() else LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, - nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight, true); + nLocalHostNonce, strSubVersion, nBestHeight, true); } @@ -2060,7 +2061,7 @@ unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), - addrKnown(5000, 0.001, insecure_rand()), + addrKnown(5000, 0.001), setInventoryKnown(SendBufferSize() / 1000) { nServices = 0; @@ -2318,7 +2319,7 @@ void DumpBanlist() { int64_t nStart = GetTimeMillis(); - CNode::SweepBanned(); //clean unused entires (if bantime has expired) + CNode::SweepBanned(); //clean unused entries (if bantime has expired) CBanDB bandb; banmap_t banmap; @@ -46,6 +46,8 @@ static const unsigned int MAX_INV_SZ = 50000; static const unsigned int MAX_ADDR_TO_SEND = 1000; /** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */ static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; +/** Maximum length of strSubVer in `version` message */ +static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** -listen default */ static const bool DEFAULT_LISTEN = true; /** -upnp default */ @@ -56,6 +58,8 @@ static const bool DEFAULT_UPNP = false; #endif /** The maximum number of entries in mapAskFor */ static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ; +/** The maximum number of peer connections to maintain. */ +static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; unsigned int ReceiveFloodSize(); unsigned int SendBufferSize(); @@ -166,6 +170,9 @@ extern CCriticalSection cs_vAddedNodes; extern NodeId nLastNodeId; extern CCriticalSection cs_nLastNodeId; +/** Subversion as sent to the P2P network in `version` messages */ +extern std::string strSubVersion; + struct LocalServiceInfo { int nScore; int nPort; @@ -688,7 +695,7 @@ public: static bool BannedSetIsDirty(); //!set the "dirty" flag for the banlist static void SetBannedSetDirty(bool dirty=true); - //!clean unused entires (if bantime has expired) + //!clean unused entries (if bantime has expired) static void SweepBanned(); void copyStats(CNodeStats &stats); diff --git a/src/netbase.cpp b/src/netbase.cpp index b7e2e57917..7a87d125c2 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -349,7 +349,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials } if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) { CloseSocket(hSocket); - return error("Proxy authentication unsuccesful"); + return error("Proxy authentication unsuccessful"); } } else if (pchRet1[1] == 0x00) { // Perform no authentication @@ -983,7 +983,7 @@ std::vector<unsigned char> CNetAddr::GetGroup() const nBits -= 8; } if (nBits > 0) - vchRet.push_back(GetByte(15 - nStartByte) | ((1 << nBits) - 1)); + vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1)); return vchRet; } diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index b1491cec01..ffe31d1942 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -249,7 +249,7 @@ unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val) unsigned int bucketindex = bucketMap.lower_bound(val)->second; unsigned int blockIndex = nBlockHeight % unconfTxs.size(); unconfTxs[blockIndex][bucketindex]++; - LogPrint("estimatefee", "adding to %s\n", dataTypeString); + LogPrint("estimatefee", "adding to %s", dataTypeString); return bucketindex; } @@ -261,7 +261,7 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe blocksAgo = 0; if (blocksAgo < 0) { LogPrint("estimatefee", "Blockpolicy error, blocks ago is negative for mempool tx\n"); - return; //This can't happen becasue we call this with our best seen height, no entries can have higher + return; //This can't happen because we call this with our best seen height, no entries can have higher } if (blocksAgo >= (int)unconfTxs.size()) { @@ -390,8 +390,9 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK()); } else { - LogPrint("estimatefee", "not adding\n"); + LogPrint("estimatefee", "not adding"); } + LogPrint("estimatefee", "\n"); } void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry) diff --git a/src/policy/fees.h b/src/policy/fees.h index ce4d782566..15577d128a 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -118,7 +118,7 @@ public: /** * Initialize the data structures. This is called by BlockPolicyEstimator's * constructor with default values. - * @param defaultBuckets contains the upper limits for the bucket boundries + * @param defaultBuckets contains the upper limits for the bucket boundaries * @param maxConfirms max number of confirms to track * @param decay how much to decay the historical moving average per block * @param dataTypeString for logging purposes diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index af6801919c..8bd1586446 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -14,14 +14,14 @@ #include "csvmodelwriter.h" #include "editaddressdialog.h" #include "guiutil.h" -#include "scicon.h" +#include "platformstyle.h" #include <QIcon> #include <QMenu> #include <QMessageBox> #include <QSortFilterProxyModel> -AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : +AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent) : QDialog(parent), ui(new Ui::AddressBookPage), model(0), @@ -30,17 +30,17 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : { ui->setupUi(this); -#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac - ui->newAddress->setIcon(QIcon()); - ui->copyAddress->setIcon(QIcon()); - ui->deleteAddress->setIcon(QIcon()); - ui->exportButton->setIcon(QIcon()); -#else - ui->newAddress->setIcon(SingleColorIcon(":/icons/add")); - ui->copyAddress->setIcon(SingleColorIcon(":/icons/editcopy")); - ui->deleteAddress->setIcon(SingleColorIcon(":/icons/remove")); - ui->exportButton->setIcon(SingleColorIcon(":/icons/export")); -#endif + if (!platformStyle->getImagesOnButtons()) { + ui->newAddress->setIcon(QIcon()); + ui->copyAddress->setIcon(QIcon()); + ui->deleteAddress->setIcon(QIcon()); + ui->exportButton->setIcon(QIcon()); + } else { + ui->newAddress->setIcon(platformStyle->SingleColorIcon(":/icons/add")); + ui->copyAddress->setIcon(platformStyle->SingleColorIcon(":/icons/editcopy")); + ui->deleteAddress->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); + ui->exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export")); + } switch(mode) { diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 09634ce336..92e6cab9ac 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -9,6 +9,7 @@ class AddressTableModel; class OptionsModel; +class PlatformStyle; namespace Ui { class AddressBookPage; @@ -39,7 +40,7 @@ public: ForEditing /**< Open address book for editing */ }; - explicit AddressBookPage(Mode mode, Tabs tab, QWidget *parent); + explicit AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent); ~AddressBookPage(); void setModel(AddressTableModel *model); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index b8aec0c268..1da2d3e344 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -15,6 +15,7 @@ #include "intro.h" #include "networkstyle.h" #include "optionsmodel.h" +#include "platformstyle.h" #include "splashscreen.h" #include "utilitydialog.h" #include "winshutdownmonitor.h" @@ -241,6 +242,7 @@ private: WalletModel *walletModel; #endif int returnValue; + const PlatformStyle *platformStyle; void startThread(); }; @@ -310,6 +312,22 @@ BitcoinApplication::BitcoinApplication(int &argc, char **argv): returnValue(0) { setQuitOnLastWindowClosed(false); + + // UI per-platform customization + // This must be done inside the BitcoinApplication constructor, or after it, because + // PlatformStyle::instantiate requires a QApplication +#if defined(Q_OS_MAC) + std::string platformName = "macosx"; +#elif defined(Q_OS_WIN) + std::string platformName = "windows"; +#else + std::string platformName = "other"; +#endif + platformName = GetArg("-uiplatform", platformName); + platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName)); + if (!platformStyle) // Fall back to "other" if specified name not found + platformStyle = PlatformStyle::instantiate("other"); + assert(platformStyle); } BitcoinApplication::~BitcoinApplication() @@ -330,6 +348,8 @@ BitcoinApplication::~BitcoinApplication() #endif delete optionsModel; optionsModel = 0; + delete platformStyle; + platformStyle = 0; } #ifdef ENABLE_WALLET @@ -346,7 +366,7 @@ void BitcoinApplication::createOptionsModel() void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) { - window = new BitcoinGUI(networkStyle, 0); + window = new BitcoinGUI(platformStyle, networkStyle, 0); pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); @@ -421,6 +441,8 @@ void BitcoinApplication::initializeResult(int retval) returnValue = retval ? 0 : 1; if(retval) { + // Log this only after AppInit2 finishes, as then logging setup is guaranteed complete + qWarning() << "Platform customization:" << platformStyle->getName(); #ifdef ENABLE_WALLET PaymentServer::LoadRootCAs(); paymentServer->setOptionsModel(optionsModel); @@ -432,7 +454,7 @@ void BitcoinApplication::initializeResult(int retval) #ifdef ENABLE_WALLET if(pwalletMain) { - walletModel = new WalletModel(pwalletMain, optionsModel); + walletModel = new WalletModel(platformStyle, pwalletMain, optionsModel); window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel); window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 396435f12b..1d3f7762ab 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -13,8 +13,8 @@ #include "openuridialog.h" #include "optionsdialog.h" #include "optionsmodel.h" +#include "platformstyle.h" #include "rpcconsole.h" -#include "scicon.h" #include "utilitydialog.h" #ifdef ENABLE_WALLET @@ -60,7 +60,7 @@ const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; -BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : +BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent) : QMainWindow(parent), clientModel(0), walletFrame(0), @@ -98,7 +98,8 @@ BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : notificator(0), rpcConsole(0), prevBlocks(0), - spinnerFrame(0) + spinnerFrame(0), + platformStyle(platformStyle) { GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this); @@ -130,12 +131,12 @@ BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : setUnifiedTitleAndToolBarOnMac(true); #endif - rpcConsole = new RPCConsole(0); + rpcConsole = new RPCConsole(platformStyle, 0); #ifdef ENABLE_WALLET if(enableWallet) { /** Create wallet frame and make it the central widget */ - walletFrame = new WalletFrame(this); + walletFrame = new WalletFrame(platformStyle, this); setCentralWidget(walletFrame); } else #endif // ENABLE_WALLET @@ -175,7 +176,7 @@ BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setSpacing(3); - unitDisplayControl = new UnitDisplayStatusBarControl(); + unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle); labelEncryptionIcon = new QLabel(); labelConnectionsIcon = new QLabel(); labelBlocksIcon = new QLabel(); @@ -247,36 +248,36 @@ void BitcoinGUI::createActions() { QActionGroup *tabGroup = new QActionGroup(this); - overviewAction = new QAction(SingleColorIcon(":/icons/overview"), tr("&Overview"), this); + overviewAction = new QAction(platformStyle->SingleColorIcon(":/icons/overview"), tr("&Overview"), this); overviewAction->setStatusTip(tr("Show general overview of wallet")); overviewAction->setToolTip(overviewAction->statusTip()); overviewAction->setCheckable(true); overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); tabGroup->addAction(overviewAction); - sendCoinsAction = new QAction(SingleColorIcon(":/icons/send"), tr("&Send"), this); + sendCoinsAction = new QAction(platformStyle->SingleColorIcon(":/icons/send"), tr("&Send"), this); sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address")); sendCoinsAction->setToolTip(sendCoinsAction->statusTip()); sendCoinsAction->setCheckable(true); sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); tabGroup->addAction(sendCoinsAction); - sendCoinsMenuAction = new QAction(TextColorIcon(":/icons/send"), sendCoinsAction->text(), this); + sendCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/send"), sendCoinsAction->text(), this); sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip()); sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip()); - receiveCoinsAction = new QAction(SingleColorIcon(":/icons/receiving_addresses"), tr("&Receive"), this); + receiveCoinsAction = new QAction(platformStyle->SingleColorIcon(":/icons/receiving_addresses"), tr("&Receive"), this); receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and bitcoin: URIs)")); receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); receiveCoinsAction->setCheckable(true); receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); tabGroup->addAction(receiveCoinsAction); - receiveCoinsMenuAction = new QAction(TextColorIcon(":/icons/receiving_addresses"), receiveCoinsAction->text(), this); + receiveCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/receiving_addresses"), receiveCoinsAction->text(), this); receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip()); receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip()); - historyAction = new QAction(SingleColorIcon(":/icons/history"), tr("&Transactions"), this); + historyAction = new QAction(platformStyle->SingleColorIcon(":/icons/history"), tr("&Transactions"), this); historyAction->setStatusTip(tr("Browse transaction history")); historyAction->setToolTip(historyAction->statusTip()); historyAction->setCheckable(true); @@ -300,46 +301,46 @@ void BitcoinGUI::createActions() connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); #endif // ENABLE_WALLET - quitAction = new QAction(TextColorIcon(":/icons/quit"), tr("E&xit"), this); + quitAction = new QAction(platformStyle->TextColorIcon(":/icons/quit"), tr("E&xit"), this); quitAction->setStatusTip(tr("Quit application")); quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); quitAction->setMenuRole(QAction::QuitRole); - aboutAction = new QAction(TextColorIcon(":/icons/about"), tr("&About Bitcoin Core"), this); + aboutAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&About Bitcoin Core"), this); aboutAction->setStatusTip(tr("Show information about Bitcoin Core")); aboutAction->setMenuRole(QAction::AboutRole); - aboutQtAction = new QAction(TextColorIcon(":/icons/about_qt"), tr("About &Qt"), this); + aboutQtAction = new QAction(platformStyle->TextColorIcon(":/icons/about_qt"), tr("About &Qt"), this); aboutQtAction->setStatusTip(tr("Show information about Qt")); aboutQtAction->setMenuRole(QAction::AboutQtRole); - optionsAction = new QAction(TextColorIcon(":/icons/options"), tr("&Options..."), this); + optionsAction = new QAction(platformStyle->TextColorIcon(":/icons/options"), tr("&Options..."), this); optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin Core")); optionsAction->setMenuRole(QAction::PreferencesRole); - toggleHideAction = new QAction(TextColorIcon(":/icons/about"), tr("&Show / Hide"), this); + toggleHideAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&Show / Hide"), this); toggleHideAction->setStatusTip(tr("Show or hide the main Window")); - encryptWalletAction = new QAction(TextColorIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); + encryptWalletAction = new QAction(platformStyle->TextColorIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet")); encryptWalletAction->setCheckable(true); - backupWalletAction = new QAction(TextColorIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); + backupWalletAction = new QAction(platformStyle->TextColorIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); backupWalletAction->setStatusTip(tr("Backup wallet to another location")); - changePassphraseAction = new QAction(TextColorIcon(":/icons/key"), tr("&Change Passphrase..."), this); + changePassphraseAction = new QAction(platformStyle->TextColorIcon(":/icons/key"), tr("&Change Passphrase..."), this); changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption")); - signMessageAction = new QAction(TextColorIcon(":/icons/edit"), tr("Sign &message..."), this); + signMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/edit"), tr("Sign &message..."), this); signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them")); - verifyMessageAction = new QAction(TextColorIcon(":/icons/verify"), tr("&Verify message..."), this); + verifyMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/verify"), tr("&Verify message..."), this); verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses")); - openRPCConsoleAction = new QAction(TextColorIcon(":/icons/debugwindow"), tr("&Debug window"), this); + openRPCConsoleAction = new QAction(platformStyle->TextColorIcon(":/icons/debugwindow"), tr("&Debug window"), this); openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); - usedSendingAddressesAction = new QAction(TextColorIcon(":/icons/address-book"), tr("&Sending addresses..."), this); + usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses..."), this); usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels")); - usedReceivingAddressesAction = new QAction(TextColorIcon(":/icons/address-book"), tr("&Receiving addresses..."), this); + usedReceivingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Receiving addresses..."), this); usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels")); - openAction = new QAction(TextColorIcon(":/icons/open"), tr("Open &URI..."), this); + openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this); openAction->setStatusTip(tr("Open a bitcoin: URI or payment request")); - showHelpMessageAction = new QAction(TextColorIcon(":/icons/info"), tr("&Command-line options"), this); + showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), tr("&Command-line options"), this); showHelpMessageAction->setMenuRole(QAction::NoRole); showHelpMessageAction->setStatusTip(tr("Show the Bitcoin Core help message to get a list with possible Bitcoin command-line options")); @@ -650,7 +651,7 @@ void BitcoinGUI::setNumConnections(int count) case 7: case 8: case 9: icon = ":/icons/connect_3"; break; default: icon = ":/icons/connect_4"; break; } - labelConnectionsIcon->setPixmap(SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelConnectionsIcon->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count)); } @@ -691,7 +692,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate) if(secs < 90*60) { tooltip = tr("Up to date") + QString(".<br>") + tooltip; - labelBlocksIcon->setPixmap(SingleColorIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); #ifdef ENABLE_WALLET if(walletFrame) @@ -737,7 +738,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate) tooltip = tr("Catching up...") + QString("<br>") + tooltip; if(count != prevBlocks) { - labelBlocksIcon->setPixmap(SingleColorIcon(QString( + labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(QString( ":/movies/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0'))) .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES; @@ -931,7 +932,7 @@ void BitcoinGUI::setEncryptionStatus(int status) break; case WalletModel::Unlocked: labelEncryptionIcon->show(); - labelEncryptionIcon->setPixmap(SingleColorIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); @@ -939,7 +940,7 @@ void BitcoinGUI::setEncryptionStatus(int status) break; case WalletModel::Locked: labelEncryptionIcon->show(); - labelEncryptionIcon->setPixmap(SingleColorIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); @@ -1041,7 +1042,7 @@ void BitcoinGUI::unsubscribeFromCoreSignals() uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); } -UnitDisplayStatusBarControl::UnitDisplayStatusBarControl() : +UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) : optionsModel(0), menu(0) { @@ -1056,7 +1057,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl() : } setMinimumSize(max_width, 0); setAlignment(Qt::AlignRight | Qt::AlignVCenter); - setStyleSheet(QString("QLabel { color : %1 }").arg(SingleColor().name())); + setStyleSheet(QString("QLabel { color : %1 }").arg(platformStyle->SingleColor().name())); } /** So that it responds to button clicks */ diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 4e50b1712a..dd0d4bb0e2 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -22,6 +22,7 @@ class ClientModel; class NetworkStyle; class Notificator; class OptionsModel; +class PlatformStyle; class RPCConsole; class SendCoinsRecipient; class UnitDisplayStatusBarControl; @@ -47,7 +48,7 @@ class BitcoinGUI : public QMainWindow public: static const QString DEFAULT_WALLET; - explicit BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent = 0); + explicit BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0); ~BitcoinGUI(); /** Set the client model. @@ -117,6 +118,8 @@ private: int prevBlocks; int spinnerFrame; + const PlatformStyle *platformStyle; + /** Create the main UI actions. */ void createActions(); /** Create the menu bar and sub-menus. */ @@ -215,7 +218,7 @@ class UnitDisplayStatusBarControl : public QLabel Q_OBJECT public: - explicit UnitDisplayStatusBarControl(); + explicit UnitDisplayStatusBarControl(const PlatformStyle *platformStyle); /** Lets the control know about the Options Model (and its signals) */ void setOptionsModel(OptionsModel *optionsModel); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 41dc2ea77e..97d6711560 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -181,6 +181,11 @@ QString ClientModel::formatFullVersion() const return QString::fromStdString(FormatFullVersion()); } +QString ClientModel::formatSubVersion() const +{ + return QString::fromStdString(strSubVersion); +} + QString ClientModel::formatBuildDate() const { return QString::fromStdString(CLIENT_DATE); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 68434f404c..ca2da3dde0 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -63,6 +63,7 @@ public: QString getStatusBarWarnings() const; QString formatFullVersion() const; + QString formatSubVersion() const; QString formatBuildDate() const; bool isReleaseVersion() const; QString clientName() const; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 934d62d5f7..51008ad2de 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -9,7 +9,7 @@ #include "bitcoinunits.h" #include "guiutil.h" #include "optionsmodel.h" -#include "scicon.h" +#include "platformstyle.h" #include "txmempool.h" #include "walletmodel.h" @@ -35,10 +35,11 @@ QList<CAmount> CoinControlDialog::payAmounts; CCoinControl* CoinControlDialog::coinControl = new CCoinControl(); bool CoinControlDialog::fSubtractFeeFromAmount = false; -CoinControlDialog::CoinControlDialog(QWidget *parent) : +CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget *parent) : QDialog(parent), ui(new Ui::CoinControlDialog), - model(0) + model(0), + platformStyle(platformStyle) { ui->setupUi(this); @@ -280,7 +281,7 @@ void CoinControlDialog::lockCoin() COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); model->lockCoin(outpt); contextMenuItem->setDisabled(true); - contextMenuItem->setIcon(COLUMN_CHECKBOX, SingleColorIcon(":/icons/lock_closed")); + contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); updateLabelLocked(); } @@ -791,7 +792,7 @@ void CoinControlDialog::updateView() COutPoint outpt(txhash, out.i); coinControl->UnSelect(outpt); // just to be sure itemOutput->setDisabled(true); - itemOutput->setIcon(COLUMN_CHECKBOX, SingleColorIcon(":/icons/lock_closed")); + itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); } // set checkbox diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 0566b02c96..8ff1eac709 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -16,6 +16,7 @@ #include <QString> #include <QTreeWidgetItem> +class PlatformStyle; class WalletModel; class CCoinControl; @@ -32,7 +33,7 @@ class CoinControlDialog : public QDialog Q_OBJECT public: - explicit CoinControlDialog(QWidget *parent = 0); + explicit CoinControlDialog(const PlatformStyle *platformStyle, QWidget *parent = 0); ~CoinControlDialog(); void setModel(WalletModel *model); @@ -57,6 +58,8 @@ private: QAction *lockAction; QAction *unlockAction; + const PlatformStyle *platformStyle; + QString strPad(QString, int, QString); void sortView(int, Qt::SortOrder); void updateView(); diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 55c4f5ac58..22c67b8040 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>560</width> - <height>400</height> + <height>440</height> </rect> </property> <property name="windowTitle"> @@ -298,6 +298,193 @@ </layout> </item> <item> + <layout class="QHBoxLayout" name="horizontalLayout_2_Network"> + <item> + <widget class="QLabel" name="proxyActiveNets"> + <property name="text"> + <string>Used for reaching peers via:</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="proxyReachIPv4"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type.</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="proxyReachIPv4Label"> + <property name="text"> + <string>IPv4</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="proxyReachIPv6"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type.</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="proxyReachIPv6Label"> + <property name="text"> + <string>IPv6</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="proxyReachTor"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type.</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="proxyReachTorLabel"> + <property name="text"> + <string>Tor</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2_Network"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="connectSocksTor"> + <property name="toolTip"> + <string>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</string> + </property> + <property name="text"> + <string>Use separate SOCKS5 proxy to reach peers via Tor hidden services:</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3_Network"> + <item> + <widget class="QLabel" name="proxyIpTorLabel"> + <property name="text"> + <string>Proxy &IP:</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="buddy"> + <cstring>proxyIpTor</cstring> + </property> + </widget> + </item> + <item> + <widget class="QValidatedLineEdit" name="proxyIpTor"> + <property name="minimumSize"> + <size> + <width>140</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>140</width> + <height>16777215</height> + </size> + </property> + <property name="toolTip"> + <string>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="proxyPortTorLabel"> + <property name="text"> + <string>&Port:</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="buddy"> + <cstring>proxyPortTor</cstring> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="proxyPortTor"> + <property name="minimumSize"> + <size> + <width>55</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>55</width> + <height>16777215</height> + </size> + </property> + <property name="toolTip"> + <string>Port of the proxy (e.g. 9050)</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_4_Network"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> <spacer name="verticalSpacer_Network"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index 7ae8237476..e8d9a958ad 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -87,6 +87,32 @@ </widget> </item> <item row="3" column="0"> + <widget class="QLabel" name="labelClientUserAgent"> + <property name="text"> + <string>User Agent</string> + </property> + <property name="indent"> + <number>10</number> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="clientUserAgent"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="text"> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="4" column="0"> <widget class="QLabel" name="label_14"> <property name="text"> <string>Using OpenSSL version</string> @@ -96,7 +122,7 @@ </property> </widget> </item> - <item row="3" column="1"> + <item row="4" column="1"> <widget class="QLabel" name="openSSLVersion"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -112,7 +138,7 @@ </property> </widget> </item> - <item row="4" column="0"> + <item row="5" column="0"> <widget class="QLabel" name="label_berkeleyDBVersion"> <property name="text"> <string>Using BerkeleyDB version</string> @@ -122,7 +148,7 @@ </property> </widget> </item> - <item row="4" column="1"> + <item row="5" column="1"> <widget class="QLabel" name="berkeleyDBVersion"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -138,14 +164,14 @@ </property> </widget> </item> - <item row="5" column="0"> + <item row="6" column="0"> <widget class="QLabel" name="label_12"> <property name="text"> <string>Build date</string> </property> </widget> </item> - <item row="5" column="1"> + <item row="6" column="1"> <widget class="QLabel" name="buildDate"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -161,14 +187,14 @@ </property> </widget> </item> - <item row="6" column="0"> + <item row="7" column="0"> <widget class="QLabel" name="label_13"> <property name="text"> <string>Startup time</string> </property> </widget> </item> - <item row="6" column="1"> + <item row="7" column="1"> <widget class="QLabel" name="startupTime"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -184,7 +210,7 @@ </property> </widget> </item> - <item row="7" column="0"> + <item row="8" column="0"> <widget class="QLabel" name="label_11"> <property name="font"> <font> @@ -197,14 +223,14 @@ </property> </widget> </item> - <item row="8" column="0"> + <item row="9" column="0"> <widget class="QLabel" name="label_8"> <property name="text"> <string>Name</string> </property> </widget> </item> - <item row="8" column="1"> + <item row="9" column="1"> <widget class="QLabel" name="networkName"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -220,14 +246,14 @@ </property> </widget> </item> - <item row="9" column="0"> + <item row="10" column="0"> <widget class="QLabel" name="label_7"> <property name="text"> <string>Number of connections</string> </property> </widget> </item> - <item row="9" column="1"> + <item row="10" column="1"> <widget class="QLabel" name="numberOfConnections"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -243,7 +269,7 @@ </property> </widget> </item> - <item row="10" column="0"> + <item row="11" column="0"> <widget class="QLabel" name="label_10"> <property name="font"> <font> @@ -256,14 +282,14 @@ </property> </widget> </item> - <item row="11" column="0"> + <item row="12" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>Current number of blocks</string> </property> </widget> </item> - <item row="11" column="1"> + <item row="12" column="1"> <widget class="QLabel" name="numberOfBlocks"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -279,14 +305,14 @@ </property> </widget> </item> - <item row="12" column="0"> + <item row="13" column="0"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Last block time</string> </property> </widget> </item> - <item row="12" column="1"> + <item row="13" column="1"> <widget class="QLabel" name="lastBlockTime"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -302,7 +328,7 @@ </property> </widget> </item> - <item row="13" column="0"> + <item row="14" column="0"> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -315,7 +341,7 @@ </property> </spacer> </item> - <item row="14" column="0"> + <item row="15" column="0"> <widget class="QLabel" name="labelDebugLogfile"> <property name="font"> <font> @@ -328,7 +354,7 @@ </property> </widget> </item> - <item row="15" column="0"> + <item row="16" column="0"> <widget class="QPushButton" name="openDebugLogfileButton"> <property name="toolTip"> <string>Open the Bitcoin Core debug log file from the current data directory. This can take a few seconds for large log files.</string> @@ -341,7 +367,7 @@ </property> </widget> </item> - <item row="16" column="0"> + <item row="17" column="0"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 117969758c..4ab87e0f32 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -6,7 +6,6 @@ #include "ui_intro.h" #include "guiutil.h" -#include "scicon.h" #include "util.h" @@ -168,7 +167,7 @@ void Intro::pickDataDirectory() /* If current default data directory does not exist, let the user choose one */ Intro intro; intro.setDataDirectory(dataDir); - intro.setWindowIcon(SingleColorIcon(":icons/bitcoin")); + intro.setWindowIcon(QIcon(":icons/bitcoin")); while(true) { diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index e28f903b2e..4541c75886 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -5,7 +5,6 @@ #include "networkstyle.h" #include "guiconstants.h" -#include "scicon.h" #include <QApplication> diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 062c4540ee..f57c1203f6 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -35,7 +35,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : ui(new Ui::OptionsDialog), model(0), mapper(0), - fProxyIpValid(true) + fProxyIpsValid(true) { ui->setupUi(this); @@ -54,10 +54,18 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : ui->proxyPort->setEnabled(false); ui->proxyPort->setValidator(new QIntValidator(1, 65535, this)); + ui->proxyIpTor->setEnabled(false); + ui->proxyPortTor->setEnabled(false); + ui->proxyPortTor->setValidator(new QIntValidator(1, 65535, this)); + connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool))); connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool))); + connect(ui->connectSocksTor, SIGNAL(toggled(bool)), ui->proxyIpTor, SLOT(setEnabled(bool))); + connect(ui->connectSocksTor, SIGNAL(toggled(bool)), ui->proxyPortTor, SLOT(setEnabled(bool))); + ui->proxyIp->installEventFilter(this); + ui->proxyIpTor->installEventFilter(this); /* Window elements init */ #ifdef Q_OS_MAC @@ -110,7 +118,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); mapper->setOrientation(Qt::Vertical); - /* setup/change UI elements when proxy IP is invalid/valid */ + /* setup/change UI elements when proxy IPs are invalid/valid */ connect(this, SIGNAL(proxyIpChecks(QValidatedLineEdit *, int)), this, SLOT(doProxyIpChecks(QValidatedLineEdit *, int))); } @@ -137,6 +145,8 @@ void OptionsDialog::setModel(OptionsModel *model) mapper->setModel(model); setMapper(); mapper->toFirst(); + + updateDefaultProxyNets(); } /* warn when one of the following settings changes by user action (placed here so init via mapper doesn't trigger them) */ @@ -149,6 +159,7 @@ void OptionsDialog::setModel(OptionsModel *model) /* Network */ connect(ui->allowIncoming, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning())); connect(ui->connectSocks, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning())); + connect(ui->connectSocksTor, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning())); /* Display */ connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning())); connect(ui->thirdPartyTxUrls, SIGNAL(textChanged(const QString &)), this, SLOT(showRestartWarning())); @@ -173,6 +184,10 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP); mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort); + mapper->addMapping(ui->connectSocksTor, OptionsModel::ProxyUseTor); + mapper->addMapping(ui->proxyIpTor, OptionsModel::ProxyIPTor); + mapper->addMapping(ui->proxyPortTor, OptionsModel::ProxyPortTor); + /* Window */ #ifndef Q_OS_MAC mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray); @@ -188,7 +203,7 @@ void OptionsDialog::setMapper() void OptionsDialog::enableOkButton() { /* prevent enabling of the OK button when data modified, if there is an invalid proxy address present */ - if(fProxyIpValid) + if(fProxyIpsValid) setOkButtonState(true); } @@ -224,6 +239,7 @@ void OptionsDialog::on_okButton_clicked() { mapper->submit(); accept(); + updateDefaultProxyNets(); } void OptionsDialog::on_cancelButton_clicked() @@ -257,11 +273,10 @@ void OptionsDialog::doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPo { Q_UNUSED(nProxyPort); - const std::string strAddrProxy = pUiProxyIp->text().toStdString(); CService addrProxy; /* Check for a valid IPv4 / IPv6 address */ - if (!(fProxyIpValid = LookupNumeric(strAddrProxy.c_str(), addrProxy))) + if (!(fProxyIpsValid = LookupNumeric(pUiProxyIp->text().toStdString().c_str(), addrProxy))) { disableOkButton(); pUiProxyIp->setValid(false); @@ -275,6 +290,28 @@ void OptionsDialog::doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPo } } +void OptionsDialog::updateDefaultProxyNets() +{ + proxyType proxy; + std::string strProxy; + QString strDefaultProxyGUI; + + GetProxy(NET_IPV4, proxy); + strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); + strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); + (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv4->setChecked(true) : ui->proxyReachIPv4->setChecked(false); + + GetProxy(NET_IPV6, proxy); + strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); + strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); + (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv6->setChecked(true) : ui->proxyReachIPv6->setChecked(false); + + GetProxy(NET_TOR, proxy); + strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); + strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); + (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false); +} + bool OptionsDialog::eventFilter(QObject *object, QEvent *event) { if(event->type() == QEvent::FocusOut) @@ -283,6 +320,10 @@ bool OptionsDialog::eventFilter(QObject *object, QEvent *event) { Q_EMIT proxyIpChecks(ui->proxyIp, ui->proxyPort->text().toInt()); } + else if(object == ui->proxyIpTor) + { + Q_EMIT proxyIpChecks(ui->proxyIpTor, ui->proxyPortTor->text().toInt()); + } } return QDialog::eventFilter(object, event); } diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index fa983e798c..348489c599 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -47,6 +47,8 @@ private Q_SLOTS: void showRestartWarning(bool fPersistent = false); void clearStatusLabel(); void doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort); + /* query the networks, for which the default proxy is used */ + void updateDefaultProxyNets(); Q_SIGNALS: void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort); @@ -55,7 +57,7 @@ private: Ui::OptionsDialog *ui; OptionsModel *model; QDataWidgetMapper *mapper; - bool fProxyIpValid; + bool fProxyIpsValid; }; #endif // BITCOIN_QT_OPTIONSDIALOG_H diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 6191edc44c..65e490570e 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -117,6 +117,16 @@ void OptionsModel::Init() else if(!settings.value("fUseProxy").toBool() && !GetArg("-proxy", "").empty()) addOverriddenOption("-proxy"); + if (!settings.contains("fUseSeparateProxyTor")) + settings.setValue("fUseSeparateProxyTor", false); + if (!settings.contains("addrSeparateProxyTor")) + settings.setValue("addrSeparateProxyTor", "127.0.0.1:9050"); + // Only try to set -onion, if user has enabled fUseSeparateProxyTor + if (settings.value("fUseSeparateProxyTor").toBool() && !SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) + addOverriddenOption("-onion"); + else if(!settings.value("fUseSeparateProxyTor").toBool() && !GetArg("-onion", "").empty()) + addOverriddenOption("-onion"); + // Display if (!settings.contains("language")) settings.setValue("language", ""); @@ -178,6 +188,20 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return strlIpPort.at(1); } + // separate Tor proxy + case ProxyUseTor: + return settings.value("fUseSeparateProxyTor", false); + case ProxyIPTor: { + // contains IP at index 0 and port at index 1 + QStringList strlIpPort = settings.value("addrSeparateProxyTor").toString().split(":", QString::SkipEmptyParts); + return strlIpPort.at(0); + } + case ProxyPortTor: { + // contains IP at index 0 and port at index 1 + QStringList strlIpPort = settings.value("addrSeparateProxyTor").toString().split(":", QString::SkipEmptyParts); + return strlIpPort.at(1); + } + #ifdef ENABLE_WALLET case SpendZeroConfChange: return settings.value("bSpendZeroConfChange"); @@ -259,6 +283,39 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in } } break; + + // separate Tor proxy + case ProxyUseTor: + if (settings.value("fUseSeparateProxyTor") != value) { + settings.setValue("fUseSeparateProxyTor", value.toBool()); + setRestartRequired(true); + } + break; + case ProxyIPTor: { + // contains current IP at index 0 and current port at index 1 + QStringList strlIpPort = settings.value("addrSeparateProxyTor").toString().split(":", QString::SkipEmptyParts); + // if that key doesn't exist or has a changed IP + if (!settings.contains("addrSeparateProxyTor") || strlIpPort.at(0) != value.toString()) { + // construct new value from new IP and current port + QString strNewValue = value.toString() + ":" + strlIpPort.at(1); + settings.setValue("addrSeparateProxyTor", strNewValue); + setRestartRequired(true); + } + } + break; + case ProxyPortTor: { + // contains current IP at index 0 and current port at index 1 + QStringList strlIpPort = settings.value("addrSeparateProxyTor").toString().split(":", QString::SkipEmptyParts); + // if that key doesn't exist or has a changed port + if (!settings.contains("addrSeparateProxyTor") || strlIpPort.at(1) != value.toString()) { + // construct new value from current IP and new port + QString strNewValue = strlIpPort.at(0) + ":" + value.toString(); + settings.setValue("addrSeparateProxyTor", strNewValue); + setRestartRequired(true); + } + } + break; + #ifdef ENABLE_WALLET case SpendZeroConfChange: if (settings.value("bSpendZeroConfChange") != value) { diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index fc26d65b04..8448cad8de 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -34,6 +34,9 @@ public: ProxyUse, // bool ProxyIP, // QString ProxyPort, // int + ProxyUseTor, // bool + ProxyIPTor, // QString + ProxyPortTor, // int DisplayUnit, // BitcoinUnits::Unit ThirdPartyTxUrls, // QString Language, // QString diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index bbd95ef478..a56c80ac63 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -10,7 +10,7 @@ #include "guiconstants.h" #include "guiutil.h" #include "optionsmodel.h" -#include "scicon.h" +#include "platformstyle.h" #include "transactionfilterproxy.h" #include "transactiontablemodel.h" #include "walletmodel.h" @@ -25,7 +25,9 @@ class TxViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: - TxViewDelegate(): QAbstractItemDelegate(), unit(BitcoinUnits::BTC) + TxViewDelegate(const PlatformStyle *platformStyle): + QAbstractItemDelegate(), unit(BitcoinUnits::BTC), + platformStyle(platformStyle) { } @@ -43,7 +45,7 @@ public: int halfheight = (mainRect.height() - 2*ypad)/2; QRect amountRect(mainRect.left() + xspace, mainRect.top()+ypad, mainRect.width() - xspace, halfheight); QRect addressRect(mainRect.left() + xspace, mainRect.top()+ypad+halfheight, mainRect.width() - xspace, halfheight); - icon = SingleColorIcon(icon, SingleColor()); + icon = platformStyle->SingleColorIcon(icon); icon.paint(painter, decorationRect); QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime(); @@ -101,11 +103,12 @@ public: } int unit; + const PlatformStyle *platformStyle; }; #include "overviewpage.moc" -OverviewPage::OverviewPage(QWidget *parent) : +OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) : QWidget(parent), ui(new Ui::OverviewPage), clientModel(0), @@ -116,13 +119,13 @@ OverviewPage::OverviewPage(QWidget *parent) : currentWatchOnlyBalance(-1), currentWatchUnconfBalance(-1), currentWatchImmatureBalance(-1), - txdelegate(new TxViewDelegate()), + txdelegate(new TxViewDelegate(platformStyle)), filter(0) { ui->setupUi(this); // use a SingleColorIcon for the "out of sync warning" icon - QIcon icon = SingleColorIcon(":/icons/warning"); + QIcon icon = platformStyle->SingleColorIcon(":/icons/warning"); icon.addPixmap(icon.pixmap(QSize(64,64), QIcon::Normal), QIcon::Disabled); // also set the disabled icon because we are using a disabled QPushButton to work around missing HiDPI support of QLabel (https://bugreports.qt.io/browse/QTBUG-42503) ui->labelTransactionsStatus->setIcon(icon); ui->labelWalletStatus->setIcon(icon); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index de5ac345da..4139eb35d3 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -12,6 +12,7 @@ class ClientModel; class TransactionFilterProxy; class TxViewDelegate; +class PlatformStyle; class WalletModel; namespace Ui { @@ -28,7 +29,7 @@ class OverviewPage : public QWidget Q_OBJECT public: - explicit OverviewPage(QWidget *parent = 0); + explicit OverviewPage(const PlatformStyle *platformStyle, QWidget *parent = 0); ~OverviewPage(); void setClientModel(ClientModel *clientModel); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 0827d99125..5cc4d00dbf 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -762,7 +762,7 @@ void PaymentServer::setOptionsModel(OptionsModel *optionsModel) void PaymentServer::handlePaymentACK(const QString& paymentACKMsg) { - // currently we don't futher process or store the paymentACK message + // currently we don't further process or store the paymentACK message Q_EMIT message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL); } diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp new file mode 100644 index 0000000000..11cbc7a47c --- /dev/null +++ b/src/qt/platformstyle.cpp @@ -0,0 +1,147 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "platformstyle.h" + +#include "guiconstants.h" + +#include <QApplication> +#include <QColor> +#include <QIcon> +#include <QImage> +#include <QPalette> +#include <QPixmap> + +static const struct { + const char *platformId; + /** Show images on push buttons */ + const bool imagesOnButtons; + /** Colorize single-color icons */ + const bool colorizeIcons; + /** Extra padding/spacing in transactionview */ + const bool useExtraSpacing; +} platform_styles[] = { + {"macosx", false, false, true}, + {"windows", true, false, false}, + /* Other: linux, unix, ... */ + {"other", true, true, false} +}; +static const unsigned platform_styles_count = sizeof(platform_styles)/sizeof(*platform_styles); + +namespace { +/* Local functions for colorizing single-color images */ + +void MakeSingleColorImage(QImage& img, const QColor& colorbase) +{ + img = img.convertToFormat(QImage::Format_ARGB32); + for (int x = img.width(); x--; ) + { + for (int y = img.height(); y--; ) + { + const QRgb rgb = img.pixel(x, y); + img.setPixel(x, y, qRgba(colorbase.red(), colorbase.green(), colorbase.blue(), qAlpha(rgb))); + } + } +} + +QIcon ColorizeIcon(const QIcon& ico, const QColor& colorbase) +{ + QIcon new_ico; + QSize sz; + Q_FOREACH(sz, ico.availableSizes()) + { + QImage img(ico.pixmap(sz).toImage()); + MakeSingleColorImage(img, colorbase); + new_ico.addPixmap(QPixmap::fromImage(img)); + } + return new_ico; +} + +QImage ColorizeImage(const QString& filename, const QColor& colorbase) +{ + QImage img(filename); + MakeSingleColorImage(img, colorbase); + return img; +} + +QIcon ColorizeIcon(const QString& filename, const QColor& colorbase) +{ + return QIcon(QPixmap::fromImage(ColorizeImage(filename, colorbase))); +} + +} + + +PlatformStyle::PlatformStyle(const QString &name, bool imagesOnButtons, bool colorizeIcons, bool useExtraSpacing): + name(name), + imagesOnButtons(imagesOnButtons), + colorizeIcons(colorizeIcons), + useExtraSpacing(useExtraSpacing), + singleColor(0,0,0), + textColor(0,0,0) +{ + // Determine icon highlighting color + if (colorizeIcons) { + const QColor colorHighlightBg(QApplication::palette().color(QPalette::Highlight)); + const QColor colorHighlightFg(QApplication::palette().color(QPalette::HighlightedText)); + const QColor colorText(QApplication::palette().color(QPalette::WindowText)); + const int colorTextLightness = colorText.lightness(); + QColor colorbase; + if (abs(colorHighlightBg.lightness() - colorTextLightness) < abs(colorHighlightFg.lightness() - colorTextLightness)) + colorbase = colorHighlightBg; + else + colorbase = colorHighlightFg; + singleColor = colorbase; + } + // Determine text color + textColor = QColor(QApplication::palette().color(QPalette::WindowText)); +} + +QImage PlatformStyle::SingleColorImage(const QString& filename) const +{ + if (!colorizeIcons) + return QImage(filename); + return ColorizeImage(filename, SingleColor()); +} + +QIcon PlatformStyle::SingleColorIcon(const QString& filename) const +{ + if (!colorizeIcons) + return QIcon(filename); + return ColorizeIcon(filename, SingleColor()); +} + +QIcon PlatformStyle::SingleColorIcon(const QIcon& icon) const +{ + if (!colorizeIcons) + return icon; + return ColorizeIcon(icon, SingleColor()); +} + +QIcon PlatformStyle::TextColorIcon(const QString& filename) const +{ + return ColorizeIcon(filename, TextColor()); +} + +QIcon PlatformStyle::TextColorIcon(const QIcon& icon) const +{ + return ColorizeIcon(icon, TextColor()); +} + +const PlatformStyle *PlatformStyle::instantiate(const QString &platformId) +{ + for (unsigned x=0; x<platform_styles_count; ++x) + { + if (platformId == platform_styles[x].platformId) + { + return new PlatformStyle( + platform_styles[x].platformId, + platform_styles[x].imagesOnButtons, + platform_styles[x].colorizeIcons, + platform_styles[x].useExtraSpacing); + } + } + return 0; +} + diff --git a/src/qt/platformstyle.h b/src/qt/platformstyle.h new file mode 100644 index 0000000000..4e763e760e --- /dev/null +++ b/src/qt/platformstyle.h @@ -0,0 +1,55 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_PLATFORMSTYLE_H +#define BITCOIN_QT_PLATFORMSTYLE_H + +#include <QIcon> +#include <QPixmap> +#include <QString> + +/* Coin network-specific GUI style information */ +class PlatformStyle +{ +public: + /** Get style associated with provided platform name, or 0 if not known */ + static const PlatformStyle *instantiate(const QString &platformId); + + const QString &getName() const { return name; } + + bool getImagesOnButtons() const { return imagesOnButtons; } + bool getUseExtraSpacing() const { return useExtraSpacing; } + + QColor TextColor() const { return textColor; } + QColor SingleColor() const { return singleColor; } + + /** Colorize an image (given filename) with the icon color */ + QImage SingleColorImage(const QString& filename) const; + + /** Colorize an icon (given filename) with the icon color */ + QIcon SingleColorIcon(const QString& filename) const; + + /** Colorize an icon (given object) with the icon color */ + QIcon SingleColorIcon(const QIcon& icon) const; + + /** Colorize an icon (given filename) with the text color */ + QIcon TextColorIcon(const QString& filename) const; + + /** Colorize an icon (given object) with the text color */ + QIcon TextColorIcon(const QIcon& icon) const; + +private: + PlatformStyle(const QString &name, bool imagesOnButtons, bool colorizeIcons, bool useExtraSpacing); + + QString name; + bool imagesOnButtons; + bool colorizeIcons; + bool useExtraSpacing; + QColor singleColor; + QColor textColor; + /* ... more to come later */ +}; + +#endif // BITCOIN_QT_PLATFORMSTYLE_H + diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 43b46c63b5..7fb68cc32a 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -10,9 +10,9 @@ #include "bitcoinunits.h" #include "guiutil.h" #include "optionsmodel.h" +#include "platformstyle.h" #include "receiverequestdialog.h" #include "recentrequeststablemodel.h" -#include "scicon.h" #include "walletmodel.h" #include <QAction> @@ -22,24 +22,25 @@ #include <QScrollBar> #include <QTextDocument> -ReceiveCoinsDialog::ReceiveCoinsDialog(QWidget *parent) : +ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent) : QDialog(parent), ui(new Ui::ReceiveCoinsDialog), - model(0) + model(0), + platformStyle(platformStyle) { ui->setupUi(this); -#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac - ui->clearButton->setIcon(QIcon()); - ui->receiveButton->setIcon(QIcon()); - ui->showRequestButton->setIcon(QIcon()); - ui->removeRequestButton->setIcon(QIcon()); -#else - ui->clearButton->setIcon(SingleColorIcon(":/icons/remove")); - ui->receiveButton->setIcon(SingleColorIcon(":/icons/receiving_addresses")); - ui->showRequestButton->setIcon(SingleColorIcon(":/icons/edit")); - ui->removeRequestButton->setIcon(SingleColorIcon(":/icons/remove")); -#endif + if (!platformStyle->getImagesOnButtons()) { + ui->clearButton->setIcon(QIcon()); + ui->receiveButton->setIcon(QIcon()); + ui->showRequestButton->setIcon(QIcon()); + ui->removeRequestButton->setIcon(QIcon()); + } else { + ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); + ui->receiveButton->setIcon(platformStyle->SingleColorIcon(":/icons/receiving_addresses")); + ui->showRequestButton->setIcon(platformStyle->SingleColorIcon(":/icons/edit")); + ui->removeRequestButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); + } // context menu actions QAction *copyLabelAction = new QAction(tr("Copy label"), this); @@ -132,7 +133,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() if(ui->reuseAddress->isChecked()) { /* Choose existing receiving address */ - AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); + AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); dlg.setModel(model->getAddressTableModel()); if(dlg.exec()) { diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 6bb159482b..eaaf129a91 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -16,6 +16,7 @@ #include <QVariant> class OptionsModel; +class PlatformStyle; class WalletModel; namespace Ui { @@ -39,7 +40,7 @@ public: MINIMUM_COLUMN_WIDTH = 130 }; - explicit ReceiveCoinsDialog(QWidget *parent = 0); + explicit ReceiveCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent = 0); ~ReceiveCoinsDialog(); void setModel(WalletModel *model); @@ -57,6 +58,8 @@ private: GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer; WalletModel *model; QMenu *contextMenu; + const PlatformStyle *platformStyle; + void copyColumnToClipboard(int column); virtual void resizeEvent(QResizeEvent *event); diff --git a/src/qt/res/icons/about_qt.png b/src/qt/res/icons/about_qt.png Binary files differindex dd27a99d0a..c40abfd3a6 100644 --- a/src/qt/res/icons/about_qt.png +++ b/src/qt/res/icons/about_qt.png diff --git a/src/qt/res/icons/clock1.png b/src/qt/res/icons/clock1.png Binary files differindex ceae5ed0d9..162204d1bb 100644 --- a/src/qt/res/icons/clock1.png +++ b/src/qt/res/icons/clock1.png diff --git a/src/qt/res/icons/clock2.png b/src/qt/res/icons/clock2.png Binary files differindex 159f69a8fc..8f4263a31c 100644 --- a/src/qt/res/icons/clock2.png +++ b/src/qt/res/icons/clock2.png diff --git a/src/qt/res/icons/clock3.png b/src/qt/res/icons/clock3.png Binary files differindex d668e35ffc..7f11a7566c 100644 --- a/src/qt/res/icons/clock3.png +++ b/src/qt/res/icons/clock3.png diff --git a/src/qt/res/icons/clock4.png b/src/qt/res/icons/clock4.png Binary files differindex 5ebf8ed7ac..fdd1a0fce3 100644 --- a/src/qt/res/icons/clock4.png +++ b/src/qt/res/icons/clock4.png diff --git a/src/qt/res/icons/clock5.png b/src/qt/res/icons/clock5.png Binary files differindex 96f15ef7d9..7d6556c6cf 100644 --- a/src/qt/res/icons/clock5.png +++ b/src/qt/res/icons/clock5.png diff --git a/src/qt/res/icons/connect0.png b/src/qt/res/icons/connect0.png Binary files differindex 58e2c3e965..ef708d81fb 100644 --- a/src/qt/res/icons/connect0.png +++ b/src/qt/res/icons/connect0.png diff --git a/src/qt/res/icons/connect1.png b/src/qt/res/icons/connect1.png Binary files differindex 949e7a922d..ed358e6f8e 100644 --- a/src/qt/res/icons/connect1.png +++ b/src/qt/res/icons/connect1.png diff --git a/src/qt/res/icons/connect2.png b/src/qt/res/icons/connect2.png Binary files differindex 143b2054fb..3bbb0d395c 100644 --- a/src/qt/res/icons/connect2.png +++ b/src/qt/res/icons/connect2.png diff --git a/src/qt/res/icons/connect3.png b/src/qt/res/icons/connect3.png Binary files differindex 143b2054fb..0db99ad8d3 100644 --- a/src/qt/res/icons/connect3.png +++ b/src/qt/res/icons/connect3.png diff --git a/src/qt/res/icons/connect4.png b/src/qt/res/icons/connect4.png Binary files differindex f96e3455ce..9dd19fc2bd 100644 --- a/src/qt/res/icons/connect4.png +++ b/src/qt/res/icons/connect4.png diff --git a/src/qt/res/icons/transaction0.png b/src/qt/res/icons/transaction0.png Binary files differindex 1091b86e68..72c44565ec 100644 --- a/src/qt/res/icons/transaction0.png +++ b/src/qt/res/icons/transaction0.png diff --git a/src/qt/res/icons/warning.png b/src/qt/res/icons/warning.png Binary files differindex 723a30a658..6bc5ac7895 100644 --- a/src/qt/res/icons/warning.png +++ b/src/qt/res/icons/warning.png diff --git a/src/qt/res/src/clock_1.svg b/src/qt/res/src/clock_1.svg index 4e49772d26..2a3d84c2d0 100644 --- a/src/qt/res/src/clock_1.svg +++ b/src/qt/res/src/clock_1.svg @@ -9,5 +9,7 @@ c-57.8,0-112.1,22.5-153,63.4c-40.9,40.9-63.4,95.2-63.4,153c0,57.8,22.5,112.1,63.4,153c40.9,40.9,95.2,63.4,153,63.4
c57.8,0,112.1-22.5,153-63.4c40.9-40.9,63.4-95.2,63.4-153c0-57.8-22.5-112.1-63.4-153C409.8,227.1,355.4,204.6,297.6,204.6z"/>
</g>
-<polygon points="478.3,253.4 297.6,184.6 297.6,420.9 534,420.9 "/>
+<path
+ d="M 478.3,253.4 297.6,184.6 c 0,0 0,78.8 0,118.2 0,117.5 -0.4,118.1 118.2,118.1 39.4,0 118.2,0 118.2,0 z"
+ id="polygon7" />
</svg>
diff --git a/src/qt/res/src/clock_2.svg b/src/qt/res/src/clock_2.svg index 995446e46e..2de8d467b7 100644 --- a/src/qt/res/src/clock_2.svg +++ b/src/qt/res/src/clock_2.svg @@ -9,6 +9,5 @@ c-57.8,0-112.1,22.5-153,63.4c-40.9,40.9-63.4,95.2-63.4,153c0,57.8,22.5,112.1,63.4,153c40.9,40.9,95.2,63.4,153,63.4
c57.8,0,112.1-22.5,153-63.4c40.9-40.9,63.4-95.2,63.4-153c0-57.8-22.5-112.1-63.4-153C409.8,227.1,355.4,204.6,297.6,204.6z"/>
</g>
-<polygon points="465.2,601.6 534,420.9 297.6,420.9 297.6,657.3 "/>
-<polygon points="478.3,253.4 297.6,184.6 297.6,420.9 534,420.9 "/>
+<polygon points="465.2,601.6 534,420.9 478.3,253.4 297.6,184.6 297.6,420.9 297.6,657.3 "/>
</svg>
diff --git a/src/qt/res/src/clock_3.svg b/src/qt/res/src/clock_3.svg index ea47a84730..b691043e3e 100644 --- a/src/qt/res/src/clock_3.svg +++ b/src/qt/res/src/clock_3.svg @@ -9,7 +9,7 @@ c-57.8,0-112.1,22.5-153,63.4c-40.9,40.9-63.4,95.2-63.4,153c0,57.8,22.5,112.1,63.4,153c40.9,40.9,95.2,63.4,153,63.4
c57.8,0,112.1-22.5,153-63.4c40.9-40.9,63.4-95.2,63.4-153c0-57.8-22.5-112.1-63.4-153C409.8,227.1,355.4,204.6,297.6,204.6z"/>
</g>
-<polygon points="117,588.5 297.6,657.3 297.6,420.9 61.3,420.9 "/>
-<polygon points="465.2,601.6 534,420.9 297.6,420.9 297.6,657.3 "/>
-<polygon points="478.3,253.4 297.6,184.6 297.6,420.9 534,420.9 "/>
+<path
+ d="M 465.2,601.6 534,420.9 478.3,253.4 297.6,184.6 c 0,0 0,78.8 0,118.2 0,117.7 0.4,118.1 -118.1,118.1 -39.4,0 -118.2,0 -118.2,0 l 55.7,167.6 180.6,68.8 z"
+ id="polygon7" />
</svg>
diff --git a/src/qt/res/src/clock_4.svg b/src/qt/res/src/clock_4.svg index 43160288d8..ea311f31e8 100644 --- a/src/qt/res/src/clock_4.svg +++ b/src/qt/res/src/clock_4.svg @@ -1,18 +1,23 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 841.9 841.9" enable-background="new 0 0 841.9 841.9" xml:space="preserve">
-<g>
- <path d="M297.6,677.3c-68.5,0-132.9-26.7-181.3-75.1S41.3,489.4,41.3,420.9s26.7-132.9,75.1-181.3c48.4-48.4,112.8-75.1,181.3-75.1
- s132.9,26.7,181.3,75.1c48.4,48.4,75.1,112.8,75.1,181.3s-26.7,132.9-75.1,181.3S366.1,677.3,297.6,677.3z M297.6,204.6
- c-57.8,0-112.1,22.5-153,63.4c-40.9,40.9-63.4,95.2-63.4,153c0,57.8,22.5,112.1,63.4,153c40.9,40.9,95.2,63.4,153,63.4
- c57.8,0,112.1-22.5,153-63.4c40.9-40.9,63.4-95.2,63.4-153c0-57.8-22.5-112.1-63.4-153C409.8,227.1,355.4,204.6,297.6,204.6z"/>
-</g>
-<polygon points="130.1,240.3 61.3,420.9 297.6,420.9 297.6,184.6 "/>
-<polygon points="117,588.5 297.6,657.3 297.6,420.9 61.3,420.9 "/>
-<polygon points="465.2,601.6 534,420.9 297.6,420.9 297.6,657.3 "/>
-<polygon points="478.3,253.4 297.6,184.6 297.6,420.9 534,420.9 "/>
-<path fill="#FFFFFF" d="M293.5,452.6h99.6c14.9,0,24.8-9.9,24.8-24.8S408,403,393.1,403h-74.8V278.2c0-14.9-9.9-24.8-24.8-24.8
- c-14.9,0-24.8,9.9-24.8,24.8v149.6C268.7,440.2,278.7,452.6,293.5,452.6z"/>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xml:space="preserve" + enable-background="new 0 0 841.9 841.9" + viewBox="0 0 841.9 841.9" + y="0px" + x="0px" + id="Ebene_1" + version="1.1"><metadata + id="metadata15"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs + id="defs13" /><g + id="g3"><path + id="path5" + d="M297.6,677.3c-68.5,0-132.9-26.7-181.3-75.1S41.3,489.4,41.3,420.9s26.7-132.9,75.1-181.3c48.4-48.4,112.8-75.1,181.3-75.1 s132.9,26.7,181.3,75.1c48.4,48.4,75.1,112.8,75.1,181.3s-26.7,132.9-75.1,181.3S366.1,677.3,297.6,677.3z M297.6,204.6 c-57.8,0-112.1,22.5-153,63.4c-40.9,40.9-63.4,95.2-63.4,153c0,57.8,22.5,112.1,63.4,153c40.9,40.9,95.2,63.4,153,63.4 c57.8,0,112.1-22.5,153-63.4c40.9-40.9,63.4-95.2,63.4-153c0-57.8-22.5-112.1-63.4-153C409.8,227.1,355.4,204.6,297.6,204.6z" /></g><path + id="polygon7" + d="M 297.6 184.6 L 130.1 240.3 L 61.3 420.9 L 117 588.5 L 297.6 657.3 L 465.2 601.6 L 534 420.9 L 478.3 253.4 L 297.6 184.6 z M 293.5 253.4 C 308.4 253.4 318.3 263.3 318.3 278.2 L 318.3 403 L 393.1 403 C 408 403 417.9 412.9 417.9 427.8 C 417.9 442.7 408 452.6 393.1 452.6 L 293.5 452.6 C 278.7 452.6 268.7 440.2 268.7 427.8 L 268.7 278.2 C 268.7 263.3 278.6 253.4 293.5 253.4 z " /></svg>
\ No newline at end of file diff --git a/src/qt/res/src/connect-0.svg b/src/qt/res/src/connect-0.svg index bedbec7777..7d2afac622 100644 --- a/src/qt/res/src/connect-0.svg +++ b/src/qt/res/src/connect-0.svg @@ -1,11 +1,66 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
-<path fill="none" stroke="#000000" stroke-miterlimit="10" d="M13.4,19.4c0.8-0.8,0.8-2,0-2.8c-0.8-0.8-2-0.8-2.8,0
- c-0.8,0.8-0.8,2.1,0,2.8C11.4,20.2,12.6,20.2,13.4,19.4z M7.8,15.8c-0.5,0-1-0.2-1.4-0.6c-0.8-0.8-0.8-2,0-2.8
- c3.1-3.1,8.2-3.1,11.3,0c0.8,0.8,0.8,2,0,2.8c-0.8,0.8-2,0.8-2.8,0c-1.6-1.6-4.1-1.6-5.7,0C8.8,15.6,8.3,15.8,7.8,15.8z"/>
-<path fill="none" stroke="#000000" stroke-miterlimit="10" d="M20.5,11.5c-0.5,0-1-0.2-1.4-0.6C15.2,7,8.8,7,4.9,10.9
- c-0.8,0.8-2,0.8-2.8,0c-0.8-0.8-0.8-2,0-2.8c5.5-5.5,14.3-5.5,19.8,0c0.8,0.8,0.8,2,0,2.8C21.5,11.3,21,11.5,20.5,11.5z"/>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + id="svg2" + viewBox="0 0 24 24" + height="24" + width="24" + version="1.2"> + <metadata + id="metadata10"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs8" /> + <g + id="g4142" + transform="matrix(0,-1,-1,0,23.96,24)"> + <g + id="g4210" + transform="matrix(-1,0,0,1,59.86,-106.6)"> + <g + id="g4289" + transform="matrix(-1,0,0,1,-16.98,0.8136)"> + <g + id="g4291"> + <path + id="path4293" + d="m -65.35,116.3 0,3 0.5,0 c 0.54,0 1,0.5 1,1 l 0,2.6 c -1.15,0.5 -2,1.6 -2,3 0,2 1.59,3.5 3.5,3.5 1.91,0 3.5,-1.5 3.5,-3.5 0,-1.4 -0.85,-2.5 -2,-3 l 0,-2.6 c 0,-2.3 -1.81,-4 -4,-4 z m 1,1.2 c 1.39,0.3 2.5,1.3 2.5,2.8 l 0,3.2 0.34,0.1 c 0.96,0.3 1.66,1.2 1.66,2.3 0,1.4 -1.11,2.5 -2.5,2.5 -1.39,0 -2.5,-1.1 -2.5,-2.5 0,-1.1 0.69,-2 1.66,-2.3 l 0.34,-0.1 0,-3.2 c 0,-0.9 -0.67,-1.5 -1.5,-1.8 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <g + style="fill:#969696;fill-opacity:1" + id="g4295"> + <path + id="path4297" + d="m -67.35,106.1 c -1.94,0 -3.5,1.6 -3.5,3.5 0,1.4 0.85,2.5 2,3 l 0,2.7 c 0,2.2 1.79,4 4,4 l 0.5,0 0,-0.5 0,-2.5 -0.5,0 c -0.55,0 -1,-0.5 -1,-1 l 0,-2.7 c 1.15,-0.5 2,-1.6 2,-3 0,-1.9 -1.57,-3.5 -3.5,-3.5 z m 0,1 c 1.37,0 2.5,1.2 2.5,2.5 0,1.1 -0.7,2 -1.66,2.3 l -0.34,0.1 0,3.3 c 0,0.9 0.67,1.5 1.5,1.8 l 0,1 c -1.38,-0.3 -2.5,-1.4 -2.5,-2.8 l 0,-3.3 -0.34,-0.1 c -0.96,-0.3 -1.66,-1.2 -1.66,-2.3 0,-1.3 1.12,-2.5 2.5,-2.5 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <path + id="path4299" + d="m -57.35,106.1 c -1.93,0 -3.5,1.6 -3.5,3.5 0,1.4 0.85,2.5 2,3 l 0,2.7 c 0,0.5 -0.45,1 -1,1 l -4.85,0 3.17,3 1.68,0 c 2.21,0 4,-1.8 4,-4 l 0,-2.7 c 1.15,-0.5 2,-1.6 2,-3 0,-1.9 -1.56,-3.5 -3.5,-3.5 z m 0,1 c 1.38,0 2.5,1.2 2.5,2.5 0,1.1 -0.7,2 -1.66,2.3 l -0.34,0.1 0,3.3 c 0,1.6 -1.35,3 -3,3 l -1.81,0 -2.04,-1 3.85,0 c 1.11,0 2,-0.9 2,-2 l 0,-3.3 -0.34,-0.1 c -0.96,-0.3 -1.66,-1.2 -1.66,-2.3 0,-1.3 1.13,-2.5 2.5,-2.5 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + </g> + <path + id="path4301" + d="m -69.84,116.3 c -2.19,0 -4,1.7 -4,4 l 0,2.6 c -1.14,0.6 -1.99,1.6 -1.99,3 0,2 1.6,3.5 3.51,3.5 1.91,0 3.5,-1.5 3.5,-3.5 0,-1.4 -0.85,-2.5 -2,-3 l 0,-2.6 c 0,-0.5 0.45,-1 1,-1 l 5.01,0 -3.36,-3 z m 0,1 1.84,0 2.19,1 -4.01,0 c -1.11,0 -2,0.9 -2,2 l 0,3.2 0.34,0.1 c 0.96,0.3 1.66,1.2 1.66,2.3 0,1.4 -1.11,2.5 -2.5,2.5 -1.39,0 -2.51,-1.1 -2.51,-2.5 0,-1.1 0.7,-2 1.66,-2.3 l 0.33,-0.1 0,-0.4 0,-2.8 c 0,-1.7 1.33,-3 3,-3 z" + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + </g> + </g> + </g> + <path + id="path4165" + d="m 12,8.77 c -0.84,0 -1.66,0.341 -2.254,0.937 -0.599,0.593 -0.942,1.403 -0.945,2.253 0,0.85 0.337,1.67 0.933,2.26 a 0.6001,0.6001 0 0 0 0,0 c 0.594,0.6 1.424,0.94 2.264,0.94 0.84,0 1.67,-0.34 2.26,-0.94 0.6,-0.59 0.94,-1.41 0.94,-2.26 0,-0.84 -0.34,-1.66 -0.95,-2.253 C 13.66,9.111 12.84,8.77 12,8.77 Z" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> +</svg> diff --git a/src/qt/res/src/connect-1.svg b/src/qt/res/src/connect-1.svg index d3d4e46a41..d17928c97d 100644 --- a/src/qt/res/src/connect-1.svg +++ b/src/qt/res/src/connect-1.svg @@ -1,21 +1,69 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
-<g>
- <path d="M12,11c1.9,0,3.6,0.7,4.9,2c0.4,0.4,0.4,1,0,1.4c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3C14.6,13.5,13.3,13,12,13
- c-1.3,0-2.6,0.5-3.5,1.5c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4C8.4,11.7,10.1,11,12,11 M12,17
- c0.3,0,0.5,0.1,0.7,0.3c0.2,0.2,0.3,0.4,0.3,0.7s-0.1,0.5-0.3,0.7C12.5,18.9,12.3,19,12,19c-0.3,0-0.5-0.1-0.7-0.3
- C11.1,18.5,11,18.3,11,18c0-0.3,0.1-0.5,0.3-0.7C11.5,17.1,11.7,17,12,17 M12,10c-2,0-4.1,0.8-5.7,2.3c-0.8,0.8-0.8,2,0,2.8
- c0.4,0.4,0.9,0.6,1.4,0.6c0.5,0,1-0.2,1.4-0.6C10,14.4,11,14,12,14c1,0,2,0.4,2.8,1.2c0.4,0.4,0.9,0.6,1.4,0.6s1-0.2,1.4-0.6
- c0.8-0.8,0.8-2,0-2.8C16.1,10.8,14,10,12,10L12,10z M12,16c-0.5,0-1,0.2-1.4,0.6c-0.8,0.8-0.8,2.1,0,2.8C11,19.8,11.5,20,12,20
- c0.5,0,1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8C13,16.2,12.5,16,12,16L12,16z"/>
-</g>
-<g>
- <path d="M12,5c3.5,0,6.7,1.3,9.2,3.8c0.4,0.4,0.4,1,0,1.4c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3C17.7,8.1,14.9,7,12,7
- c-2.9,0-5.7,1.1-7.8,3.2c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4C5.3,6.4,8.5,5,12,5 M12,4
- C8.4,4,4.8,5.4,2.1,8.1c-0.8,0.8-0.8,2,0,2.8c0.4,0.4,0.9,0.6,1.4,0.6c0.5,0,1-0.2,1.4-0.6C6.9,9,9.4,8,12,8c2.6,0,5.1,1,7.1,2.9
- c0.4,0.4,0.9,0.6,1.4,0.6c0.5,0,1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8C19.2,5.4,15.6,4,12,4L12,4z"/>
-</g>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.2" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2"> + <metadata + id="metadata10"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs8" /> + <g + id="g4210" + transform="translate(0,0.25)"> + <g + id="g4142" + transform="matrix(0,-1,-1,0,23.96,23.75)"> + <g + id="g4213" + transform="matrix(-1,0,0,1,59.86,-106.6)"> + <g + id="g4289" + transform="matrix(-1,0,0,1,-16.98,0.8136)"> + <g + id="g4291"> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m -65.35,116.3 0,3 0.5,0 c 0.54,0 1,0.5 1,1 l 0,2.6 c -1.15,0.5 -2,1.6 -2,3 0,2 1.59,3.5 3.5,3.5 1.91,0 3.5,-1.5 3.5,-3.5 0,-1.4 -0.85,-2.5 -2,-3 l 0,-2.6 c 0,-2.3 -1.81,-4 -4,-4 z m 1,1.2 c 1.39,0.3 2.5,1.3 2.5,2.8 l 0,3.2 0.34,0.1 c 0.96,0.3 1.66,1.2 1.66,2.3 0,1.4 -1.11,2.5 -2.5,2.5 -1.39,0 -2.5,-1.1 -2.5,-2.5 0,-1.1 0.69,-2 1.66,-2.3 l 0.34,-0.1 0,-3.2 c 0,-0.9 -0.67,-1.5 -1.5,-1.8 z" + id="path4293" /> + <g + id="g4295"> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m -67.35,106.1 c -1.94,0 -3.5,1.6 -3.5,3.5 0,1.4 0.85,2.5 2,3 l 0,2.7 c 0,2.2 1.79,4 4,4 l 0.5,0 0,-0.5 0,-2.5 -0.5,0 c -0.55,0 -1,-0.5 -1,-1 l 0,-2.7 c 1.15,-0.5 2,-1.6 2,-3 0,-1.9 -1.57,-3.5 -3.5,-3.5 z m 0,1 c 1.37,0 2.5,1.2 2.5,2.5 0,1.1 -0.7,2 -1.66,2.3 l -0.34,0.1 0,3.3 c 0,0.9 0.67,1.5 1.5,1.8 l 0,1 c -1.38,-0.3 -2.5,-1.4 -2.5,-2.8 l 0,-3.3 -0.34,-0.1 c -0.96,-0.3 -1.66,-1.2 -1.66,-2.3 0,-1.3 1.12,-2.5 2.5,-2.5 z" + id="path4297" /> + <path + id="path4145" + d="m -57.35,106 c -1.99,0 -3.6,1.7 -3.6,3.6 0,1.4 0.83,2.6 2,3.2 l 0,2.5 c 0,0.5 -0.41,0.9 -0.9,0.9 l -4.35,0 a 0.6001,0.6001 0 0 0 -0.6,0.6 l 0,2 a 0.6001,0.6001 0 0 0 0.6,0.6 l 4.35,0 c 2.26,0 4.1,-1.9 4.1,-4.1 l 0,-2.5 c 1.17,-0.6 2,-1.8 2,-3.2 0,-1.9 -1.6,-3.6 -3.6,-3.6 z" + style="" /> + </g> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m -69.84,116.3 c -2.19,0 -4,1.7 -4,4 l 0,2.6 c -1.14,0.6 -1.99,1.6 -1.99,3 0,2 1.6,3.5 3.51,3.5 1.91,0 3.5,-1.5 3.5,-3.5 0,-1.4 -0.85,-2.5 -2,-3 l 0,-2.6 c 0,-0.5 0.45,-1 1,-1 l 5.01,0 -3.34,-3 z m 0,1 2.02,0 2.01,1 -4.01,0 c -1.11,0 -2,0.9 -2,2 l 0,3.2 0.34,0.1 c 0.96,0.3 1.66,1.2 1.66,2.3 0,1.4 -1.11,2.5 -2.5,2.5 -1.39,0 -2.51,-1.1 -2.51,-2.5 0,-1.1 0.7,-2 1.66,-2.3 l 0.33,-0.1 0,-0.4 0,-2.8 c 0,-1.7 1.33,-3 3,-3 z" + id="path4301" /> + </g> + </g> + </g> + <path + id="path4173" + d="m 12,8.764 c -0.84,0 -1.67,0.336 -2.264,0.931 a 0.6001,0.6001 0 0 0 -0,0 C 9.138,10.29 8.802,11.11 8.801,11.96 c 0,0.85 0.337,1.67 0.933,2.26 a 0.6001,0.6001 0 0 0 0,0 c 0.594,0.6 1.424,0.94 2.264,0.94 0.84,0 1.67,-0.34 2.26,-0.94 0.6,-0.59 0.94,-1.41 0.94,-2.26 0,-0.84 -0.34,-1.67 -0.94,-2.265 C 13.67,9.1 12.84,8.764 12,8.764 Z" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + </g> +</svg> diff --git a/src/qt/res/src/connect-2.svg b/src/qt/res/src/connect-2.svg index d5becc52b7..841ca6071d 100644 --- a/src/qt/res/src/connect-2.svg +++ b/src/qt/res/src/connect-2.svg @@ -1,22 +1,59 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
-<path d="M13.4,19.4c0.8-0.8,0.8-2,0-2.8c-0.8-0.8-2-0.8-2.8,0c-0.8,0.8-0.8,2.1,0,2.8C11.4,20.2,12.6,20.2,13.4,19.4z"/>
-<g>
- <path d="M12,11c1.9,0,3.6,0.7,4.9,2c0.4,0.4,0.4,1,0,1.4c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3C14.6,13.5,13.3,13,12,13
- c-1.3,0-2.6,0.5-3.5,1.5c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4C8.4,11.7,10.1,11,12,11 M12,17
- c0.3,0,0.5,0.1,0.7,0.3c0.2,0.2,0.3,0.4,0.3,0.7s-0.1,0.5-0.3,0.7C12.5,18.9,12.3,19,12,19c-0.3,0-0.5-0.1-0.7-0.3
- C11.1,18.5,11,18.3,11,18c0-0.3,0.1-0.5,0.3-0.7C11.5,17.1,11.7,17,12,17 M12,10c-2,0-4.1,0.8-5.7,2.3c-0.8,0.8-0.8,2,0,2.8
- c0.4,0.4,0.9,0.6,1.4,0.6c0.5,0,1-0.2,1.4-0.6C10,14.4,11,14,12,14c1,0,2,0.4,2.8,1.2c0.4,0.4,0.9,0.6,1.4,0.6s1-0.2,1.4-0.6
- c0.8-0.8,0.8-2,0-2.8C16.1,10.8,14,10,12,10L12,10z M12,16c-0.5,0-1,0.2-1.4,0.6c-0.8,0.8-0.8,2.1,0,2.8C11,19.8,11.5,20,12,20
- c0.5,0,1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8C13,16.2,12.5,16,12,16L12,16z"/>
-</g>
-<g>
- <path d="M12,5c3.5,0,6.7,1.3,9.2,3.8c0.4,0.4,0.4,1,0,1.4c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3C17.7,8.1,14.9,7,12,7
- c-2.9,0-5.7,1.1-7.8,3.2c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4C5.3,6.4,8.5,5,12,5 M12,4
- C8.4,4,4.8,5.4,2.1,8.1c-0.8,0.8-0.8,2,0,2.8c0.4,0.4,0.9,0.6,1.4,0.6c0.5,0,1-0.2,1.4-0.6C6.9,9,9.4,8,12,8c2.6,0,5.1,1,7.1,2.9
- c0.4,0.4,0.9,0.6,1.4,0.6c0.5,0,1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8C19.2,5.4,15.6,4,12,4L12,4z"/>
-</g>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.2" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2"> + <metadata + id="metadata10"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs8" /> + <g + id="g4210" + transform="matrix(0,1,-1,0,130.6,-35.86)"> + <g + id="g4289" + transform="matrix(-1,0,0,1,-16.98,0.8136)"> + <g + id="g4291"> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m -65.35,116.3 0,3 0.5,0 c 0.54,0 1,0.5 1,1 l 0,2.6 c -1.15,0.5 -2,1.6 -2,3 0,2 1.59,3.5 3.5,3.5 1.91,0 3.5,-1.5 3.5,-3.5 0,-1.4 -0.85,-2.5 -2,-3 l 0,-2.6 c 0,-2.3 -1.81,-4 -4,-4 z m 1,1.2 c 1.39,0.3 2.5,1.3 2.5,2.8 l 0,3.2 0.34,0.1 c 0.96,0.3 1.66,1.2 1.66,2.3 0,1.4 -1.11,2.5 -2.5,2.5 -1.39,0 -2.5,-1.1 -2.5,-2.5 0,-1.1 0.69,-2 1.66,-2.3 l 0.34,-0.1 0,-3.2 c 0,-0.9 -0.67,-1.5 -1.5,-1.8 z" + id="path4293" /> + <g + id="g4295"> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m -67.35,106.1 c -1.94,0 -3.5,1.6 -3.5,3.5 0,1.4 0.85,2.5 2,3 l 0,2.7 c 0,2.2 1.79,4 4,4 l 0.5,0 0,-0.5 0,-2.5 -0.5,0 c -0.55,0 -1,-0.5 -1,-1 l 0,-2.7 c 1.15,-0.5 2,-1.6 2,-3 0,-1.9 -1.57,-3.5 -3.5,-3.5 z m 0,1 c 1.37,0 2.5,1.2 2.5,2.5 0,1.1 -0.7,2 -1.66,2.3 l -0.34,0.1 0,3.3 c 0,0.9 0.67,1.5 1.5,1.8 l 0,1 c -1.38,-0.3 -2.5,-1.4 -2.5,-2.8 l 0,-3.3 -0.34,-0.1 c -0.96,-0.3 -1.66,-1.2 -1.66,-2.3 0,-1.3 1.12,-2.5 2.5,-2.5 z" + id="path4297" /> + <path + id="path4142" + d="m -57.35,106 c -1.99,0 -3.6,1.7 -3.6,3.6 0,1.4 0.83,2.6 2,3.2 l 0,2.5 c 0,0.5 -0.41,0.9 -0.9,0.9 l -4.35,0 a 0.6001,0.6001 0 0 0 -0.6,0.6 l 0,2 a 0.6001,0.6001 0 0 0 0.6,0.6 l 4.35,0 c 2.26,0 4.1,-1.9 4.1,-4.1 l 0,-2.5 c 1.17,-0.6 2,-1.8 2,-3.2 0,-1.9 -1.6,-3.6 -3.6,-3.6 z" /> + </g> + <path + id="path4148" + d="m -69.84,116.2 c -2.24,0 -4.1,1.8 -4.1,4.1 l 0,2.5 c -1.17,0.5 -1.99,1.7 -1.99,3.1 0,2 1.64,3.6 3.61,3.6 1.96,0 3.6,-1.6 3.6,-3.6 0,-1.4 -0.83,-2.6 -2,-3.2 l 0,-2.4 c 0,-0.5 0.41,-0.9 0.9,-0.9 l 4.51,0 a 0.6001,0.6001 0 0 0 0.6,-0.6 l 0,-2 a 0.6001,0.6001 0 0 0 -0.6,-0.6 l -4.53,0 z" /> + </g> + </g> + <path + id="path4170" + d="m 47.86,115.4 c -0.84,0 -1.65,0.4 -2.24,1 -0.64,0.5 -0.96,1.3 -0.96,2.2 0,0.9 0.32,1.7 0.96,2.2 0.59,0.6 1.4,1 2.24,1 0.84,0 1.65,-0.4 2.24,-1 0.64,-0.5 0.96,-1.3 0.96,-2.2 0,-0.9 -0.32,-1.7 -0.96,-2.2 -0.59,-0.6 -1.4,-1 -2.24,-1 z" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> +</svg> diff --git a/src/qt/res/src/connect-3.svg b/src/qt/res/src/connect-3.svg index 9bfa04721f..b06e67daf8 100644 --- a/src/qt/res/src/connect-3.svg +++ b/src/qt/res/src/connect-3.svg @@ -1,16 +1,72 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
-<path d="M13.4,19.4c0.8-0.8,0.8-2,0-2.8c-0.8-0.8-2-0.8-2.8,0c-0.8,0.8-0.8,2.1,0,2.8C11.4,20.2,12.6,20.2,13.4,19.4z"/>
-<path d="M13.4,19.4c0.8-0.8,0.8-2,0-2.8c-0.8-0.8-2-0.8-2.8,0c-0.8,0.8-0.8,2.1,0,2.8C11.4,20.2,12.6,20.2,13.4,19.4z M7.8,15.8
- c-0.5,0-1-0.2-1.4-0.6c-0.8-0.8-0.8-2,0-2.8c3.1-3.1,8.2-3.1,11.3,0c0.8,0.8,0.8,2,0,2.8c-0.8,0.8-2,0.8-2.8,0
- c-1.6-1.6-4.1-1.6-5.7,0C8.8,15.6,8.3,15.8,7.8,15.8z"/>
-<g>
- <path d="M12,5c3.5,0,6.7,1.3,9.2,3.8c0.4,0.4,0.4,1,0,1.4c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3C17.7,8.1,14.9,7,12,7
- c-2.9,0-5.7,1.1-7.8,3.2c-0.2,0.2-0.4,0.3-0.7,0.3c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4C5.3,6.4,8.5,5,12,5 M12,4
- C8.4,4,4.8,5.4,2.1,8.1c-0.8,0.8-0.8,2,0,2.8c0.4,0.4,0.9,0.6,1.4,0.6c0.5,0,1-0.2,1.4-0.6C6.9,9,9.4,8,12,8c2.6,0,5.1,1,7.1,2.9
- c0.4,0.4,0.9,0.6,1.4,0.6c0.5,0,1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8C19.2,5.4,15.6,4,12,4L12,4z"/>
-</g>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + id="svg2" + viewBox="0 0 24 24" + height="24" + width="24" + version="1.2"> + <metadata + id="metadata10"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs8" /> + <g + transform="translate(0.2636,0.29)" + id="g4160"> + <g + id="g4210" + transform="matrix(0,1,-1,0,130.3,-36.15)"> + <g + id="g4289" + transform="matrix(-1,0,0,1,-16.98,0.8136)"> + <g + id="g4291"> + <path + id="path4147" + d="m -64.85,116.2 a 0.6001,0.6001 0 0 0 -0.6,0.6 l 0,2 a 0.6001,0.6001 0 0 0 0.6,0.6 c 0.48,0 0.9,0.4 0.9,0.9 l 0,2.4 c -1.18,0.6 -2,1.8 -2,3.2 0,2 1.64,3.6 3.6,3.6 1.97,0 3.6,-1.6 3.6,-3.6 0,-1.4 -0.83,-2.6 -2,-3.2 l 0,-2.4 c 0,-2.3 -1.86,-4.1 -4.1,-4.1 z" + style="" /> + <g + id="g4295"> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m -67.35,106.1 c -1.94,0 -3.5,1.6 -3.5,3.5 0,1.4 0.85,2.5 2,3 l 0,2.7 c 0,2.2 1.79,4 4,4 l 0.5,0 0,-0.5 0,-2.5 -0.5,0 c -0.55,0 -1,-0.5 -1,-1 l 0,-2.7 c 1.15,-0.5 2,-1.6 2,-3 0,-1.9 -1.57,-3.5 -3.5,-3.5 z m 0,1 c 1.37,0 2.5,1.2 2.5,2.5 0,1.1 -0.7,2 -1.66,2.3 l -0.34,0.1 0,3.3 c 0,0.9 0.67,1.5 1.5,1.8 l 0,1 c -1.38,-0.3 -2.5,-1.4 -2.5,-2.8 l 0,-3.3 -0.34,-0.1 c -0.96,-0.3 -1.66,-1.2 -1.66,-2.3 0,-1.3 1.12,-2.5 2.5,-2.5 z" + id="path4297" /> + <path + id="path4145" + d="m -57.35,106 c -1.99,0 -3.6,1.7 -3.6,3.6 0,1.4 0.83,2.6 2,3.2 l 0,2.5 c 0,0.5 -0.41,0.9 -0.9,0.9 l -4.35,0 a 0.6001,0.6001 0 0 0 -0.6,0.6 l 0,2 a 0.6001,0.6001 0 0 0 0.6,0.6 l 4.35,0 c 2.26,0 4.1,-1.9 4.1,-4.1 l 0,-2.5 c 1.17,-0.6 2,-1.8 2,-3.2 0,-1.9 -1.6,-3.6 -3.6,-3.6 z" + style="" /> + </g> + <path + id="path4149" + d="m -69.84,116.2 c -2.24,0 -4.1,1.8 -4.1,4.1 l 0,2.5 c -1.17,0.5 -1.99,1.7 -1.99,3.1 0,2 1.64,3.6 3.61,3.6 1.96,0 3.6,-1.6 3.6,-3.6 0,-1.4 -0.83,-2.6 -2,-3.2 l 0,-2.4 c 0,-0.5 0.41,-0.9 0.9,-0.9 l 4.51,0 a 0.6001,0.6001 0 0 0 0.6,-0.6 l 0,-2 a 0.6001,0.6001 0 0 0 -0.6,-0.6 l -4.53,0 z" + style="" /> + </g> + </g> + </g> + <g + transform="matrix(0,1,1,0,-106.3,-36.15)" + id="g4142"> + <g + transform="matrix(-1,0,0,1,-16.98,0.8136)" + id="g4144" /> + </g> + </g> + <path + id="path4170" + d="m 15.2,12 c 0,-0.84 -0.4,-1.65 -1,-2.242 -0.5,-0.64 -1.3,-0.96 -2.2,-0.96 -0.9,0 -1.7,0.32 -2.2,0.96 -0.6,0.592 -1,1.402 -1,2.242 0,0.84 0.4,1.65 1,2.24 0.5,0.64 1.3,0.96 2.2,0.96 0.9,0 1.7,-0.32 2.2,-0.96 0.6,-0.59 1,-1.4 1,-2.24 z" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> +</svg> diff --git a/src/qt/res/src/connect-4.svg b/src/qt/res/src/connect-4.svg new file mode 100644 index 0000000000..0abc7955fd --- /dev/null +++ b/src/qt/res/src/connect-4.svg @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.2" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg2"> + <metadata + id="metadata10"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs8" /> + <g + id="g4142" + transform="matrix(0,-1,-1,0,23.96,24)"> + <g + transform="matrix(-1,0,0,1,59.86,-106.6)" + id="g4210"> + <g + transform="matrix(-1,0,0,1,-16.98,0.8136)" + id="g4289"> + <g + id="g4291"> + <path + id="path4153" + d="m -64.85,116.2 a 0.6001,0.6001 0 0 0 -0.6,0.6 l 0,2 a 0.6001,0.6001 0 0 0 0.6,0.6 c 0.48,0 0.9,0.4 0.9,0.9 l 0,2.4 c -1.18,0.6 -2,1.8 -2,3.2 0,2 1.64,3.6 3.6,3.6 1.97,0 3.6,-1.6 3.6,-3.6 0,-1.4 -0.83,-2.6 -2,-3.2 l 0,-2.4 c 0,-2.3 -1.86,-4.1 -4.1,-4.1 z" + style="" /> + <g + id="g4295"> + <path + id="path4149" + d="m -67.35,106 c -2,0 -3.6,1.7 -3.6,3.6 0,1.4 0.83,2.6 2,3.2 l 0,2.5 c 0,2.2 1.84,4.1 4.1,4.1 a 0.6001,0.6001 0 0 0 0.6,-0.6 l 0,-2 a 0.6001,0.6001 0 0 0 -0.6,-0.6 c -0.49,0 -0.9,-0.4 -0.9,-0.9 l 0,-2.5 c 1.17,-0.6 2,-1.8 2,-3.2 0,-1.9 -1.61,-3.6 -3.6,-3.6 z" + style="" /> + <path + id="path4147" + d="m -57.35,106 c -1.99,0 -3.6,1.7 -3.6,3.6 0,1.4 0.83,2.6 2,3.2 l 0,2.5 c 0,0.5 -0.41,0.9 -0.9,0.9 l -4.35,0 a 0.6001,0.6001 0 0 0 -0.6,0.6 l 0,2 a 0.6001,0.6001 0 0 0 0.6,0.6 l 4.35,0 c 2.26,0 4.1,-1.9 4.1,-4.1 l 0,-2.5 c 1.17,-0.6 2,-1.8 2,-3.2 0,-1.9 -1.6,-3.6 -3.6,-3.6 z" + style="" /> + </g> + <path + id="path4155" + d="m -69.84,116.2 c -2.24,0 -4.1,1.8 -4.1,4.1 l 0,2.5 c -1.17,0.5 -1.99,1.7 -1.99,3.1 0,2 1.64,3.6 3.61,3.6 1.96,0 3.6,-1.6 3.6,-3.6 0,-1.4 -0.83,-2.6 -2,-3.2 l 0,-2.4 c 0,-0.5 0.41,-0.9 0.9,-0.9 l 4.51,0 a 0.6001,0.6001 0 0 0 0.6,-0.6 l 0,-2 a 0.6001,0.6001 0 0 0 -0.6,-0.6 l -4.53,0 z" + style="" /> + </g> + </g> + </g> + </g> + <path + id="path4170" + d="m 15.2,12 c 0,-0.84 -0.4,-1.65 -1,-2.24 C 13.7,9.12 12.9,8.8 12,8.8 c -0.9,0 -1.7,0.32 -2.2,0.96 -0.6,0.59 -1,1.4 -1,2.24 0,0.84 0.4,1.65 1,2.24 0.5,0.64 1.3,0.96 2.2,0.96 0.9,0 1.7,-0.32 2.2,-0.96 0.6,-0.59 1,-1.4 1,-2.24 z" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> +</svg> diff --git a/src/qt/res/src/qt.svg b/src/qt/res/src/qt.svg index 9ef54f493c..373c91f0c6 100644 --- a/src/qt/res/src/qt.svg +++ b/src/qt/res/src/qt.svg @@ -1,25 +1,26 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 841.9 595.3" enable-background="new 0 0 841.9 595.3" xml:space="preserve">
-<g>
- <path d="M182.8,310c0-74.4,0-148.8,0-220.7c0-19.8,5-39.7,19.8-54.6c12.4-12.4,27.3-17.4,44.6-19.8c37.2-5,74.4,2.5,109.1,7.4
- C428.4,34.7,497.8,44.6,569.8,57c27.3,5,57,9.9,84.3,12.4c7.4,0,5,5,5,9.9c0,91.8,0,181.1,0,272.8c0,32.2,0,64.5,0,99.2
- c0,14.9-5,29.8-12.4,44.6c-9.9,14.9-22.3,22.3-39.7,27.3c-69.4,12.4-138.9,22.3-208.3,34.7c-57,9.9-114.1,19.8-171.1,29.8
- c-2.5,0-5,0-7.4-2.5c-12.4-14.9-22.3-24.8-32.2-34.7c-2.5-2.5-2.5-7.4-2.5-9.9c0-71.9,0-143.9,0-215.8
- C182.8,320,182.8,315,182.8,310z M430.9,436.5c9.9-7.4,19.8-12.4,29.8-19.8c14.9-14.9,24.8-32.2,29.8-54.6
- c12.4-54.6,14.9-111.6,0-166.2c-12.4-47.1-42.2-74.4-84.3-79.4c-37.2-2.5-67,7.4-86.8,39.7c-7.4,14.9-12.4,29.8-14.9,44.6
- c-9.9,39.7-9.9,81.9-5,121.5c2.5,22.3,7.4,44.6,17.4,67c12.4,24.8,29.8,42.2,54.6,49.6c2.5,0,5,2.5,5,5c5,12.4,7.4,22.3,12.4,34.7
- s17.4,19.8,32.2,22.3c14.9,2.5,27.3,2.5,42.2,0c2.5,0,2.5-2.5,2.5-2.5c0-9.9,0-22.3,0-32.2C438.3,461.3,433.3,456.4,430.9,436.5z
- M505.3,191c0,12.4,0,22.3,0,34.7c0,2.5,2.5,2.5,5,2.5c5,0,7.4,0,12.4,0c0,2.5,0,5,0,9.9c0,44.6,0,86.8,0,131.5
- c0,7.4,0,17.4,2.5,24.8c2.5,12.4,12.4,22.3,24.8,24.8c19.8,5,37.2-2.5,54.6-9.9l2.5-2.5c0-9.9,0-19.8,0-29.8
- c-7.4,2.5-14.9,5-22.3,5s-12.4-2.5-14.9-9.9c0-5-2.5-9.9-2.5-14.9c0-39.7,0-79.4,0-119.1c0-2.5,0-5,0-7.4c9.9,0,19.8,0,29.8,2.5
- c5,0,7.4-2.5,7.4-7.4c0-7.4,0-14.9,0-22.3c0-5-2.5-7.4-7.4-7.4c-7.4,0-14.9-2.5-22.3-2.5c-5,0-7.4-2.5-7.4-7.4
- c0-14.9,0-29.8,0-42.2c0-5-2.5-5-5-7.4c-5,0-12.4,0-17.4-2.5s-7.4,0-9.9,7.4c-2.5,17.4-7.4,32.2-12.4,49.6
- C520.2,191,512.7,191,505.3,191z"/>
- <path d="M443.3,277.8c-2.5,27.3-5,57-9.9,84.3c0,7.4-5,17.4-9.9,24.8c-12.4,17.4-32.2,14.9-44.6-2.5c-9.9-14.9-12.4-32.2-14.9-49.6
- c-5-42.2-5-81.9,0-124c5-12.4,7.4-24.8,14.9-37.2c12.4-17.4,34.7-17.4,47.1-2.5c2.5,5,7.4,9.9,7.4,14.9c2.5,9.9,5,19.8,7.4,32.2
- c2.5,9.9,2.5,22.3,2.5,34.7C440.8,260.4,440.8,270.4,443.3,277.8L443.3,277.8z"/>
-</g>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + id="Ebene_1" + x="0px" + y="0px" + viewBox="0 0 609.4 609.4" + enable-background="new 0 0 841.9 595.3" + xml:space="preserve" + width="609.4" + height="609.4"><metadata + id="metadata13"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs + id="defs11" /><g + id="g4151" + transform="matrix(2.553,0,0,2.553,-2149,281.5)"><path + id="path26" + transform="matrix(0.3917,0,0,0.3917,841.8,-110.3)" + d="M 153.7 16.9 C 115 16.44 69.7 31.67 67.96 86.81 L 67.96 550.7 L 105 592.4 L 495.9 526.7 C 521.4 522.1 541.9 490.2 541.9 455.9 L 541.9 77.77 L 183.4 18.97 C 179.4 18.3 161.4 17 157.5 16.99 C 156.2 16.94 155 16.91 153.7 16.9 z M 273.5 124.1 C 278.1 124.1 282.7 124.3 287.3 124.9 L 287.3 125 C 320 128.8 343.7 144.2 359.3 170.9 C 374.6 197.1 382 234.6 382 284 C 382 329.2 376.4 364.7 365.4 390.7 C 354.2 417.2 337.1 434.6 313.4 442.8 C 315.9 455 319.5 463.2 324.1 467.5 C 326.9 469.8 330.7 471.4 335.3 471.9 L 335.6 471.9 L 336.3 471.9 L 341.5 471.9 C 343.5 471.9 344.5 472.4 348.3 471.9 L 348.3 507.9 L 332 510.2 C 327.2 510.7 322.6 510.9 318.2 510.9 C 303.9 510.9 292.4 507.6 283.5 500.5 C 272 491.3 263.6 473.4 258.2 447.1 C 233.2 441.8 213.5 425.9 200 399.1 C 186.5 372.1 179.3 332.2 179.3 280.4 C 179.3 224.5 189 183.2 207.7 157 C 223.8 134.9 245.7 124.1 273.5 124.1 z M 424.4 143.5 L 455.1 146.9 L 455.1 202.2 L 488.2 204.8 L 488.2 239.5 L 455.1 237.8 L 455.1 364.7 C 455.1 375.6 457.6 382.8 460.2 386.1 C 460.2 388.9 465.3 390.4 467.8 390.4 L 470.4 390.4 C 478 389.9 485.7 387.9 493.4 384.1 L 493.4 415.7 C 478 422.1 465.3 425.7 450 426.9 C 450 427.2 447.4 427.2 444.8 427.2 C 432.1 427.2 424.4 423.6 416.8 416.5 C 409.1 408.1 404 394.5 404 376.1 L 404 235.6 L 390.2 234.8 L 390.2 197.6 L 411.7 199.1 L 424.4 143.5 z M 284.5 166.4 C 272.5 166.4 263.3 173.3 256.9 187.3 C 250.1 202.5 246.7 233.9 246.7 281.7 C 246.7 327.6 250.1 360.3 256.9 379.5 C 263.3 397.8 273 406.8 285.8 406.8 L 287.3 406.8 C 300.1 406 309.5 397.1 316.2 380.5 C 322.6 363.9 325.6 331.5 325.6 283 C 325.6 239.4 322.6 209.5 316.2 193 C 309.8 176.4 300.1 167.5 287.3 166.4 L 284.5 166.4 z " + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" /></g></svg>
\ No newline at end of file diff --git a/src/qt/res/src/transaction0.svg b/src/qt/res/src/transaction0.svg new file mode 100644 index 0000000000..e7fcd8214c --- /dev/null +++ b/src/qt/res/src/transaction0.svg @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + id="svg4142" + viewBox="0 0 128 127.9" + height="36.12mm" + width="36.12mm"> + <defs + id="defs4144" /> + <metadata + id="metadata4147"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + transform="translate(-284.4,-501.6)"> + <path + id="path4792" + d="m 348.8,513.8 c -12.7,-0.7 -24.9,9.1 -27,21.7 -3.8,8.8 7.2,13.7 13.7,9.2 3.1,-7.5 9.4,-17.9 18.9,-11.6 9.7,6.1 2.1,17.6 -3,24.1 -6.1,7.8 -11.4,14.8 -8.9,23 5.4,17.7 10.8,3.7 12.8,-0.1 4.3,-8.2 6,-8.8 11.5,-16.1 6.4,-8.6 11.6,-19.9 7.7,-30.8 -2.8,-11.5 -13.9,-19.9 -25.7,-19.4 z m -0.7,84.7 c -11.4,2.4 -9.1,19.5 2.7,17.1 11.8,-2.4 8.7,-19.5 -2.7,-17.1 z" + style="fill:#000000" /> + </g> +</svg> diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index b2717558ca..d9d4f1d0ed 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -8,7 +8,7 @@ #include "clientmodel.h" #include "guiutil.h" #include "peertablemodel.h" -#include "scicon.h" +#include "platformstyle.h" #include "chainparams.h" #include "rpcserver.h" @@ -200,21 +200,22 @@ void RPCExecutor::request(const QString &command) } } -RPCConsole::RPCConsole(QWidget *parent) : +RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) : QWidget(parent), ui(new Ui::RPCConsole), clientModel(0), historyPtr(0), cachedNodeid(-1), - contextMenu(0) + contextMenu(0), + platformStyle(platformStyle) { ui->setupUi(this); GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this); -#ifndef Q_OS_MAC - ui->openDebugLogfileButton->setIcon(SingleColorIcon(":/icons/export")); -#endif - ui->clearButton->setIcon(SingleColorIcon(":/icons/remove")); + if (platformStyle->getImagesOnButtons()) { + ui->openDebugLogfileButton->setIcon(platformStyle->SingleColorIcon(":/icons/export")); + } + ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); // Install event filter for up and down arrow ui->lineEdit->installEventFilter(this); @@ -329,10 +330,10 @@ void RPCConsole::setClientModel(ClientModel *model) // Provide initial values ui->clientVersion->setText(model->formatFullVersion()); + ui->clientUserAgent->setText(model->formatSubVersion()); ui->clientName->setText(model->clientName()); ui->buildDate->setText(model->formatBuildDate()); ui->startupTime->setText(model->formatClientStartupTime()); - ui->networkName->setText(QString::fromStdString(Params().NetworkIDString())); } } @@ -363,7 +364,7 @@ void RPCConsole::clear() ui->messagesWidget->document()->addResource( QTextDocument::ImageResource, QUrl(ICON_MAPPING[i].url), - SingleColorImage(ICON_MAPPING[i].source, SingleColor()).scaled(ICON_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + platformStyle->SingleColorImage(ICON_MAPPING[i].source).scaled(ICON_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } // Set default style sheet @@ -569,7 +570,7 @@ void RPCConsole::peerLayoutChanged() if (detailNodeRow < 0) { - // detail node dissapeared from table (node disconnected) + // detail node disappeared from table (node disconnected) fUnselect = true; } else diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 6f42aa08b7..b94efee84a 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -13,6 +13,7 @@ #include <QWidget> class ClientModel; +class PlatformStyle; namespace Ui { class RPCConsole; @@ -29,7 +30,7 @@ class RPCConsole: public QWidget Q_OBJECT public: - explicit RPCConsole(QWidget *parent); + explicit RPCConsole(const PlatformStyle *platformStyle, QWidget *parent); ~RPCConsole(); void setClientModel(ClientModel *model); @@ -106,6 +107,7 @@ private: int historyPtr; NodeId cachedNodeid; QMenu *contextMenu; + const PlatformStyle *platformStyle; }; #endif // BITCOIN_QT_RPCCONSOLE_H diff --git a/src/qt/scicon.cpp b/src/qt/scicon.cpp deleted file mode 100644 index c493b5569e..0000000000 --- a/src/qt/scicon.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2014 The Bitcoin developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "scicon.h" - -#include <QApplication> -#include <QColor> -#include <QIcon> -#include <QImage> -#include <QPalette> -#include <QPixmap> - -namespace { - -void MakeSingleColorImage(QImage& img, const QColor& colorbase) -{ - img = img.convertToFormat(QImage::Format_ARGB32); - for (int x = img.width(); x--; ) - { - for (int y = img.height(); y--; ) - { - const QRgb rgb = img.pixel(x, y); - img.setPixel(x, y, qRgba(colorbase.red(), colorbase.green(), colorbase.blue(), qAlpha(rgb))); - } - } -} - -} - -QImage SingleColorImage(const QString& filename, const QColor& colorbase) -{ - QImage img(filename); -#if !defined(WIN32) && !defined(MAC_OSX) - MakeSingleColorImage(img, colorbase); -#endif - return img; -} - -QIcon SingleColorIcon(const QIcon& ico, const QColor& colorbase) -{ -#if defined(WIN32) || defined(MAC_OSX) - return ico; -#else - QIcon new_ico; - QSize sz; - Q_FOREACH(sz, ico.availableSizes()) - { - QImage img(ico.pixmap(sz).toImage()); - MakeSingleColorImage(img, colorbase); - new_ico.addPixmap(QPixmap::fromImage(img)); - } - return new_ico; -#endif -} - -QIcon SingleColorIcon(const QString& filename, const QColor& colorbase) -{ - return QIcon(QPixmap::fromImage(SingleColorImage(filename, colorbase))); -} - -QColor SingleColor() -{ -#if defined(WIN32) || defined(MAC_OSX) - return QColor(0,0,0); -#else - const QColor colorHighlightBg(QApplication::palette().color(QPalette::Highlight)); - const QColor colorHighlightFg(QApplication::palette().color(QPalette::HighlightedText)); - const QColor colorText(QApplication::palette().color(QPalette::WindowText)); - const int colorTextLightness = colorText.lightness(); - QColor colorbase; - if (abs(colorHighlightBg.lightness() - colorTextLightness) < abs(colorHighlightFg.lightness() - colorTextLightness)) - colorbase = colorHighlightBg; - else - colorbase = colorHighlightFg; - return colorbase; -#endif -} - -QIcon SingleColorIcon(const QString& filename) -{ - return SingleColorIcon(filename, SingleColor()); -} - -static QColor TextColor() -{ - return QColor(QApplication::palette().color(QPalette::WindowText)); -} - -QIcon TextColorIcon(const QString& filename) -{ - return SingleColorIcon(filename, TextColor()); -} - -QIcon TextColorIcon(const QIcon& ico) -{ - return SingleColorIcon(ico, TextColor()); -} diff --git a/src/qt/scicon.h b/src/qt/scicon.h deleted file mode 100644 index 1388069ddb..0000000000 --- a/src/qt/scicon.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2014 The Bitcoin developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_QT_SCICON_H -#define BITCOIN_QT_SCICON_H - -#include <QtCore> - -QT_BEGIN_NAMESPACE -class QColor; -class QIcon; -class QString; -QT_END_NAMESPACE - -QImage SingleColorImage(const QString& filename, const QColor&); -QIcon SingleColorIcon(const QIcon&, const QColor&); -QIcon SingleColorIcon(const QString& filename, const QColor&); -QColor SingleColor(); -QIcon SingleColorIcon(const QString& filename); -QIcon TextColorIcon(const QIcon&); -QIcon TextColorIcon(const QString& filename); - -#endif // BITCOIN_QT_SCICON_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 2407fcbc4a..60a3fc128e 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -11,7 +11,7 @@ #include "coincontroldialog.h" #include "guiutil.h" #include "optionsmodel.h" -#include "scicon.h" +#include "platformstyle.h" #include "sendcoinsentry.h" #include "walletmodel.h" @@ -27,25 +27,26 @@ #include <QSettings> #include <QTextDocument> -SendCoinsDialog::SendCoinsDialog(QWidget *parent) : +SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent) : QDialog(parent), ui(new Ui::SendCoinsDialog), clientModel(0), model(0), fNewRecipientAllowed(true), - fFeeMinimized(true) + fFeeMinimized(true), + platformStyle(platformStyle) { ui->setupUi(this); -#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac - ui->addButton->setIcon(QIcon()); - ui->clearButton->setIcon(QIcon()); - ui->sendButton->setIcon(QIcon()); -#else - ui->addButton->setIcon(SingleColorIcon(":/icons/add")); - ui->clearButton->setIcon(SingleColorIcon(":/icons/remove")); - ui->sendButton->setIcon(SingleColorIcon(":/icons/send")); -#endif + if (!platformStyle->getImagesOnButtons()) { + ui->addButton->setIcon(QIcon()); + ui->clearButton->setIcon(QIcon()); + ui->sendButton->setIcon(QIcon()); + } else { + ui->addButton->setIcon(platformStyle->SingleColorIcon(":/icons/add")); + ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); + ui->sendButton->setIcon(platformStyle->SingleColorIcon(":/icons/send")); + } GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this); @@ -364,7 +365,7 @@ void SendCoinsDialog::accept() SendCoinsEntry *SendCoinsDialog::addEntry() { - SendCoinsEntry *entry = new SendCoinsEntry(this); + SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this); entry->setModel(model); ui->entries->addWidget(entry); connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); @@ -711,7 +712,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked) // Coin Control: button inputs -> show actual coin control dialog void SendCoinsDialog::coinControlButtonClicked() { - CoinControlDialog dlg; + CoinControlDialog dlg(platformStyle); dlg.setModel(model); dlg.exec(); coinControlUpdateLabels(); @@ -753,10 +754,9 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) } else // Valid address { - CPubKey pubkey; CKeyID keyid; addr.GetKeyID(keyid); - if (!model->getPubKey(keyid, pubkey)) // Unknown change address + if (!model->havePrivKey(keyid)) // Unknown change address { ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index c833da84b2..391905ffcd 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -12,6 +12,7 @@ class ClientModel; class OptionsModel; +class PlatformStyle; class SendCoinsEntry; class SendCoinsRecipient; @@ -31,7 +32,7 @@ class SendCoinsDialog : public QDialog Q_OBJECT public: - explicit SendCoinsDialog(QWidget *parent = 0); + explicit SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent = 0); ~SendCoinsDialog(); void setClientModel(ClientModel *clientModel); @@ -60,6 +61,7 @@ private: WalletModel *model; bool fNewRecipientAllowed; bool fFeeMinimized; + const PlatformStyle *platformStyle; // Process WalletModel::SendCoinsReturn and generate a pair consisting // of a message and message flags for use in Q_EMIT message(). diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 90a8cbdc4e..44aa8ad1af 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -9,30 +9,30 @@ #include "addresstablemodel.h" #include "guiutil.h" #include "optionsmodel.h" -#include "scicon.h" +#include "platformstyle.h" #include "walletmodel.h" #include <QApplication> #include <QClipboard> -SendCoinsEntry::SendCoinsEntry(QWidget *parent) : +SendCoinsEntry::SendCoinsEntry(const PlatformStyle *platformStyle, QWidget *parent) : QStackedWidget(parent), ui(new Ui::SendCoinsEntry), - model(0) + model(0), + platformStyle(platformStyle) { ui->setupUi(this); - ui->addressBookButton->setIcon(SingleColorIcon(":/icons/address-book")); - ui->pasteButton->setIcon(SingleColorIcon(":/icons/editpaste")); - ui->deleteButton->setIcon(SingleColorIcon(":/icons/remove")); - ui->deleteButton_is->setIcon(SingleColorIcon(":/icons/remove")); - ui->deleteButton_s->setIcon(SingleColorIcon(":/icons/remove")); + ui->addressBookButton->setIcon(platformStyle->SingleColorIcon(":/icons/address-book")); + ui->pasteButton->setIcon(platformStyle->SingleColorIcon(":/icons/editpaste")); + ui->deleteButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); + ui->deleteButton_is->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); + ui->deleteButton_s->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); setCurrentWidget(ui->SendCoins); -#ifdef Q_OS_MAC - ui->payToLayout->setSpacing(4); -#endif + if (platformStyle->getUseExtraSpacing()) + ui->payToLayout->setSpacing(4); #if QT_VERSION >= 0x040700 ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); #endif @@ -65,7 +65,7 @@ void SendCoinsEntry::on_addressBookButton_clicked() { if(!model) return; - AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::SendingTab, this); + AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); if(dlg.exec()) { diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index d7e655fdc3..107ab70158 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -10,6 +10,7 @@ #include <QStackedWidget> class WalletModel; +class PlatformStyle; namespace Ui { class SendCoinsEntry; @@ -25,7 +26,7 @@ class SendCoinsEntry : public QStackedWidget Q_OBJECT public: - explicit SendCoinsEntry(QWidget *parent = 0); + explicit SendCoinsEntry(const PlatformStyle *platformStyle, QWidget *parent = 0); ~SendCoinsEntry(); void setModel(WalletModel *model); @@ -64,6 +65,7 @@ private: SendCoinsRecipient recipient; Ui::SendCoinsEntry *ui; WalletModel *model; + const PlatformStyle *platformStyle; bool updateLabel(const QString &address); }; diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index d3984923a8..60e8e36ebe 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -7,7 +7,7 @@ #include "addressbookpage.h" #include "guiutil.h" -#include "scicon.h" +#include "platformstyle.h" #include "walletmodel.h" #include "base58.h" @@ -20,21 +20,22 @@ #include <QClipboard> -SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget *parent) : +SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *platformStyle, QWidget *parent) : QDialog(parent), ui(new Ui::SignVerifyMessageDialog), - model(0) + model(0), + platformStyle(platformStyle) { ui->setupUi(this); - ui->addressBookButton_SM->setIcon(SingleColorIcon(":/icons/address-book")); - ui->pasteButton_SM->setIcon(SingleColorIcon(":/icons/editpaste")); - ui->copySignatureButton_SM->setIcon(SingleColorIcon(":/icons/editcopy")); - ui->signMessageButton_SM->setIcon(SingleColorIcon(":/icons/edit")); - ui->clearButton_SM->setIcon(SingleColorIcon(":/icons/remove")); - ui->addressBookButton_VM->setIcon(SingleColorIcon(":/icons/address-book")); - ui->verifyMessageButton_VM->setIcon(SingleColorIcon(":/icons/transaction_0")); - ui->clearButton_VM->setIcon(SingleColorIcon(":/icons/remove")); + ui->addressBookButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/address-book")); + ui->pasteButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/editpaste")); + ui->copySignatureButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/editcopy")); + ui->signMessageButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/edit")); + ui->clearButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); + ui->addressBookButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/address-book")); + ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/transaction_0")); + ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); #if QT_VERSION >= 0x040700 ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature")); @@ -94,7 +95,7 @@ void SignVerifyMessageDialog::on_addressBookButton_SM_clicked() { if (model && model->getAddressTableModel()) { - AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); + AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { @@ -185,7 +186,7 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() { if (model && model->getAddressTableModel()) { - AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::SendingTab, this); + AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h index bf841e4f8b..d651d5049b 100644 --- a/src/qt/signverifymessagedialog.h +++ b/src/qt/signverifymessagedialog.h @@ -7,6 +7,7 @@ #include <QDialog> +class PlatformStyle; class WalletModel; namespace Ui { @@ -18,7 +19,7 @@ class SignVerifyMessageDialog : public QDialog Q_OBJECT public: - explicit SignVerifyMessageDialog(QWidget *parent); + explicit SignVerifyMessageDialog(const PlatformStyle *platformStyle, QWidget *parent); ~SignVerifyMessageDialog(); void setModel(WalletModel *model); @@ -34,6 +35,7 @@ protected: private: Ui::SignVerifyMessageDialog *ui; WalletModel *model; + const PlatformStyle *platformStyle; private Q_SLOTS: /* sign message */ diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 8430e017c1..c15b64c327 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -57,7 +57,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) QPainter pixPaint(&pixmap); pixPaint.setPen(QColor(100,100,100)); - // draw a slighly radial gradient + // draw a slightly radial gradient QRadialGradient gradient(QPoint(0,0), splashSize.width()/devicePixelRatio); gradient.setColorAt(0, Qt::white); gradient.setColorAt(1, QColor(247,247,247)); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index af78a51d0f..801c6c62d2 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -165,7 +165,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (fAllFromMe) { - if(fAllFromMe == ISMINE_WATCH_ONLY) + if(fAllFromMe & ISMINE_WATCH_ONLY) strHTML += "<b>" + tr("From") + ":</b> " + tr("watch-only") + "<br>"; // @@ -190,7 +190,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); if(toSelf == ISMINE_SPENDABLE) strHTML += " (own address)"; - else if(toSelf == ISMINE_WATCH_ONLY) + else if(toSelf & ISMINE_WATCH_ONLY) strHTML += " (watch-only)"; strHTML += "<br>"; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 15d13e9fc9..d8623daf5d 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -56,7 +56,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * CTxDestination address; sub.idx = parts.size(); // sequence number sub.credit = txout.nValue; - sub.involvesWatchAddress = mine == ISMINE_WATCH_ONLY; + sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { // Received by Bitcoin Address @@ -86,7 +86,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * BOOST_FOREACH(const CTxIn& txin, wtx.vin) { isminetype mine = wallet->IsMine(txin); - if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true; + if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; if(fAllFromMe > mine) fAllFromMe = mine; } @@ -94,7 +94,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * BOOST_FOREACH(const CTxOut& txout, wtx.vout) { isminetype mine = wallet->IsMine(txout); - if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true; + if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; if(fAllToMe > mine) fAllToMe = mine; } diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index f384562a50..98ad1a44b6 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -8,7 +8,7 @@ #include "guiconstants.h" #include "guiutil.h" #include "optionsmodel.h" -#include "scicon.h" +#include "platformstyle.h" #include "transactiondesc.h" #include "transactionrecord.h" #include "walletmodel.h" @@ -222,12 +222,13 @@ public: } }; -TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *parent): +TransactionTableModel::TransactionTableModel(const PlatformStyle *platformStyle, CWallet* wallet, WalletModel *parent): QAbstractTableModel(parent), wallet(wallet), walletModel(parent), priv(new TransactionTablePriv(wallet, this)), - fProcessingQueuedTransactions(false) + fProcessingQueuedTransactions(false), + platformStyle(platformStyle) { columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); priv->refreshWallet(); @@ -521,7 +522,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Qt::DecorationRole: { QIcon icon = qvariant_cast<QIcon>(index.data(RawDecorationRole)); - return TextColorIcon(icon); + return platformStyle->TextColorIcon(icon); } case Qt::DisplayRole: switch(index.column()) diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 25c82c764b..2089f703a6 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -10,6 +10,7 @@ #include <QAbstractTableModel> #include <QStringList> +class PlatformStyle; class TransactionRecord; class TransactionTablePriv; class WalletModel; @@ -23,7 +24,7 @@ class TransactionTableModel : public QAbstractTableModel Q_OBJECT public: - explicit TransactionTableModel(CWallet* wallet, WalletModel *parent = 0); + explicit TransactionTableModel(const PlatformStyle *platformStyle, CWallet* wallet, WalletModel *parent = 0); ~TransactionTableModel(); enum ColumnIndex { @@ -82,6 +83,7 @@ private: QStringList columns; TransactionTablePriv *priv; bool fProcessingQueuedTransactions; + const PlatformStyle *platformStyle; void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 998789b3ae..54e5a82720 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -10,7 +10,7 @@ #include "editaddressdialog.h" #include "guiutil.h" #include "optionsmodel.h" -#include "scicon.h" +#include "platformstyle.h" #include "transactiondescdialog.h" #include "transactionfilterproxy.h" #include "transactionrecord.h" @@ -35,7 +35,7 @@ #include <QUrl> #include <QVBoxLayout> -TransactionView::TransactionView(QWidget *parent) : +TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) : QWidget(parent), model(0), transactionProxyModel(0), transactionView(0) { @@ -44,27 +44,28 @@ TransactionView::TransactionView(QWidget *parent) : QHBoxLayout *hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0,0,0,0); -#ifdef Q_OS_MAC - hlayout->setSpacing(5); - hlayout->addSpacing(26); -#else - hlayout->setSpacing(0); - hlayout->addSpacing(23); -#endif + + if (platformStyle->getUseExtraSpacing()) { + hlayout->setSpacing(5); + hlayout->addSpacing(26); + } else { + hlayout->setSpacing(0); + hlayout->addSpacing(23); + } watchOnlyWidget = new QComboBox(this); watchOnlyWidget->setFixedWidth(24); watchOnlyWidget->addItem("", TransactionFilterProxy::WatchOnlyFilter_All); - watchOnlyWidget->addItem(SingleColorIcon(":/icons/eye_plus"), "", TransactionFilterProxy::WatchOnlyFilter_Yes); - watchOnlyWidget->addItem(SingleColorIcon(":/icons/eye_minus"), "", TransactionFilterProxy::WatchOnlyFilter_No); + watchOnlyWidget->addItem(platformStyle->SingleColorIcon(":/icons/eye_plus"), "", TransactionFilterProxy::WatchOnlyFilter_Yes); + watchOnlyWidget->addItem(platformStyle->SingleColorIcon(":/icons/eye_minus"), "", TransactionFilterProxy::WatchOnlyFilter_No); hlayout->addWidget(watchOnlyWidget); dateWidget = new QComboBox(this); -#ifdef Q_OS_MAC - dateWidget->setFixedWidth(121); -#else - dateWidget->setFixedWidth(120); -#endif + if (platformStyle->getUseExtraSpacing()) { + dateWidget->setFixedWidth(121); + } else { + dateWidget->setFixedWidth(120); + } dateWidget->addItem(tr("All"), All); dateWidget->addItem(tr("Today"), Today); dateWidget->addItem(tr("This week"), ThisWeek); @@ -75,11 +76,11 @@ TransactionView::TransactionView(QWidget *parent) : hlayout->addWidget(dateWidget); typeWidget = new QComboBox(this); -#ifdef Q_OS_MAC - typeWidget->setFixedWidth(121); -#else - typeWidget->setFixedWidth(120); -#endif + if (platformStyle->getUseExtraSpacing()) { + typeWidget->setFixedWidth(121); + } else { + typeWidget->setFixedWidth(120); + } typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES); typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) | @@ -102,11 +103,11 @@ TransactionView::TransactionView(QWidget *parent) : #if QT_VERSION >= 0x040700 amountWidget->setPlaceholderText(tr("Min amount")); #endif -#ifdef Q_OS_MAC - amountWidget->setFixedWidth(97); -#else - amountWidget->setFixedWidth(100); -#endif + if (platformStyle->getUseExtraSpacing()) { + amountWidget->setFixedWidth(97); + } else { + amountWidget->setFixedWidth(100); + } amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); hlayout->addWidget(amountWidget); @@ -121,11 +122,11 @@ TransactionView::TransactionView(QWidget *parent) : vlayout->setSpacing(0); int width = view->verticalScrollBar()->sizeHint().width(); // Cover scroll bar width with spacing -#ifdef Q_OS_MAC - hlayout->addSpacing(width+2); -#else - hlayout->addSpacing(width); -#endif + if (platformStyle->getUseExtraSpacing()) { + hlayout->addSpacing(width+2); + } else { + hlayout->addSpacing(width); + } // Always show scroll bar view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); view->setTabKeyNavigation(false); diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 6c35362be4..ac157fb98d 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -10,6 +10,7 @@ #include <QWidget> #include <QKeyEvent> +class PlatformStyle; class TransactionFilterProxy; class WalletModel; @@ -32,7 +33,7 @@ class TransactionView : public QWidget Q_OBJECT public: - explicit TransactionView(QWidget *parent = 0); + explicit TransactionView(const PlatformStyle *platformStyle, QWidget *parent = 0); void setModel(WalletModel *model); diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 892947bf3a..ba8c28464d 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -12,9 +12,10 @@ #include <QHBoxLayout> #include <QLabel> -WalletFrame::WalletFrame(BitcoinGUI *_gui) : +WalletFrame::WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui) : QFrame(_gui), - gui(_gui) + gui(_gui), + platformStyle(platformStyle) { // Leave HBox hook for adding a list view later QHBoxLayout *walletFrameLayout = new QHBoxLayout(this); @@ -42,7 +43,7 @@ bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel) if (!gui || !clientModel || !walletModel || mapWalletViews.count(name) > 0) return false; - WalletView *walletView = new WalletView(this); + WalletView *walletView = new WalletView(platformStyle, this); walletView->setBitcoinGUI(gui); walletView->setClientModel(clientModel); walletView->setWalletModel(walletModel); diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 5a5e2ab944..9a56e97f9c 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -10,6 +10,7 @@ class BitcoinGUI; class ClientModel; +class PlatformStyle; class SendCoinsRecipient; class WalletModel; class WalletView; @@ -23,7 +24,7 @@ class WalletFrame : public QFrame Q_OBJECT public: - explicit WalletFrame(BitcoinGUI *_gui = 0); + explicit WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui = 0); ~WalletFrame(); void setClientModel(ClientModel *clientModel); @@ -45,6 +46,8 @@ private: bool bOutOfSync; + const PlatformStyle *platformStyle; + WalletView *currentWalletView(); public Q_SLOTS: diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 7820047b6b..5c21db8bdf 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -27,7 +27,7 @@ #include <boost/foreach.hpp> -WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : +WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), transactionTableModel(0), recentRequestsTableModel(0), @@ -39,7 +39,7 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p fForceCheckBalanceChanged = false; addressTableModel = new AddressTableModel(wallet, this); - transactionTableModel = new TransactionTableModel(wallet, this); + transactionTableModel = new TransactionTableModel(platformStyle, wallet, this); recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); // This timer will be fired repeatedly to update the balance @@ -556,6 +556,11 @@ bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const return wallet->GetPubKey(address, vchPubKeyOut); } +bool WalletModel::havePrivKey(const CKeyID &address) const +{ + return wallet->HaveKey(address); +} + // returns a list of COutputs from COutPoints void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs) { diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 40bc623543..a5e877d81f 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -17,6 +17,7 @@ class AddressTableModel; class OptionsModel; +class PlatformStyle; class RecentRequestsTableModel; class TransactionTableModel; class WalletModelTransaction; @@ -100,7 +101,7 @@ class WalletModel : public QObject Q_OBJECT public: - explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0); + explicit WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0); ~WalletModel(); enum StatusCode // Returned by sendCoins @@ -186,6 +187,7 @@ public: UnlockContext requestUnlock(); bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + bool havePrivKey(const CKeyID &address) const; void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs); bool isSpent(const COutPoint& outpoint) const; void listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index c5f556b444..fa96f62e03 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -11,8 +11,8 @@ #include "guiutil.h" #include "optionsmodel.h" #include "overviewpage.h" +#include "platformstyle.h" #include "receivecoinsdialog.h" -#include "scicon.h" #include "sendcoinsdialog.h" #include "signverifymessagedialog.h" #include "transactiontablemodel.h" @@ -29,31 +29,32 @@ #include <QPushButton> #include <QVBoxLayout> -WalletView::WalletView(QWidget *parent): +WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent): QStackedWidget(parent), clientModel(0), - walletModel(0) + walletModel(0), + platformStyle(platformStyle) { // Create tabs - overviewPage = new OverviewPage(); + overviewPage = new OverviewPage(platformStyle); transactionsPage = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); QHBoxLayout *hbox_buttons = new QHBoxLayout(); - transactionView = new TransactionView(this); + transactionView = new TransactionView(platformStyle, this); vbox->addWidget(transactionView); QPushButton *exportButton = new QPushButton(tr("&Export"), this); exportButton->setToolTip(tr("Export the data in the current tab to a file")); -#ifndef Q_OS_MAC // Icons on push buttons are very uncommon on Mac - exportButton->setIcon(SingleColorIcon(":/icons/export")); -#endif + if (platformStyle->getImagesOnButtons()) { + exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export")); + } hbox_buttons->addStretch(); hbox_buttons->addWidget(exportButton); vbox->addLayout(hbox_buttons); transactionsPage->setLayout(vbox); - receiveCoinsPage = new ReceiveCoinsDialog(); - sendCoinsPage = new SendCoinsDialog(); + receiveCoinsPage = new ReceiveCoinsDialog(platformStyle); + sendCoinsPage = new SendCoinsDialog(platformStyle); addWidget(overviewPage); addWidget(transactionsPage); @@ -182,7 +183,7 @@ void WalletView::gotoSendCoinsPage(QString addr) void WalletView::gotoSignMessageTab(QString addr) { // calls show() in showTab_SM() - SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(this); + SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this); signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose); signVerifyMessageDialog->setModel(walletModel); signVerifyMessageDialog->showTab_SM(true); @@ -194,7 +195,7 @@ void WalletView::gotoSignMessageTab(QString addr) void WalletView::gotoVerifyMessageTab(QString addr) { // calls show() in showTab_VM() - SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(this); + SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this); signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose); signVerifyMessageDialog->setModel(walletModel); signVerifyMessageDialog->showTab_VM(true); @@ -272,7 +273,7 @@ void WalletView::usedSendingAddresses() { if(!walletModel) return; - AddressBookPage *dlg = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab, this); + AddressBookPage *dlg = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::SendingTab, this); dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setModel(walletModel->getAddressTableModel()); dlg->show(); @@ -282,7 +283,7 @@ void WalletView::usedReceivingAddresses() { if(!walletModel) return; - AddressBookPage *dlg = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this); + AddressBookPage *dlg = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this); dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setModel(walletModel->getAddressTableModel()); dlg->show(); diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 87c5d7bfbf..f97cf1ee80 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -12,6 +12,7 @@ class BitcoinGUI; class ClientModel; class OverviewPage; +class PlatformStyle; class ReceiveCoinsDialog; class SendCoinsDialog; class SendCoinsRecipient; @@ -34,7 +35,7 @@ class WalletView : public QStackedWidget Q_OBJECT public: - explicit WalletView(QWidget *parent); + explicit WalletView(const PlatformStyle *platformStyle, QWidget *parent); ~WalletView(); void setBitcoinGUI(BitcoinGUI *gui); @@ -64,6 +65,7 @@ private: TransactionView *transactionView; QProgressDialog *progressDialog; + const PlatformStyle *platformStyle; public Q_SLOTS: /** Switch to overview (home) page */ diff --git a/src/rest.cpp b/src/rest.cpp index 0dd238b683..74d27e73bb 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -65,6 +65,8 @@ public: extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); +extern UniValue mempoolInfoToJSON(); +extern UniValue mempoolToJSON(bool fVerbose = false); extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); extern UniValue blockheaderToJSON(const CBlockIndex* blockindex); @@ -293,6 +295,58 @@ static bool rest_chaininfo(AcceptedConnection* conn, return true; // continue to process further HTTP reqs on this cxn } +static bool rest_mempool_info(AcceptedConnection* conn, + const std::string& strURIPart, + const std::string& strRequest, + const std::map<std::string, std::string>& mapHeaders, + bool fRun) +{ + vector<string> params; + const RetFormat rf = ParseDataFormat(params, strURIPart); + + switch (rf) { + case RF_JSON: { + UniValue mempoolInfoObject = mempoolInfoToJSON(); + + string strJSON = mempoolInfoObject.write() + "\n"; + conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; + return true; + } + default: { + throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: json)"); + } + } + + // not reached + return true; // continue to process further HTTP reqs on this cxn +} + +static bool rest_mempool_contents(AcceptedConnection* conn, + const std::string& strURIPart, + const std::string& strRequest, + const std::map<std::string, std::string>& mapHeaders, + bool fRun) +{ + vector<string> params; + const RetFormat rf = ParseDataFormat(params, strURIPart); + + switch (rf) { + case RF_JSON: { + UniValue mempoolObject = mempoolToJSON(true); + + string strJSON = mempoolObject.write() + "\n"; + conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; + return true; + } + default: { + throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: json)"); + } + } + + // not reached + return true; // continue to process further HTTP reqs on this cxn +} + static bool rest_tx(AcceptedConnection* conn, const std::string& strURIPart, const std::string& strRequest, @@ -553,6 +607,8 @@ static const struct { {"/rest/block/notxdetails/", rest_block_notxdetails}, {"/rest/block/", rest_block_extended}, {"/rest/chaininfo", rest_chaininfo}, + {"/rest/mempool/info", rest_mempool_info}, + {"/rest/mempool/contents", rest_mempool_contents}, {"/rest/headers/", rest_headers}, {"/rest/getutxos", rest_getutxos}, }; diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 5817f0ce57..e6751de96b 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "amount.h" #include "chain.h" #include "chainparams.h" #include "checkpoints.h" @@ -174,45 +175,8 @@ UniValue getdifficulty(const UniValue& params, bool fHelp) return GetDifficulty(); } - -UniValue getrawmempool(const UniValue& params, bool fHelp) +UniValue mempoolToJSON(bool fVerbose = false) { - if (fHelp || params.size() > 1) - throw runtime_error( - "getrawmempool ( verbose )\n" - "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" - "\nArguments:\n" - "1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" - "\nResult: (for verbose = false):\n" - "[ (json array of string)\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - "]\n" - "\nResult: (for verbose = true):\n" - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - " \"size\" : n, (numeric) transaction size in bytes\n" - " \"fee\" : n, (numeric) transaction fee in bitcoins\n" - " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" - " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" - " \"currentpriority\" : n, (numeric) transaction priority now\n" - " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" - " \"transactionid\", (string) parent transaction id\n" - " ... ]\n" - " }, ...\n" - "}\n" - "\nExamples\n" - + HelpExampleCli("getrawmempool", "true") - + HelpExampleRpc("getrawmempool", "true") - ); - - LOCK(cs_main); - - bool fVerbose = false; - if (params.size() > 0) - fVerbose = params[0].get_bool(); - if (fVerbose) { LOCK(mempool.cs); @@ -260,6 +224,47 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) } } +UniValue getrawmempool(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getrawmempool ( verbose )\n" + "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" + "\nArguments:\n" + "1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" + "\nResult: (for verbose = false):\n" + "[ (json array of string)\n" + " \"transactionid\" (string) The transaction id\n" + " ,...\n" + "]\n" + "\nResult: (for verbose = true):\n" + "{ (json object)\n" + " \"transactionid\" : { (json object)\n" + " \"size\" : n, (numeric) transaction size in bytes\n" + " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" + " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" + " \"height\" : n, (numeric) block height when transaction entered pool\n" + " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" + " \"currentpriority\" : n, (numeric) transaction priority now\n" + " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" + " \"transactionid\", (string) parent transaction id\n" + " ... ]\n" + " }, ...\n" + "}\n" + "\nExamples\n" + + HelpExampleCli("getrawmempool", "true") + + HelpExampleRpc("getrawmempool", "true") + ); + + LOCK(cs_main); + + bool fVerbose = false; + if (params.size() > 0) + fVerbose = params[0].get_bool(); + + return mempoolToJSON(fVerbose); +} + UniValue getblockhash(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -461,7 +466,7 @@ UniValue gettxout(const UniValue& params, bool fHelp) "{\n" " \"bestblock\" : \"hash\", (string) the block hash\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" - " \"value\" : x.xxx, (numeric) The transaction value in btc\n" + " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"code\", (string) \n" " \"hex\" : \"hex\", (string) \n" @@ -756,6 +761,16 @@ UniValue getchaintips(const UniValue& params, bool fHelp) return res; } +UniValue mempoolInfoToJSON() +{ + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("size", (int64_t) mempool.size())); + ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); + ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); + + return ret; +} + UniValue getmempoolinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -773,12 +788,7 @@ UniValue getmempoolinfo(const UniValue& params, bool fHelp) + HelpExampleRpc("getmempoolinfo", "") ); - UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("size", (int64_t) mempool.size())); - ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); - ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); - - return ret; + return mempoolInfoToJSON(); } UniValue invalidateblock(const UniValue& params, bool fHelp) diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index b41e960e8a..0c8e6d6d66 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -87,6 +87,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "lockunspent", 1 }, { "importprivkey", 2 }, { "importaddress", 2 }, + { "importaddress", 3 }, + { "importpubkey", 2 }, { "verifychain", 0 }, { "verifychain", 1 }, { "keypoolrefill", 0 }, diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index b7d4ff58fc..620a46be15 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -138,8 +138,12 @@ UniValue generate(const UniValue& params, bool fHelp) boost::shared_ptr<CReserveScript> coinbaseScript; GetMainSignals().ScriptForMining(coinbaseScript); + // If the keypool is exhausted, no script is returned at all. Catch this. + if (!coinbaseScript) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + //throw an error if no script was provided - if (!coinbaseScript->reserveScript.size()) + if (coinbaseScript->reserveScript.empty()) throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)"); { // Don't keep cs_main locked diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 82003e09b9..6d10d1ce15 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -60,8 +60,8 @@ UniValue getinfo(const UniValue& params, bool fHelp) " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in btc/kb\n" - " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in btc/kb\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n" + " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" " \"errors\": \"...\" (string) any error messages\n" "}\n" "\nExamples:\n" diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index ed903f9fd3..56ec59171d 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -423,7 +423,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) " }\n" " ,...\n" " ],\n" - " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in btc/kb\n" + " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" " \"localaddresses\": [ (array) list of local addresses\n" " {\n" " \"address\": \"xxxx\", (string) network address\n" @@ -443,8 +443,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) UniValue obj(UniValue::VOBJ); obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("subversion", - FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()))); + obj.push_back(Pair("subversion", strSubVersion)); obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); obj.push_back(Pair("timeoffset", GetTimeOffset())); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 62d2ef69ef..9eeca5b7d9 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -149,7 +149,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) " ],\n" " \"vout\" : [ (array of json objects)\n" " {\n" - " \"value\" : x.xxx, (numeric) The value in btc\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" " \"n\" : n, (numeric) index\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"asm\", (string) the asm\n" @@ -318,8 +318,9 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 2) throw runtime_error( - "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...}\n" - "\nCreate a transaction spending the given inputs and sending to the given addresses.\n" + "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...}\n" + "\nCreate a transaction spending the given inputs and creating new outputs.\n" + "Outputs can be addresses or data.\n" "Returns hex-encoded raw transaction.\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network.\n" @@ -328,23 +329,25 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) "1. \"transactions\" (string, required) A json array of json objects\n" " [\n" " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" + " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n (numeric, required) The output number\n" " }\n" " ,...\n" " ]\n" - "2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n" + "2. \"outputs\" (string, required) a json object with outputs\n" " {\n" - " \"address\": x.xxx (numeric, required) The key is the bitcoin address, the value is the btc amount\n" - " ,...\n" + " \"address\": x.xxx (numeric, required) The key is the bitcoin address, the value is the " + CURRENCY_UNIT + " amount\n" + " \"data\": \"hex\", (string, required) The key is \"data\", the value is hex encoded data\n" + " ...\n" " }\n" - "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" "\nExamples\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") ); LOCK(cs_main); @@ -375,19 +378,27 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) set<CBitcoinAddress> setAddress; vector<string> addrList = sendTo.getKeys(); BOOST_FOREACH(const string& name_, addrList) { - CBitcoinAddress address(name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); - setAddress.insert(address); + if (name_ == "data") { + std::vector<unsigned char> data = ParseHexV(sendTo[name_].getValStr(),"Data"); + + CTxOut out(0, CScript() << OP_RETURN << data); + rawTx.vout.push_back(out); + } else { + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); - CScript scriptPubKey = GetScriptForDestination(address.Get()); - CAmount nAmount = AmountFromValue(sendTo[name_]); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); + setAddress.insert(address); - CTxOut out(nAmount, scriptPubKey); - rawTx.vout.push_back(out); + CScript scriptPubKey = GetScriptForDestination(address.Get()); + CAmount nAmount = AmountFromValue(sendTo[name_]); + + CTxOut out(nAmount, scriptPubKey); + rawTx.vout.push_back(out); + } } return EncodeHexTx(rawTx); @@ -422,7 +433,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) " ],\n" " \"vout\" : [ (array of json objects)\n" " {\n" - " \"value\" : x.xxx, (numeric) The value in btc\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" " \"n\" : n, (numeric) index\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"asm\", (string) the asm\n" diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 03c123a361..9362401b1e 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -363,6 +363,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "importprivkey", &importprivkey, true }, { "wallet", "importwallet", &importwallet, true }, { "wallet", "importaddress", &importaddress, true }, + { "wallet", "importpubkey", &importpubkey, true }, { "wallet", "keypoolrefill", &keypoolrefill, true }, { "wallet", "listaccounts", &listaccounts, false }, { "wallet", "listaddressgroupings", &listaddressgroupings, false }, @@ -606,7 +607,7 @@ void StartRPCThreads() LogPrintf("No rpcpassword set - using random cookie authentication\n"); if (!GenerateAuthCookie(&strRPCUserColonPass)) { uiInterface.ThreadSafeMessageBox( - _("Error: A fatal internal error occured, see debug.log for details"), // Same message as AbortNode + _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode "", CClientUIInterface::MSG_ERROR); StartShutdown(); return; @@ -670,7 +671,7 @@ void StartRPCThreads() vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::any(), defaultPort)); vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::any(), defaultPort)); // Prefer making the socket dual IPv6/IPv4 instead of binding - // to both addresses seperately. + // to both addresses separately. bBindAny = true; } @@ -752,14 +753,14 @@ void StopRPCThreads() { acceptor->cancel(ec); if (ec) - LogPrintf("%s: Warning: %s when cancelling acceptor", __func__, ec.message()); + LogPrintf("%s: Warning: %s when cancelling acceptor\n", __func__, ec.message()); } rpc_acceptors.clear(); BOOST_FOREACH(const PAIRTYPE(std::string, boost::shared_ptr<deadline_timer>) &timer, deadlineTimers) { timer.second->cancel(ec); if (ec) - LogPrintf("%s: Warning: %s when cancelling timer", __func__, ec.message()); + LogPrintf("%s: Warning: %s when cancelling timer\n", __func__, ec.message()); } deadlineTimers.clear(); diff --git a/src/rpcserver.h b/src/rpcserver.h index 89d3980223..3a71fd510f 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -161,6 +161,7 @@ extern UniValue clearbanned(const UniValue& params, bool fHelp); extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp extern UniValue importprivkey(const UniValue& params, bool fHelp); extern UniValue importaddress(const UniValue& params, bool fHelp); +extern UniValue importpubkey(const UniValue& params, bool fHelp); extern UniValue dumpwallet(const UniValue& params, bool fHelp); extern UniValue importwallet(const UniValue& params, bool fHelp); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index d5bb588b71..06115f5619 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -6,6 +6,7 @@ #include <assert.h> #include <boost/bind.hpp> +#include <boost/thread/reverse_lock.hpp> #include <utility> CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false) @@ -65,11 +66,12 @@ void CScheduler::serviceQueue() Function f = taskQueue.begin()->second; taskQueue.erase(taskQueue.begin()); - // Unlock before calling f, so it can reschedule itself or another task - // without deadlocking: - lock.unlock(); - f(); - lock.lock(); + { + // Unlock before calling f, so it can reschedule itself or another task + // without deadlocking: + boost::reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock); + f(); + } } catch (...) { --nThreadsServicingQueue; throw; diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 0b78fdf5a8..bd5e54b33f 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1128,7 +1128,7 @@ bool TransactionSignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const { - // There are two times of nLockTime: lock-by-blockheight + // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether // nLockTime < LOCKTIME_THRESHOLD. // diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 66657127ab..1d5aac7b34 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -286,6 +286,11 @@ CScript GetScriptForDestination(const CTxDestination& dest) return script; } +CScript GetScriptForRawPubKey(const CPubKey& pubKey) +{ + return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG; +} + CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys) { CScript script; diff --git a/src/script/standard.h b/src/script/standard.h index 46ae5f9f10..9e17dac700 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -73,6 +73,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet); CScript GetScriptForDestination(const CTxDestination& dest); +CScript GetScriptForRawPubKey(const CPubKey& pubkey); CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys); #endif // BITCOIN_SCRIPT_STANDARD_H diff --git a/src/sync.h b/src/sync.h index 705647e4a5..68a9443084 100644 --- a/src/sync.h +++ b/src/sync.h @@ -16,7 +16,7 @@ //////////////////////////////////////////////// // // -// THE SIMPLE DEFINITON, EXCLUDING DEBUG CODE // +// THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE // // // //////////////////////////////////////////////// diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index b8d5606cc3..dd3c51d09b 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -162,8 +162,8 @@ BOOST_AUTO_TEST_CASE(AlertNotify) SetMockTime(11); const std::vector<unsigned char>& alertKey = Params(CBaseChainParams::MAIN).AlertKey(); - boost::filesystem::path temp = GetTempPath() / "alertnotify.txt"; - boost::filesystem::remove(temp); + boost::filesystem::path temp = GetTempPath() / + boost::filesystem::unique_path("alertnotify-%%%%.txt"); mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string(); diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 1bda8a7ea1..6b30d6aa8a 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -469,7 +469,7 @@ static std::vector<unsigned char> RandomData() BOOST_AUTO_TEST_CASE(rolling_bloom) { // last-100-entry, 1% false positive: - CRollingBloomFilter rb1(100, 0.01, 0); + CRollingBloomFilter rb1(100, 0.01); // Overfill: static const int DATASIZE=399; @@ -500,7 +500,7 @@ BOOST_AUTO_TEST_CASE(rolling_bloom) BOOST_CHECK(nHits < 175); BOOST_CHECK(rb1.contains(data[DATASIZE-1])); - rb1.clear(); + rb1.reset(); BOOST_CHECK(!rb1.contains(data[DATASIZE-1])); // Now roll through data, make sure last 100 entries @@ -527,7 +527,7 @@ BOOST_AUTO_TEST_CASE(rolling_bloom) BOOST_CHECK(nHits < 100); // last-1000-entry, 0.01% false positive: - CRollingBloomFilter rb2(1000, 0.001, 0); + CRollingBloomFilter rb2(1000, 0.001); for (int i = 0; i < DATASIZE; i++) { rb2.insert(data[i]); } diff --git a/src/test/data/bitcoin-util-test.json b/src/test/data/bitcoin-util-test.json index afd35af503..3bf80ca434 100644 --- a/src/test/data/bitcoin-util-test.json +++ b/src/test/data/bitcoin-util-test.json @@ -56,5 +56,35 @@ "sign=ALL", "outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"], "output_cmp": "txcreatesign.hex" + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0", + "outdata=4:badhexdata"], + "return_code": 1 + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0", + "outdata=badhexdata"], + "return_code": 1 + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0", + "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o", + "outdata=4:54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"], + "output_cmp": "txcreatedata1.hex" + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0", + "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o", + "outdata=54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"], + "output_cmp": "txcreatedata2.hex" } ] diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 20bdbd08a5..5cad5af7c3 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -128,7 +128,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], -["By-time locks, with argument just beyond tx nLockTime (but within numerical boundries)"], +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 NOP2 1"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], @@ -181,7 +181,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP2 1"]], "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffff7f", "P2SH,CHECKLOCKTIMEVERIFY"], -["6 byte non-minimally-encoded arguments are invalid even in their contents are valid"], +["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 NOP2 1"]], "010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 24fff575c1..9744a3c848 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -197,7 +197,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], -["By-time locks, with argument just beyond tx nLockTime (but within numerical boundries)"], +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], diff --git a/src/test/data/txcreatedata1.hex b/src/test/data/txcreatedata1.hex new file mode 100644 index 0000000000..eccc7604e6 --- /dev/null +++ b/src/test/data/txcreatedata1.hex @@ -0,0 +1 @@ +01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d71700000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000 diff --git a/src/test/data/txcreatedata2.hex b/src/test/data/txcreatedata2.hex new file mode 100644 index 0000000000..3c7644c297 --- /dev/null +++ b/src/test/data/txcreatedata2.hex @@ -0,0 +1 @@ +01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000 diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index a0c5592a95..eb61a2884d 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -60,18 +60,18 @@ BOOST_AUTO_TEST_CASE(boolarg) BOOST_CHECK(!GetBoolArg("-foo", false)); BOOST_CHECK(!GetBoolArg("-foo", true)); - ResetArgs("-foo -nofoo"); // -foo should win - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); - - ResetArgs("-foo=1 -nofoo=1"); // -foo should win - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); + ResetArgs("-foo -nofoo"); // -nofoo should win + BOOST_CHECK(!GetBoolArg("-foo", false)); + BOOST_CHECK(!GetBoolArg("-foo", true)); - ResetArgs("-foo=0 -nofoo=0"); // -foo should win + ResetArgs("-foo=1 -nofoo=1"); // -nofoo should win BOOST_CHECK(!GetBoolArg("-foo", false)); BOOST_CHECK(!GetBoolArg("-foo", true)); + ResetArgs("-foo=0 -nofoo=0"); // -nofoo=0 should win + BOOST_CHECK(GetBoolArg("-foo", false)); + BOOST_CHECK(GetBoolArg("-foo", true)); + // New 0.6 feature: treat -- same as -: ResetArgs("--foo=1"); BOOST_CHECK(GetBoolArg("-foo", false)); @@ -150,9 +150,9 @@ BOOST_AUTO_TEST_CASE(boolargno) BOOST_CHECK(GetBoolArg("-foo", true)); BOOST_CHECK(GetBoolArg("-foo", false)); - ResetArgs("-foo --nofoo"); - BOOST_CHECK(GetBoolArg("-foo", true)); - BOOST_CHECK(GetBoolArg("-foo", false)); + ResetArgs("-foo --nofoo"); // --nofoo should win + BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!GetBoolArg("-foo", false)); ResetArgs("-nofoo -foo"); // foo always wins: BOOST_CHECK(GetBoolArg("-foo", true)); diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp new file mode 100644 index 0000000000..faaddffad8 --- /dev/null +++ b/src/test/limitedmap_tests.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2012-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "limitedmap.h" + +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(limitedmap_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(limitedmap_test) +{ + // create a limitedmap capped at 10 items + limitedmap<int, int> map(10); + + // check that the max size is 10 + BOOST_CHECK(map.max_size() == 10); + + // check that it's empty + BOOST_CHECK(map.size() == 0); + + // insert (-1, -1) + map.insert(std::pair<int, int>(-1, -1)); + + // make sure that the size is updated + BOOST_CHECK(map.size() == 1); + + // make sure that the new items is in the map + BOOST_CHECK(map.count(-1) == 1); + + // insert 10 new items + for (int i = 0; i < 10; i++) { + map.insert(std::pair<int, int>(i, i + 1)); + } + + // make sure that the map now contains 10 items... + BOOST_CHECK(map.size() == 10); + + // ...and that the first item has been discarded + BOOST_CHECK(map.count(-1) == 0); + + // iterate over the map, both with an index and an iterator + limitedmap<int, int>::const_iterator it = map.begin(); + for (int i = 0; i < 10; i++) { + // make sure the item is present + BOOST_CHECK(map.count(i) == 1); + + // use the iterator to check for the expected key adn value + BOOST_CHECK(it->first == i); + BOOST_CHECK(it->second == i + 1); + + // use find to check for the value + BOOST_CHECK(map.find(i)->second == i + 1); + + // update and recheck + map.update(it, i + 2); + BOOST_CHECK(map.find(i)->second == i + 2); + + it++; + } + + // check that we've exhausted the iterator + BOOST_CHECK(it == map.end()); + + // resize the map to 5 items + map.max_size(5); + + // check that the max size and size are now 5 + BOOST_CHECK(map.max_size() == 5); + BOOST_CHECK(map.size() == 5); + + // check that items less than 5 have been discarded + // and items greater than 5 are retained + for (int i = 0; i < 10; i++) { + if (i < 5) { + BOOST_CHECK(map.count(i) == 0); + } else { + BOOST_CHECK(map.count(i) == 1); + } + } + + // erase some items not in the map + for (int i = 100; i < 1000; i += 100) { + map.erase(i); + } + + // check that the size is unaffected + BOOST_CHECK(map.size() == 5); + + // erase the remaining elements + for (int i = 5; i < 10; i++) { + map.erase(i); + } + + // check that the map is now empty + BOOST_CHECK(map.empty()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 7154476c7c..3f99dc98d9 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -7,6 +7,7 @@ #include <string> +#include <boost/assign/list_of.hpp> #include <boost/test/unit_test.hpp> using namespace std; @@ -156,4 +157,20 @@ BOOST_AUTO_TEST_CASE(subnet_test) BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); } +BOOST_AUTO_TEST_CASE(netbase_getgroup) +{ + BOOST_CHECK(CNetAddr("127.0.0.1").GetGroup() == boost::assign::list_of(0)); // Local -> !Routable() + BOOST_CHECK(CNetAddr("257.0.0.1").GetGroup() == boost::assign::list_of(0)); // !Valid -> !Routable() + BOOST_CHECK(CNetAddr("10.0.0.1").GetGroup() == boost::assign::list_of(0)); // RFC1918 -> !Routable() + BOOST_CHECK(CNetAddr("169.254.1.1").GetGroup() == boost::assign::list_of(0)); // RFC3927 -> !Routable() + BOOST_CHECK(CNetAddr("1.2.3.4").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // IPv4 + BOOST_CHECK(CNetAddr("::FFFF:0:102:304").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC6145 + BOOST_CHECK(CNetAddr("64:FF9B::102:304").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC6052 + BOOST_CHECK(CNetAddr("2002:102:304:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC3964 + BOOST_CHECK(CNetAddr("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC4380 + BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == boost::assign::list_of((unsigned char)NET_TOR)(239)); // Tor + BOOST_CHECK(CNetAddr("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV6)(32)(1)(4)(112)(175)); //he.net + BOOST_CHECK(CNetAddr("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV6)(32)(1)(32)(1)); //IPv6 +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 7946b02855..a65572e6f6 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -110,6 +110,24 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign) BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); } +BOOST_AUTO_TEST_CASE(rpc_createraw_op_return) +{ + BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"68656c6c6f776f726c64\"}")); + + // Allow more than one data transaction output + BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"68656c6c6f776f726c64\",\"data\":\"68656c6c6f776f726c64\"}")); + + // Key not "data" (bad address) + BOOST_CHECK_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"somedata\":\"68656c6c6f776f726c64\"}"), runtime_error); + + // Bad hex encoding of data output + BOOST_CHECK_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"12345\"}"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"12345g\"}"), runtime_error); + + // Data 81 bytes long + BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081\"}")); +} + BOOST_AUTO_TEST_CASE(rpc_format_monetary_values) { BOOST_CHECK(ValueFromAmount(0LL).write() == "0.00000000"); diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index 9368963ff2..52f41be8ae 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -27,8 +27,6 @@ BOOST_FIXTURE_TEST_SUITE(rpc_wallet_tests, TestingSetup) BOOST_AUTO_TEST_CASE(rpc_addmultisig) { - LOCK(pwalletMain->cs_wallet); - rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; // old, 65-byte-long: @@ -68,25 +66,28 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) { // Test RPC calls for various wallet statistics UniValue r; - - LOCK2(cs_main, pwalletMain->cs_wallet); - - CPubKey demoPubkey = pwalletMain->GenerateNewKey(); - CBitcoinAddress demoAddress = CBitcoinAddress(CTxDestination(demoPubkey.GetID())); + CPubKey demoPubkey; + CBitcoinAddress demoAddress; UniValue retValue; string strAccount = "walletDemoAccount"; - string strPurpose = "receive"; - BOOST_CHECK_NO_THROW({ /*Initialize Wallet with an account */ - CWalletDB walletdb(pwalletMain->strWalletFile); - CAccount account; - account.vchPubKey = demoPubkey; - pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, strPurpose); - walletdb.WriteAccount(strAccount, account); - }); - - CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey(); - CBitcoinAddress setaccountDemoAddress = CBitcoinAddress(CTxDestination(setaccountDemoPubkey.GetID())); - + CBitcoinAddress setaccountDemoAddress; + { + LOCK(pwalletMain->cs_wallet); + + demoPubkey = pwalletMain->GenerateNewKey(); + demoAddress = CBitcoinAddress(CTxDestination(demoPubkey.GetID())); + string strPurpose = "receive"; + BOOST_CHECK_NO_THROW({ /*Initialize Wallet with an account */ + CWalletDB walletdb(pwalletMain->strWalletFile); + CAccount account; + account.vchPubKey = demoPubkey; + pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, strPurpose); + walletdb.WriteAccount(strAccount, account); + }); + + CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey(); + setaccountDemoAddress = CBitcoinAddress(CTxDestination(setaccountDemoPubkey.GetID())); + } /********************************* * setaccount *********************************/ diff --git a/src/timedata.cpp b/src/timedata.cpp index c3e9c75f6e..a14d69c116 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -40,16 +40,20 @@ static int64_t abs64(int64_t n) return (n >= 0 ? n : -n); } +#define BITCOIN_TIMEDATA_MAX_SAMPLES 200 + void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) { LOCK(cs_nTimeOffset); // Ignore duplicates static set<CNetAddr> setKnown; + if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES) + return; if (!setKnown.insert(ip).second) return; // Add data - static CMedianFilter<int64_t> vTimeOffsets(200,0); + static CMedianFilter<int64_t> vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0); vTimeOffsets.input(nOffsetSample); LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 5bc06e5056..c921dae45d 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -348,7 +348,7 @@ CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const minerPolicyEstimator->Write(fileout); } catch (const std::exception&) { - LogPrintf("CTxMemPool::WriteFeeEstimates(): unable to write policy estimator data (non-fatal)"); + LogPrintf("CTxMemPool::WriteFeeEstimates(): unable to write policy estimator data (non-fatal)\n"); return false; } return true; @@ -367,7 +367,7 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) minerPolicyEstimator->Read(filein); } catch (const std::exception&) { - LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy estimator data (non-fatal)"); + LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy estimator data (non-fatal)\n"); return false; } return true; diff --git a/src/util.cpp b/src/util.cpp index 37d52037c0..a7ec740de8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -315,18 +315,21 @@ int LogPrintStr(const std::string &str) return ret; } -static void InterpretNegativeSetting(string name, map<string, string>& mapSettingsRet) +/** Interpret string as boolean, for argument parsing */ +static bool InterpretBool(const std::string& strValue) { - // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set - if (name.find("-no") == 0) + if (strValue.empty()) + return true; + return (atoi(strValue) != 0); +} + +/** Turn -noX into -X=0 */ +static void InterpretNegativeSetting(std::string& strKey, std::string& strValue) +{ + if (strKey.length()>3 && strKey[0]=='-' && strKey[1]=='n' && strKey[2]=='o') { - std::string positive("-"); - positive.append(name.begin()+3, name.end()); - if (mapSettingsRet.count(positive) == 0) - { - bool value = !GetBoolArg(name, false); - mapSettingsRet[positive] = (value ? "1" : "0"); - } + strKey = "-" + strKey.substr(3); + strValue = InterpretBool(strValue) ? "0" : "1"; } } @@ -358,17 +361,11 @@ void ParseParameters(int argc, const char* const argv[]) // If both --foo and -foo are set, the last takes effect. if (str.length() > 1 && str[1] == '-') str = str.substr(1); + InterpretNegativeSetting(str, strValue); mapArgs[str] = strValue; mapMultiArgs[str].push_back(strValue); } - - // New 0.6 features: - BOOST_FOREACH(const PAIRTYPE(string,string)& entry, mapArgs) - { - // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set - InterpretNegativeSetting(entry.first, mapArgs); - } } std::string GetArg(const std::string& strArg, const std::string& strDefault) @@ -388,11 +385,7 @@ int64_t GetArg(const std::string& strArg, int64_t nDefault) bool GetBoolArg(const std::string& strArg, bool fDefault) { if (mapArgs.count(strArg)) - { - if (mapArgs[strArg].empty()) - return true; - return (atoi(mapArgs[strArg]) != 0); - } + return InterpretBool(mapArgs[strArg]); return fDefault; } @@ -543,13 +536,11 @@ void ReadConfigFile(map<string, string>& mapSettingsRet, { // Don't overwrite existing settings so command line settings override bitcoin.conf string strKey = string("-") + it->string_key; + string strValue = it->value[0]; + InterpretNegativeSetting(strKey, strValue); if (mapSettingsRet.count(strKey) == 0) - { - mapSettingsRet[strKey] = it->value[0]; - // interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set) - InterpretNegativeSetting(strKey, mapSettingsRet); - } - mapMultiSettingsRet[strKey].push_back(it->value[0]); + mapSettingsRet[strKey] = strValue; + mapMultiSettingsRet[strKey].push_back(strValue); } // If datadir is changed in .conf file: ClearDatadirCache(); diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index c7f7e21679..c86ad9758e 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -186,7 +186,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) } if (keyPass && keyFail) { - LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all."); + LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n"); assert(false); } if (keyFail || !keyPass) @@ -255,7 +255,7 @@ bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) co { LOCK(cs_KeyStore); if (!IsCrypted()) - return CKeyStore::GetPubKey(address, vchPubKeyOut); + return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); if (mi != mapCryptedKeys.end()) @@ -263,6 +263,8 @@ bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) co vchPubKeyOut = (*mi).second.first; return true; } + // Check for watch-only pubkeys + return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); } return false; } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index dbe36a2be1..8d557979c0 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -149,46 +149,124 @@ UniValue importprivkey(const UniValue& params, bool fHelp) return NullUniValue; } +void ImportAddress(const CBitcoinAddress& address, const string& strLabel); +void ImportScript(const CScript& script, const string& strLabel, bool isRedeemScript) +{ + if (!isRedeemScript && ::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE) + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); + + pwalletMain->MarkDirty(); + + if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); + + if (isRedeemScript) { + if (!pwalletMain->HaveCScript(script) && !pwalletMain->AddCScript(script)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); + ImportAddress(CBitcoinAddress(CScriptID(script)), strLabel); + } +} + +void ImportAddress(const CBitcoinAddress& address, const string& strLabel) +{ + CScript script = GetScriptForDestination(address.Get()); + ImportScript(script, strLabel, false); + // add to address book or update label + if (address.IsValid()) + pwalletMain->SetAddressBook(address.Get(), strLabel, "receive"); +} + UniValue importaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 3) + if (fHelp || params.size() < 1 || params.size() > 4) throw runtime_error( - "importaddress \"address\" ( \"label\" rescan )\n" - "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n" + "importaddress \"address\" ( \"label\" rescan p2sh )\n" + "\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend.\n" "\nArguments:\n" - "1. \"address\" (string, required) The address\n" + "1. \"script\" (string, required) The hex-encoded script (or address)\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" + "4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n" "\nNote: This call can take minutes to complete if rescan is true.\n" + "If you have the full public key, you should call importpublickey instead of this.\n" "\nExamples:\n" - "\nImport an address with rescan\n" - + HelpExampleCli("importaddress", "\"myaddress\"") + + "\nImport a script with rescan\n" + + HelpExampleCli("importaddress", "\"myscript\"") + "\nImport using a label without rescan\n" - + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") + + + HelpExampleCli("importaddress", "\"myscript\" \"testing\" false") + "\nAs a JSON-RPC call\n" - + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false") + + HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false") ); if (fPruneMode) throw JSONRPCError(RPC_WALLET_ERROR, "Importing addresses is disabled in pruned mode"); - LOCK2(cs_main, pwalletMain->cs_wallet); + string strLabel = ""; + if (params.size() > 1) + strLabel = params[1].get_str(); + + // Whether to perform rescan after import + bool fRescan = true; + if (params.size() > 2) + fRescan = params[2].get_bool(); - CScript script; + // Whether to import a p2sh version, too + bool fP2SH = false; + if (params.size() > 3) + fP2SH = params[3].get_bool(); + + LOCK2(cs_main, pwalletMain->cs_wallet); CBitcoinAddress address(params[0].get_str()); if (address.IsValid()) { - script = GetScriptForDestination(address.Get()); + if (fP2SH) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead"); + ImportAddress(address, strLabel); } else if (IsHex(params[0].get_str())) { std::vector<unsigned char> data(ParseHex(params[0].get_str())); - script = CScript(data.begin(), data.end()); + ImportScript(CScript(data.begin(), data.end()), strLabel, fP2SH); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script"); } + if (fRescan) + { + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); + pwalletMain->ReacceptWalletTransactions(); + } + + return NullUniValue; +} + +UniValue importpubkey(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() < 1 || params.size() > 4) + throw runtime_error( + "importpubkey \"pubkey\" ( \"label\" rescan )\n" + "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n" + "\nArguments:\n" + "1. \"pubkey\" (string, required) The hex-encoded public key\n" + "2. \"label\" (string, optional, default=\"\") An optional label\n" + "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" + "\nNote: This call can take minutes to complete if rescan is true.\n" + "\nExamples:\n" + "\nImport a public key with rescan\n" + + HelpExampleCli("importpubkey", "\"mypubkey\"") + + "\nImport using a label without rescan\n" + + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") + + "\nAs a JSON-RPC call\n" + + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false") + ); + + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Importing public keys is disabled in pruned mode"); + string strLabel = ""; if (params.size() > 1) strLabel = params[1].get_str(); @@ -198,33 +276,28 @@ UniValue importaddress(const UniValue& params, bool fHelp) if (params.size() > 2) fRescan = params[2].get_bool(); - { - if (::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE) - throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); - - // add to address book or update label - if (address.IsValid()) - pwalletMain->SetAddressBook(address.Get(), strLabel, "receive"); - - // Don't throw error in case an address is already there - if (pwalletMain->HaveWatchOnly(script)) - return NullUniValue; + if (!IsHex(params[0].get_str())) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string"); + std::vector<unsigned char> data(ParseHex(params[0].get_str())); + CPubKey pubKey(data.begin(), data.end()); + if (!pubKey.IsFullyValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); - pwalletMain->MarkDirty(); + LOCK2(cs_main, pwalletMain->cs_wallet); - if (!pwalletMain->AddWatchOnly(script)) - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); + ImportAddress(CBitcoinAddress(pubKey.GetID()), strLabel); + ImportScript(GetScriptForRawPubKey(pubKey), strLabel, false); - if (fRescan) - { - pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); - pwalletMain->ReacceptWalletTransactions(); - } + if (fRescan) + { + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); + pwalletMain->ReacceptWalletTransactions(); } return NullUniValue; } + UniValue importwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 342ce13af1..5d182f3d42 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -390,7 +390,7 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) + HelpRequiringPassphrase() + "\nArguments:\n" "1. \"bitcoinaddress\" (string, required) The bitcoin address to send to.\n" - "2. \"amount\" (numeric, required) The amount in btc to send. eg 0.1\n" + "2. \"amount\" (numeric, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" " This is not part of the transaction, just kept in your wallet.\n" "4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n" @@ -452,7 +452,7 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp) " [\n" " [\n" " \"bitcoinaddress\", (string) The bitcoin address\n" - " amount, (numeric) The amount in btc\n" + " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"account\" (string, optional) The account (DEPRECATED)\n" " ]\n" " ,...\n" @@ -556,7 +556,7 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) "1. \"bitcoinaddress\" (string, required) The bitcoin address for transactions.\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" "\nResult:\n" - "amount (numeric) The total amount in btc received at this address.\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" "\nExamples:\n" "\nThe amount from transactions with at least 1 confirmation\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") + @@ -614,7 +614,7 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) "1. \"account\" (string, required) The selected account, may be the default account using \"\".\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" "\nResult:\n" - "amount (numeric) The total amount in btc received for this account.\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" "\nExamples:\n" "\nAmount received by the default account with at least 1 confirmation\n" + HelpExampleCli("getreceivedbyaccount", "\"\"") + @@ -707,7 +707,7 @@ UniValue getbalance(const UniValue& params, bool fHelp) "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" "3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n" "\nResult:\n" - "amount (numeric) The total amount in btc received for this account.\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" "\nExamples:\n" "\nThe total amount in the wallet\n" + HelpExampleCli("getbalance", "") + @@ -793,14 +793,15 @@ UniValue movecmd(const UniValue& params, bool fHelp) "\nArguments:\n" "1. \"fromaccount\" (string, required) The name of the account to move funds from. May be the default account using \"\".\n" "2. \"toaccount\" (string, required) The name of the account to move funds to. May be the default account using \"\".\n" - "3. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" - "4. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" + "3. amount (numeric) Quantity of " + CURRENCY_UNIT + " to move between accounts.\n" + "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" + "5. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" "\nResult:\n" - "true|false (boolean) true if successfull.\n" + "true|false (boolean) true if successful.\n" "\nExamples:\n" - "\nMove 0.01 btc from the default account to the account named tabby\n" + "\nMove 0.01 " + CURRENCY_UNIT + " from the default account to the account named tabby\n" + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + - "\nMove 0.01 btc timotei to akiko with a comment and funds have 6 confirmations\n" + "\nMove 0.01 " + CURRENCY_UNIT + " timotei to akiko with a comment and funds have 6 confirmations\n" + HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") + "\nAs a json rpc call\n" + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") @@ -867,7 +868,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp) "\nArguments:\n" "1. \"fromaccount\" (string, required) The name of the account to send funds from. May be the default account using \"\".\n" "2. \"tobitcoinaddress\" (string, required) The bitcoin address to send funds to.\n" - "3. amount (numeric, required) The amount in btc. (transaction fee is added on top).\n" + "3. amount (numeric, required) The amount in " + CURRENCY_UNIT + " (transaction fee is added on top).\n" "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" " This is not part of the transaction, just kept in your wallet.\n" @@ -877,7 +878,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp) "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" "\nExamples:\n" - "\nSend 0.01 btc from the default account to the address, must have at least 1 confirmation\n" + "\nSend 0.01 " + CURRENCY_UNIT + " from the default account to the address, must have at least 1 confirmation\n" + HelpExampleCli("sendfrom", "\"\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") + "\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n" + HelpExampleCli("sendfrom", "\"tabby\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") + @@ -932,7 +933,7 @@ UniValue sendmany(const UniValue& params, bool fHelp) "1. \"fromaccount\" (string, required) DEPRECATED. The account to send the funds from. Should be \"\" for the default account\n" "2. \"amounts\" (string, required) A json object with addresses and amounts\n" " {\n" - " \"address\":amount (numeric) The bitcoin address is the key, the numeric amount in btc is the value\n" + " \"address\":amount (numeric) The bitcoin address is the key, the numeric amount in " + CURRENCY_UNIT + " is the value\n" " ,...\n" " }\n" "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" @@ -1233,7 +1234,7 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" " \"address\" : \"receivingaddress\", (string) The receiving address\n" " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" - " \"amount\" : x.xxx, (numeric) The total amount in btc received by the address\n" + " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" " }\n" " ,...\n" @@ -1405,11 +1406,11 @@ UniValue listtransactions(const UniValue& params, bool fHelp) " transaction between accounts, and not associated with an address,\n" " transaction id or block. 'send' and 'receive' transactions are \n" " associated with an address, transaction id and block details\n" - " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the\n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n" " 'move' category for moves outbound. It is positive for the 'receive' category,\n" " and for the 'move' category for inbound funds.\n" " \"vout\" : n, (numeric) the vout value\n" - " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the \n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" " 'send' category of transactions.\n" " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" " 'receive' category of transactions.\n" @@ -1600,10 +1601,10 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n" " \"address\":\"bitcoinaddress\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n" " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" - " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the 'move' category for moves \n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n" " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" " \"vout\" : n, (numeric) the vout value\n" - " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the 'send' category of transactions.\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n" " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n" @@ -1686,7 +1687,7 @@ UniValue gettransaction(const UniValue& params, bool fHelp) "2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n" "\nResult:\n" "{\n" - " \"amount\" : x.xxx, (numeric) The transaction amount in btc\n" + " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" " \"blockhash\" : \"hash\", (string) The block hash\n" " \"blockindex\" : xx, (numeric) The block index\n" @@ -1699,7 +1700,7 @@ UniValue gettransaction(const UniValue& params, bool fHelp) " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" " \"address\" : \"bitcoinaddress\", (string) The bitcoin address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" - " \"amount\" : x.xxx (numeric) The amount in btc\n" + " \"amount\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"vout\" : n, (numeric) the vout value\n" " }\n" " ,...\n" @@ -2165,7 +2166,7 @@ UniValue settxfee(const UniValue& params, bool fHelp) "settxfee amount\n" "\nSet the transaction fee per kB.\n" "\nArguments:\n" - "1. amount (numeric, required) The transaction fee in BTC/kB rounded to the nearest 0.00000001\n" + "1. amount (numeric, required) The transaction fee in " + CURRENCY_UNIT + "/kB rounded to the nearest 0.00000001\n" "\nResult\n" "true|false (boolean) Returns true if successful\n" "\nExamples:\n" @@ -2194,14 +2195,14 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total confirmed bitcoin balance of the wallet\n" - " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed bitcoin balance of the wallet\n" - " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet\n" + " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n" " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in btc/kb\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") @@ -2278,7 +2279,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) " \"address\" : \"address\", (string) the bitcoin address\n" " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" " \"scriptPubKey\" : \"key\", (string) the script key\n" - " \"amount\" : x.xxx, (numeric) the transaction amount in btc\n" + " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n (numeric) The number of confirmations\n" " }\n" " ,...\n" @@ -2367,15 +2368,20 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "fundrawtransaction \"hexstring\"\n" + "fundrawtransaction \"hexstring\" includeWatching\n" "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" "This will not modify existing inputs, and will add one change output to the outputs.\n" "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" "The inputs added will not be signed, use signrawtransaction for that.\n" + "Note that all existing inputs must have their previous output transaction be in the wallet.\n" + "Note that all inputs selected must be of standard form and P2SH scripts must be" + "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n" + "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n" "\nArguments:\n" - "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" + "2. includeWatching (boolean, optional, default false) Also select inputs which are watch only\n" "\nResult:\n" "{\n" " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" @@ -2394,18 +2400,22 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") ); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); // parse hex string from parameter CTransaction origTx; if (!DecodeHexTx(origTx, params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + bool includeWatching = false; + if (params.size() > 1) + includeWatching = true; + CMutableTransaction tx(origTx); CAmount nFee; string strFailReason; int nChangePos = -1; - if(!pwalletMain->FundTransaction(tx, nFee, nChangePos, strFailReason)) + if(!pwalletMain->FundTransaction(tx, nFee, nChangePos, strFailReason, includeWatching)) throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); UniValue result(UniValue::VOBJ); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 58b9ccc078..c3b1172201 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -114,6 +114,9 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) script = GetScriptForDestination(pubkey.GetID()); if (HaveWatchOnly(script)) RemoveWatchOnly(script); + script = GetScriptForRawPubKey(pubkey); + if (HaveWatchOnly(script)) + RemoveWatchOnly(script); if (!fFileBacked) return true; @@ -1527,7 +1530,9 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected((*it).first, i))) - vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO)); + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO))); } } } @@ -1743,7 +1748,7 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx* return res; } -bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason) +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason, bool includeWatching) { vector<CRecipient> vecSend; @@ -1756,6 +1761,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nC CCoinControl coinControl; coinControl.fAllowOtherInputs = true; + coinControl.fAllowWatchOnly = includeWatching; BOOST_FOREACH(const CTxIn& txin, tx.vin) coinControl.Select(txin.prevout); @@ -2109,7 +2115,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) if (!wtxNew.AcceptToMemoryPool(false)) { // This must not fail. The transaction has already been signed and recorded. - LogPrintf("CommitTransaction(): Error: Transaction not valid"); + LogPrintf("CommitTransaction(): Error: Transaction not valid\n"); return false; } wtxNew.RelayWalletTransaction(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index ae007e4673..bd30b67b09 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -627,7 +627,7 @@ public: CAmount GetWatchOnlyBalance() const; CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const; - bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason); + bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, bool includeWatching); bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); diff --git a/src/wallet/wallet_ismine.cpp b/src/wallet/wallet_ismine.cpp index 5482348e35..d27b1531e3 100644 --- a/src/wallet/wallet_ismine.cpp +++ b/src/wallet/wallet_ismine.cpp @@ -9,6 +9,7 @@ #include "keystore.h" #include "script/script.h" #include "script/standard.h" +#include "script/sign.h" #include <boost/foreach.hpp> @@ -40,7 +41,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) { if (keystore.HaveWatchOnly(scriptPubKey)) - return ISMINE_WATCH_ONLY; + return ISMINE_WATCH_UNSOLVABLE; return ISMINE_NO; } @@ -85,7 +86,10 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) } } - if (keystore.HaveWatchOnly(scriptPubKey)) - return ISMINE_WATCH_ONLY; + if (keystore.HaveWatchOnly(scriptPubKey)) { + // TODO: This could be optimized some by doing some work after the above solver + CScript scriptSig; + return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, scriptSig) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; + } return ISMINE_NO; } diff --git a/src/wallet/wallet_ismine.h b/src/wallet/wallet_ismine.h index 7846565f8d..9f45f76c6b 100644 --- a/src/wallet/wallet_ismine.h +++ b/src/wallet/wallet_ismine.h @@ -17,8 +17,12 @@ class CScript; enum isminetype { ISMINE_NO = 0, - ISMINE_WATCH_ONLY = 1, - ISMINE_SPENDABLE = 2, + //! Indicates that we dont know how to create a scriptSig that would solve this if we were given the appropriate private keys + ISMINE_WATCH_UNSOLVABLE = 1, + //! Indicates that we know how to create a scriptSig that would solve this if we were given the appropriate private keys + ISMINE_WATCH_SOLVABLE = 2, + ISMINE_WATCH_ONLY = ISMINE_WATCH_SOLVABLE | ISMINE_WATCH_UNSOLVABLE, + ISMINE_SPENDABLE = 4, ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE }; /** used for bitflags of isminetype */ |